diff --git a/docs/cli/index.md b/docs/cli/index.md index 9ce439d05..05f029a13 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 a463479aa..510aa3080 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -1014,6 +1014,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..a4dc4766f --- /dev/null +++ b/docs/providers/fireworks.md @@ -0,0 +1,245 @@ +--- +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, Kimi K2.5 + +## 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. Configure the Fireworks provider with available models +3. Let you pick your default model +4. Set up 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 + +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` +- **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 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 | +| **Long context** | `kimi-k2-instruct-0905` | 262k context window | + +## 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-v3p1` | DeepSeek V3.1 | 163k | General | +| `deepseek-v3p1-terminus` | DeepSeek V3.1 Terminus | 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 | 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 | +| `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-instruct` | Qwen3 VL 30B A3B Instruct | 262k | Vision | +| `qwen3-vl-30b-a3b-thinking` | Qwen3 VL 30B A3B Thinking | 262k | Vision, reasoning | + +## Model Catalog + +Clawdbot includes a curated catalog of popular Fireworks serverless LLM models. + +## 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 + +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. + +## 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: false, + 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 c18ad70fb..73beb0d83 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..4ca2e2132 --- /dev/null +++ b/src/agents/fireworks-models.ts @@ -0,0 +1,265 @@ +import type { ModelDefinitionConfig } from "../config/types.js"; + +export const FIREWORKS_BASE_URL = "https://api.fireworks.ai/inference/v1"; +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. + * + * 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: false, + input: ["text"] as const, + contextWindow: 163840, + maxTokens: 8192, + }, + + // GLM models - both support "advanced thinking controls" + { + id: "accounts/fireworks/models/glm-4p6", + name: "GLM-4.6", + reasoning: true, + 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 - designed for "powerful reasoning, agentic tasks" + { + id: "accounts/fireworks/models/gpt-oss-120b", + name: "OpenAI gpt-oss-120b", + reasoning: true, + input: ["text"] as const, + contextWindow: 131072, + maxTokens: 8192, + }, + { + id: "accounts/fireworks/models/gpt-oss-20b", + name: "OpenAI gpt-oss-20b", + reasoning: true, + 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-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", + 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 - compact MoE for coding and agentic tasks + { + 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-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", + 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, + }; +} + +/** + * Returns Fireworks models from the static catalog. + */ +export function discoverFireworksModels(): ModelDefinitionConfig[] { + return FIREWORKS_MODEL_CATALOG.map(buildFireworksModelDefinition); +} diff --git a/src/agents/model-auth.ts b/src/agents/model-auth.ts index 96e4e4ae6..c930ffc66 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 a176dac8a..de5f928f2 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 { }; } +function buildFireworksProvider(): ProviderConfig { + const models = discoverFireworksModels(); + 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 = { ...buildFireworksProvider(), 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 8f31635f0..f18f6b604 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..1f9dac49e 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: "US-based fast 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: "US-based fast 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 921ee01d1..0f9afc4d4 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: MoltbotConfig): MoltbotConfig { }; } +/** + * 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: MoltbotConfig, params: { diff --git a/src/commands/onboard-auth.credentials.ts b/src/commands/onboard-auth.credentials.ts index b2fb58542..f57aac062 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 7d952730c..4a7a1d160 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;