-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Add Claude Code LLM provider support #1799
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,354 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| CoreAssistantMessage, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ModelMessage, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| CoreSystemMessage, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| CoreUserMessage, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| generateObject, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| generateText, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ImagePart, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| NoObjectGeneratedError, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| TextPart, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ToolSet, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Tool, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } from "ai"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import type { LanguageModelV2 } from "@ai-sdk/provider"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { LogLine } from "../types/public/logs"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { AvailableModel, ClientOptions } from "../types/public/model"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| CreateChatCompletionOptions, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| LLMClient, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| LLMResponse, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } from "./LLMClient"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Type for claude-code model names | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export type ClaudeCodeModelName = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | "claude-code-opus" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | "claude-code-sonnet" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| | "claude-code-haiku"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Map from Stagehand model names to claude-code provider model names | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const claudeCodeModelMap: Record<ClaudeCodeModelName, string> = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "claude-code-opus": "opus", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "claude-code-sonnet": "sonnet", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "claude-code-haiku": "haiku", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export class ClaudeCodeClient extends LLMClient { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public type = "claude-code" as const; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public hasVision = true; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private model: LanguageModelV2; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private logger?: (message: LogLine) => void; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| constructor({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| modelName, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logger, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| clientOptions, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| modelName: ClaudeCodeModelName; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logger?: (message: LogLine) => void; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| clientOptions?: ClientOptions; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| super(modelName as AvailableModel); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.logger = logger; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.clientOptions = clientOptions; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Dynamically import the claude-code provider | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // This is done lazily to avoid requiring the package if not used | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const providerModelName = claudeCodeModelMap[modelName]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.model = this.createClaudeCodeModel(providerModelName); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private createClaudeCodeModel(modelName: string): LanguageModelV2 { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Dynamic require to handle optional dependency | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // eslint-disable-next-line @typescript-eslint/no-var-requires | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let claudeCode: (modelName: string) => LanguageModelV2; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Try to import the claude-code provider | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const provider = require("ai-sdk-provider-claude-code"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| claudeCode = provider.claudeCode; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new Error( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "ai-sdk-provider-claude-code package is not installed. " + | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "Please install it with: npm install ai-sdk-provider-claude-code\n" + | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "Also ensure Claude Code CLI is installed and authenticated via 'claude login'.", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return claudeCode(modelName); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public getLanguageModel(): LanguageModelV2 { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return this.model; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async createChatCompletion<T = LLMResponse>({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: The Prompt for AI agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| options, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }: CreateChatCompletionOptions): Promise<T> { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+83
to
+85
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Then replace each |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.logger?.({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| category: "claude-code", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| message: "creating chat completion", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| level: 2, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| auxiliary: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| options: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| value: JSON.stringify({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ...options, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| image: undefined, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| messages: options.messages.map((msg) => ({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ...msg, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| content: Array.isArray(msg.content) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ? msg.content.map((c) => | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "image_url" in c | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ? { ...c, image_url: { url: "[IMAGE_REDACTED]" } } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| : c, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| : msg.content, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| })), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type: "object", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| modelName: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| value: this.modelName, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type: "string", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const formattedMessages: ModelMessage[] = options.messages.map( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| (message) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (Array.isArray(message.content)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (message.role === "system") { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const systemMessage: CoreSystemMessage = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| role: "system", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| content: message.content | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .map((c) => ("text" in c ? c.text : "")) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .join("\n"), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return systemMessage; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const contentParts = message.content.map((content) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if ("image_url" in content) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const imageContent: ImagePart = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type: "image", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| image: content.image_url.url, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return imageContent; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const textContent: TextPart = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type: "text", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| text: content.text, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return textContent; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (message.role === "user") { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const userMessage: CoreUserMessage = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| role: "user", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| content: contentParts, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return userMessage; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const textOnlyParts = contentParts.map((part) => ({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type: "text" as const, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| text: part.type === "image" ? "[Image]" : part.text, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| })); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const assistantMessage: CoreAssistantMessage = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| role: "assistant", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| content: textOnlyParts, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return assistantMessage; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| role: message.role, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| content: message.content, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Add image to messages if provided | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (options.image) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| formattedMessages.push({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| role: "user", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| content: [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type: "image", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| image: options.image.buffer.toString("base64"), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } as ImagePart, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ...(options.image.description | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ? [{ type: "text" as const, text: options.image.description }] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| : []), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let objectResponse: Awaited<ReturnType<typeof generateObject>>; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (options.response_model) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| objectResponse = await generateObject({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: Prompt for AI agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| model: this.model, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| messages: formattedMessages, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| schema: options.response_model.schema, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| temperature: options.temperature, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+189
to
+194
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (err) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (NoObjectGeneratedError.isInstance(err)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.logger?.({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| category: "claude-code", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| message: `error: ${err.message}`, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| level: 0, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| auxiliary: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cause: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| value: JSON.stringify(err.cause ?? {}), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type: "object", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| text: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| value: err.text ?? "", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type: "string", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| response: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| value: JSON.stringify(err.response ?? {}), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type: "object", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| usage: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| value: JSON.stringify(err.usage ?? {}), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type: "object", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| finishReason: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| value: err.finishReason ?? "unknown", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type: "string", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| requestId: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| value: options.requestId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type: "string", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw err; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw err; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const result = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| data: objectResponse.object, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| usage: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| prompt_tokens: objectResponse.usage.inputTokens ?? 0, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| completion_tokens: objectResponse.usage.outputTokens ?? 0, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| reasoning_tokens: objectResponse.usage.reasoningTokens ?? 0, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cached_input_tokens: objectResponse.usage.cachedInputTokens ?? 0, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| total_tokens: objectResponse.usage.totalTokens ?? 0, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } as T; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.logger?.({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| category: "claude-code", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| message: "response", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| level: 1, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| auxiliary: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| response: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| value: JSON.stringify({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| object: objectResponse.object, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| usage: objectResponse.usage, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| finishReason: objectResponse.finishReason, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type: "object", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| requestId: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| value: options.requestId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type: "string", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return result; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const tools: ToolSet = {}; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (options.tools && options.tools.length > 0) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (const tool of options.tools) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| tools[tool.name] = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| description: tool.description, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| inputSchema: tool.parameters, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } as Tool; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const textResponse = await generateText({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: Prompt for AI agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| model: this.model, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| messages: formattedMessages, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| tools: Object.keys(tools).length > 0 ? tools : undefined, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| toolChoice: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Object.keys(tools).length > 0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ? options.tool_choice === "required" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ? "required" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| : options.tool_choice === "none" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ? "none" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| : "auto" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| : undefined, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| temperature: options.temperature, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+278
to
+291
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Same issue as the
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Transform AI SDK response to match LLMResponse format | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const transformedToolCalls = (textResponse.toolCalls || []).map( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| (toolCall) => ({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| id: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| toolCall.toolCallId || | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| `call_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type: "function", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| function: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name: toolCall.toolName, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| arguments: JSON.stringify(toolCall.input), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const result = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| id: `chatcmpl_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| object: "chat.completion", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| created: Math.floor(Date.now() / 1000), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| model: this.modelName, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| choices: [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| index: 0, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| message: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| role: "assistant", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| content: textResponse.text || null, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| tool_calls: transformedToolCalls, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| finish_reason: textResponse.finishReason || "stop", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| usage: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| prompt_tokens: textResponse.usage.inputTokens ?? 0, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| completion_tokens: textResponse.usage.outputTokens ?? 0, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| reasoning_tokens: textResponse.usage.reasoningTokens ?? 0, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| cached_input_tokens: textResponse.usage.cachedInputTokens ?? 0, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| total_tokens: textResponse.usage.totalTokens ?? 0, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } as T; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.logger?.({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| category: "claude-code", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| message: "response", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| level: 2, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| auxiliary: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| response: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| value: JSON.stringify({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| text: textResponse.text, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| usage: textResponse.usage, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| finishReason: textResponse.finishReason, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type: "object", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| requestId: { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| value: options.requestId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| type: "string", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return result; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,6 +12,10 @@ import { | |
| import { AISdkClient } from "./aisdk"; | ||
| import { AnthropicClient } from "./AnthropicClient"; | ||
| import { CerebrasClient } from "./CerebrasClient"; | ||
| import { | ||
| ClaudeCodeClient, | ||
| ClaudeCodeModelName, | ||
| } from "./ClaudeCodeClient"; | ||
| import { GoogleClient } from "./GoogleClient"; | ||
| import { GroqClient } from "./GroqClient"; | ||
| import { LLMClient } from "./LLMClient"; | ||
|
|
@@ -79,6 +83,9 @@ const modelToProviderMap: { [key in AvailableModel]: ModelProvider } = { | |
| "claude-3-5-sonnet-20241022": "anthropic", | ||
| "claude-3-7-sonnet-20250219": "anthropic", | ||
| "claude-3-7-sonnet-latest": "anthropic", | ||
| "claude-code-opus": "claude-code", | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P1: Custom agent: Ensure we never check against hardcoded lists of allowed LLM model names Adding three new hardcoded model names ( Consider supporting these models through the existing Prompt for AI agents |
||
| "claude-code-sonnet": "claude-code", | ||
| "claude-code-haiku": "claude-code", | ||
| "cerebras-llama-3.3-70b": "cerebras", | ||
| "cerebras-llama-3.1-8b": "cerebras", | ||
| "groq-llama-3.3-70b-versatile": "groq", | ||
|
|
@@ -192,6 +199,12 @@ export class LLMProvider { | |
| modelName: availableModel, | ||
| clientOptions, | ||
| }); | ||
| case "claude-code": | ||
| return new ClaudeCodeClient({ | ||
| logger: this.logger, | ||
| modelName: availableModel as ClaudeCodeModelName, | ||
| clientOptions, | ||
| }); | ||
| default: | ||
| throw new UnsupportedModelProviderError([ | ||
| ...new Set(Object.values(modelToProviderMap)), | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inaccurate "lazy loading" comment
The comment on line 56 says "This is done lazily to avoid requiring the package if not used", but
createClaudeCodeModelis called synchronously on line 58 — directly in the constructor body. Therequire()call executes eagerly at construction time (i.e., whenLLMProvider.getClient()instantiates the client), not lazily when the model is first used.Consider either: (a) updating the comment to accurately describe eager initialization, or (b) actually deferring the
require()to the firstcreateChatCompletioncall if lazy loading is truly desired.