diff --git a/src/api/providers/cline.ts b/src/api/providers/cline.ts index 74a993f77d..11d0d641e2 100644 --- a/src/api/providers/cline.ts +++ b/src/api/providers/cline.ts @@ -1,18 +1,19 @@ import { Anthropic } from "@anthropic-ai/sdk" -import { Stream as AnthropicStream } from "@anthropic-ai/sdk/streaming" +import OpenAI from "openai" import { ApiHandler } from "../" import { ApiHandlerOptions, ModelInfo } from "../../shared/api" import { ApiStream } from "../transform/stream" +import { convertToOpenAiMessages } from "../transform/openai-format" export class ClineHandler implements ApiHandler { private options: ApiHandlerOptions - private client: Anthropic + private client: OpenAI constructor(options: ApiHandlerOptions) { this.options = options - this.client = new Anthropic({ - apiKey: this.options.clineApiKey || "", + this.client = new OpenAI({ baseURL: "https://api.cline.bot/v1", + apiKey: this.options.clineApiKey || "", defaultHeaders: { "X-Firebase-Token": this.options.authToken || "", }, @@ -21,72 +22,40 @@ export class ClineHandler implements ApiHandler { async *createMessage(systemPrompt: string, messages: Anthropic.Messages.MessageParam[]): ApiStream { const model = this.getModel() - const stream = await this.client.messages.create({ + const openAiMessages: OpenAI.Chat.ChatCompletionMessageParam[] = [ + { role: "system", content: systemPrompt }, + ...convertToOpenAiMessages(messages), + ] + + const stream = await this.client.chat.completions.create({ model: model.id, - max_tokens: model.info.maxTokens || 8192, + messages: openAiMessages, temperature: 0, - system: [{ text: systemPrompt, type: "text" }], - messages, stream: true, + stream_options: { include_usage: true }, }) for await (const chunk of stream) { - switch (chunk.type) { - case "message_start": - const usage = chunk.message.usage as any - const result: any = { - type: "usage", - inputTokens: usage.input_tokens || 0, - outputTokens: usage.output_tokens || 0, - } - if (usage.cache_creation_input_tokens) { - result.cacheWriteTokens = usage.cache_creation_input_tokens - } - if (usage.cache_read_input_tokens) { - result.cacheReadTokens = usage.cache_read_input_tokens - } - yield result - break - case "message_delta": - yield { - type: "usage", - inputTokens: 0, - outputTokens: chunk.usage.output_tokens || 0, - } - break - case "content_block_start": - switch (chunk.content_block.type) { - case "text": - if (chunk.index > 0) { - yield { - type: "text", - text: "\n", - } - } - yield { - type: "text", - text: chunk.content_block.text, - } - break - } - break - case "content_block_delta": - switch (chunk.delta.type) { - case "text_delta": - yield { - type: "text", - text: chunk.delta.text, - } - break - } - break + const delta = chunk.choices[0]?.delta + if (delta?.content) { + yield { + type: "text", + text: delta.content, + } + } + if (chunk.usage) { + yield { + type: "usage", + inputTokens: chunk.usage.prompt_tokens || 0, + outputTokens: chunk.usage.completion_tokens || 0, + } } } } getModel() { return { - id: "claude-3-5-sonnet", + id: "anthropic/claude-3.5-sonnet:beta", info: { maxTokens: 8192, contextWindow: 200_000, diff --git a/src/shared/api.ts b/src/shared/api.ts index c5d3a09e71..4699fadbf4 100644 --- a/src/shared/api.ts +++ b/src/shared/api.ts @@ -232,6 +232,25 @@ export const vertexModels = { }, } as const satisfies Record +// Cline +export type ClineModelId = keyof typeof clineModels +export const clineDefaultModelId: ClineModelId = "anthropic/claude-3.5-sonnet:beta" +export const clineModels = { + "anthropic/claude-3.5-sonnet:beta": { + maxTokens: 8192, + contextWindow: 200_000, + supportsImages: true, + supportsComputerUse: true, + supportsPromptCache: true, + inputPrice: 3.0, + outputPrice: 15.0, + cacheWritesPrice: 3.75, + cacheReadsPrice: 0.3, + description: + "Claude 3.5 Sonnet delivers better-than-Opus capabilities, faster-than-Sonnet speeds, at the same Sonnet prices.", + }, +} as const satisfies Record + export const openAiModelInfoSaneDefaults: ModelInfo = { maxTokens: -1, contextWindow: 128_000, diff --git a/webview-ui/src/components/settings/ApiOptions.tsx b/webview-ui/src/components/settings/ApiOptions.tsx index 1e9d1a5d48..605c23b7f1 100644 --- a/webview-ui/src/components/settings/ApiOptions.tsx +++ b/webview-ui/src/components/settings/ApiOptions.tsx @@ -19,6 +19,8 @@ import { azureOpenAiDefaultApiVersion, bedrockDefaultModelId, bedrockModels, + clineDefaultModelId, + clineModels, deepSeekDefaultModelId, deepSeekModels, geminiDefaultModelId, @@ -810,6 +812,7 @@ const ApiOptions = ({ showModelOptions, apiErrorMessage, modelIdErrorMessage, is + {selectedProvider === "cline" && createDropdown(clineModels)} {selectedProvider === "anthropic" && createDropdown(anthropicModels)} {selectedProvider === "bedrock" && createDropdown(bedrockModels)} {selectedProvider === "vertex" && createDropdown(vertexModels)} @@ -1004,7 +1007,7 @@ export function normalizeApiConfiguration(apiConfiguration?: ApiConfiguration): } switch (provider) { case "cline": - return getProviderData(anthropicModels, anthropicDefaultModelId) + return getProviderData(clineModels, clineDefaultModelId) case "anthropic": return getProviderData(anthropicModels, anthropicDefaultModelId) case "bedrock":