Search
Code3,144
- **Qwen3-32B**: `reasoning_effort` must be `'none'` or `'default'`- **OpenAI o1/o3**: `reasoning_effort` can be `'low'`, `'medium'`, `'high'`- **Anthropic**: Uses `extended_thinking` budget tokens instead- **Some models**: Don't support certain parameters at all// =============================================================================// Anthropic Base Parameters// =============================================================================const ANTHROPIC_BASE_PARAMETERS: ModelParameter[] = [ { key: 'temperature',];const ANTHROPIC_THINKING_PARAMETERS: ModelParameter[] = [ ...ANTHROPIC_BASE_PARAMETERS, { key: 'reasoningEffort',// =============================================================================// Anthropic Model Registry// =============================================================================export const ANTHROPIC_MODEL_CONFIGS: ModelConfig[] = [ // Claude 4.5 Series (Latest - 2025) { id: 'claude-opus-4-5-20251124', name: 'Claude Opus 4.5', provider: 'anthropic', description: 'Most advanced model for complex tasks requiring extended autonomous operation and deep reasoning.', parameters: ANTHROPIC_THINKING_PARAMETERS, capabilities: { thinking: true, image: true, streaming: true, tools: true } }, id: 'claude-sonnet-4-5-20250929', name: 'Claude Sonnet 4.5', provider: 'anthropic', description: 'Optimized for real-world agents, coding, and computer use with enhanced capabilities.', parameters: ANTHROPIC_THINKING_PARAMETERS, capabilities: { thinking: true, image: true, streaming: true, tools: true } }, id: 'claude-haiku-4-5-20251015', name: 'Claude Haiku 4.5', provider: 'anthropic', description: 'Lightweight and fast model designed for real-time experiences with low latency.', parameters: ANTHROPIC_BASE_PARAMETERS, capabilities: { image: true, streaming: true, tools: true } }, id: 'claude-opus-4-1-20250805', name: 'Claude Opus 4.1', provider: 'anthropic', description: 'Most capable and intelligent model, setting new standards in complex reasoning and advanced coding.', parameters: ANTHROPIC_THINKING_PARAMETERS, capabilities: { thinking: true, image: true, streaming: true, tools: true } }, id: 'claude-opus-4-20250514', name: 'Claude Opus 4', provider: 'anthropic', description: 'Highly capable model for complex tasks requiring deep reasoning.', parameters: ANTHROPIC_THINKING_PARAMETERS, capabilities: { thinking: true, image: true, streaming: true, tools: true } }, id: 'claude-sonnet-4-20250514', name: 'Claude Sonnet 4', provider: 'anthropic', description: 'Excellent balance of performance, speed, and cost with extended thinking.', parameters: ANTHROPIC_THINKING_PARAMETERS, capabilities: { thinking: true, image: true, streaming: true, tools: true } }, id: 'claude-3-7-sonnet-20250219', name: 'Claude 3.7 Sonnet', provider: 'anthropic', description: 'Hybrid model with extended thinking capabilities.', parameters: ANTHROPIC_THINKING_PARAMETERS, capabilities: { thinking: true, image: true, streaming: true, tools: true } }, id: 'claude-3-5-sonnet-20241022', name: 'Claude 3.5 Sonnet', provider: 'anthropic', description: 'High performance for complex reasoning and coding.', parameters: ANTHROPIC_THINKING_PARAMETERS, capabilities: { thinking: true, image: true, streaming: true, tools: true } }, id: 'claude-3-5-haiku-20241022', name: 'Claude 3.5 Haiku', provider: 'anthropic', description: 'Fast and cost-effective for simple tasks.', parameters: ANTHROPIC_BASE_PARAMETERS, capabilities: { image: true, streaming: true, tools: true } }, id: 'claude-3-opus-20240229', name: 'Claude 3 Opus', provider: 'anthropic', description: 'Highly capable for complex analysis and coding.', parameters: ANTHROPIC_BASE_PARAMETERS, capabilities: { image: true, streaming: true, tools: true } }, id: 'claude-3-sonnet-20240229', name: 'Claude 3 Sonnet', provider: 'anthropic', description: 'Balanced performance and cost.', parameters: ANTHROPIC_BASE_PARAMETERS, capabilities: { image: true, streaming: true, tools: true } }, id: 'claude-3-haiku-20240307', name: 'Claude 3 Haiku', provider: 'anthropic', description: 'Fastest Claude 3 model.', parameters: ANTHROPIC_BASE_PARAMETERS, capabilities: { streaming: true, tools: true } } ...GROQ_MODEL_CONFIGS, ...OPENAI_MODEL_CONFIGS, ...ANTHROPIC_MODEL_CONFIGS, ...CUSTOM_MODEL_CONFIGS,];// Anthropic Chat Provider// Uses Anthropic's Messages API which has a different format than OpenAIimport type { ChatProvider, ChatMessage, ChatCompletionOptions, ChatCompletionResponse, StreamChunk } from "./types.ts";// Anthropic uses a different message formatinterface AnthropicMessage { role: "user" | "assistant"; content: string;}export class AnthropicProvider implements ChatProvider { name = "anthropic"; defaultModel = "claude-sonnet-4-20250514"; private apiKey: string | undefined; private baseUrl = "https://api.anthropic.com/v1"; private apiVersion = "2023-06-01"; constructor() { this.apiKey = Deno.env.get("ANTHROPIC_API_KEY"); } } // Convert OpenAI-style messages to Anthropic format private convertMessages(messages: ChatMessage[], systemPrompt?: string): { system?: string; messages: AnthropicMessage[] } { const anthropicMessages: AnthropicMessage[] = []; let system = systemPrompt || ""; for (const msg of messages) { if (msg.role === "system") { // Anthropic handles system prompts separately system = system ? system + "\n\n" + msg.content : msg.content; } else { anthropicMessages.push({ role: msg.role as "user" | "assistant", content: msg.content, return { system: system || undefined, messages: anthropicMessages, }; } if (!apiKey) { throw new Error("ANTHROPIC_API_KEY not configured. Please add your API key in the chat settings."); } const topP = options?.topP ?? 1; const { system, messages: anthropicMessages } = this.convertMessages(messages, options?.systemPrompt); // Build request headers - Anthropic uses different auth header const headers: Record<string, string> = { "x-api-key": apiKey, "anthropic-version": this.apiVersion, "Content-Type": "application/json", }; // Enable extended thinking for Claude 3.5+ if requested if (options?.reasoningEffort && this.supportsExtendedThinking(model)) { headers["anthropic-beta"] = "interleaved-thinking-2025-05-14"; } // Build request body - Anthropic format const requestBody: Record<string, unknown> = { model, messages: anthropicMessages, max_tokens: maxTokens, stream: false, if (!response.ok) { const errorText = await response.text(); console.error("Anthropic API error:", response.status, errorText); let errorMsg = "Failed to get response from Anthropic API"; try { const errorData = JSON.parse(errorText); errorMsg = errorData.error?.message || errorData.message || errorMsg; if (response.status === 401 || response.status === 403) { errorMsg = "Anthropic API key is invalid or missing. Please check your API key in Settings → Models."; } } catch { // If error text isn't JSON, use the status code if (response.status === 401 || response.status === 403) { errorMsg = "Anthropic API key is invalid or missing. Please check your API key in Settings → Models."; } else { errorMsg = `Anthropic API error (${response.status}): ${errorText.substring(0, 200)}`; } } const data = await response.json(); console.log("[Anthropic API complete response]:", JSON.stringify(data, null, 2)); if (!data.content || !data.content[0]) { throw new Error("Invalid response from Anthropic API"); } if (!apiKey) { throw new Error("ANTHROPIC_API_KEY not configured. Please add your API key in the chat settings."); } const topP = options?.topP ?? 1; const { system, messages: anthropicMessages } = this.convertMessages(messages, options?.systemPrompt); // Build request headers const headers: Record<string, string> = { "x-api-key": apiKey, "anthropic-version": this.apiVersion, "Content-Type": "application/json", }; if (options?.reasoningEffort && this.supportsExtendedThinking(model)) { headers["anthropic-beta"] = "interleaved-thinking-2025-05-14"; } const requestBody: Record<string, unknown> = { model, messages: anthropicMessages, max_tokens: maxTokens, stream: true, } console.log("[Anthropic stream] Request body:", JSON.stringify(requestBody, null, 2)); const response = await fetch(this.baseUrl + "/messages", { if (!response.ok) { const errorText = await response.text(); console.error("[Anthropic stream] API error:", response.status, errorText); let errorMsg = "Failed to start stream from Anthropic API"; try { const errorData = JSON.parse(errorText); errorMsg = errorData.error?.message || errorData.message || errorMsg; if (response.status === 401 || response.status === 403) { errorMsg = "Anthropic API key is invalid or missing. Please check your API key in Settings → Models."; } } catch { // If error text isn't JSON, use the status code if (response.status === 401 || response.status === 403) { errorMsg = "Anthropic API key is invalid or missing. Please check your API key in Settings → Models."; } else { errorMsg = `Anthropic API error (${response.status}): ${errorText.substring(0, 200)}`; } } if (done) { console.log("[Anthropic stream] Reader done"); yield { content: "", done: true }; break; lineCount++; if (lineCount <= 5) { console.log("[Anthropic stream] Line " + lineCount + ":", trimmed.substring(0, 200)); } if (currentEvent === "error" || parsed.type === "error") { const errorMsg = parsed.error?.message || JSON.stringify(parsed) || "Unknown streaming error"; console.error("[Anthropic stream] API error:", errorMsg); throw new Error("Anthropic API error: " + errorMsg); } currentEvent = ""; } catch (e) { if (e instanceof Error && e.message.startsWith("Anthropic API error:")) { throw e; } console.error("[Anthropic stream] JSON parse error:", e, "data:", data.substring(0, 100)); } } } } finally { console.log("[Anthropic stream] Total lines processed:", lineCount); reader.releaseLock(); } groq: '', openai: '', anthropic: '' }, keys); console.log('[Storage] Loaded API keys for providers:', Object.keys(this.apiKeys).filter(k => this.apiKeys[k])); groq: '', openai: '', anthropic: '' }; localStorage.removeItem(API_KEYS_STORAGE_KEY); provider = 'openai'; } else if (model.startsWith('claude')) { provider = 'anthropic'; } else { provider = 'groq'; } // Check API key for standard providers (groq, openai, anthropic) if (provider && this.apiKeys) { var apiKey = this.apiKeys[provider]; { id: 'o1-mini', name: 'o1-mini', desc: 'Efficient reasoning model for coding and math.', logo: '/public/openai-logo.png', provider: 'openai', thinking: true }, // Anthropic - Claude 4 Series (Latest) { id: 'claude-opus-4-1-20250805', name: 'Claude Opus 4.1', desc: 'Most capable and intelligent model, setting new standards.', logo: 'vscode-icons:file-type-claude', provider: 'anthropic', thinking: true, image: true, tools: true }, { id: 'claude-opus-4-20250514', name: 'Claude Opus 4', desc: 'Highly capable model for complex tasks.', logo: 'vscode-icons:file-type-claude', provider: 'anthropic', thinking: true, image: true, tools: true }, { id: 'claude-sonnet-4-20250514', name: 'Claude Sonnet 4', desc: 'Excellent balance of performance, speed, and cost.', logo: 'vscode-icons:file-type-claude', provider: 'anthropic', thinking: true, image: true, tools: true }, // Anthropic - Claude 3.7 Series { id: 'claude-3-7-sonnet-20250219', name: 'Claude 3.7 Sonnet', desc: 'Hybrid model with extended thinking capabilities.', logo: 'vscode-icons:file-type-claude', provider: 'anthropic', thinking: true, image: true, tools: true }, // Anthropic - Claude 3.5 Series { id: 'claude-3-5-sonnet-20241022', name: 'Claude 3.5 Sonnet', desc: 'High performance for complex reasoning and coding.', logo: 'vscode-icons:file-type-claude', provider: 'anthropic', thinking: true, image: true, tools: true }, { id: 'claude-3-5-haiku-20241022', name: 'Claude 3.5 Haiku', desc: 'Fast and cost-effective for simple tasks.', logo: 'vscode-icons:file-type-claude', provider: 'anthropic', image: true, tools: true }]; groq: '', // Groq API key openai: '', // OpenAI API key anthropic: '', // Anthropic API key custom: '', // Custom provider API key (optional) }, 'groq': 'Groq', 'openai': 'OpenAI', 'anthropic': 'Anthropic', 'custom': 'Custom Provider' }; apiKeyPrefix: 'sk-' }, anthropic: { name: 'Anthropic', baseUrl: 'https://api.anthropic.com/v1', logo: 'vscode-icons:file-type-claude', docsUrl: 'https://docs.anthropic.com', apiKeyUrl: 'https://console.anthropic.com/settings/keys', apiKeyPrefix: 'sk-ant-' }, { id: 'o1-pro', name: 'o1-pro', provider: 'openai', desc: 'Enhanced compute for hard problems.', capabilities: { thinking: true, streaming: true, image: true } }, // Anthropic - Claude 4 Series (Latest) { id: 'claude-opus-4-1-20250805', name: 'Claude Opus 4.1', provider: 'anthropic', desc: 'Most capable and intelligent model, setting new standards in complex reasoning.', capabilities: { thinking: true, image: true, streaming: true, tools: true } }, { id: 'claude-opus-4-20250514', name: 'Claude Opus 4', provider: 'anthropic', desc: 'Highly capable model for complex tasks requiring deep reasoning.', capabilities: { thinking: true, image: true, streaming: true, tools: true } }, { id: 'claude-sonnet-4-20250514', name: 'Claude Sonnet 4', provider: 'anthropic', desc: 'Excellent balance of performance, speed, and cost with extended thinking.', capabilities: { thinking: true, image: true, streaming: true, tools: true } }, // Anthropic - Claude 3.7 Series { id: 'claude-3-7-sonnet-20250219', name: 'Claude 3.7 Sonnet', provider: 'anthropic', desc: 'Hybrid model with extended thinking capabilities.', capabilities: { thinking: true, image: true, streaming: true, tools: true } }, // Anthropic - Claude 3.5 Series { id: 'claude-3-5-sonnet-20241022', name: 'Claude 3.5 Sonnet', provider: 'anthropic', desc: 'High performance for complex reasoning and coding.', capabilities: { thinking: true, image: true, streaming: true, tools: true } }, { id: 'claude-3-5-haiku-20241022', name: 'Claude 3.5 Haiku', provider: 'anthropic', desc: 'Fast and cost-effective for simple tasks.', capabilities: { image: true, streaming: true, tools: true } }, // Anthropic - Claude 3 Series (Legacy) { id: 'claude-3-opus-20240229', name: 'Claude 3 Opus', provider: 'anthropic', desc: 'Highly capable for complex analysis and coding.', capabilities: { image: true, streaming: true, tools: true } }, { id: 'claude-3-sonnet-20240229', name: 'Claude 3 Sonnet', provider: 'anthropic', desc: 'Balanced performance and cost.', capabilities: { image: true, streaming: true, tools: true } }, { id: 'claude-3-haiku-20240307', name: 'Claude 3 Haiku', provider: 'anthropic', desc: 'Fastest Claude 3 model.', capabilities: { streaming: true, tools: true } }]; groq: '/public/groq-logo-orange.png', openai: '/public/openai-logo.png', anthropic: 'vscode-icons:file-type-claude', custom: 'lucide:message-square' }; groq: '/public/groq-logo-orange.png', openai: '/public/openai-logo.png', anthropic: 'vscode-icons:file-type-claude', custom: '/public/custom-logo.png' }; project, branchId, // anthropicApiKey, // bearerToken, selectedFiles,- [x] Add a "view source" / "send me a PR" link- [x] Show the HTTP preview in second column if there is one (and let the user pick which one to preview in the iframe)- [x] Figure out a convention to teach in the anthropic prompt mod where the LLM always checks the readme for the scope (if not provided) and keeps it up to take with every change- [x] Ability to create new projects from the interface- [x] Figure out why OpenTownie can't create HTTP vals. Maybe give it a seperate tool for it?- [x] Start a timer for messages- [x] Add more indicators that it's "still working"- [x] Require users supply their own Anthropic token?- [x] Add cost indications on messages- [x] Add a bell noise when the message is done to let us know