diff options
| author | Adam Malczewski <[email protected]> | 2026-05-23 19:07:21 +0900 |
|---|---|---|
| committer | Adam Malczewski <[email protected]> | 2026-05-23 19:07:21 +0900 |
| commit | 997b00034435440d412f955e05e53f09bae83f9e (patch) | |
| tree | 22d1f530e9e1a97bd19286456d2f06793bd030f7 /packages/api/src | |
| parent | ff00dec6ae2971bee38c74cb00fe034de9a839ee (diff) | |
| download | dispatch-997b00034435440d412f955e05e53f09bae83f9e.tar.gz dispatch-997b00034435440d412f955e05e53f09bae83f9e.zip | |
feat: google gemini provider, adaptive thinking for opus 4.7, model search filter
- Added Google (Gemini) as a provider: add-key UI, env var resolution via resolveApiKey, usage tracking via native models endpoint + gemini.google.com cookie scraping
- @ai-sdk/anthropic upgraded to v3 (adaptive thinking support) with LanguageModelV1 cast for ai v4 compat
- Claude Opus 4.7 uses adaptive thinking (type: adaptive); all other models keep explicit budget tokens
- Model selector modal: search filter with space matching dash/underscore
- Copy button: all tool results truncated at 300 chars
- Sidebar layout fix: Claude Reset panel removed from flex-1 fill to prevent overlap
Diffstat (limited to 'packages/api/src')
| -rw-r--r-- | packages/api/src/agent-manager.ts | 2 | ||||
| -rw-r--r-- | packages/api/src/routes/models.ts | 32 |
2 files changed, 28 insertions, 6 deletions
diff --git a/packages/api/src/agent-manager.ts b/packages/api/src/agent-manager.ts index 5b19eb3..1a28371 100644 --- a/packages/api/src/agent-manager.ts +++ b/packages/api/src/agent-manager.ts @@ -548,7 +548,7 @@ export class AgentManager { } } else { // Standard key: resolve from env var - const envKey = resolveApiKey(key.id); + const envKey = resolveApiKey(key.id, key.env); if (envKey) { apiKey = envKey; baseURL = key.base_url; diff --git a/packages/api/src/routes/models.ts b/packages/api/src/routes/models.ts index b250263..1a54abb 100644 --- a/packages/api/src/routes/models.ts +++ b/packages/api/src/routes/models.ts @@ -6,6 +6,7 @@ import { type ClaudeAccount, fetchAnthropicModels, fetchCopilotUsage, + fetchGoogleUsage, fetchOpencodeUsage, getAccountUsage, getAnthropicHeaders, @@ -112,7 +113,7 @@ modelsRoutes.get("/available", async (c) => { }); } - const apiKeyValue = resolveApiKey(keyId); + const apiKeyValue = resolveApiKey(keyId, key.definition.env); if (!apiKeyValue) { return c.json({ error: `no API key found for ${keyId}` }, 500); } @@ -148,7 +149,7 @@ modelsRoutes.get("/available", async (c) => { return c.json({ error: "failed to parse provider response", details: String(err) }, 502); } - const models = data.data.map((m) => m.id); + const models = data.data.map((m) => m.id.replace(/^models\//, "")); return c.json({ models }); }); @@ -291,7 +292,7 @@ modelsRoutes.get("/key-usage", async (c) => { }, }); } else if (provider === "github-copilot") { - const token = resolveApiKey(keyId); + const token = resolveApiKey(keyId, key.definition.env); if (!token) { return c.json({ error: `no API key found for ${keyId}` }, 502); } @@ -307,6 +308,21 @@ modelsRoutes.get("/key-usage", async (c) => { resetAt: report.resetAt, plan: report.plan, }); + } else if (provider === "google") { + const token = resolveApiKey(keyId, key.definition.env); + if (!token) { + return c.json({ error: `no API key found for ${keyId}. Set GOOGLE_API_KEY env var.` }, 502); + } + const report = await fetchGoogleUsage(token, key.definition.base_url); + if (!report) { + return c.json({ error: "failed to fetch Google usage data" }, 502); + } + return c.json({ + provider: "google", + models: report.models, + currentUsage: report.currentUsage, + weeklyUsage: report.weeklyUsage, + }); } else { return c.json({ error: "usage tracking not supported for this provider" }, 400); } @@ -400,13 +416,13 @@ modelsRoutes.get("/credentials-status", (c) => { // ─── Add key to dispatch.toml ───────────────────────────────── -const VALID_PROVIDERS = ["anthropic", "opencode-go", "github-copilot"] as const; +const VALID_PROVIDERS = ["anthropic", "opencode-go", "google"] as const; type SupportedProvider = (typeof VALID_PROVIDERS)[number]; const PROVIDER_BASE_URLS: Record<SupportedProvider, string> = { anthropic: "https://api.anthropic.com/v1", "opencode-go": "https://opencode.ai/zen/go/v1", - "github-copilot": "https://api.githubcopilot.com", + google: "https://generativelanguage.googleapis.com/v1beta/openai", }; modelsRoutes.post("/add-key", async (c) => { @@ -445,6 +461,12 @@ modelsRoutes.post("/add-key", async (c) => { if (provider === "anthropic") { const credPath = `${homedir()}/.claude/.credentials-${id}.json`; newBlock += `\ncredentials_file = "${credPath}"`; + } else { + const envVar = + provider === "google" + ? "GOOGLE_API_KEY" + : `DISPATCH_${id.toUpperCase().replace(/-/g, "_")}_KEY`; + newBlock += `\nenv = "${envVar}"`; } newBlock += "\n"; |
