From 4bf61bc930571ce1521e39197e36ac751ecd32c6 Mon Sep 17 00:00:00 2001 From: Dylan Huang Date: Mon, 26 Jan 2026 14:43:58 -0800 Subject: [PATCH 1/6] feat(providers): add Fireworks AI provider integration - Add fireworks-models.ts with static catalog of 24 serverless LLM models - Add dynamic model discovery from Fireworks API with pagination - Integrate into onboarding (interactive + non-interactive flows) - Add fireworks-api-key auth choice and --fireworks-api-key CLI flag - Add FIREWORKS_API_KEY to envMap for auto-detection - Add Fireworks to resolveImplicitProviders - Add docs/providers/fireworks.md documentation - Update docs navigation and CLI reference Model IDs use full format: accounts/fireworks/models/ Default model: deepseek-v3p2 (DeepSeek V3.2) --- docs/cli/index.md | 4 +- docs/docs.json | 2 + docs/providers/fireworks.md | 251 ++++++++++++ docs/providers/index.md | 1 + src/agents/fireworks-models.ts | 373 ++++++++++++++++++ src/agents/model-auth.ts | 1 + src/agents/models-config.providers.ts | 17 + src/cli/program/register.onboard.ts | 4 +- src/commands/auth-choice-options.ts | 12 + .../auth-choice.apply.api-providers.ts | 65 +++ .../auth-choice.preferred-provider.ts | 1 + src/commands/onboard-auth.config-core.ts | 83 ++++ src/commands/onboard-auth.credentials.ts | 13 + src/commands/onboard-auth.ts | 7 + .../local/auth-choice.ts | 21 + src/commands/onboard-types.ts | 2 + 16 files changed, 855 insertions(+), 2 deletions(-) create mode 100644 docs/providers/fireworks.md create mode 100644 src/agents/fireworks-models.ts diff --git a/docs/cli/index.md b/docs/cli/index.md index c49677cbf..9f179ff6e 100644 --- a/docs/cli/index.md +++ b/docs/cli/index.md @@ -297,7 +297,7 @@ Options: - `--non-interactive` - `--mode ` - `--flow ` (manual is an alias for advanced) -- `--auth-choice ` +- `--auth-choice ` - `--token-provider ` (non-interactive; used with `--auth-choice token`) - `--token ` (non-interactive; used with `--auth-choice token`) - `--token-profile-id ` (non-interactive; default: `:manual`) @@ -311,6 +311,8 @@ Options: - `--gemini-api-key ` - `--zai-api-key ` - `--minimax-api-key ` +- `--venice-api-key ` +- `--fireworks-api-key ` - `--opencode-zen-api-key ` - `--gateway-port ` - `--gateway-bind ` diff --git a/docs/docs.json b/docs/docs.json index 2cc5ae78b..a2cfa0a13 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -1012,6 +1012,8 @@ "providers/vercel-ai-gateway", "providers/openrouter", "providers/synthetic", + "providers/venice", + "providers/fireworks", "providers/opencode", "providers/glm", "providers/zai" diff --git a/docs/providers/fireworks.md b/docs/providers/fireworks.md new file mode 100644 index 000000000..a7fc0e57c --- /dev/null +++ b/docs/providers/fireworks.md @@ -0,0 +1,251 @@ +--- +summary: "Use Fireworks AI serverless models in Clawdbot" +read_when: + - You want fast serverless inference in Clawdbot + - You want Fireworks AI setup guidance +--- +# Fireworks AI + +Fireworks AI provides fast, cost-effective serverless inference for popular open-source models including DeepSeek, Qwen, Llama, GLM, and more. All models run on optimized infrastructure with low latency and competitive pricing. + +## Why Fireworks in Clawdbot + +- **Fast inference** with optimized serving infrastructure. +- **Wide model selection** including DeepSeek V3.2, Qwen3, Llama 3.3, GLM-4.7, and more. +- **Serverless** — no infrastructure management, pay per token. +- **OpenAI-compatible** `/v1` endpoints. + +## Features + +- **Serverless inference**: No GPU management, instant scaling +- **OpenAI-compatible API**: Standard `/v1` endpoints for easy integration +- **Streaming**: Supported on all models +- **Function calling**: Supported on most models +- **Vision**: Supported on vision-capable models (Qwen VL series) +- **Reasoning models**: DeepSeek R1, Qwen3 Thinking, Kimi K2 Thinking + +## Setup + +### 1. Get API Key + +1. Sign up at [fireworks.ai](https://fireworks.ai) +2. Go to **Account → API Keys → Create API Key** +3. Copy your API key + +### 2. Configure Clawdbot + +**Option A: Environment Variable** + +```bash +export FIREWORKS_API_KEY="fw_xxxxxxxxxxxx" +``` + +**Option B: Interactive Setup (Recommended)** + +```bash +clawdbot onboard --auth-choice fireworks-api-key +``` + +This will: +1. Prompt for your API key (or use existing `FIREWORKS_API_KEY`) +2. Discover available Fireworks models via API +3. Let you pick your default model +4. Configure the provider automatically + +**Option C: Non-interactive** + +```bash +clawdbot onboard --non-interactive \ + --auth-choice fireworks-api-key \ + --fireworks-api-key "fw_xxxxxxxxxxxx" +``` + +### 3. Verify Setup + +```bash +clawdbot chat --model fireworks/accounts/fireworks/models/deepseek-v3p2 "Hello, are you working?" +``` + +## Model Selection + +After setup, Clawdbot discovers models from the Fireworks API. Pick based on your needs: + +- **Default**: `deepseek-v3p2` (DeepSeek V3.2) — strong reasoning, balanced performance. +- **Best reasoning**: `deepseek-r1-0528` or `qwen3-235b-a22b-thinking-2507` +- **Coding**: `qwen3-coder-480b-a35b-instruct` +- **Vision**: `qwen3-vl-235b-a22b-instruct` or `qwen2p5-vl-32b-instruct` + +Change your default model anytime: + +```bash +clawdbot models set fireworks/accounts/fireworks/models/deepseek-v3p2 +clawdbot models set fireworks/accounts/fireworks/models/qwen3-235b-a22b-thinking-2507 +``` + +List all available models: + +```bash +clawdbot models list | grep fireworks +``` + +## Which Model Should I Use? + +| Use Case | Recommended Model | Why | +|----------|-------------------|-----| +| **General chat** | `deepseek-v3p2` | Strong all-around, reasoning support | +| **Complex reasoning** | `deepseek-r1-0528` | Best for step-by-step reasoning | +| **Coding** | `qwen3-coder-480b-a35b-instruct` | Code-optimized, 262k context | +| **Vision tasks** | `qwen3-vl-235b-a22b-instruct` | Best multimodal capabilities | +| **Fast + cheap** | `qwen3-8b` | Lightweight, low latency | +| **Long context** | `kimi-k2-instruct-0905` | 262k context window | + +## Available Models (24 Total) + +### Text Models + +| Model ID | Name | Context | Features | +|----------|------|---------|----------| +| `deepseek-r1-0528` | DeepSeek R1 05/28 | 163k | Reasoning | +| `deepseek-v3-0324` | DeepSeek V3 03-24 | 163k | General | +| `deepseek-v3p1` | DeepSeek V3.1 | 163k | General | +| `deepseek-v3p1-terminus` | DeepSeek V3.1 Terminus | 163k | General | +| `deepseek-v3p2` | DeepSeek V3.2 | 163k | Reasoning | +| `glm-4p6` | GLM-4.6 | 202k | General | +| `glm-4p7` | GLM-4.7 | 202k | Reasoning | +| `gpt-oss-120b` | OpenAI gpt-oss-120b | 131k | General | +| `gpt-oss-20b` | OpenAI gpt-oss-20b | 131k | General | +| `kimi-k2-instruct-0905` | Kimi K2 Instruct 0905 | 262k | Long context | +| `kimi-k2-thinking` | Kimi K2 Thinking | 256k | Reasoning | +| `llama-v3p3-70b-instruct` | Llama 3.3 70B Instruct | 131k | General | +| `minimax-m2` | MiniMax-M2 | 196k | General | +| `minimax-m2p1` | MiniMax-M2.1 | 204k | General | +| `qwen3-235b-a22b` | Qwen3 235B A22B | 131k | General | +| `qwen3-235b-a22b-instruct-2507` | Qwen3 235B A22B Instruct 2507 | 262k | General | +| `qwen3-235b-a22b-thinking-2507` | Qwen3 235B A22B Thinking 2507 | 262k | Reasoning | +| `qwen3-8b` | Qwen3 8B | 40k | Fast | +| `qwen3-coder-480b-a35b-instruct` | Qwen3 Coder 480B A35B Instruct | 262k | Coding | + +### Vision Models + +| Model ID | Name | Context | Features | +|----------|------|---------|----------| +| `qwen2p5-vl-32b-instruct` | Qwen2.5-VL 32B Instruct | 128k | Vision | +| `qwen3-vl-235b-a22b-instruct` | Qwen3 VL 235B A22B Instruct | 262k | Vision | +| `qwen3-vl-235b-a22b-thinking` | Qwen3 VL 235B A22B Thinking | 262k | Vision, reasoning | +| `qwen3-vl-30b-a3b-thinking` | Qwen3 VL 30B A3B Thinking | 262k | Vision, reasoning | + +## Model Discovery + +Clawdbot automatically discovers models from the Fireworks API when `FIREWORKS_API_KEY` is set. The discovery: + +- Fetches all serverless models (`supports_serverless=true`) +- Filters out deprecated models +- Filters out non-LLM models (image generation) +- Falls back to a static catalog if the API is unreachable + +## Model IDs + +Fireworks model IDs use the full resource path format: + +``` +accounts/fireworks/models/ +``` + +When using models in Clawdbot, prefix with the provider: + +```bash +clawdbot chat --model fireworks/accounts/fireworks/models/deepseek-v3p2 +``` + +## Streaming and Tool Support + +| Feature | Support | +|---------|---------| +| **Streaming** | All models | +| **Function calling** | Most models (check `supportsTools` in API) | +| **Vision/Images** | Vision models only | +| **JSON mode** | Supported via `response_format` | + +## Pricing + +Fireworks uses pay-per-token pricing. Check [fireworks.ai/pricing](https://fireworks.ai/pricing) for current rates. Generally: + +- Smaller models (8B-30B): Lower cost, faster +- Larger models (70B+): Higher quality, higher cost +- MoE models: Cost-effective for their capability + +## Usage Examples + +```bash +# Use DeepSeek V3.2 (recommended default) +clawdbot chat --model fireworks/accounts/fireworks/models/deepseek-v3p2 + +# Use reasoning model +clawdbot chat --model fireworks/accounts/fireworks/models/deepseek-r1-0528 + +# Use coding model +clawdbot chat --model fireworks/accounts/fireworks/models/qwen3-coder-480b-a35b-instruct + +# Use vision model +clawdbot chat --model fireworks/accounts/fireworks/models/qwen3-vl-235b-a22b-instruct +``` + +## Troubleshooting + +### API key not recognized + +```bash +echo $FIREWORKS_API_KEY +clawdbot models list | grep fireworks +``` + +Ensure the key is valid and has not expired. + +### Model not available + +The Fireworks model catalog updates dynamically. Run `clawdbot models list` to see currently available models. Some models may be temporarily offline or deprecated. + +### Connection issues + +Fireworks API is at `https://api.fireworks.ai`. Ensure your network allows HTTPS connections. + +### Model discovery fails + +If model discovery fails, Clawdbot falls back to a static catalog of popular models. Check your API key and network connection. + +## Config file example + +```json5 +{ + env: { FIREWORKS_API_KEY: "fw_..." }, + agents: { defaults: { model: { primary: "fireworks/accounts/fireworks/models/deepseek-v3p2" } } }, + models: { + mode: "merge", + providers: { + fireworks: { + baseUrl: "https://api.fireworks.ai/inference/v1", + apiKey: "${FIREWORKS_API_KEY}", + api: "openai-completions", + models: [ + { + id: "accounts/fireworks/models/deepseek-v3p2", + name: "DeepSeek V3.2", + reasoning: true, + input: ["text"], + cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, + contextWindow: 163840, + maxTokens: 8192 + } + ] + } + } + } +} +``` + +## Links + +- [Fireworks AI](https://fireworks.ai) +- [API Documentation](https://docs.fireworks.ai) +- [Pricing](https://fireworks.ai/pricing) +- [Model List](https://fireworks.ai/models) diff --git a/docs/providers/index.md b/docs/providers/index.md index b4779d201..ba4a63026 100644 --- a/docs/providers/index.md +++ b/docs/providers/index.md @@ -45,6 +45,7 @@ See [Venice AI](/providers/venice). - [GLM models](/providers/glm) - [MiniMax](/providers/minimax) - [Venius (Venice AI, privacy-focused)](/providers/venice) +- [Fireworks AI (serverless inference)](/providers/fireworks) - [Ollama (local models)](/providers/ollama) ## Transcription providers diff --git a/src/agents/fireworks-models.ts b/src/agents/fireworks-models.ts new file mode 100644 index 000000000..a4a30cb9a --- /dev/null +++ b/src/agents/fireworks-models.ts @@ -0,0 +1,373 @@ +import type { ModelDefinitionConfig } from "../config/types.js"; + +export const FIREWORKS_BASE_URL = "https://api.fireworks.ai/inference/v1"; +export const FIREWORKS_API_BASE_URL = "https://api.fireworks.ai"; +export const FIREWORKS_ACCOUNT_ID = "fireworks"; +export const FIREWORKS_DEFAULT_MODEL_ID = "accounts/fireworks/models/deepseek-v3p2"; +export const FIREWORKS_DEFAULT_MODEL_REF = `fireworks/${FIREWORKS_DEFAULT_MODEL_ID}`; + +// Fireworks uses pay-per-token pricing; rates vary by model. +// Set to 0 as a default; override in models.json for accurate costs. +export const FIREWORKS_DEFAULT_COST = { + input: 0, + output: 0, + cacheRead: 0, + cacheWrite: 0, +}; + +/** + * Static catalog of Fireworks AI serverless models. + * + * This catalog serves as a fallback when the Fireworks API is unreachable. + * Only includes LLM models (no image generation), non-deprecated models, + * and models that support serverless inference. + * + * Model IDs use the full format: accounts/fireworks/models/ + */ +export const FIREWORKS_MODEL_CATALOG = [ + // DeepSeek models + { + id: "accounts/fireworks/models/deepseek-r1-0528", + name: "DeepSeek R1 05/28", + reasoning: true, + input: ["text"] as const, + contextWindow: 163840, + maxTokens: 8192, + }, + { + id: "accounts/fireworks/models/deepseek-v3-0324", + name: "DeepSeek V3 03-24", + reasoning: false, + input: ["text"] as const, + contextWindow: 163840, + maxTokens: 8192, + }, + { + id: "accounts/fireworks/models/deepseek-v3p1", + name: "DeepSeek V3.1", + reasoning: false, + input: ["text"] as const, + contextWindow: 163840, + maxTokens: 8192, + }, + { + id: "accounts/fireworks/models/deepseek-v3p1-terminus", + name: "DeepSeek V3.1 Terminus", + reasoning: false, + input: ["text"] as const, + contextWindow: 163840, + maxTokens: 8192, + }, + { + id: "accounts/fireworks/models/deepseek-v3p2", + name: "DeepSeek V3.2", + reasoning: true, + input: ["text"] as const, + contextWindow: 163840, + maxTokens: 8192, + }, + + // GLM models + { + id: "accounts/fireworks/models/glm-4p6", + name: "GLM-4.6", + reasoning: false, + input: ["text"] as const, + contextWindow: 202752, + maxTokens: 8192, + }, + { + id: "accounts/fireworks/models/glm-4p7", + name: "GLM-4.7", + reasoning: true, + input: ["text"] as const, + contextWindow: 202752, + maxTokens: 8192, + }, + + // OpenAI gpt-oss models + { + id: "accounts/fireworks/models/gpt-oss-120b", + name: "OpenAI gpt-oss-120b", + reasoning: false, + input: ["text"] as const, + contextWindow: 131072, + maxTokens: 8192, + }, + { + id: "accounts/fireworks/models/gpt-oss-20b", + name: "OpenAI gpt-oss-20b", + reasoning: false, + input: ["text"] as const, + contextWindow: 131072, + maxTokens: 8192, + }, + + // Kimi models + { + id: "accounts/fireworks/models/kimi-k2-instruct-0905", + name: "Kimi K2 Instruct 0905", + reasoning: false, + input: ["text"] as const, + contextWindow: 262144, + maxTokens: 8192, + }, + { + id: "accounts/fireworks/models/kimi-k2-thinking", + name: "Kimi K2 Thinking", + reasoning: true, + input: ["text"] as const, + // API returns 0 but description says 256k + contextWindow: 256000, + maxTokens: 8192, + }, + + // Llama models + { + id: "accounts/fireworks/models/llama-v3p3-70b-instruct", + name: "Llama 3.3 70B Instruct", + reasoning: false, + input: ["text"] as const, + contextWindow: 131072, + maxTokens: 8192, + }, + + // MiniMax models + { + id: "accounts/fireworks/models/minimax-m2", + name: "MiniMax-M2", + reasoning: false, + input: ["text"] as const, + contextWindow: 196608, + maxTokens: 8192, + }, + { + id: "accounts/fireworks/models/minimax-m2p1", + name: "MiniMax-M2.1", + reasoning: false, + input: ["text"] as const, + contextWindow: 204800, + maxTokens: 8192, + }, + + // Qwen text models + { + id: "accounts/fireworks/models/qwen3-235b-a22b", + name: "Qwen3 235B A22B", + reasoning: false, + input: ["text"] as const, + contextWindow: 131072, + maxTokens: 8192, + }, + { + id: "accounts/fireworks/models/qwen3-235b-a22b-instruct-2507", + name: "Qwen3 235B A22B Instruct 2507", + reasoning: false, + input: ["text"] as const, + contextWindow: 262144, + maxTokens: 8192, + }, + { + id: "accounts/fireworks/models/qwen3-235b-a22b-thinking-2507", + name: "Qwen3 235B A22B Thinking 2507", + reasoning: true, + input: ["text"] as const, + contextWindow: 262144, + maxTokens: 8192, + }, + { + id: "accounts/fireworks/models/qwen3-8b", + name: "Qwen3 8B", + reasoning: false, + input: ["text"] as const, + contextWindow: 40960, + maxTokens: 8192, + }, + { + id: "accounts/fireworks/models/qwen3-coder-480b-a35b-instruct", + name: "Qwen3 Coder 480B A35B Instruct", + reasoning: false, + input: ["text"] as const, + contextWindow: 262144, + maxTokens: 8192, + }, + + // Qwen vision models + { + id: "accounts/fireworks/models/qwen2p5-vl-32b-instruct", + name: "Qwen2.5-VL 32B Instruct", + reasoning: false, + input: ["text", "image"] as const, + contextWindow: 128000, + maxTokens: 8192, + }, + { + id: "accounts/fireworks/models/qwen3-vl-235b-a22b-instruct", + name: "Qwen3 VL 235B A22B Instruct", + reasoning: false, + input: ["text", "image"] as const, + contextWindow: 262144, + maxTokens: 8192, + }, + { + id: "accounts/fireworks/models/qwen3-vl-235b-a22b-thinking", + name: "Qwen3 VL 235B A22B Thinking", + reasoning: true, + input: ["text", "image"] as const, + contextWindow: 262144, + maxTokens: 8192, + }, + { + id: "accounts/fireworks/models/qwen3-vl-30b-a3b-thinking", + name: "Qwen3 VL 30B A3B Thinking", + reasoning: true, + input: ["text", "image"] as const, + contextWindow: 262144, + maxTokens: 8192, + }, +] as const; + +export type FireworksCatalogEntry = (typeof FIREWORKS_MODEL_CATALOG)[number]; + +/** + * Build a ModelDefinitionConfig from a Fireworks catalog entry. + */ +export function buildFireworksModelDefinition(entry: FireworksCatalogEntry): ModelDefinitionConfig { + return { + id: entry.id, + name: entry.name, + reasoning: entry.reasoning, + input: [...entry.input], + cost: FIREWORKS_DEFAULT_COST, + contextWindow: entry.contextWindow, + maxTokens: entry.maxTokens, + }; +} + +// Fireworks API response types +interface FireworksModel { + name: string; // Full resource name: accounts/fireworks/models/ + displayName?: string; + description?: string; + contextLength?: number; + kind?: string; // HF_BASE_MODEL, FLUMINA_BASE_MODEL, etc. + supportsImageInput?: boolean; + supportsTools?: boolean; + supportsServerless?: boolean; + deprecationDate?: { year: number; month: number; day: number } | null; +} + +interface FireworksModelsResponse { + models: FireworksModel[]; + nextPageToken?: string; +} + +function isDeprecated( + deprecationDate?: { year: number; month: number; day: number } | null, +): boolean { + if (!deprecationDate) return false; + const { year, month, day } = deprecationDate; + const depDate = new Date(year, month - 1, day); + return depDate < new Date(); +} + +function isLlmModel(model: FireworksModel): boolean { + // Skip image generation models (FLUX, etc.) - they use FLUMINA_BASE_MODEL kind + // and have contextLength of 0 + if (model.kind === "FLUMINA_BASE_MODEL") return false; + if (!model.contextLength || model.contextLength === 0) return false; + return true; +} + +/** + * Discover serverless LLM models from Fireworks API with fallback to static catalog. + */ +export async function discoverFireworksModels(params?: { + apiKey?: string; +}): Promise { + // Skip API discovery in test environment + if (process.env.NODE_ENV === "test" || process.env.VITEST) { + return FIREWORKS_MODEL_CATALOG.map(buildFireworksModelDefinition); + } + + const apiKey = params?.apiKey ?? process.env.FIREWORKS_API_KEY; + if (!apiKey) { + console.warn("[fireworks-models] No API key available, using static catalog"); + return FIREWORKS_MODEL_CATALOG.map(buildFireworksModelDefinition); + } + + try { + const allModels: FireworksModel[] = []; + let pageToken: string | undefined; + + // Paginate through results + do { + const url = new URL(`${FIREWORKS_API_BASE_URL}/v1/accounts/${FIREWORKS_ACCOUNT_ID}/models`); + url.searchParams.set("filter", "supports_serverless=true"); + url.searchParams.set("pageSize", "200"); + if (pageToken) { + url.searchParams.set("pageToken", pageToken); + } + + const response = await fetch(url.toString(), { + headers: { Authorization: `Bearer ${apiKey}` }, + signal: AbortSignal.timeout(10000), + }); + + if (!response.ok) { + console.warn( + `[fireworks-models] Failed to list models: HTTP ${response.status}, using static catalog`, + ); + return FIREWORKS_MODEL_CATALOG.map(buildFireworksModelDefinition); + } + + const data = (await response.json()) as FireworksModelsResponse; + allModels.push(...(data.models ?? [])); + pageToken = data.nextPageToken; + } while (pageToken); + + // Filter and build model definitions + const catalogById = new Map( + FIREWORKS_MODEL_CATALOG.map((m) => [m.id, m]), + ); + const models: ModelDefinitionConfig[] = []; + + for (const apiModel of allModels) { + // Skip deprecated models + if (isDeprecated(apiModel.deprecationDate)) continue; + // Skip non-LLM models (image gen, etc.) + if (!isLlmModel(apiModel)) continue; + + // Use the full name as the ID (accounts/fireworks/models/) + const id = apiModel.name; + const catalogEntry = catalogById.get(id); + + if (catalogEntry) { + // Use catalog metadata for known models + models.push(buildFireworksModelDefinition(catalogEntry)); + } else { + // Create definition for newly discovered models not in catalog + const isReasoning = + id.toLowerCase().includes("r1") || + id.toLowerCase().includes("thinking") || + id.toLowerCase().includes("reasoning") || + (apiModel.description?.toLowerCase().includes("reasoning") ?? false); + + models.push({ + id, + name: apiModel.displayName ?? id.replace(/^accounts\/fireworks\/models\//, ""), + reasoning: isReasoning, + input: apiModel.supportsImageInput ? ["text", "image"] : ["text"], + cost: FIREWORKS_DEFAULT_COST, + contextWindow: apiModel.contextLength ?? 128000, + maxTokens: 8192, + }); + } + } + + return models.length > 0 ? models : FIREWORKS_MODEL_CATALOG.map(buildFireworksModelDefinition); + } catch (error) { + console.warn(`[fireworks-models] Discovery failed: ${String(error)}, using static catalog`); + return FIREWORKS_MODEL_CATALOG.map(buildFireworksModelDefinition); + } +} diff --git a/src/agents/model-auth.ts b/src/agents/model-auth.ts index 680d0f53c..0ca6c98b3 100644 --- a/src/agents/model-auth.ts +++ b/src/agents/model-auth.ts @@ -283,6 +283,7 @@ export function resolveEnvApiKey(provider: string): EnvApiKeyResult | null { minimax: "MINIMAX_API_KEY", synthetic: "SYNTHETIC_API_KEY", venice: "VENICE_API_KEY", + fireworks: "FIREWORKS_API_KEY", mistral: "MISTRAL_API_KEY", opencode: "OPENCODE_API_KEY", }; diff --git a/src/agents/models-config.providers.ts b/src/agents/models-config.providers.ts index 996f09dd0..52271ecf9 100644 --- a/src/agents/models-config.providers.ts +++ b/src/agents/models-config.providers.ts @@ -12,6 +12,7 @@ import { SYNTHETIC_BASE_URL, SYNTHETIC_MODEL_CATALOG, } from "./synthetic-models.js"; +import { discoverFireworksModels, FIREWORKS_BASE_URL } from "./fireworks-models.js"; import { discoverVeniceModels, VENICE_BASE_URL } from "./venice-models.js"; type ModelsConfig = NonNullable; @@ -350,6 +351,15 @@ async function buildVeniceProvider(): Promise { }; } +async function buildFireworksProvider(apiKey?: string): Promise { + const models = await discoverFireworksModels({ apiKey }); + return { + baseUrl: FIREWORKS_BASE_URL, + api: "openai-completions", + models, + }; +} + async function buildOllamaProvider(): Promise { const models = await discoverOllamaModels(); return { @@ -402,6 +412,13 @@ export async function resolveImplicitProviders(params: { providers.venice = { ...(await buildVeniceProvider()), apiKey: veniceKey }; } + const fireworksKey = + resolveEnvApiKeyVarName("fireworks") ?? + resolveApiKeyFromProfiles({ provider: "fireworks", store: authStore }); + if (fireworksKey) { + providers.fireworks = { ...(await buildFireworksProvider(fireworksKey)), apiKey: fireworksKey }; + } + const qwenProfiles = listProfilesForProvider(authStore, "qwen-portal"); if (qwenProfiles.length > 0) { providers["qwen-portal"] = { diff --git a/src/cli/program/register.onboard.ts b/src/cli/program/register.onboard.ts index eac6a60df..f551de485 100644 --- a/src/cli/program/register.onboard.ts +++ b/src/cli/program/register.onboard.ts @@ -52,7 +52,7 @@ export function registerOnboardCommand(program: Command) { .option("--mode ", "Wizard mode: local|remote") .option( "--auth-choice ", - "Auth: setup-token|token|chutes|openai-codex|openai-api-key|openrouter-api-key|ai-gateway-api-key|moonshot-api-key|kimi-code-api-key|synthetic-api-key|venice-api-key|gemini-api-key|zai-api-key|apiKey|minimax-api|minimax-api-lightning|opencode-zen|skip", + "Auth: setup-token|claude-cli|token|chutes|openai-codex|openai-api-key|openrouter-api-key|ai-gateway-api-key|moonshot-api-key|kimi-code-api-key|synthetic-api-key|venice-api-key|fireworks-api-key|codex-cli|gemini-api-key|zai-api-key|apiKey|minimax-api|minimax-api-lightning|opencode-zen|skip", ) .option( "--token-provider ", @@ -75,6 +75,7 @@ export function registerOnboardCommand(program: Command) { .option("--minimax-api-key ", "MiniMax API key") .option("--synthetic-api-key ", "Synthetic API key") .option("--venice-api-key ", "Venice API key") + .option("--fireworks-api-key ", "Fireworks API key") .option("--opencode-zen-api-key ", "OpenCode Zen API key") .option("--gateway-port ", "Gateway port") .option("--gateway-bind ", "Gateway bind: loopback|tailnet|lan|auto|custom") @@ -125,6 +126,7 @@ export function registerOnboardCommand(program: Command) { minimaxApiKey: opts.minimaxApiKey as string | undefined, syntheticApiKey: opts.syntheticApiKey as string | undefined, veniceApiKey: opts.veniceApiKey as string | undefined, + fireworksApiKey: opts.fireworksApiKey as string | undefined, opencodeZenApiKey: opts.opencodeZenApiKey as string | undefined, gatewayPort: typeof gatewayPort === "number" && Number.isFinite(gatewayPort) diff --git a/src/commands/auth-choice-options.ts b/src/commands/auth-choice-options.ts index 6b49ff17b..75b795b1c 100644 --- a/src/commands/auth-choice-options.ts +++ b/src/commands/auth-choice-options.ts @@ -20,6 +20,7 @@ export type AuthChoiceGroupId = | "minimax" | "synthetic" | "venice" + | "fireworks" | "qwen"; export type AuthChoiceGroup = { @@ -71,6 +72,12 @@ const AUTH_CHOICE_GROUP_DEFS: { hint: "Privacy-focused (uncensored models)", choices: ["venice-api-key"], }, + { + value: "fireworks", + label: "Fireworks AI", + hint: "Serverless inference (DeepSeek, Qwen, Llama)", + choices: ["fireworks-api-key"], + }, { value: "google", label: "Google", @@ -147,6 +154,11 @@ export function buildAuthChoiceOptions(params: { label: "Venice AI API key", hint: "Privacy-focused inference (uncensored models)", }); + options.push({ + value: "fireworks-api-key", + label: "Fireworks AI API key", + hint: "Serverless inference (DeepSeek, Qwen, Llama, and more)", + }); options.push({ value: "github-copilot", label: "GitHub Copilot (GitHub device login)", diff --git a/src/commands/auth-choice.apply.api-providers.ts b/src/commands/auth-choice.apply.api-providers.ts index 8be02008b..4c85fee16 100644 --- a/src/commands/auth-choice.apply.api-providers.ts +++ b/src/commands/auth-choice.apply.api-providers.ts @@ -13,6 +13,8 @@ import { } from "./google-gemini-model-default.js"; import { applyAuthProfileConfig, + applyFireworksConfig, + applyFireworksProviderConfig, applyKimiCodeConfig, applyKimiCodeProviderConfig, applyMoonshotConfig, @@ -28,12 +30,14 @@ import { applyVercelAiGatewayConfig, applyVercelAiGatewayProviderConfig, applyZaiConfig, + FIREWORKS_DEFAULT_MODEL_REF, KIMI_CODE_MODEL_REF, MOONSHOT_DEFAULT_MODEL_REF, OPENROUTER_DEFAULT_MODEL_REF, SYNTHETIC_DEFAULT_MODEL_REF, VENICE_DEFAULT_MODEL_REF, VERCEL_AI_GATEWAY_DEFAULT_MODEL_REF, + setFireworksApiKey, setGeminiApiKey, setKimiCodeApiKey, setMoonshotApiKey, @@ -83,6 +87,8 @@ export async function applyAuthChoiceApiProviders( authChoice = "synthetic-api-key"; } else if (params.opts.tokenProvider === "venice") { authChoice = "venice-api-key"; + } else if (params.opts.tokenProvider === "fireworks") { + authChoice = "fireworks-api-key"; } else if (params.opts.tokenProvider === "opencode") { authChoice = "opencode-zen"; } @@ -522,6 +528,65 @@ export async function applyAuthChoiceApiProviders( return { config: nextConfig, agentModelOverride }; } + if (authChoice === "fireworks-api-key") { + let hasCredential = false; + + if (!hasCredential && params.opts?.token && params.opts?.tokenProvider === "fireworks") { + await setFireworksApiKey(normalizeApiKeyInput(params.opts.token), params.agentDir); + hasCredential = true; + } + + if (!hasCredential) { + await params.prompter.note( + [ + "Fireworks AI provides fast serverless inference for open models.", + "Get your API key at: https://fireworks.ai/account/api-keys", + "Supports DeepSeek, Qwen, Llama, and many more models.", + ].join("\n"), + "Fireworks AI", + ); + } + + const envKey = resolveEnvApiKey("fireworks"); + if (envKey) { + const useExisting = await params.prompter.confirm({ + message: `Use existing FIREWORKS_API_KEY (${envKey.source}, ${formatApiKeyPreview(envKey.apiKey)})?`, + initialValue: true, + }); + if (useExisting) { + await setFireworksApiKey(envKey.apiKey, params.agentDir); + hasCredential = true; + } + } + if (!hasCredential) { + const key = await params.prompter.text({ + message: "Enter Fireworks AI API key", + validate: validateApiKeyInput, + }); + await setFireworksApiKey(normalizeApiKeyInput(String(key)), params.agentDir); + } + nextConfig = applyAuthProfileConfig(nextConfig, { + profileId: "fireworks:default", + provider: "fireworks", + mode: "api_key", + }); + { + const applied = await applyDefaultModelChoice({ + config: nextConfig, + setDefaultModel: params.setDefaultModel, + defaultModel: FIREWORKS_DEFAULT_MODEL_REF, + applyDefaultConfig: applyFireworksConfig, + applyProviderConfig: applyFireworksProviderConfig, + noteDefault: FIREWORKS_DEFAULT_MODEL_REF, + noteAgentModel, + prompter: params.prompter, + }); + nextConfig = applied.config; + agentModelOverride = applied.agentModelOverride ?? agentModelOverride; + } + return { config: nextConfig, agentModelOverride }; + } + if (authChoice === "opencode-zen") { let hasCredential = false; if (!hasCredential && params.opts?.token && params.opts?.tokenProvider === "opencode") { diff --git a/src/commands/auth-choice.preferred-provider.ts b/src/commands/auth-choice.preferred-provider.ts index 6fe26b59a..1d8a56e1c 100644 --- a/src/commands/auth-choice.preferred-provider.ts +++ b/src/commands/auth-choice.preferred-provider.ts @@ -20,6 +20,7 @@ const PREFERRED_PROVIDER_BY_AUTH_CHOICE: Partial> = { "zai-api-key": "zai", "synthetic-api-key": "synthetic", "venice-api-key": "venice", + "fireworks-api-key": "fireworks", "github-copilot": "github-copilot", "copilot-proxy": "copilot-proxy", "minimax-cloud": "minimax", diff --git a/src/commands/onboard-auth.config-core.ts b/src/commands/onboard-auth.config-core.ts index 0d3a8523a..5ed3ac583 100644 --- a/src/commands/onboard-auth.config-core.ts +++ b/src/commands/onboard-auth.config-core.ts @@ -1,3 +1,9 @@ +import { + buildFireworksModelDefinition, + FIREWORKS_BASE_URL, + FIREWORKS_DEFAULT_MODEL_REF, + FIREWORKS_MODEL_CATALOG, +} from "../agents/fireworks-models.js"; import { buildSyntheticModelDefinition, SYNTHETIC_BASE_URL, @@ -411,6 +417,83 @@ export function applyVeniceConfig(cfg: ClawdbotConfig): ClawdbotConfig { }; } +/** + * Apply Fireworks provider configuration without changing the default model. + * Registers Fireworks models and sets up the provider, but preserves existing model selection. + */ +export function applyFireworksProviderConfig(cfg: ClawdbotConfig): ClawdbotConfig { + const models = { ...cfg.agents?.defaults?.models }; + models[FIREWORKS_DEFAULT_MODEL_REF] = { + ...models[FIREWORKS_DEFAULT_MODEL_REF], + alias: models[FIREWORKS_DEFAULT_MODEL_REF]?.alias ?? "DeepSeek V3.2", + }; + + const providers = { ...cfg.models?.providers }; + const existingProvider = providers.fireworks; + const existingModels = Array.isArray(existingProvider?.models) ? existingProvider.models : []; + const fireworksModels = FIREWORKS_MODEL_CATALOG.map(buildFireworksModelDefinition); + const mergedModels = [ + ...existingModels, + ...fireworksModels.filter( + (model) => !existingModels.some((existing) => existing.id === model.id), + ), + ]; + const { apiKey: existingApiKey, ...existingProviderRest } = (existingProvider ?? {}) as Record< + string, + unknown + > as { apiKey?: string }; + const resolvedApiKey = typeof existingApiKey === "string" ? existingApiKey : undefined; + const normalizedApiKey = resolvedApiKey?.trim(); + providers.fireworks = { + ...existingProviderRest, + baseUrl: FIREWORKS_BASE_URL, + api: "openai-completions", + ...(normalizedApiKey ? { apiKey: normalizedApiKey } : {}), + models: mergedModels.length > 0 ? mergedModels : fireworksModels, + }; + + return { + ...cfg, + agents: { + ...cfg.agents, + defaults: { + ...cfg.agents?.defaults, + models, + }, + }, + models: { + mode: cfg.models?.mode ?? "merge", + providers, + }, + }; +} + +/** + * Apply Fireworks provider configuration AND set Fireworks as the default model. + * Use this when Fireworks is the primary provider choice during onboarding. + */ +export function applyFireworksConfig(cfg: ClawdbotConfig): ClawdbotConfig { + const next = applyFireworksProviderConfig(cfg); + const existingModel = next.agents?.defaults?.model; + return { + ...next, + agents: { + ...next.agents, + defaults: { + ...next.agents?.defaults, + model: { + ...(existingModel && "fallbacks" in (existingModel as Record) + ? { + fallbacks: (existingModel as { fallbacks?: string[] }).fallbacks, + } + : undefined), + primary: FIREWORKS_DEFAULT_MODEL_REF, + }, + }, + }, + }; +} + export function applyAuthProfileConfig( cfg: ClawdbotConfig, params: { diff --git a/src/commands/onboard-auth.credentials.ts b/src/commands/onboard-auth.credentials.ts index 0c7dff409..f5d080592 100644 --- a/src/commands/onboard-auth.credentials.ts +++ b/src/commands/onboard-auth.credentials.ts @@ -112,6 +112,19 @@ export async function setVeniceApiKey(key: string, agentDir?: string) { }); } +export async function setFireworksApiKey(key: string, agentDir?: string) { + // Write to resolved agent dir so gateway finds credentials on startup. + upsertAuthProfile({ + profileId: "fireworks:default", + credential: { + type: "api_key", + provider: "fireworks", + key, + }, + agentDir: resolveAuthAgentDir(agentDir), + }); +} + export const ZAI_DEFAULT_MODEL_REF = "zai/glm-4.7"; export const OPENROUTER_DEFAULT_MODEL_REF = "openrouter/auto"; export const VERCEL_AI_GATEWAY_DEFAULT_MODEL_REF = "vercel-ai-gateway/anthropic/claude-opus-4.5"; diff --git a/src/commands/onboard-auth.ts b/src/commands/onboard-auth.ts index b122d89cf..43668ec47 100644 --- a/src/commands/onboard-auth.ts +++ b/src/commands/onboard-auth.ts @@ -1,3 +1,7 @@ +export { + FIREWORKS_DEFAULT_MODEL_ID, + FIREWORKS_DEFAULT_MODEL_REF, +} from "../agents/fireworks-models.js"; export { SYNTHETIC_DEFAULT_MODEL_ID, SYNTHETIC_DEFAULT_MODEL_REF, @@ -5,6 +9,8 @@ export { export { VENICE_DEFAULT_MODEL_ID, VENICE_DEFAULT_MODEL_REF } from "../agents/venice-models.js"; export { applyAuthProfileConfig, + applyFireworksConfig, + applyFireworksProviderConfig, applyKimiCodeConfig, applyKimiCodeProviderConfig, applyMoonshotConfig, @@ -35,6 +41,7 @@ export { export { OPENROUTER_DEFAULT_MODEL_REF, setAnthropicApiKey, + setFireworksApiKey, setGeminiApiKey, setKimiCodeApiKey, setMinimaxApiKey, diff --git a/src/commands/onboard-non-interactive/local/auth-choice.ts b/src/commands/onboard-non-interactive/local/auth-choice.ts index c5558596a..18c6cb66c 100644 --- a/src/commands/onboard-non-interactive/local/auth-choice.ts +++ b/src/commands/onboard-non-interactive/local/auth-choice.ts @@ -8,6 +8,7 @@ import { buildTokenProfileId, validateAnthropicSetupToken } from "../../auth-tok import { applyGoogleGeminiModelDefault } from "../../google-gemini-model-default.js"; import { applyAuthProfileConfig, + applyFireworksConfig, applyKimiCodeConfig, applyMinimaxApiConfig, applyMinimaxConfig, @@ -19,6 +20,7 @@ import { applyVercelAiGatewayConfig, applyZaiConfig, setAnthropicApiKey, + setFireworksApiKey, setGeminiApiKey, setKimiCodeApiKey, setMinimaxApiKey, @@ -309,6 +311,25 @@ export async function applyNonInteractiveAuthChoice(params: { return applyVeniceConfig(nextConfig); } + if (authChoice === "fireworks-api-key") { + const resolved = await resolveNonInteractiveApiKey({ + provider: "fireworks", + cfg: baseConfig, + flagValue: opts.fireworksApiKey, + flagName: "--fireworks-api-key", + envVar: "FIREWORKS_API_KEY", + runtime, + }); + if (!resolved) return null; + if (resolved.source !== "profile") await setFireworksApiKey(resolved.key); + nextConfig = applyAuthProfileConfig(nextConfig, { + profileId: "fireworks:default", + provider: "fireworks", + mode: "api_key", + }); + return applyFireworksConfig(nextConfig); + } + if ( authChoice === "minimax-cloud" || authChoice === "minimax-api" || diff --git a/src/commands/onboard-types.ts b/src/commands/onboard-types.ts index aa1d9afe0..ef1af7a72 100644 --- a/src/commands/onboard-types.ts +++ b/src/commands/onboard-types.ts @@ -17,6 +17,7 @@ export type AuthChoice = | "kimi-code-api-key" | "synthetic-api-key" | "venice-api-key" + | "fireworks-api-key" | "codex-cli" | "apiKey" | "gemini-api-key" @@ -70,6 +71,7 @@ export type OnboardOptions = { minimaxApiKey?: string; syntheticApiKey?: string; veniceApiKey?: string; + fireworksApiKey?: string; opencodeZenApiKey?: string; gatewayPort?: number; gatewayBind?: GatewayBind; From a9c2ccdb3bae553d0f39d49ae3a80bf629d15117 Mon Sep 17 00:00:00 2001 From: Dylan Huang Date: Mon, 26 Jan 2026 15:10:09 -0800 Subject: [PATCH 2/6] fix(fireworks): correct reasoning field for models and simplify to static catalog - Set reasoning=true for gpt-oss models ("powerful reasoning, agentic tasks") - Set reasoning=true for MiniMax-M2/M2.1 ("long-thinking") - Set reasoning=true for GLM-4.6 ("advanced thinking controls") - Set reasoning=false for DeepSeek V3.2 (standard chat model) - Remove dynamic API fetching, use static catalog only - Remove unused API constants and types --- src/agents/fireworks-models.ts | 148 +++----------------------- src/agents/models-config.providers.ts | 6 +- 2 files changed, 15 insertions(+), 139 deletions(-) diff --git a/src/agents/fireworks-models.ts b/src/agents/fireworks-models.ts index a4a30cb9a..556a5d70a 100644 --- a/src/agents/fireworks-models.ts +++ b/src/agents/fireworks-models.ts @@ -1,8 +1,6 @@ import type { ModelDefinitionConfig } from "../config/types.js"; export const FIREWORKS_BASE_URL = "https://api.fireworks.ai/inference/v1"; -export const FIREWORKS_API_BASE_URL = "https://api.fireworks.ai"; -export const FIREWORKS_ACCOUNT_ID = "fireworks"; export const FIREWORKS_DEFAULT_MODEL_ID = "accounts/fireworks/models/deepseek-v3p2"; export const FIREWORKS_DEFAULT_MODEL_REF = `fireworks/${FIREWORKS_DEFAULT_MODEL_ID}`; @@ -18,7 +16,6 @@ export const FIREWORKS_DEFAULT_COST = { /** * Static catalog of Fireworks AI serverless models. * - * This catalog serves as a fallback when the Fireworks API is unreachable. * Only includes LLM models (no image generation), non-deprecated models, * and models that support serverless inference. * @@ -61,17 +58,17 @@ export const FIREWORKS_MODEL_CATALOG = [ { id: "accounts/fireworks/models/deepseek-v3p2", name: "DeepSeek V3.2", - reasoning: true, + reasoning: false, input: ["text"] as const, contextWindow: 163840, maxTokens: 8192, }, - // GLM models + // GLM models - both support "advanced thinking controls" { id: "accounts/fireworks/models/glm-4p6", name: "GLM-4.6", - reasoning: false, + reasoning: true, input: ["text"] as const, contextWindow: 202752, maxTokens: 8192, @@ -85,11 +82,11 @@ export const FIREWORKS_MODEL_CATALOG = [ maxTokens: 8192, }, - // OpenAI gpt-oss models + // OpenAI gpt-oss models - designed for "powerful reasoning, agentic tasks" { id: "accounts/fireworks/models/gpt-oss-120b", name: "OpenAI gpt-oss-120b", - reasoning: false, + reasoning: true, input: ["text"] as const, contextWindow: 131072, maxTokens: 8192, @@ -97,7 +94,7 @@ export const FIREWORKS_MODEL_CATALOG = [ { id: "accounts/fireworks/models/gpt-oss-20b", name: "OpenAI gpt-oss-20b", - reasoning: false, + reasoning: true, input: ["text"] as const, contextWindow: 131072, maxTokens: 8192, @@ -132,11 +129,11 @@ export const FIREWORKS_MODEL_CATALOG = [ maxTokens: 8192, }, - // MiniMax models + // MiniMax models - "long-thinking" and "complex reasoning tasks" { id: "accounts/fireworks/models/minimax-m2", name: "MiniMax-M2", - reasoning: false, + reasoning: true, input: ["text"] as const, contextWindow: 196608, maxTokens: 8192, @@ -144,7 +141,7 @@ export const FIREWORKS_MODEL_CATALOG = [ { id: "accounts/fireworks/models/minimax-m2p1", name: "MiniMax-M2.1", - reasoning: false, + reasoning: true, input: ["text"] as const, contextWindow: 204800, maxTokens: 8192, @@ -244,130 +241,9 @@ export function buildFireworksModelDefinition(entry: FireworksCatalogEntry): Mod }; } -// Fireworks API response types -interface FireworksModel { - name: string; // Full resource name: accounts/fireworks/models/ - displayName?: string; - description?: string; - contextLength?: number; - kind?: string; // HF_BASE_MODEL, FLUMINA_BASE_MODEL, etc. - supportsImageInput?: boolean; - supportsTools?: boolean; - supportsServerless?: boolean; - deprecationDate?: { year: number; month: number; day: number } | null; -} - -interface FireworksModelsResponse { - models: FireworksModel[]; - nextPageToken?: string; -} - -function isDeprecated( - deprecationDate?: { year: number; month: number; day: number } | null, -): boolean { - if (!deprecationDate) return false; - const { year, month, day } = deprecationDate; - const depDate = new Date(year, month - 1, day); - return depDate < new Date(); -} - -function isLlmModel(model: FireworksModel): boolean { - // Skip image generation models (FLUX, etc.) - they use FLUMINA_BASE_MODEL kind - // and have contextLength of 0 - if (model.kind === "FLUMINA_BASE_MODEL") return false; - if (!model.contextLength || model.contextLength === 0) return false; - return true; -} - /** - * Discover serverless LLM models from Fireworks API with fallback to static catalog. + * Returns Fireworks models from the static catalog. */ -export async function discoverFireworksModels(params?: { - apiKey?: string; -}): Promise { - // Skip API discovery in test environment - if (process.env.NODE_ENV === "test" || process.env.VITEST) { - return FIREWORKS_MODEL_CATALOG.map(buildFireworksModelDefinition); - } - - const apiKey = params?.apiKey ?? process.env.FIREWORKS_API_KEY; - if (!apiKey) { - console.warn("[fireworks-models] No API key available, using static catalog"); - return FIREWORKS_MODEL_CATALOG.map(buildFireworksModelDefinition); - } - - try { - const allModels: FireworksModel[] = []; - let pageToken: string | undefined; - - // Paginate through results - do { - const url = new URL(`${FIREWORKS_API_BASE_URL}/v1/accounts/${FIREWORKS_ACCOUNT_ID}/models`); - url.searchParams.set("filter", "supports_serverless=true"); - url.searchParams.set("pageSize", "200"); - if (pageToken) { - url.searchParams.set("pageToken", pageToken); - } - - const response = await fetch(url.toString(), { - headers: { Authorization: `Bearer ${apiKey}` }, - signal: AbortSignal.timeout(10000), - }); - - if (!response.ok) { - console.warn( - `[fireworks-models] Failed to list models: HTTP ${response.status}, using static catalog`, - ); - return FIREWORKS_MODEL_CATALOG.map(buildFireworksModelDefinition); - } - - const data = (await response.json()) as FireworksModelsResponse; - allModels.push(...(data.models ?? [])); - pageToken = data.nextPageToken; - } while (pageToken); - - // Filter and build model definitions - const catalogById = new Map( - FIREWORKS_MODEL_CATALOG.map((m) => [m.id, m]), - ); - const models: ModelDefinitionConfig[] = []; - - for (const apiModel of allModels) { - // Skip deprecated models - if (isDeprecated(apiModel.deprecationDate)) continue; - // Skip non-LLM models (image gen, etc.) - if (!isLlmModel(apiModel)) continue; - - // Use the full name as the ID (accounts/fireworks/models/) - const id = apiModel.name; - const catalogEntry = catalogById.get(id); - - if (catalogEntry) { - // Use catalog metadata for known models - models.push(buildFireworksModelDefinition(catalogEntry)); - } else { - // Create definition for newly discovered models not in catalog - const isReasoning = - id.toLowerCase().includes("r1") || - id.toLowerCase().includes("thinking") || - id.toLowerCase().includes("reasoning") || - (apiModel.description?.toLowerCase().includes("reasoning") ?? false); - - models.push({ - id, - name: apiModel.displayName ?? id.replace(/^accounts\/fireworks\/models\//, ""), - reasoning: isReasoning, - input: apiModel.supportsImageInput ? ["text", "image"] : ["text"], - cost: FIREWORKS_DEFAULT_COST, - contextWindow: apiModel.contextLength ?? 128000, - maxTokens: 8192, - }); - } - } - - return models.length > 0 ? models : FIREWORKS_MODEL_CATALOG.map(buildFireworksModelDefinition); - } catch (error) { - console.warn(`[fireworks-models] Discovery failed: ${String(error)}, using static catalog`); - return FIREWORKS_MODEL_CATALOG.map(buildFireworksModelDefinition); - } +export function discoverFireworksModels(): ModelDefinitionConfig[] { + return FIREWORKS_MODEL_CATALOG.map(buildFireworksModelDefinition); } diff --git a/src/agents/models-config.providers.ts b/src/agents/models-config.providers.ts index 52271ecf9..592313d63 100644 --- a/src/agents/models-config.providers.ts +++ b/src/agents/models-config.providers.ts @@ -351,8 +351,8 @@ async function buildVeniceProvider(): Promise { }; } -async function buildFireworksProvider(apiKey?: string): Promise { - const models = await discoverFireworksModels({ apiKey }); +function buildFireworksProvider(): ProviderConfig { + const models = discoverFireworksModels(); return { baseUrl: FIREWORKS_BASE_URL, api: "openai-completions", @@ -416,7 +416,7 @@ export async function resolveImplicitProviders(params: { resolveEnvApiKeyVarName("fireworks") ?? resolveApiKeyFromProfiles({ provider: "fireworks", store: authStore }); if (fireworksKey) { - providers.fireworks = { ...(await buildFireworksProvider(fireworksKey)), apiKey: fireworksKey }; + providers.fireworks = { ...buildFireworksProvider(), apiKey: fireworksKey }; } const qwenProfiles = listProfilesForProvider(authStore, "qwen-portal"); From 296ab06d28aa1d6e948b5f74765033c953959211 Mon Sep 17 00:00:00 2001 From: Dylan Huang Date: Mon, 26 Jan 2026 15:13:19 -0800 Subject: [PATCH 3/6] fix(lint): remove unused DeliverableMessageChannel import --- src/infra/heartbeat-visibility.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/infra/heartbeat-visibility.ts b/src/infra/heartbeat-visibility.ts index e4943464c..c24b10417 100644 --- a/src/infra/heartbeat-visibility.ts +++ b/src/infra/heartbeat-visibility.ts @@ -1,6 +1,6 @@ import type { ClawdbotConfig } from "../config/config.js"; import type { ChannelHeartbeatVisibilityConfig } from "../config/types.channels.js"; -import type { DeliverableMessageChannel, GatewayMessageChannel } from "../utils/message-channel.js"; +import type { GatewayMessageChannel } from "../utils/message-channel.js"; export type ResolvedHeartbeatVisibility = { showOk: boolean; From 97c4f560dba84cb0d588df9ff3abe9ca13550e53 Mon Sep 17 00:00:00 2001 From: Dylan Huang Date: Mon, 26 Jan 2026 15:17:15 -0800 Subject: [PATCH 4/6] docs(fireworks): update to reflect static catalog and correct reasoning fields --- docs/providers/fireworks.md | 38 +++++++++++++++---------------------- 1 file changed, 15 insertions(+), 23 deletions(-) diff --git a/docs/providers/fireworks.md b/docs/providers/fireworks.md index a7fc0e57c..6719ec84a 100644 --- a/docs/providers/fireworks.md +++ b/docs/providers/fireworks.md @@ -48,9 +48,9 @@ clawdbot onboard --auth-choice fireworks-api-key This will: 1. Prompt for your API key (or use existing `FIREWORKS_API_KEY`) -2. Discover available Fireworks models via API +2. Configure the Fireworks provider with available models 3. Let you pick your default model -4. Configure the provider automatically +4. Set up the provider automatically **Option C: Non-interactive** @@ -68,7 +68,7 @@ clawdbot chat --model fireworks/accounts/fireworks/models/deepseek-v3p2 "Hello, ## Model Selection -After setup, Clawdbot discovers models from the Fireworks API. Pick based on your needs: +Clawdbot includes a curated catalog of popular Fireworks models. Pick based on your needs: - **Default**: `deepseek-v3p2` (DeepSeek V3.2) — strong reasoning, balanced performance. - **Best reasoning**: `deepseek-r1-0528` or `qwen3-235b-a22b-thinking-2507` @@ -92,8 +92,9 @@ clawdbot models list | grep fireworks | Use Case | Recommended Model | Why | |----------|-------------------|-----| -| **General chat** | `deepseek-v3p2` | Strong all-around, reasoning support | +| **General chat** | `deepseek-v3p2` | Strong all-around performance | | **Complex reasoning** | `deepseek-r1-0528` | Best for step-by-step reasoning | +| **Agentic tasks** | `gpt-oss-120b` | Designed for reasoning and agentic use | | **Coding** | `qwen3-coder-480b-a35b-instruct` | Code-optimized, 262k context | | **Vision tasks** | `qwen3-vl-235b-a22b-instruct` | Best multimodal capabilities | | **Fast + cheap** | `qwen3-8b` | Lightweight, low latency | @@ -109,16 +110,16 @@ clawdbot models list | grep fireworks | `deepseek-v3-0324` | DeepSeek V3 03-24 | 163k | General | | `deepseek-v3p1` | DeepSeek V3.1 | 163k | General | | `deepseek-v3p1-terminus` | DeepSeek V3.1 Terminus | 163k | General | -| `deepseek-v3p2` | DeepSeek V3.2 | 163k | Reasoning | -| `glm-4p6` | GLM-4.6 | 202k | General | +| `deepseek-v3p2` | DeepSeek V3.2 | 163k | General | +| `glm-4p6` | GLM-4.6 | 202k | Reasoning | | `glm-4p7` | GLM-4.7 | 202k | Reasoning | -| `gpt-oss-120b` | OpenAI gpt-oss-120b | 131k | General | -| `gpt-oss-20b` | OpenAI gpt-oss-20b | 131k | General | +| `gpt-oss-120b` | OpenAI gpt-oss-120b | 131k | Reasoning | +| `gpt-oss-20b` | OpenAI gpt-oss-20b | 131k | Reasoning | | `kimi-k2-instruct-0905` | Kimi K2 Instruct 0905 | 262k | Long context | | `kimi-k2-thinking` | Kimi K2 Thinking | 256k | Reasoning | | `llama-v3p3-70b-instruct` | Llama 3.3 70B Instruct | 131k | General | -| `minimax-m2` | MiniMax-M2 | 196k | General | -| `minimax-m2p1` | MiniMax-M2.1 | 204k | General | +| `minimax-m2` | MiniMax-M2 | 196k | Reasoning | +| `minimax-m2p1` | MiniMax-M2.1 | 204k | Reasoning | | `qwen3-235b-a22b` | Qwen3 235B A22B | 131k | General | | `qwen3-235b-a22b-instruct-2507` | Qwen3 235B A22B Instruct 2507 | 262k | General | | `qwen3-235b-a22b-thinking-2507` | Qwen3 235B A22B Thinking 2507 | 262k | Reasoning | @@ -134,14 +135,9 @@ clawdbot models list | grep fireworks | `qwen3-vl-235b-a22b-thinking` | Qwen3 VL 235B A22B Thinking | 262k | Vision, reasoning | | `qwen3-vl-30b-a3b-thinking` | Qwen3 VL 30B A3B Thinking | 262k | Vision, reasoning | -## Model Discovery +## Model Catalog -Clawdbot automatically discovers models from the Fireworks API when `FIREWORKS_API_KEY` is set. The discovery: - -- Fetches all serverless models (`supports_serverless=true`) -- Filters out deprecated models -- Filters out non-LLM models (image generation) -- Falls back to a static catalog if the API is unreachable +Clawdbot includes a curated catalog of popular Fireworks serverless LLM models. ## Model IDs @@ -203,16 +199,12 @@ Ensure the key is valid and has not expired. ### Model not available -The Fireworks model catalog updates dynamically. Run `clawdbot models list` to see currently available models. Some models may be temporarily offline or deprecated. +Run `clawdbot models list` to see currently available models in the catalog. If a model you need is missing, you can add it manually to your config file. ### Connection issues Fireworks API is at `https://api.fireworks.ai`. Ensure your network allows HTTPS connections. -### Model discovery fails - -If model discovery fails, Clawdbot falls back to a static catalog of popular models. Check your API key and network connection. - ## Config file example ```json5 @@ -230,7 +222,7 @@ If model discovery fails, Clawdbot falls back to a static catalog of popular mod { id: "accounts/fireworks/models/deepseek-v3p2", name: "DeepSeek V3.2", - reasoning: true, + reasoning: false, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 163840, From d97cb26c386c14eff5666a199a9c027fb0c00ffe Mon Sep 17 00:00:00 2001 From: Dylan Huang Date: Mon, 26 Jan 2026 15:21:37 -0800 Subject: [PATCH 5/6] chore(onboarding): update Fireworks AI hint to US-based fast inference --- src/commands/auth-choice-options.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/commands/auth-choice-options.ts b/src/commands/auth-choice-options.ts index 75b795b1c..1f9dac49e 100644 --- a/src/commands/auth-choice-options.ts +++ b/src/commands/auth-choice-options.ts @@ -75,7 +75,7 @@ const AUTH_CHOICE_GROUP_DEFS: { { value: "fireworks", label: "Fireworks AI", - hint: "Serverless inference (DeepSeek, Qwen, Llama)", + hint: "US-based fast inference (DeepSeek, Qwen, Llama)", choices: ["fireworks-api-key"], }, { @@ -157,7 +157,7 @@ export function buildAuthChoiceOptions(params: { options.push({ value: "fireworks-api-key", label: "Fireworks AI API key", - hint: "Serverless inference (DeepSeek, Qwen, Llama, and more)", + hint: "US-based fast inference (DeepSeek, Qwen, Llama, and more)", }); options.push({ value: "github-copilot", From 1e695ba95f1a12c2cb364d06e60050e19aaef8d3 Mon Sep 17 00:00:00 2001 From: Dylan Huang Date: Tue, 27 Jan 2026 10:57:53 -0800 Subject: [PATCH 6/6] feat(fireworks): sync model catalog with latest API response - Add Kimi K2.5 (vision + reasoning, 262k context) - Add Qwen3 VL 30B A3B Instruct - Update MiniMax models to not be reasoning (agentic/coding instead) - Update display names to match API (Deepseek vs DeepSeek) - Update model count in docs from 24 to 26 --- docs/providers/fireworks.md | 16 +++++++++------- src/agents/fireworks-models.ts | 28 ++++++++++++++++++++++------ 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/docs/providers/fireworks.md b/docs/providers/fireworks.md index 6719ec84a..a4dc4766f 100644 --- a/docs/providers/fireworks.md +++ b/docs/providers/fireworks.md @@ -22,7 +22,7 @@ Fireworks AI provides fast, cost-effective serverless inference for popular open - **Streaming**: Supported on all models - **Function calling**: Supported on most models - **Vision**: Supported on vision-capable models (Qwen VL series) -- **Reasoning models**: DeepSeek R1, Qwen3 Thinking, Kimi K2 Thinking +- **Reasoning models**: DeepSeek R1, Qwen3 Thinking, Kimi K2 Thinking, Kimi K2.5 ## Setup @@ -100,26 +100,27 @@ clawdbot models list | grep fireworks | **Fast + cheap** | `qwen3-8b` | Lightweight, low latency | | **Long context** | `kimi-k2-instruct-0905` | 262k context window | -## Available Models (24 Total) +## Available Models (26 Total) ### Text Models | Model ID | Name | Context | Features | |----------|------|---------|----------| -| `deepseek-r1-0528` | DeepSeek R1 05/28 | 163k | Reasoning | -| `deepseek-v3-0324` | DeepSeek V3 03-24 | 163k | General | +| `deepseek-r1-0528` | Deepseek R1 05/28 | 163k | Reasoning | +| `deepseek-v3-0324` | Deepseek V3 03-24 | 163k | General | | `deepseek-v3p1` | DeepSeek V3.1 | 163k | General | | `deepseek-v3p1-terminus` | DeepSeek V3.1 Terminus | 163k | General | -| `deepseek-v3p2` | DeepSeek V3.2 | 163k | General | +| `deepseek-v3p2` | Deepseek v3.2 | 163k | General | | `glm-4p6` | GLM-4.6 | 202k | Reasoning | | `glm-4p7` | GLM-4.7 | 202k | Reasoning | | `gpt-oss-120b` | OpenAI gpt-oss-120b | 131k | Reasoning | | `gpt-oss-20b` | OpenAI gpt-oss-20b | 131k | Reasoning | | `kimi-k2-instruct-0905` | Kimi K2 Instruct 0905 | 262k | Long context | +| `kimi-k2p5` | Kimi K2.5 | 262k | Vision, reasoning | | `kimi-k2-thinking` | Kimi K2 Thinking | 256k | Reasoning | | `llama-v3p3-70b-instruct` | Llama 3.3 70B Instruct | 131k | General | -| `minimax-m2` | MiniMax-M2 | 196k | Reasoning | -| `minimax-m2p1` | MiniMax-M2.1 | 204k | Reasoning | +| `minimax-m2` | MiniMax-M2 | 196k | Agentic, coding | +| `minimax-m2p1` | MiniMax-M2.1 | 204k | Agentic, coding | | `qwen3-235b-a22b` | Qwen3 235B A22B | 131k | General | | `qwen3-235b-a22b-instruct-2507` | Qwen3 235B A22B Instruct 2507 | 262k | General | | `qwen3-235b-a22b-thinking-2507` | Qwen3 235B A22B Thinking 2507 | 262k | Reasoning | @@ -133,6 +134,7 @@ clawdbot models list | grep fireworks | `qwen2p5-vl-32b-instruct` | Qwen2.5-VL 32B Instruct | 128k | Vision | | `qwen3-vl-235b-a22b-instruct` | Qwen3 VL 235B A22B Instruct | 262k | Vision | | `qwen3-vl-235b-a22b-thinking` | Qwen3 VL 235B A22B Thinking | 262k | Vision, reasoning | +| `qwen3-vl-30b-a3b-instruct` | Qwen3 VL 30B A3B Instruct | 262k | Vision | | `qwen3-vl-30b-a3b-thinking` | Qwen3 VL 30B A3B Thinking | 262k | Vision, reasoning | ## Model Catalog diff --git a/src/agents/fireworks-models.ts b/src/agents/fireworks-models.ts index 556a5d70a..4ca2e2132 100644 --- a/src/agents/fireworks-models.ts +++ b/src/agents/fireworks-models.ts @@ -25,7 +25,7 @@ export const FIREWORKS_MODEL_CATALOG = [ // DeepSeek models { id: "accounts/fireworks/models/deepseek-r1-0528", - name: "DeepSeek R1 05/28", + name: "Deepseek R1 05/28", reasoning: true, input: ["text"] as const, contextWindow: 163840, @@ -33,7 +33,7 @@ export const FIREWORKS_MODEL_CATALOG = [ }, { id: "accounts/fireworks/models/deepseek-v3-0324", - name: "DeepSeek V3 03-24", + name: "Deepseek V3 03-24", reasoning: false, input: ["text"] as const, contextWindow: 163840, @@ -57,7 +57,7 @@ export const FIREWORKS_MODEL_CATALOG = [ }, { id: "accounts/fireworks/models/deepseek-v3p2", - name: "DeepSeek V3.2", + name: "Deepseek v3.2", reasoning: false, input: ["text"] as const, contextWindow: 163840, @@ -109,6 +109,14 @@ export const FIREWORKS_MODEL_CATALOG = [ contextWindow: 262144, maxTokens: 8192, }, + { + id: "accounts/fireworks/models/kimi-k2p5", + name: "Kimi K2.5", + reasoning: true, + input: ["text", "image"] as const, + contextWindow: 262144, + maxTokens: 8192, + }, { id: "accounts/fireworks/models/kimi-k2-thinking", name: "Kimi K2 Thinking", @@ -129,11 +137,11 @@ export const FIREWORKS_MODEL_CATALOG = [ maxTokens: 8192, }, - // MiniMax models - "long-thinking" and "complex reasoning tasks" + // MiniMax models - compact MoE for coding and agentic tasks { id: "accounts/fireworks/models/minimax-m2", name: "MiniMax-M2", - reasoning: true, + reasoning: false, input: ["text"] as const, contextWindow: 196608, maxTokens: 8192, @@ -141,7 +149,7 @@ export const FIREWORKS_MODEL_CATALOG = [ { id: "accounts/fireworks/models/minimax-m2p1", name: "MiniMax-M2.1", - reasoning: true, + reasoning: false, input: ["text"] as const, contextWindow: 204800, maxTokens: 8192, @@ -214,6 +222,14 @@ export const FIREWORKS_MODEL_CATALOG = [ contextWindow: 262144, maxTokens: 8192, }, + { + id: "accounts/fireworks/models/qwen3-vl-30b-a3b-instruct", + name: "Qwen3 VL 30B A3B Instruct", + reasoning: false, + input: ["text", "image"] as const, + contextWindow: 262144, + maxTokens: 8192, + }, { id: "accounts/fireworks/models/qwen3-vl-30b-a3b-thinking", name: "Qwen3 VL 30B A3B Thinking",