@unified-llm/core
Version:
Unified LLM interface.
312 lines • 13.6 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AnthropicProvider = void 0;
const sdk_1 = __importDefault(require("@anthropic-ai/sdk"));
const base_provider_1 = __importDefault(require("../base-provider"));
const validation_1 = require("../../utils/validation");
// Anthropic実装
class AnthropicProvider extends base_provider_1.default {
constructor({ apiKey, model, tools }) {
super({ model, tools });
this.client = new sdk_1.default({ apiKey });
}
async chat(request) {
(0, validation_1.validateChatRequest)(request);
try {
const anthropicRequest = await this.convertToAnthropicFormat(request);
let response = await this.client.messages.create(anthropicRequest);
let messages = [...anthropicRequest.messages];
// stop_reason が 'tool_use' の場合、ツールを実行して結果を返す
while (response.stop_reason === 'tool_use' && this.tools) {
const toolUseBlocks = response.content.filter(block => block.type === 'tool_use');
const toolResults = [];
for (const toolBlock of toolUseBlocks) {
const customFunction = this.tools.find(func => func.function.name === toolBlock.name);
if (customFunction) {
try {
// CustomFunctionのargsとtool_useのinputをマージ
const mergedArgs = {
...(customFunction.args || {}),
...toolBlock.input
};
const result = await customFunction.handler(mergedArgs);
toolResults.push({
type: 'tool_result',
tool_use_id: toolBlock.id,
content: typeof result === 'string' ? result : JSON.stringify(result),
});
}
catch (error) {
toolResults.push({
type: 'tool_result',
tool_use_id: toolBlock.id,
is_error: true,
content: error instanceof Error ? error.message : 'Unknown error',
});
}
}
}
// ツール実行結果を含めて再度リクエスト
if (toolResults.length > 0) {
messages = [
...messages,
{
role: 'assistant',
content: response.content,
},
{
role: 'user',
content: toolResults,
},
];
const followUpRequest = {
...anthropicRequest,
messages: messages,
};
response = await this.client.messages.create(followUpRequest);
}
else {
// ツール結果がない場合はループを抜ける
break;
}
}
return this.convertFromAnthropicFormat(response);
}
catch (error) {
throw this.handleError(error);
}
}
async *stream(request) {
(0, validation_1.validateChatRequest)(request);
const anthropicRequest = await this.convertToAnthropicFormat(request);
const stream = await this.client.messages.create({
...anthropicRequest,
stream: true,
});
for await (const chunk of stream) {
if (chunk.type === 'content_block_delta' && chunk.delta.type === 'text_delta') {
yield this.convertStreamChunk(chunk);
}
}
}
async convertToAnthropicFormat(request) {
var _a, _b, _c, _d, _e, _f;
if (!request.model && !this.model) {
throw new Error('Model is required for Anthropic requests');
}
const systemMessage = request.messages.find(m => m.role === 'system');
const otherMessages = request.messages.filter(m => m.role !== 'system');
const messages = await Promise.all(otherMessages.map(async (msg) => {
const content = this.normalizeContent(msg.content);
const anthropicContent = await Promise.all(content.map(async (c) => {
var _a, _b;
switch (c.type) {
case 'text':
return { type: 'text', text: c.text };
case 'image':
return {
type: 'image',
source: {
type: (c.source.url ? 'url' : 'base64'),
media_type: c.source.media_type || 'image/jpeg',
data: c.source.data,
url: c.source.url,
},
};
case 'tool_use': {
// customFunctionsからツールを検索して実行
const customFunction = (_a = this.tools) === null || _a === void 0 ? void 0 : _a.find(func => func.function.name === c.name);
if (customFunction) {
try {
// CustomFunctionのargsとtool_useのinputをマージ
const mergedArgs = {
...(customFunction.args || {}),
...c.input
};
const result = await customFunction.handler(mergedArgs);
return {
type: 'tool_result',
tool_use_id: c.id,
is_error: false,
content: [{
type: 'text',
text: typeof result === 'string' ? result : JSON.stringify(result),
}],
};
}
catch (error) {
return {
type: 'tool_result',
tool_use_id: c.id,
is_error: true,
content: [{
type: 'text',
text: error instanceof Error ? error.message : 'Unknown error',
}],
};
}
}
return {
type: 'tool_use',
id: c.id,
name: c.name,
input: c.input,
};
}
case 'tool_result':
return {
type: 'tool_result',
tool_use_id: c.tool_use_id,
is_error: c.is_error,
content: ((_b = c.content) === null || _b === void 0 ? void 0 : _b.map(tc => ({
type: 'text',
text: tc.type === 'text' ? tc.text : '[Unsupported content]',
}))) || [],
};
default:
return { type: 'text', text: '[Unsupported content type]' };
}
}));
return {
role: msg.role === 'assistant' ? 'assistant' : 'user',
content: anthropicContent,
};
}));
// toolsの結合: request.toolsとcustomFunctionsを統合
const tools = [
...(((_a = request.tools) === null || _a === void 0 ? void 0 : _a.map(tool => ({
name: tool.function.name,
description: tool.function.description || '',
input_schema: {
type: 'object',
...tool.function.parameters || {},
},
}))) || []),
...(this.tools ? this.tools.map((func) => ({
name: func.function.name,
description: func.function.description || '',
input_schema: {
type: 'object',
...func.function.parameters || {},
},
})) : []),
];
return {
model: request.model || this.model,
messages: messages,
system: systemMessage ? this.extractTextFromContent(systemMessage.content) : undefined,
max_tokens: ((_b = request.generation_config) === null || _b === void 0 ? void 0 : _b.max_tokens) || 4096,
temperature: (_c = request.generation_config) === null || _c === void 0 ? void 0 : _c.temperature,
top_p: (_d = request.generation_config) === null || _d === void 0 ? void 0 : _d.top_p,
top_k: (_e = request.generation_config) === null || _e === void 0 ? void 0 : _e.top_k,
stop_sequences: (_f = request.generation_config) === null || _f === void 0 ? void 0 : _f.stop_sequences,
tools: tools.length > 0 ? tools : undefined,
};
}
convertFromAnthropicFormat(response) {
const content = response.content.map(block => {
switch (block.type) {
case 'text':
return { type: 'text', text: block.text };
case 'tool_use':
return {
type: 'tool_use',
id: block.id,
name: block.name,
input: block.input,
};
default:
return { type: 'text', text: '[Unknown content type]' };
}
});
const unifiedMessage = {
id: response.id,
role: response.role,
content,
created_at: new Date(),
};
const usage = {
input_tokens: response.usage.input_tokens,
output_tokens: response.usage.output_tokens,
total_tokens: response.usage.input_tokens + response.usage.output_tokens,
};
return {
id: response.id,
model: response.model,
provider: 'anthropic',
message: unifiedMessage,
usage,
finish_reason: response.stop_reason,
created_at: new Date(),
raw_response: response,
};
}
convertStreamChunk(chunk) {
if (!this.model) {
throw new Error('Model is required for streaming responses');
}
const content = [{
type: 'text',
text: chunk.delta.text,
}];
const unifiedMessage = {
id: this.generateMessageId(),
role: 'assistant',
content,
created_at: new Date(),
};
return {
id: this.generateMessageId(),
model: this.model,
provider: 'anthropic',
message: unifiedMessage,
created_at: new Date(),
raw_response: chunk,
};
}
extractTextFromContent(content) {
if (typeof content === 'string')
return content;
const textContent = content.find(c => c.type === 'text');
return (textContent === null || textContent === void 0 ? void 0 : textContent.text) || '';
}
handleError(error) {
var _a;
if (error instanceof sdk_1.default.APIError) {
const errorBody = (_a = error.error) === null || _a === void 0 ? void 0 : _a.error;
return {
code: (errorBody === null || errorBody === void 0 ? void 0 : errorBody.type) || 'anthropic_error',
message: (errorBody === null || errorBody === void 0 ? void 0 : errorBody.message) || error.message,
type: this.mapErrorType(error.status),
status_code: error.status,
provider: 'anthropic',
details: error,
};
}
return {
code: 'unknown_error',
message: error.message || 'Unknown error occurred',
type: 'api_error',
provider: 'anthropic',
details: error,
};
}
mapErrorType(status) {
if (!status)
return 'api_error';
if (status === 429)
return 'rate_limit';
if (status === 401)
return 'authentication';
if (status >= 400 && status < 500)
return 'invalid_request';
if (status >= 500)
return 'server_error';
return 'api_error';
}
}
exports.AnthropicProvider = AnthropicProvider;
//# sourceMappingURL=provider.js.map