From a8e9caf7b059b8f0dd7f8455bfc212c35ef65c6f Mon Sep 17 00:00:00 2001 From: Will Chen Date: Wed, 10 Sep 2025 15:59:54 -0700 Subject: [PATCH] Turbo models (#1249) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary by cubic Adds “Dyad Turbo” models for Pro users and centralizes model/provider constants. Pro users can pick fast, cost‑effective models directly from the ModelPicker, with clearer labels and gating. - **New Features** - Added Dyad Turbo provider in ModelPicker with Qwen3 Coder and Kimi K2 (Pro only). - Turbo options are hidden for non‑Pro users; “Pro only” badge shown where applicable. - “Smart Auto” label now applies only to the Auto model to avoid confusion. - **Refactors** - Moved all model/provider constants into language_model_constants.ts and updated imports (helpers, client, thinking utils). --- src/components/ModelPicker.tsx | 25 +- src/ipc/shared/language_model_constants.ts | 468 +++++++++++++++++++++ src/ipc/shared/language_model_helpers.ts | 443 +------------------ src/ipc/utils/get_model_client.ts | 6 +- src/ipc/utils/thinking_utils.ts | 2 +- 5 files changed, 497 insertions(+), 447 deletions(-) create mode 100644 src/ipc/shared/language_model_constants.ts diff --git a/src/components/ModelPicker.tsx b/src/components/ModelPicker.tsx index 728ff13..6ccf2f6 100644 --- a/src/components/ModelPicker.tsx +++ b/src/components/ModelPicker.tsx @@ -25,6 +25,7 @@ import { LocalModel } from "@/ipc/ipc_types"; import { useLanguageModelProviders } from "@/hooks/useLanguageModelProviders"; import { useSettings } from "@/hooks/useSettings"; import { PriceBadge } from "@/components/PriceBadge"; +import { TURBO_MODELS } from "@/ipc/shared/language_model_constants"; export function ModelPicker() { const { settings, updateSettings } = useSettings(); @@ -108,6 +109,13 @@ export function ModelPicker() { const autoModels = !loading && modelsByProviders && modelsByProviders["auto"] ? modelsByProviders["auto"].filter((model) => { + if ( + settings && + !isDyadProEnabled(settings) && + model.apiName === "turbo" + ) { + return false; + } if ( settings && isDyadProEnabled(settings) && @@ -144,6 +152,9 @@ export function ModelPicker() { const provider = providers?.find((p) => p.id === providerId); return !(provider && provider.secondary); }); + if (settings && isDyadProEnabled(settings)) { + primaryProviders.unshift(["auto", TURBO_MODELS]); + } const secondaryProviders = providerEntries.filter(([providerId, models]) => { if (models.length === 0) return false; const provider = providers?.find((p) => p.id === providerId); @@ -220,7 +231,7 @@ export function ModelPicker() {
- {isSmartAutoEnabled + {isSmartAutoEnabled && model.apiName === "auto" ? "Smart Auto" : model.displayName} @@ -228,7 +239,7 @@ export function ModelPicker() {
{isSmartAutoEnabled && ( - Dyad Pro + Pro only )} {model.tag && ( @@ -241,7 +252,7 @@ export function ModelPicker() { - {isSmartAutoEnabled ? ( + {isSmartAutoEnabled && model.apiName === "auto" ? (

Smart Auto uses a cheaper model for easier tasks @@ -274,12 +285,16 @@ export function ModelPicker() { return true; }); const provider = providers?.find((p) => p.id === providerId); + const providerDisplayName = + provider?.id === "auto" + ? "Dyad Turbo" + : (provider?.name ?? providerId); return (

- {provider?.name ?? providerId} + {providerDisplayName} {provider?.type === "cloud" && !provider?.secondary && isDyadProEnabled(settings) && ( @@ -300,7 +315,7 @@ export function ModelPicker() { - {(provider?.name ?? providerId) + " Models"} + {providerDisplayName + " Models"} {models.map((model) => ( diff --git a/src/ipc/shared/language_model_constants.ts b/src/ipc/shared/language_model_constants.ts new file mode 100644 index 0000000..be3945a --- /dev/null +++ b/src/ipc/shared/language_model_constants.ts @@ -0,0 +1,468 @@ +import { LanguageModel } from "../ipc_types"; + +export const PROVIDERS_THAT_SUPPORT_THINKING: (keyof typeof MODEL_OPTIONS)[] = [ + "google", + "vertex", + "auto", +]; + +export interface ModelOption { + name: string; + displayName: string; + description: string; + dollarSigns?: number; + temperature?: number; + tag?: string; + maxOutputTokens?: number; + contextWindow?: number; +} + +export const MODEL_OPTIONS: Record = { + openai: [ + // https://platform.openai.com/docs/models/gpt-5 + { + name: "gpt-5", + displayName: "GPT 5", + description: "OpenAI's flagship model", + // Technically it's 128k but OpenAI errors if you set max_tokens instead of max_completion_tokens + maxOutputTokens: undefined, + contextWindow: 400_000, + // Requires temperature to be default value (1) + temperature: 1, + dollarSigns: 3, + }, + // https://platform.openai.com/docs/models/gpt-5-mini + { + name: "gpt-5-mini", + displayName: "GPT 5 Mini", + description: "OpenAI's lightweight, but intelligent model", + // Technically it's 128k but OpenAI errors if you set max_tokens instead of max_completion_tokens + maxOutputTokens: undefined, + contextWindow: 400_000, + // Requires temperature to be default value (1) + temperature: 1, + dollarSigns: 2, + }, + // https://platform.openai.com/docs/models/gpt-5-nano + { + name: "gpt-5-nano", + displayName: "GPT 5 Nano", + description: "Fastest, most cost-efficient version of GPT-5", + // Technically it's 128k but OpenAI errors if you set max_tokens instead of max_completion_tokens + maxOutputTokens: undefined, + contextWindow: 400_000, + // Requires temperature to be default value (1) + temperature: 1, + dollarSigns: 1, + }, + // https://platform.openai.com/docs/models/o4-mini + { + name: "o4-mini", + displayName: "o4 mini", + description: "Reasoning model", + // Technically the max output tokens is 100k, *however* if the user has a lot of input tokens, + // then setting a high max output token will cause the request to fail because + // the max output tokens is *included* in the context window limit. + maxOutputTokens: 32_000, + contextWindow: 200_000, + temperature: 0, + dollarSigns: 2, + }, + ], + // https://docs.anthropic.com/en/docs/about-claude/models/all-models#model-comparison-table + anthropic: [ + { + name: "claude-sonnet-4-20250514", + displayName: "Claude 4 Sonnet", + description: "Excellent coder (note: >200k tokens is very expensive!)", + // See comment below for Claude 3.7 Sonnet for why we set this to 16k + maxOutputTokens: 16_000, + contextWindow: 1_000_000, + temperature: 0, + dollarSigns: 5, + }, + { + name: "claude-3-7-sonnet-latest", + displayName: "Claude 3.7 Sonnet", + description: "Excellent coder", + // Technically the max output tokens is 64k, *however* if the user has a lot of input tokens, + // then setting a high max output token will cause the request to fail because + // the max output tokens is *included* in the context window limit, see: + // https://docs.anthropic.com/en/docs/build-with-claude/extended-thinking#max-tokens-and-context-window-size-with-extended-thinking + maxOutputTokens: 16_000, + contextWindow: 200_000, + temperature: 0, + dollarSigns: 4, + }, + { + name: "claude-3-5-sonnet-20241022", + displayName: "Claude 3.5 Sonnet", + description: "Good coder, excellent at following instructions", + maxOutputTokens: 8_000, + contextWindow: 200_000, + temperature: 0, + dollarSigns: 4, + }, + { + name: "claude-3-5-haiku-20241022", + displayName: "Claude 3.5 Haiku", + description: "Lightweight coder", + maxOutputTokens: 8_000, + contextWindow: 200_000, + temperature: 0, + dollarSigns: 2, + }, + ], + google: [ + // https://ai.google.dev/gemini-api/docs/models#gemini-2.5-pro-preview-03-25 + { + name: "gemini-2.5-pro", + displayName: "Gemini 2.5 Pro", + description: "Google's Gemini 2.5 Pro model", + // See Flash 2.5 comment below (go 1 below just to be safe, even though it seems OK now). + maxOutputTokens: 65_536 - 1, + // Gemini context window = input token + output token + contextWindow: 1_048_576, + temperature: 0, + dollarSigns: 3, + }, + // https://ai.google.dev/gemini-api/docs/models#gemini-2.5-flash-preview + { + name: "gemini-2.5-flash", + displayName: "Gemini 2.5 Flash", + description: "Google's Gemini 2.5 Flash model (free tier available)", + // Weirdly for Vertex AI, the output token limit is *exclusive* of the stated limit. + maxOutputTokens: 65_536 - 1, + // Gemini context window = input token + output token + contextWindow: 1_048_576, + temperature: 0, + dollarSigns: 2, + }, + ], + vertex: [ + // Vertex Gemini 2.5 Pro + { + name: "gemini-2.5-pro", + displayName: "Gemini 2.5 Pro", + description: "Vertex Gemini 2.5 Pro", + maxOutputTokens: 65_536 - 1, + contextWindow: 1_048_576, + temperature: 0, + }, + // Vertex Gemini 2.5 Flash + { + name: "gemini-2.5-flash", + displayName: "Gemini 2.5 Flash", + description: "Vertex Gemini 2.5 Flash", + maxOutputTokens: 65_536 - 1, + contextWindow: 1_048_576, + temperature: 0, + }, + ], + openrouter: [ + { + name: "qwen/qwen3-coder:free", + displayName: "Qwen3 Coder (free)", + description: "Use for free (data may be used for training)", + maxOutputTokens: 32_000, + contextWindow: 262_000, + temperature: 0, + dollarSigns: 0, + }, + // https://openrouter.ai/deepseek/deepseek-chat-v3-0324:free + { + name: "deepseek/deepseek-chat-v3.1:free", + displayName: "DeepSeek v3.1 (free)", + description: "Use for free (data may be used for training)", + maxOutputTokens: 32_000, + contextWindow: 128_000, + temperature: 0, + dollarSigns: 0, + }, + { + name: "deepseek/deepseek-chat-v3-0324:free", + displayName: "DeepSeek v3 (free)", + description: "Use for free (data may be used for training)", + maxOutputTokens: 32_000, + contextWindow: 128_000, + temperature: 0, + dollarSigns: 0, + }, + { + name: "qwen/qwen3-coder", + displayName: "Qwen3 Coder", + description: "Qwen's best coding model", + maxOutputTokens: 32_000, + contextWindow: 262_000, + temperature: 0, + dollarSigns: 2, + }, + { + name: "deepseek/deepseek-chat-v3.1", + displayName: "DeepSeek v3.1", + description: "Strong cost-effective model with optional thinking", + maxOutputTokens: 32_000, + contextWindow: 128_000, + temperature: 0, + dollarSigns: 2, + }, + // https://openrouter.ai/moonshotai/kimi-k2 + { + name: "moonshotai/kimi-k2-0905", + displayName: "Kimi K2", + description: "Powerful cost-effective model (updated to 0905)", + maxOutputTokens: 32_000, + contextWindow: 256_000, + temperature: 0, + dollarSigns: 2, + }, + ], + auto: [ + { + name: "auto", + displayName: "Auto", + description: "Automatically selects the best model", + tag: "Default", + // These are below Gemini 2.5 Pro & Flash limits + // which are the ones defaulted to for both regular auto + // and smart auto. + maxOutputTokens: 32_000, + contextWindow: 1_000_000, + temperature: 0, + }, + { + name: "free", + displayName: "Free (OpenRouter)", + description: "Selects from one of the free OpenRouter models", + tag: "Free", + // These are below Gemini 2.5 Pro & Flash limits + // which are the ones defaulted to for both regular auto + // and smart auto. + maxOutputTokens: 32_000, + contextWindow: 128_000, + temperature: 0, + }, + { + name: "turbo", + displayName: "Turbo (Pro)", + description: "Use very fast open-source frontier models", + maxOutputTokens: 32_000, + contextWindow: 256_000, + temperature: 0, + }, + ], + azure: [ + { + name: "gpt-5", + displayName: "GPT-5", + description: "Azure OpenAI GPT-5 model with reasoning capabilities", + maxOutputTokens: 128_000, + contextWindow: 400_000, + temperature: 0, + }, + { + name: "gpt-5-mini", + displayName: "GPT-5 Mini", + description: "Azure OpenAI GPT-5 Mini model", + maxOutputTokens: 128_000, + contextWindow: 400_000, + temperature: 0, + }, + { + name: "gpt-5-nano", + displayName: "GPT-5 Nano", + description: "Azure OpenAI GPT-5 Nano model", + maxOutputTokens: 128_000, + contextWindow: 400_000, + temperature: 0, + }, + { + name: "gpt-5-chat", + displayName: "GPT-5 Chat", + description: "Azure OpenAI GPT-5 Chat model", + maxOutputTokens: 16_384, + contextWindow: 128_000, + temperature: 0, + }, + ], + xai: [ + // https://docs.x.ai/docs/models + { + name: "grok-code-fast-1", + displayName: "Grok Code Fast", + description: "Fast coding model", + maxOutputTokens: 32_000, + contextWindow: 256_000, + temperature: 0, + dollarSigns: 1, + }, + { + name: "grok-4", + displayName: "Grok 4", + description: "Most capable coding model", + maxOutputTokens: 32_000, + contextWindow: 256_000, + temperature: 0, + dollarSigns: 4, + }, + { + name: "grok-3", + displayName: "Grok 3", + description: "Powerful coding model", + maxOutputTokens: 32_000, + contextWindow: 131_072, + temperature: 0, + dollarSigns: 4, + }, + ], + bedrock: [ + { + name: "us.anthropic.claude-sonnet-4-20250514-v1:0", + displayName: "Claude 4 Sonnet", + description: "Excellent coder (note: >200k tokens is very expensive!)", + maxOutputTokens: 16_000, + contextWindow: 1_000_000, + temperature: 0, + }, + { + name: "us.anthropic.claude-3-7-sonnet-20250219-v1:0", + displayName: "Claude 3.7 Sonnet", + description: "Excellent coder", + maxOutputTokens: 16_000, + contextWindow: 200_000, + temperature: 0, + }, + { + name: "us.anthropic.claude-3-5-sonnet-20241022-v2:0", + displayName: "Claude 3.5 Sonnet", + description: "Good coder, excellent at following instructions", + maxOutputTokens: 8_000, + contextWindow: 200_000, + temperature: 0, + }, + ], +}; + +export const TURBO_MODELS: LanguageModel[] = [ + { + apiName: "qwen3-coder:turbo", + displayName: "Qwen3 Coder", + description: "Qwen's best coding model (very fast)", + maxOutputTokens: 32_000, + contextWindow: 131_000, + temperature: 0, + dollarSigns: 2, + type: "cloud", + }, + { + apiName: "kimi-k2:turbo", + displayName: "Kimi K2", + description: "Kimi 0905 update (fast)", + maxOutputTokens: 16_000, + contextWindow: 256_000, + temperature: 0, + dollarSigns: 2, + type: "cloud", + }, +]; + +export const FREE_OPENROUTER_MODEL_NAMES = MODEL_OPTIONS.openrouter + .filter((model) => model.name.endsWith(":free")) + .map((model) => model.name); + +export const PROVIDER_TO_ENV_VAR: Record = { + openai: "OPENAI_API_KEY", + anthropic: "ANTHROPIC_API_KEY", + google: "GEMINI_API_KEY", + openrouter: "OPENROUTER_API_KEY", + azure: "AZURE_API_KEY", + xai: "XAI_API_KEY", + bedrock: "AWS_BEARER_TOKEN_BEDROCK", +}; + +export const CLOUD_PROVIDERS: Record< + string, + { + displayName: string; + hasFreeTier?: boolean; + websiteUrl?: string; + gatewayPrefix: string; + secondary?: boolean; + } +> = { + openai: { + displayName: "OpenAI", + hasFreeTier: false, + websiteUrl: "https://platform.openai.com/api-keys", + gatewayPrefix: "", + }, + anthropic: { + displayName: "Anthropic", + hasFreeTier: false, + websiteUrl: "https://console.anthropic.com/settings/keys", + gatewayPrefix: "anthropic/", + }, + google: { + displayName: "Google", + hasFreeTier: true, + websiteUrl: "https://aistudio.google.com/app/apikey", + gatewayPrefix: "gemini/", + }, + vertex: { + displayName: "Google Vertex AI", + hasFreeTier: false, + websiteUrl: "https://console.cloud.google.com/vertex-ai", + // Use the same gateway prefix as Google Gemini for Dyad Pro compatibility. + gatewayPrefix: "gemini/", + secondary: true, + }, + openrouter: { + displayName: "OpenRouter", + hasFreeTier: true, + websiteUrl: "https://openrouter.ai/settings/keys", + gatewayPrefix: "openrouter/", + }, + auto: { + displayName: "Dyad", + websiteUrl: "https://academy.dyad.sh/settings", + gatewayPrefix: "dyad/", + }, + azure: { + displayName: "Azure OpenAI", + hasFreeTier: false, + websiteUrl: "https://portal.azure.com/", + gatewayPrefix: "", + secondary: true, + }, + xai: { + displayName: "xAI", + hasFreeTier: false, + websiteUrl: "https://console.x.ai/", + gatewayPrefix: "xai/", + secondary: true, + }, + bedrock: { + displayName: "AWS Bedrock", + hasFreeTier: false, + websiteUrl: "https://console.aws.amazon.com/bedrock/", + gatewayPrefix: "bedrock/", + secondary: true, + }, +}; + +export const LOCAL_PROVIDERS: Record< + string, + { + displayName: string; + hasFreeTier: boolean; + } +> = { + ollama: { + displayName: "Ollama", + hasFreeTier: true, + }, + lmstudio: { + displayName: "LM Studio", + hasFreeTier: true, + }, +}; diff --git a/src/ipc/shared/language_model_helpers.ts b/src/ipc/shared/language_model_helpers.ts index 911a975..62a8ef7 100644 --- a/src/ipc/shared/language_model_helpers.ts +++ b/src/ipc/shared/language_model_helpers.ts @@ -5,443 +5,12 @@ import { } from "@/db/schema"; import type { LanguageModelProvider, LanguageModel } from "@/ipc/ipc_types"; import { eq } from "drizzle-orm"; - -export const PROVIDERS_THAT_SUPPORT_THINKING: (keyof typeof MODEL_OPTIONS)[] = [ - "google", - "vertex", - "auto", -]; - -export interface ModelOption { - name: string; - displayName: string; - description: string; - dollarSigns?: number; - temperature?: number; - tag?: string; - maxOutputTokens?: number; - contextWindow?: number; -} - -export const MODEL_OPTIONS: Record = { - openai: [ - // https://platform.openai.com/docs/models/gpt-5 - { - name: "gpt-5", - displayName: "GPT 5", - description: "OpenAI's flagship model", - // Technically it's 128k but OpenAI errors if you set max_tokens instead of max_completion_tokens - maxOutputTokens: undefined, - contextWindow: 400_000, - // Requires temperature to be default value (1) - temperature: 1, - dollarSigns: 3, - }, - // https://platform.openai.com/docs/models/gpt-5-mini - { - name: "gpt-5-mini", - displayName: "GPT 5 Mini", - description: "OpenAI's lightweight, but intelligent model", - // Technically it's 128k but OpenAI errors if you set max_tokens instead of max_completion_tokens - maxOutputTokens: undefined, - contextWindow: 400_000, - // Requires temperature to be default value (1) - temperature: 1, - dollarSigns: 2, - }, - // https://platform.openai.com/docs/models/gpt-5-nano - { - name: "gpt-5-nano", - displayName: "GPT 5 Nano", - description: "Fastest, most cost-efficient version of GPT-5", - // Technically it's 128k but OpenAI errors if you set max_tokens instead of max_completion_tokens - maxOutputTokens: undefined, - contextWindow: 400_000, - // Requires temperature to be default value (1) - temperature: 1, - dollarSigns: 1, - }, - // https://platform.openai.com/docs/models/o4-mini - { - name: "o4-mini", - displayName: "o4 mini", - description: "Reasoning model", - // Technically the max output tokens is 100k, *however* if the user has a lot of input tokens, - // then setting a high max output token will cause the request to fail because - // the max output tokens is *included* in the context window limit. - maxOutputTokens: 32_000, - contextWindow: 200_000, - temperature: 0, - dollarSigns: 2, - }, - ], - // https://docs.anthropic.com/en/docs/about-claude/models/all-models#model-comparison-table - anthropic: [ - { - name: "claude-sonnet-4-20250514", - displayName: "Claude 4 Sonnet", - description: "Excellent coder (note: >200k tokens is very expensive!)", - // See comment below for Claude 3.7 Sonnet for why we set this to 16k - maxOutputTokens: 16_000, - contextWindow: 1_000_000, - temperature: 0, - dollarSigns: 5, - }, - { - name: "claude-3-7-sonnet-latest", - displayName: "Claude 3.7 Sonnet", - description: "Excellent coder", - // Technically the max output tokens is 64k, *however* if the user has a lot of input tokens, - // then setting a high max output token will cause the request to fail because - // the max output tokens is *included* in the context window limit, see: - // https://docs.anthropic.com/en/docs/build-with-claude/extended-thinking#max-tokens-and-context-window-size-with-extended-thinking - maxOutputTokens: 16_000, - contextWindow: 200_000, - temperature: 0, - dollarSigns: 4, - }, - { - name: "claude-3-5-sonnet-20241022", - displayName: "Claude 3.5 Sonnet", - description: "Good coder, excellent at following instructions", - maxOutputTokens: 8_000, - contextWindow: 200_000, - temperature: 0, - dollarSigns: 4, - }, - { - name: "claude-3-5-haiku-20241022", - displayName: "Claude 3.5 Haiku", - description: "Lightweight coder", - maxOutputTokens: 8_000, - contextWindow: 200_000, - temperature: 0, - dollarSigns: 2, - }, - ], - google: [ - // https://ai.google.dev/gemini-api/docs/models#gemini-2.5-pro-preview-03-25 - { - name: "gemini-2.5-pro", - displayName: "Gemini 2.5 Pro", - description: "Google's Gemini 2.5 Pro model", - // See Flash 2.5 comment below (go 1 below just to be safe, even though it seems OK now). - maxOutputTokens: 65_536 - 1, - // Gemini context window = input token + output token - contextWindow: 1_048_576, - temperature: 0, - dollarSigns: 3, - }, - // https://ai.google.dev/gemini-api/docs/models#gemini-2.5-flash-preview - { - name: "gemini-2.5-flash", - displayName: "Gemini 2.5 Flash", - description: "Google's Gemini 2.5 Flash model (free tier available)", - // Weirdly for Vertex AI, the output token limit is *exclusive* of the stated limit. - maxOutputTokens: 65_536 - 1, - // Gemini context window = input token + output token - contextWindow: 1_048_576, - temperature: 0, - dollarSigns: 2, - }, - ], - vertex: [ - // Vertex Gemini 2.5 Pro - { - name: "gemini-2.5-pro", - displayName: "Gemini 2.5 Pro", - description: "Vertex Gemini 2.5 Pro", - maxOutputTokens: 65_536 - 1, - contextWindow: 1_048_576, - temperature: 0, - }, - // Vertex Gemini 2.5 Flash - { - name: "gemini-2.5-flash", - displayName: "Gemini 2.5 Flash", - description: "Vertex Gemini 2.5 Flash", - maxOutputTokens: 65_536 - 1, - contextWindow: 1_048_576, - temperature: 0, - }, - ], - openrouter: [ - { - name: "qwen/qwen3-coder:free", - displayName: "Qwen3 Coder (free)", - description: "Use for free (data may be used for training)", - maxOutputTokens: 32_000, - contextWindow: 262_000, - temperature: 0, - dollarSigns: 0, - }, - // https://openrouter.ai/deepseek/deepseek-chat-v3-0324:free - { - name: "deepseek/deepseek-chat-v3.1:free", - displayName: "DeepSeek v3.1 (free)", - description: "Use for free (data may be used for training)", - maxOutputTokens: 32_000, - contextWindow: 128_000, - temperature: 0, - dollarSigns: 0, - }, - { - name: "deepseek/deepseek-chat-v3-0324:free", - displayName: "DeepSeek v3 (free)", - description: "Use for free (data may be used for training)", - maxOutputTokens: 32_000, - contextWindow: 128_000, - temperature: 0, - dollarSigns: 0, - }, - { - name: "qwen/qwen3-coder", - displayName: "Qwen3 Coder", - description: "Qwen's best coding model", - maxOutputTokens: 32_000, - contextWindow: 262_000, - temperature: 0, - dollarSigns: 2, - }, - { - name: "deepseek/deepseek-chat-v3.1", - displayName: "DeepSeek v3.1", - description: "Strong cost-effective model with optional thinking", - maxOutputTokens: 32_000, - contextWindow: 128_000, - temperature: 0, - dollarSigns: 2, - }, - // https://openrouter.ai/moonshotai/kimi-k2 - { - name: "moonshotai/kimi-k2-0905", - displayName: "Kimi K2", - description: "Powerful cost-effective model (updated to 0905)", - maxOutputTokens: 32_000, - contextWindow: 256_000, - temperature: 0, - dollarSigns: 2, - }, - ], - auto: [ - { - name: "auto", - displayName: "Auto", - description: "Automatically selects the best model", - tag: "Default", - // These are below Gemini 2.5 Pro & Flash limits - // which are the ones defaulted to for both regular auto - // and smart auto. - maxOutputTokens: 32_000, - contextWindow: 1_000_000, - temperature: 0, - }, - { - name: "free", - displayName: "Free (OpenRouter)", - description: "Selects from one of the free OpenRouter models", - tag: "Free", - // These are below Gemini 2.5 Pro & Flash limits - // which are the ones defaulted to for both regular auto - // and smart auto. - maxOutputTokens: 32_000, - contextWindow: 128_000, - temperature: 0, - }, - ], - azure: [ - { - name: "gpt-5", - displayName: "GPT-5", - description: "Azure OpenAI GPT-5 model with reasoning capabilities", - maxOutputTokens: 128_000, - contextWindow: 400_000, - temperature: 0, - }, - { - name: "gpt-5-mini", - displayName: "GPT-5 Mini", - description: "Azure OpenAI GPT-5 Mini model", - maxOutputTokens: 128_000, - contextWindow: 400_000, - temperature: 0, - }, - { - name: "gpt-5-nano", - displayName: "GPT-5 Nano", - description: "Azure OpenAI GPT-5 Nano model", - maxOutputTokens: 128_000, - contextWindow: 400_000, - temperature: 0, - }, - { - name: "gpt-5-chat", - displayName: "GPT-5 Chat", - description: "Azure OpenAI GPT-5 Chat model", - maxOutputTokens: 16_384, - contextWindow: 128_000, - temperature: 0, - }, - ], - xai: [ - // https://docs.x.ai/docs/models - { - name: "grok-code-fast-1", - displayName: "Grok Code Fast", - description: "Fast coding model", - maxOutputTokens: 32_000, - contextWindow: 256_000, - temperature: 0, - dollarSigns: 1, - }, - { - name: "grok-4", - displayName: "Grok 4", - description: "Most capable coding model", - maxOutputTokens: 32_000, - contextWindow: 256_000, - temperature: 0, - dollarSigns: 4, - }, - { - name: "grok-3", - displayName: "Grok 3", - description: "Powerful coding model", - maxOutputTokens: 32_000, - contextWindow: 131_072, - temperature: 0, - dollarSigns: 4, - }, - ], - bedrock: [ - { - name: "us.anthropic.claude-sonnet-4-20250514-v1:0", - displayName: "Claude 4 Sonnet", - description: "Excellent coder (note: >200k tokens is very expensive!)", - maxOutputTokens: 16_000, - contextWindow: 1_000_000, - temperature: 0, - }, - { - name: "us.anthropic.claude-3-7-sonnet-20250219-v1:0", - displayName: "Claude 3.7 Sonnet", - description: "Excellent coder", - maxOutputTokens: 16_000, - contextWindow: 200_000, - temperature: 0, - }, - { - name: "us.anthropic.claude-3-5-sonnet-20241022-v2:0", - displayName: "Claude 3.5 Sonnet", - description: "Good coder, excellent at following instructions", - maxOutputTokens: 8_000, - contextWindow: 200_000, - temperature: 0, - }, - ], -}; - -export const FREE_OPENROUTER_MODEL_NAMES = MODEL_OPTIONS.openrouter - .filter((model) => model.name.endsWith(":free")) - .map((model) => model.name); - -export const PROVIDER_TO_ENV_VAR: Record = { - openai: "OPENAI_API_KEY", - anthropic: "ANTHROPIC_API_KEY", - google: "GEMINI_API_KEY", - openrouter: "OPENROUTER_API_KEY", - azure: "AZURE_API_KEY", - xai: "XAI_API_KEY", - bedrock: "AWS_BEARER_TOKEN_BEDROCK", -}; - -export const CLOUD_PROVIDERS: Record< - string, - { - displayName: string; - hasFreeTier?: boolean; - websiteUrl?: string; - gatewayPrefix: string; - secondary?: boolean; - } -> = { - openai: { - displayName: "OpenAI", - hasFreeTier: false, - websiteUrl: "https://platform.openai.com/api-keys", - gatewayPrefix: "", - }, - anthropic: { - displayName: "Anthropic", - hasFreeTier: false, - websiteUrl: "https://console.anthropic.com/settings/keys", - gatewayPrefix: "anthropic/", - }, - google: { - displayName: "Google", - hasFreeTier: true, - websiteUrl: "https://aistudio.google.com/app/apikey", - gatewayPrefix: "gemini/", - }, - vertex: { - displayName: "Google Vertex AI", - hasFreeTier: false, - websiteUrl: "https://console.cloud.google.com/vertex-ai", - // Use the same gateway prefix as Google Gemini for Dyad Pro compatibility. - gatewayPrefix: "gemini/", - secondary: true, - }, - openrouter: { - displayName: "OpenRouter", - hasFreeTier: true, - websiteUrl: "https://openrouter.ai/settings/keys", - gatewayPrefix: "openrouter/", - }, - auto: { - displayName: "Dyad", - websiteUrl: "https://academy.dyad.sh/settings", - gatewayPrefix: "dyad/", - }, - azure: { - displayName: "Azure OpenAI", - hasFreeTier: false, - websiteUrl: "https://portal.azure.com/", - gatewayPrefix: "", - secondary: true, - }, - xai: { - displayName: "xAI", - hasFreeTier: false, - websiteUrl: "https://console.x.ai/", - gatewayPrefix: "xai/", - secondary: true, - }, - bedrock: { - displayName: "AWS Bedrock", - hasFreeTier: false, - websiteUrl: "https://console.aws.amazon.com/bedrock/", - gatewayPrefix: "bedrock/", - secondary: true, - }, -}; - -const LOCAL_PROVIDERS: Record< - string, - { - displayName: string; - hasFreeTier: boolean; - } -> = { - ollama: { - displayName: "Ollama", - hasFreeTier: true, - }, - lmstudio: { - displayName: "LM Studio", - hasFreeTier: true, - }, -}; - +import { + LOCAL_PROVIDERS, + CLOUD_PROVIDERS, + MODEL_OPTIONS, + PROVIDER_TO_ENV_VAR, +} from "./language_model_constants"; /** * Fetches language model providers from both the database (custom) and hardcoded constants (cloud), * merging them with custom providers taking precedence. diff --git a/src/ipc/utils/get_model_client.ts b/src/ipc/utils/get_model_client.ts index 33d90bf..57f66c4 100644 --- a/src/ipc/utils/get_model_client.ts +++ b/src/ipc/utils/get_model_client.ts @@ -15,10 +15,8 @@ import type { } from "../../lib/schemas"; import { getEnvVar } from "./read_env"; import log from "electron-log"; -import { - FREE_OPENROUTER_MODEL_NAMES, - getLanguageModelProviders, -} from "../shared/language_model_helpers"; +import { FREE_OPENROUTER_MODEL_NAMES } from "../shared/language_model_constants"; +import { getLanguageModelProviders } from "../shared/language_model_helpers"; import { LanguageModelProvider } from "../ipc_types"; import { createDyadEngine } from "./llm_engine_provider"; diff --git a/src/ipc/utils/thinking_utils.ts b/src/ipc/utils/thinking_utils.ts index c27f8f2..d475f9f 100644 --- a/src/ipc/utils/thinking_utils.ts +++ b/src/ipc/utils/thinking_utils.ts @@ -1,4 +1,4 @@ -import { PROVIDERS_THAT_SUPPORT_THINKING } from "../shared/language_model_helpers"; +import { PROVIDERS_THAT_SUPPORT_THINKING } from "../shared/language_model_constants"; import type { UserSettings } from "../../lib/schemas"; function getThinkingBudgetTokens(