diff --git a/src/libs/agent-runtime/anthropic/index.ts b/src/libs/agent-runtime/anthropic/index.ts index 5fa09970f30..6467ae5ded8 100644 --- a/src/libs/agent-runtime/anthropic/index.ts +++ b/src/libs/agent-runtime/anthropic/index.ts @@ -27,21 +27,11 @@ export class LobeAnthropicAI implements LobeRuntimeAI { } async chat(payload: ChatStreamPayload, options?: ChatCompetitionOptions) { - const { messages, model, max_tokens, temperature, top_p } = payload; - const system_message = messages.find((m) => m.role === 'system'); - const user_messages = messages.filter((m) => m.role !== 'system'); - try { + const anthropicPayload = this.buildAnthropicPayload(payload); + const response = await this.client.messages.create( - { - max_tokens: max_tokens || 4096, - messages: buildAnthropicMessages(user_messages), - model: model, - stream: true, - system: system_message?.content as string, - temperature: temperature, - top_p: top_p, - }, + { ...anthropicPayload, stream: true }, { signal: options?.signal }, ); @@ -71,6 +61,15 @@ export class LobeAnthropicAI implements LobeRuntimeAI { provider: ModelProvider.Anthropic, }); } + + case 403: { + throw AgentRuntimeError.chat({ + endpoint: desensitizedEndpoint, + error: error as any, + errorType: AgentRuntimeErrorType.LocationNotSupportError, + provider: ModelProvider.Anthropic, + }); + } default: { break; } @@ -84,6 +83,22 @@ export class LobeAnthropicAI implements LobeRuntimeAI { }); } } + + private buildAnthropicPayload(payload: ChatStreamPayload) { + const { messages, model, max_tokens, temperature, top_p } = payload; + const system_message = messages.find((m) => m.role === 'system'); + const user_messages = messages.filter((m) => m.role !== 'system'); + + return { + max_tokens: max_tokens || 4096, + messages: buildAnthropicMessages(user_messages), + model: model, + stream: true, + system: system_message?.content as string, + temperature: temperature, + top_p: top_p, + }; + } } export default LobeAnthropicAI; diff --git a/src/libs/agent-runtime/utils/anthropicHelpers.test.ts b/src/libs/agent-runtime/utils/anthropicHelpers.test.ts index 37866e6b42e..8cf8d7f5e61 100644 --- a/src/libs/agent-runtime/utils/anthropicHelpers.test.ts +++ b/src/libs/agent-runtime/utils/anthropicHelpers.test.ts @@ -1,7 +1,11 @@ import { describe, expect, it } from 'vitest'; import { OpenAIChatMessage, UserMessageContentPart } from '../types/chat'; -import { buildAnthropicBlock, buildAnthropicMessage } from './anthropicHelpers'; +import { + buildAnthropicBlock, + buildAnthropicMessage, + buildAnthropicMessages, +} from './anthropicHelpers'; import { parseDataUri } from './uriParser'; describe('anthropicHelpers', () => { @@ -48,4 +52,63 @@ describe('anthropicHelpers', () => { expect(result).toEqual({ content: [{ type: 'text', text: 'Hello!' }], role: 'assistant' }); }); }); + + describe('buildAnthropicMessages', () => { + it('should correctly convert OpenAI Messages to Anthropic Messages', () => { + const messages: OpenAIChatMessage[] = [ + { content: 'Hello', role: 'user' }, + { content: 'Hi', role: 'assistant' }, + ]; + + const result = buildAnthropicMessages(messages); + expect(result).toHaveLength(2); + expect(result).toEqual([ + { content: 'Hello', role: 'user' }, + { content: 'Hi', role: 'assistant' }, + ]); + }); + + it('messages should end with user', () => { + const messages: OpenAIChatMessage[] = [ + { content: 'Hello', role: 'user' }, + { content: 'Hello', role: 'user' }, + { content: 'Hi', role: 'assistant' }, + ]; + + const contents = buildAnthropicMessages(messages); + + expect(contents).toHaveLength(4); + expect(contents).toEqual([ + { content: 'Hello', role: 'user' }, + { content: '_', role: 'assistant' }, + { content: 'Hello', role: 'user' }, + { content: 'Hi', role: 'assistant' }, + ]); + }); + + it('messages should pair', () => { + const messages: OpenAIChatMessage[] = [ + { content: 'a', role: 'assistant' }, + { content: 'b', role: 'assistant' }, + { content: 'c', role: 'assistant' }, + { content: 'd', role: 'assistant' }, + { content: '你好', role: 'user' }, + ]; + + const contents = buildAnthropicMessages(messages); + + expect(contents).toHaveLength(9); + expect(contents).toEqual([ + { content: '_', role: 'user' }, + { content: 'a', role: 'assistant' }, + { content: '_', role: 'user' }, + { content: 'b', role: 'assistant' }, + { content: '_', role: 'user' }, + { content: 'c', role: 'assistant' }, + { content: '_', role: 'user' }, + { content: 'd', role: 'assistant' }, + { content: '你好', role: 'user' }, + ]); + }); + }); }); diff --git a/src/libs/agent-runtime/utils/anthropicHelpers.ts b/src/libs/agent-runtime/utils/anthropicHelpers.ts index 89e69ece38f..9a1bab62873 100644 --- a/src/libs/agent-runtime/utils/anthropicHelpers.ts +++ b/src/libs/agent-runtime/utils/anthropicHelpers.ts @@ -37,5 +37,21 @@ export const buildAnthropicMessage = ( }; export const buildAnthropicMessages = ( - messages: OpenAIChatMessage[], -): Anthropic.Messages.MessageParam[] => messages.map((message) => buildAnthropicMessage(message)); + oaiMessages: OpenAIChatMessage[], +): Anthropic.Messages.MessageParam[] => { + const messages: Anthropic.Messages.MessageParam[] = []; + let lastRole = 'assistant'; + + oaiMessages.forEach((message) => { + const anthropicMessage = buildAnthropicMessage(message); + + if (lastRole === anthropicMessage.role) { + messages.push({ content: '_', role: lastRole === 'user' ? 'assistant' : 'user' }); + } + + lastRole = anthropicMessage.role; + messages.push(anthropicMessage); + }); + + return messages; +};