| |
| |
| |
| |
| |
| |
| |
|
|
| import { BaseChatModel } from '@langchain/core/language_models/chat_models'; |
| import { BaseMessage, HumanMessage, AIMessage, SystemMessage } from '@langchain/core/messages'; |
| import { CallbackManagerForLLMRun } from '@langchain/core/callbacks/manager'; |
| import { ChatResult } from '@langchain/core/outputs'; |
| import type { LanguageModel } from 'ai'; |
|
|
| import { callLLM, streamLLM } from '@/lib/ai/llm'; |
| import type { ThinkingConfig } from '@/lib/types/provider'; |
| import { createLogger } from '@/lib/logger'; |
|
|
| const log = createLogger('AISdkAdapter'); |
|
|
| |
| |
| |
| export type StreamChunk = |
| | { type: 'delta'; content: string } |
| | { |
| type: 'tool_calls'; |
| toolCalls: { |
| id: string; |
| index: number; |
| type: 'function'; |
| function: { name: string; arguments: string }; |
| }[]; |
| } |
| | { type: 'done'; content: string }; |
|
|
| |
| |
| |
| |
| |
| |
| export class AISdkLangGraphAdapter extends BaseChatModel { |
| private languageModel: LanguageModel; |
| private thinking?: ThinkingConfig; |
|
|
| constructor(languageModel: LanguageModel, thinking?: ThinkingConfig) { |
| super({}); |
| this.languageModel = languageModel; |
| this.thinking = thinking; |
| } |
|
|
| _llmType(): string { |
| return 'ai-sdk'; |
| } |
|
|
| _combineLLMOutput() { |
| return {}; |
| } |
|
|
| |
| |
| |
| private convertMessages( |
| messages: BaseMessage[], |
| ): { role: 'system' | 'user' | 'assistant'; content: string }[] { |
| return messages.map((msg) => { |
| if (msg instanceof HumanMessage) { |
| return { role: 'user' as const, content: msg.content as string }; |
| } else if (msg instanceof AIMessage) { |
| return { role: 'assistant' as const, content: msg.content as string }; |
| } else if (msg instanceof SystemMessage) { |
| return { role: 'system' as const, content: msg.content as string }; |
| } else { |
| return { role: 'user' as const, content: msg.content as string }; |
| } |
| }); |
| } |
|
|
| async _generate( |
| messages: BaseMessage[], |
| _options?: this['ParsedCallOptions'], |
| _runManager?: CallbackManagerForLLMRun, |
| ): Promise<ChatResult> { |
| const aiMessages = this.convertMessages(messages); |
|
|
| try { |
| const result = await callLLM( |
| { |
| model: this.languageModel, |
| messages: aiMessages, |
| }, |
| 'chat-adapter', |
| undefined, |
| this.thinking, |
| ); |
|
|
| const content = result.text || ''; |
|
|
| log.info('[AI SDK Adapter] Response:', { |
| textLength: content.length, |
| }); |
|
|
| |
| const aiMessage = new AIMessage({ content }); |
|
|
| return { |
| generations: [ |
| { |
| text: content, |
| message: aiMessage, |
| }, |
| ], |
| llmOutput: {}, |
| }; |
| } catch (error) { |
| log.error('[AI SDK Adapter Error]', error); |
| throw error; |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| async *streamGenerate( |
| messages: BaseMessage[], |
| options?: { tools?: Record<string, unknown>; signal?: AbortSignal }, |
| ): AsyncGenerator<StreamChunk> { |
| const aiMessages = this.convertMessages(messages); |
|
|
| const result = streamLLM( |
| { |
| model: this.languageModel, |
| messages: aiMessages, |
| abortSignal: options?.signal, |
| }, |
| 'chat-adapter-stream', |
| this.thinking, |
| ); |
|
|
| let fullContent = ''; |
|
|
| for await (const chunk of result.textStream) { |
| if (chunk) { |
| fullContent += chunk; |
| yield { type: 'delta', content: chunk }; |
| } |
| } |
|
|
| |
| yield { type: 'done', content: fullContent }; |
| } |
| } |
|
|