chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 13:34:48 +08:00
commit 77bb5bf71f
3762 changed files with 353249 additions and 0 deletions
+13
View File
@@ -0,0 +1,13 @@
import rootConfig from '../../eslint.config.mjs'
export default [
...rootConfig,
{
languageOptions: {
parserOptions: {
project: ['./tsconfig.json'],
tsconfigRootDir: import.meta.dirname,
},
},
},
]
+5
View File
@@ -0,0 +1,5 @@
# Anthropic Integration
This integration allows your bot to choose from a curated list of Claude models from [Anthropic](https://docs.anthropic.com/en/docs/about-claude/models) as the LLM of your choice for a node, workflow, or skill in your bot.
Usage is charged to the AI Spend of your workspace in Botpress Cloud at the [same pricing](https://www.anthropic.com/pricing) (at cost) as directly with Anthropic.
+16
View File
@@ -0,0 +1,16 @@
<svg version="1.1" id="Layer_1" xmlns:x="ns_extend;" xmlns:i="ns_ai;" xmlns:graph="ns_graphs;" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 92.2 65" style="enable-background:new 0 0 92.2 65;" xml:space="preserve">
<style type="text/css">
.st0{fill:#181818;}
</style>
<metadata>
<sfw xmlns="ns_sfw;">
<slices>
</slices>
<sliceSourceBounds bottomLeftOrigin="true" height="65" width="92.2" x="-43.7" y="-98">
</sliceSourceBounds>
</sfw>
</metadata>
<path class="st0" d="M66.5,0H52.4l25.7,65h14.1L66.5,0z M25.7,0L0,65h14.4l5.3-13.6h26.9L51.8,65h14.4L40.5,0C40.5,0,25.7,0,25.7,0z
M24.3,39.3l8.8-22.8l8.8,22.8H24.3z">
</path>
</svg>

After

Width:  |  Height:  |  Size: 714 B

@@ -0,0 +1,30 @@
import { z, IntegrationDefinition } from '@botpress/sdk'
import { ModelId } from 'src/schemas'
import llm from './bp_modules/llm'
export default new IntegrationDefinition({
name: 'anthropic',
title: 'Anthropic',
description: 'Access a curated list of Claude models to set as your chosen LLM.',
version: '18.0.1',
readme: 'hub.md',
icon: 'icon.svg',
entities: {
modelRef: {
schema: z.object({
id: ModelId,
}),
},
},
secrets: {
ANTHROPIC_API_KEY: {
description: 'Anthropic API key',
},
},
attributes: {
category: 'AI Models',
repo: 'botpress',
},
}).extend(llm, ({ entities }) => ({
entities: { modelRef: entities.modelRef },
}))
+24
View File
@@ -0,0 +1,24 @@
{
"name": "@botpresshub/anthropic",
"scripts": {
"build": "bp add -y && bp build",
"check:type": "tsc --noEmit",
"check:bplint": "bp lint",
"test": "vitest --run"
},
"private": true,
"dependencies": {
"@anthropic-ai/sdk": "^0.52.0",
"@botpress/client": "workspace:*",
"@botpress/common": "workspace:*",
"@botpress/sdk": "workspace:*"
},
"devDependencies": {
"@botpress/cli": "workspace:*",
"@botpress/sdk": "workspace:*",
"@botpresshub/llm": "workspace:*"
},
"bpDependencies": {
"llm": "../../interfaces/llm"
}
}
@@ -0,0 +1,385 @@
import Anthropic from '@anthropic-ai/sdk'
import { ToolChoiceAny, ToolChoiceTool, ToolChoiceAuto } from '@anthropic-ai/sdk/resources'
import { MessageCreateParamsNonStreaming } from '@anthropic-ai/sdk/resources/messages'
import { InvalidPayloadError } from '@botpress/client'
import { llm } from '@botpress/common'
import { z, IntegrationLogger } from '@botpress/sdk'
import assert from 'assert'
import { ReasoningModelIdReplacements, ThinkingModeBudgetTokens } from 'src'
import { ModelId } from 'src/schemas'
// Reference: https://docs.anthropic.com/en/api/errors
const AnthropicInnerErrorSchema = z.object({
type: z.literal('error'),
error: z.object({ type: z.string(), message: z.string() }),
})
export async function generateContent(
input: llm.GenerateContentInput,
anthropic: Anthropic,
logger: IntegrationLogger,
params: {
models: Record<ModelId, llm.ModelDetails>
defaultModel: ModelId
}
): Promise<llm.GenerateContentOutput> {
let modelId = (input.model?.id || params.defaultModel) as ModelId
if (modelId in ReasoningModelIdReplacements) {
const replacementModelId = ReasoningModelIdReplacements[modelId]!
if (input.reasoningEffort === undefined) {
input.reasoningEffort = 'medium'
}
// TODO: Uncomment this when we have removed the fake "reasoning" model IDs from the model list.
// logger
// .forBot()
// .warn(
// `The model "${modelId}" has been deprecated, using "${replacementModelId}" instead with a "${input.reasoningEffort}" reasoning effort`
// )
modelId = replacementModelId
input.model = { id: modelId }
}
const model = params.models[modelId]
if (!model) {
throw new InvalidPayloadError(
`Model ID "${modelId}" is not allowed, supported model IDs are: ${Object.keys(params.models).join(', ')}`
)
}
if (input.messages.length === 0 && !input.systemPrompt) {
throw new InvalidPayloadError('At least one message or a system prompt is required')
}
if (input.maxTokens && input.maxTokens > model.output.maxTokens) {
throw new InvalidPayloadError(
`maxTokens must be less than or equal to ${model.output.maxTokens} for model ID "${modelId}`
)
}
if (input.responseFormat === 'json_object') {
input.systemPrompt =
(input.systemPrompt || '') +
'\n\nYour response must always be in valid JSON format and expressed as a JSON object.'
}
if (input.messages.length === 0) {
// Anthropic requires at least one message, so we add one by default if none were provided.
input.messages.push({
type: 'text',
role: 'user',
content: 'Follow the instructions provided in the system prompt.',
})
}
const messages: Anthropic.MessageParam[] = []
for (const message of input.messages) {
messages.push(await mapToAnthropicMessage(message))
}
let response: Anthropic.Messages.Message | undefined
const request: MessageCreateParamsNonStreaming = {
model: modelId,
max_tokens: input.maxTokens ?? model.output.maxTokens,
temperature: input.temperature,
top_p: input.topP,
system: input.systemPrompt,
stop_sequences: input.stopSequences,
metadata: {
user_id: input.userId,
},
tools: mapToAnthropicTools(input),
tool_choice: mapToAnthropicToolChoice(input.toolChoice),
messages,
}
// TODO: Remove this check once all 3.x models are removed,
// see https://platform.claude.com/docs/en/about-claude/models/migration-guide#breaking-changes
if (modelId === 'claude-opus-4-7') {
// This is a stricter check for Opuse 4.7 as it does not support sampling parameters
// see https://platform.claude.com/docs/en/about-claude/models/migration-guide#additional-breaking-changes
request.top_k = undefined
request.top_p = undefined
request.temperature = undefined
} else if (
(modelId === 'claude-sonnet-4-5-20250929' ||
modelId === 'claude-haiku-4-5-20251001' ||
modelId === 'claude-sonnet-4-6' ||
modelId === 'claude-opus-4-6') &&
request.temperature !== undefined &&
request.top_p !== undefined
) {
request.temperature = undefined
}
const thinkingBudgetTokens = ThinkingModeBudgetTokens[input.reasoningEffort ?? 'none'] // Default to not use reasoning as Claude models use optional reasoning
if (thinkingBudgetTokens) {
// Claude requires a non-zero thinking budget when thinking mode is enabled.
request.thinking = {
// Reference: https://docs.anthropic.com/en/docs/build-with-claude/extended-thinking
type: 'enabled',
budget_tokens: thinkingBudgetTokens,
}
// IMPORTANT: Thinking mode requires the max tokens to be greater than the thinking budget tokens, and we assume here that the max tokens indicated in the action input don't take into account the thinking budget tokens.
request.max_tokens = request.thinking.budget_tokens + request.max_tokens
// Note: These params aren't supported in thinking mode, see: https://docs.anthropic.com/en/docs/build-with-claude/extended-thinking#important-considerations-when-using-extended-thinking
request.temperature = undefined
request.top_k = undefined
request.top_p = undefined
}
if (request.max_tokens > model.output.maxTokens) {
request.max_tokens = model.output.maxTokens
logger
.forBot()
.warn(
`Received maxTokens parameter greater than the maximum output tokens allowed for model "${modelId}", capped parameter value to ${request.max_tokens}`
)
}
if (input.debug) {
logger.forBot().info('Request being sent to Anthropic: ' + JSON.stringify(request, null, 2))
}
try {
response = await anthropic.messages.create(request)
} catch (err: any) {
if (err instanceof Anthropic.APIError) {
const parsedInnerError = AnthropicInnerErrorSchema.safeParse(err.error)
if (parsedInnerError.success) {
throw llm.createUpstreamProviderFailedError(
err,
`Anthropic error ${err.status} (${parsedInnerError.data.error.type}) - ${parsedInnerError.data.error.message}`
)
}
}
throw llm.createUpstreamProviderFailedError(err)
} finally {
if (input.debug && response) {
logger.forBot().info('Response received from Anthropic: ' + JSON.stringify(response, null, 2))
}
}
const { input_tokens: inputTokens, output_tokens: outputTokens } = response.usage
const inputCost = calculateTokenCost(model.input.costPer1MTokens, inputTokens)
const outputCost = calculateTokenCost(model.output.costPer1MTokens, outputTokens)
const cost = inputCost + outputCost
const content = response.content
.filter((x): x is Anthropic.TextBlock => x.type === 'text') // Claude models only return "text", "thinking", or "tool_use" blocks at the moment.
.map((content) => content.text)
.join('\n\n')
return {
id: response.id,
provider: 'anthropic',
model: response.model,
choices: [
// Claude models don't support multiple "choices" or completions, they only return one.
{
role: response.role,
type: 'multipart',
index: 0,
stopReason: mapToStopReason(response.stop_reason),
toolCalls: mapToToolCalls(response),
content,
},
],
usage: {
inputTokens,
inputCost,
outputTokens,
outputCost,
},
botpress: {
cost, // DEPRECATED
},
}
}
function calculateTokenCost(costPer1MTokens: number, tokenCount: number) {
return (costPer1MTokens / 1_000_000) * tokenCount
}
async function mapToAnthropicMessage(message: llm.Message): Promise<Anthropic.MessageParam> {
return {
role: message.role,
content: await mapToAnthropicMessageContent(message),
}
}
async function mapToAnthropicMessageContent(message: llm.Message): Promise<Anthropic.MessageParam['content']> {
if (message.type === 'text') {
if (typeof message.content !== 'string') {
throw new InvalidPayloadError('`content` must be a string when message type is "text"')
}
return message.content as string
} else if (message.type === 'multipart') {
if (!Array.isArray(message.content)) {
throw new InvalidPayloadError('`content` must be an array when message type is "multipart"')
}
return await mapMultipartMessageContentToAnthropicContent(message.content)
} else if (message.type === 'tool_calls') {
if (!message.toolCalls) {
throw new InvalidPayloadError('`toolCalls` is required when message type is "tool_calls"')
} else if (message.toolCalls.length === 0) {
throw new InvalidPayloadError('`toolCalls` must contain at least one tool call')
}
return message.toolCalls.map(
(toolCall) =>
<Anthropic.ToolUseBlockParam>{
type: 'tool_use',
id: toolCall.id,
name: toolCall.function.name,
input: toolCall.function.arguments,
}
)
} else if (message.type === 'tool_result') {
return [
<Anthropic.ToolResultBlockParam>{
type: 'tool_result',
tool_use_id: message.toolResultCallId,
content: [
{
type: 'text',
text: message.content as string,
},
],
},
]
} else {
throw new InvalidPayloadError(`Message type "${message.type}" is not supported`)
}
}
async function mapMultipartMessageContentToAnthropicContent(
content: NonNullable<llm.Message['content']>
): Promise<Anthropic.MessageParam['content']> {
assert(typeof content !== 'string')
const anthropicContent: Anthropic.MessageParam['content'] = []
for (const part of content) {
if (part.type === 'text') {
anthropicContent.push(<Anthropic.TextBlockParam>{
type: 'text',
text: part.text,
})
} else if (part.type === 'image') {
if (!part.url) {
throw new InvalidPayloadError('`url` is required when part type is "image"')
}
let buffer: Buffer
try {
const response = await fetch(part.url)
buffer = Buffer.from(await response.arrayBuffer())
const contentTypeHeader = response.headers.get('content-type')
if (!part.mimeType && contentTypeHeader) {
part.mimeType = contentTypeHeader
}
} catch (err: any) {
throw new InvalidPayloadError(
`Failed to retrieve image in message content from the provided URL: ${part.url} (Error: ${err.message})`
)
}
anthropicContent.push(<Anthropic.ImageBlockParam>{
type: 'image',
source: {
type: 'base64',
media_type: part.mimeType,
data: buffer.toString('base64'),
},
})
}
}
return anthropicContent
}
function mapToAnthropicTools(input: llm.GenerateContentInput): Anthropic.Tool[] | undefined {
if (input.toolChoice?.type === 'none') {
// Don't return any tools if tool choice was to not use any tools
return []
}
const anthropicTools = input.tools?.map(
(tool) =>
<Anthropic.Tool>{
name: tool.function.name,
description: tool.function.description,
input_schema: tool.function.argumentsSchema,
}
)
// note: don't send an empty tools array
return anthropicTools?.length ? anthropicTools : undefined
}
function mapToAnthropicToolChoice(
toolChoice: llm.GenerateContentInput['toolChoice']
): Anthropic.MessageCreateParams['tool_choice'] {
if (!toolChoice) {
return undefined
}
switch (toolChoice.type) {
case 'any':
return <ToolChoiceAny>{ type: 'any' }
case 'auto':
return <ToolChoiceAuto>{ type: 'auto' }
case 'specific':
return <ToolChoiceTool>{
type: 'tool',
name: toolChoice.functionName,
}
case 'none': // This is handled when passing the tool list by removing all tools, as Anthropic doesn't support a "none" tool choice
default:
return undefined
}
}
function mapToStopReason(
anthropicStopReason: Anthropic.Message['stop_reason']
): llm.GenerateContentOutput['choices'][0]['stopReason'] {
switch (anthropicStopReason) {
case 'end_turn':
case 'stop_sequence':
return 'stop'
case 'max_tokens':
return 'max_tokens'
case 'tool_use':
return 'tool_calls'
default:
return 'other'
}
}
function mapToToolCalls(response: Anthropic.Message): llm.ToolCall[] | undefined {
const toolCalls = response.content.filter((x): x is Anthropic.ToolUseBlock => x.type === 'tool_use')
return toolCalls.map((toolCall) => {
return {
id: toolCall.id,
type: 'function',
function: {
name: toolCall.name,
arguments: toolCall.input as llm.ToolCall['function']['arguments'],
},
}
})
}
+213
View File
@@ -0,0 +1,213 @@
import Anthropic from '@anthropic-ai/sdk'
import { llm } from '@botpress/common'
import { GenerateContentInput } from '@botpress/common/src/llm'
import { generateContent } from './actions/generate-content'
import { DefaultModel, ModelId } from './schemas'
import * as bp from '.botpress'
const anthropic = new Anthropic({
apiKey: bp.secrets.ANTHROPIC_API_KEY,
timeout: 10 * 60 * 1000, // 10 minute timeout, we set it here to avoid the error thrown by the Anthropic SDK when not using streaming if the request maxTokens parameters is too high (see: https://github.com/anthropics/anthropic-sdk-typescript?tab=readme-ov-file#long-requests)
})
export type ReasoningEffort = NonNullable<GenerateContentInput['reasoningEffort']>
export const ThinkingModeBudgetTokens: Record<ReasoningEffort, number> = {
none: 0,
dynamic: 8192, // Note: Anthropic doesn't support dynamic reasoning, so we default this to the same value as "medium"
low: 2048,
medium: 8192,
high: 16_384,
// Note: we cannot go above 20K tokens for the thinking mode budget as that would require us to use streaming, see:
// https://docs.anthropic.com/en/docs/build-with-claude/extended-thinking#important-considerations-when-using-extended-thinking
}
export const ReasoningModelIdReplacements: Partial<Record<ModelId, ModelId>> = {
// These "reasoning" model IDs didn't really exist in Anthropic, we used it as a simple way for users to switch between the reasoning mode and the standard mode, but this approach has been deprecated in favor of specifying a reasoning effort in the request to activate reasoning in the model.
'claude-haiku-4-5-reasoning-20251001': 'claude-haiku-4-5-20251001',
'claude-sonnet-4-5-reasoning-20250929': 'claude-sonnet-4-5-20250929',
'claude-sonnet-4-reasoning-20250514': 'claude-sonnet-4-20250514',
}
const LanguageModels: Record<ModelId, llm.ModelDetails> = {
// Reference: https://docs.anthropic.com/en/docs/about-claude/models
// NOTE: We don't support returning "thinking" blocks from Claude in the integration action output as the concept of "thinking" blocks is a Claude-specific feature that other providers don't have. For now we won't support this as an official feature in the integration so it needs to be taken into account when using reasoning mode and passing a multi-turn conversation history in the generateContent action input.
// For more information, see: https://docs.anthropic.com/en/docs/build-with-claude/extended-thinking#preserving-thinking-blocks
// NOTE: We intentionally didn't include the Opus model as it's the most expensive model in the market, it's not very popular, and no users have ever requested it so far.
'claude-opus-4-7': {
name: 'Claude Opus 4.7',
description:
"Claude Opus 4.7 is Anthropic's most capable model to date. Building on Opus 4.6, it advances frontier coding, agentic reasoning, and enterprise workflows.",
tags: ['recommended', 'reasoning', 'agents', 'vision', 'general-purpose', 'coding'],
input: {
costPer1MTokens: 5,
maxTokens: 1_000_000,
},
output: {
costPer1MTokens: 15,
maxTokens: 128_000,
},
},
'claude-opus-4-6': {
name: 'Claude Opus 4.6',
description:
'Claude Opus 4.6 is anthropic\s most capable model to date. Building on the intelligence of Opus 4.5, it brings new levels of reliability and precision to coding, agents, and enterprise workflows.',
tags: ['recommended', 'reasoning', 'agents', 'vision', 'general-purpose', 'coding'],
input: {
costPer1MTokens: 5,
maxTokens: 1_000_000,
},
output: {
costPer1MTokens: 15,
maxTokens: 128_000,
},
},
'claude-sonnet-4-6': {
name: 'Claude Sonnet 4.6',
description:
'Claude Sonnet 4.6 offers the best combination of speed and intelligence in the Claude family. It features adaptive thinking for dynamic reasoning allocation, delivering fast responses for simple queries and deeper analysis for complex tasks.',
tags: ['recommended', 'reasoning', 'agents', 'vision', 'general-purpose', 'coding'],
input: {
costPer1MTokens: 3,
maxTokens: 200_000,
},
output: {
costPer1MTokens: 15,
maxTokens: 64_000,
},
},
'claude-haiku-4-5-20251001': {
name: 'Claude Haiku 4.5',
description:
"Claude Haiku 4.5 is Anthropic's fastest and most efficient model, delivering near-frontier intelligence at a fraction of the cost and latency of larger Claude models. Matching Claude Sonnet 4's performance across reasoning, coding, and computer-use tasks, Haiku 4.5 brings frontier-level capability to real-time and high-volume applications.",
tags: ['recommended', 'agents', 'vision', 'general-purpose', 'coding'],
input: {
costPer1MTokens: 1,
maxTokens: 200_000,
},
output: {
costPer1MTokens: 5,
maxTokens: 64_000,
},
},
'claude-haiku-4-5-reasoning-20251001': {
name: 'Claude Haiku 4.5 (Reasoning Mode)',
description:
'This model uses the "Extended Thinking" mode and will use a significantly higher amount of output tokens than the Standard Mode, so this model should only be used for tasks that actually require it.\n\nClaude Haiku 4.5 is Anthropic\'s fastest and most efficient model, delivering near-frontier intelligence at a fraction of the cost and latency of larger Claude models. Matching Claude Sonnet 4\'s performance across reasoning, coding, and computer-use tasks, Haiku 4.5 brings frontier-level capability to real-time and high-volume applications.',
tags: ['recommended', 'reasoning', 'agents', 'vision', 'general-purpose', 'coding'],
input: {
costPer1MTokens: 1,
maxTokens: 200_000,
},
output: {
costPer1MTokens: 5,
maxTokens: 64_000,
},
},
'claude-sonnet-4-5-20250929': {
name: 'Claude Sonnet 4.5',
description:
"Claude Sonnet 4.5 is Anthropic's most advanced Sonnet model to date, optimized for real-world agents and coding workflows. It delivers state-of-the-art performance on coding benchmarks, with improvements across system design, code security, and specification adherence.",
tags: ['recommended', 'agents', 'vision', 'general-purpose', 'coding'],
input: {
costPer1MTokens: 3,
maxTokens: 200_000,
},
output: {
costPer1MTokens: 15,
maxTokens: 64_000,
},
},
'claude-sonnet-4-5-reasoning-20250929': {
name: 'Claude Sonnet 4.5 (Reasoning Mode)',
description:
'This model uses the "Extended Thinking" mode and will use a significantly higher amount of output tokens than the Standard Mode, so this model should only be used for tasks that actually require it.\n\nClaude Sonnet 4.5 is Anthropic\'s most advanced Sonnet model to date, optimized for real-world agents and coding workflows. It delivers state-of-the-art performance on coding benchmarks, with improvements across system design, code security, and specification adherence.',
tags: ['recommended', 'reasoning', 'agents', 'vision', 'general-purpose', 'coding'],
input: {
costPer1MTokens: 3,
maxTokens: 200_000,
},
output: {
costPer1MTokens: 15,
maxTokens: 64_000,
},
},
'claude-sonnet-4-20250514': {
name: 'Claude Sonnet 4',
description:
'Claude Sonnet 4 significantly enhances the capabilities of its predecessor, Sonnet 3.7, excelling in both coding and reasoning tasks with improved precision and controllability. Sonnet 4 balances capability and computational efficiency, making it suitable for a broad range of applications from routine coding tasks to complex software development projects. Key enhancements include improved autonomous codebase navigation, reduced error rates in agent-driven workflows, and increased reliability in following intricate instructions.',
tags: ['agents', 'vision', 'general-purpose', 'coding'],
input: {
costPer1MTokens: 3,
maxTokens: 200_000,
},
output: {
costPer1MTokens: 15,
maxTokens: 64_000,
},
},
'claude-sonnet-4-reasoning-20250514': {
name: 'Claude Sonnet 4 (Reasoning Mode)',
description:
'This model uses the "Extended Thinking" mode and will use a significantly higher amount of output tokens than the Standard Mode, so this model should only be used for tasks that actually require it.\n\nClaude Sonnet 4 significantly enhances the capabilities of its predecessor, Sonnet 3.7, excelling in both coding and reasoning tasks with improved precision and controllability. Sonnet 4 balances capability and computational efficiency, making it suitable for a broad range of applications from routine coding tasks to complex software development projects. Key enhancements include improved autonomous codebase navigation, reduced error rates in agent-driven workflows, and increased reliability in following intricate instructions.',
tags: ['vision', 'reasoning', 'general-purpose', 'agents', 'coding'],
input: {
costPer1MTokens: 3,
maxTokens: 200_000,
},
output: {
costPer1MTokens: 15,
maxTokens: 64_000,
},
},
'claude-3-5-sonnet-20241022': {
name: 'Claude 3.5 Sonnet (October 2024)',
description:
'Claude 3.5 Sonnet delivers better-than-Opus capabilities, faster-than-Sonnet speeds, at the same Sonnet prices. Sonnet is particularly good at coding, data science, visual processing, and agentic tasks.',
tags: ['vision', 'general-purpose', 'agents', 'coding', 'function-calling', 'storytelling'],
input: {
costPer1MTokens: 3,
maxTokens: 200_000,
},
output: {
costPer1MTokens: 15,
maxTokens: 8192,
},
},
'claude-3-5-sonnet-20240620': {
name: 'Claude 3.5 Sonnet (June 2024)',
description:
'Claude 3.5 Sonnet delivers better-than-Opus capabilities, faster-than-Sonnet speeds, at the same Sonnet prices. Sonnet is particularly good at coding, data science, visual processing, and agentic tasks.',
tags: ['vision', 'general-purpose', 'agents', 'coding', 'function-calling', 'storytelling'],
input: {
costPer1MTokens: 3,
maxTokens: 200_000,
},
output: {
costPer1MTokens: 15,
maxTokens: 4096,
},
},
}
export default new bp.Integration({
register: async () => {},
unregister: async () => {},
actions: {
generateContent: async ({ input, logger, metadata }) => {
const output = await generateContent(<llm.GenerateContentInput>input, anthropic, logger, {
models: LanguageModels,
defaultModel: DefaultModel,
})
metadata.setCost(output.botpress.cost)
return output
},
listLanguageModels: async ({}) => {
return {
models: Object.entries(LanguageModels).map(([id, model]) => ({ id: <ModelId>id, ...model })),
}
},
},
channels: {},
handler: async () => {},
})
+22
View File
@@ -0,0 +1,22 @@
import { z } from '@botpress/sdk'
export type ModelId = z.infer<typeof ModelId>
export const DefaultModel: ModelId = 'claude-sonnet-4-5-20250929'
export const ModelId = z
.enum([
'claude-opus-4-7',
'claude-opus-4-6',
'claude-sonnet-4-6',
'claude-haiku-4-5-20251001',
'claude-haiku-4-5-reasoning-20251001',
'claude-sonnet-4-5-20250929',
'claude-sonnet-4-5-reasoning-20250929',
'claude-sonnet-4-20250514',
'claude-sonnet-4-reasoning-20250514',
'claude-3-5-sonnet-20241022',
'claude-3-5-sonnet-20240620',
])
.describe('Model to use for content generation')
.placeholder(DefaultModel)
+8
View File
@@ -0,0 +1,8 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"paths": { "*": ["./*"] },
"outDir": "dist"
},
"include": [".botpress/**/*", "definitions/**/*", "src/**/*", "*.ts"]
}
+2
View File
@@ -0,0 +1,2 @@
import config from '../../vitest.config'
export default config