@lobehub/chat
Version:
Lobe Chat - an open-source, high-performance chatbot framework that supports speech synthesis, multimodal, and extensible Function Call plugin system. Supports one-click free deployment of your private ChatGPT/LLM web application.
101 lines (84 loc) • 3.14 kB
text/typescript
import type Anthropic from '@anthropic-ai/sdk';
import debug from 'debug';
import { buildAnthropicMessages, buildAnthropicTools } from '../../core/contextBuilders/anthropic';
import { GenerateObjectOptions, GenerateObjectPayload } from '../../types';
const log = debug('lobe-model-runtime:anthropic:generate-object');
/**
* Generate structured output using Anthropic Claude API with Function Calling
*/
export const createAnthropicGenerateObject = async (
client: Anthropic,
payload: GenerateObjectPayload,
options?: GenerateObjectOptions,
) => {
const { schema, messages, model, tools } = payload;
log('generateObject called with model: %s', model);
log('schema: %O', schema);
log('messages count: %d', messages.length);
// Convert messages to Anthropic format
const system_message = messages.find((m) => m.role === 'system')?.content;
const user_messages = messages.filter((m) => m.role !== 'system');
const anthropicMessages = await buildAnthropicMessages(user_messages);
log('converted %d messages to Anthropic format', anthropicMessages.length);
const systemPrompts = system_message
? [
{
text: system_message,
type: 'text' as const,
},
]
: undefined;
let finalTools;
let tool_choice: Anthropic.ToolChoiceAny | Anthropic.ToolChoiceTool;
if (tools) {
finalTools = buildAnthropicTools(tools);
tool_choice = { type: 'any' };
} else if (schema) {
// Convert OpenAI-style schema to Anthropic tool format
const tool: Anthropic.ToolUnion = {
description:
schema.description || 'Generate structured output according to the provided schema',
input_schema: schema.schema as any,
name: schema.name || 'structured_output',
};
log('converted tool: %O', tool);
finalTools = [tool];
tool_choice = { name: tool.name, type: 'tool' };
} else {
throw new Error('tools or schema is required');
}
try {
log('calling Anthropic API with max_tokens: %d', 8192);
const response = await client.messages.create(
{
max_tokens: 8192,
messages: anthropicMessages,
model,
system: systemPrompts,
tool_choice: tool_choice,
tools: finalTools,
},
{ signal: options?.signal },
);
log('received response with %d content blocks', response.content.length);
log('response: %O', response);
// Extract the tool use result
if (tool_choice.type === 'tool') {
const toolUseBlock = response.content.find(
(block) => block.type === 'tool_use' && block.name === tool_choice.name,
);
if (!toolUseBlock || toolUseBlock.type !== 'tool_use') {
log('no tool use found in response (expected tool: %s)', tool_choice.name);
return undefined;
}
log('extracted tool input: %O', toolUseBlock.input);
return toolUseBlock.input;
}
return response.content
.filter((block) => block.type === 'tool_use')
.map((block) => ({ arguments: block.input, name: block.name }));
} catch (error) {
log('generateObject error: %O', error);
throw error;
}
};