summaryrefslogtreecommitdiffhomepage
path: root/packages/api/src
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-05-23 19:07:21 +0900
committerAdam Malczewski <[email protected]>2026-05-23 19:07:21 +0900
commit997b00034435440d412f955e05e53f09bae83f9e (patch)
tree22d1f530e9e1a97bd19286456d2f06793bd030f7 /packages/api/src
parentff00dec6ae2971bee38c74cb00fe034de9a839ee (diff)
downloaddispatch-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.ts2
-rw-r--r--packages/api/src/routes/models.ts32
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";