feat(ai): add Fireworks provider support closes #3519
This commit is contained in:
@@ -2,6 +2,10 @@
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
- Added Fireworks provider support via Fireworks' Anthropic-compatible Messages API, including built-in models sourced from models.dev and `FIREWORKS_API_KEY` auth ([#3519](https://github.com/badlogic/pi-mono/issues/3519))
|
||||
|
||||
### Fixed
|
||||
|
||||
- Hardened Anthropic streaming against malformed tool-call JSON by owning SSE parsing with defensive JSON repair, replacing the deprecated `fine-grained-tool-streaming` beta header with per-tool `eager_input_streaming`, and updating stale test model references ([#3175](https://github.com/badlogic/pi-mono/issues/3175))
|
||||
|
||||
@@ -66,6 +66,7 @@ Unified LLM API with automatic model discovery, provider configuration, token an
|
||||
- **Amazon Bedrock**
|
||||
- **OpenCode Zen**
|
||||
- **OpenCode Go**
|
||||
- **Fireworks** (uses Anthropic-compatible API)
|
||||
- **Kimi For Coding** (Moonshot AI, uses Anthropic-compatible API)
|
||||
- **Any OpenAI-compatible API**: Ollama, vLLM, LM Studio, etc.
|
||||
|
||||
@@ -1025,6 +1026,7 @@ In Node.js environments, you can set environment variables to avoid passing API
|
||||
| Groq | `GROQ_API_KEY` |
|
||||
| Cerebras | `CEREBRAS_API_KEY` |
|
||||
| xAI | `XAI_API_KEY` |
|
||||
| Fireworks | `FIREWORKS_API_KEY` |
|
||||
| OpenRouter | `OPENROUTER_API_KEY` |
|
||||
| Vercel AI Gateway | `AI_GATEWAY_API_KEY` |
|
||||
| zAI | `ZAI_API_KEY` |
|
||||
|
||||
@@ -468,6 +468,33 @@ async function loadModelsDevData(): Promise<Model<any>[]> {
|
||||
}
|
||||
}
|
||||
|
||||
// Process Fireworks models
|
||||
if (data["fireworks-ai"]?.models) {
|
||||
for (const [modelId, model] of Object.entries(data["fireworks-ai"].models)) {
|
||||
const m = model as ModelsDevModel;
|
||||
if (m.tool_call !== true) continue;
|
||||
|
||||
models.push({
|
||||
id: modelId,
|
||||
name: m.name || modelId,
|
||||
api: "anthropic-messages",
|
||||
provider: "fireworks",
|
||||
// Fireworks Anthropic-compatible API - SDK appends /v1/messages
|
||||
baseUrl: "https://api.fireworks.ai/inference",
|
||||
reasoning: m.reasoning === true,
|
||||
input: m.modalities?.input?.includes("image") ? ["text", "image"] : ["text"],
|
||||
cost: {
|
||||
input: m.cost?.input || 0,
|
||||
output: m.cost?.output || 0,
|
||||
cacheRead: m.cost?.cache_read || 0,
|
||||
cacheWrite: m.cost?.cache_write || 0,
|
||||
},
|
||||
contextWindow: m.limit?.context || 4096,
|
||||
maxTokens: m.limit?.output || 4096,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Process OpenCode models (Zen and Go)
|
||||
// API mapping based on provider.npm field:
|
||||
// - @ai-sdk/openai → openai-responses
|
||||
|
||||
@@ -123,6 +123,7 @@ export function getEnvApiKey(provider: any): string | undefined {
|
||||
minimax: "MINIMAX_API_KEY",
|
||||
"minimax-cn": "MINIMAX_CN_API_KEY",
|
||||
huggingface: "HF_TOKEN",
|
||||
fireworks: "FIREWORKS_API_KEY",
|
||||
opencode: "OPENCODE_API_KEY",
|
||||
"opencode-go": "OPENCODE_API_KEY",
|
||||
"kimi-coding": "KIMI_API_KEY",
|
||||
|
||||
@@ -2698,6 +2698,314 @@ export const MODELS = {
|
||||
maxTokens: 40000,
|
||||
} satisfies Model<"openai-completions">,
|
||||
},
|
||||
"fireworks": {
|
||||
"accounts/fireworks/models/deepseek-v3p1": {
|
||||
id: "accounts/fireworks/models/deepseek-v3p1",
|
||||
name: "DeepSeek V3.1",
|
||||
api: "anthropic-messages",
|
||||
provider: "fireworks",
|
||||
baseUrl: "https://api.fireworks.ai/inference",
|
||||
reasoning: true,
|
||||
input: ["text"],
|
||||
cost: {
|
||||
input: 0.56,
|
||||
output: 1.68,
|
||||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
},
|
||||
contextWindow: 163840,
|
||||
maxTokens: 163840,
|
||||
} satisfies Model<"anthropic-messages">,
|
||||
"accounts/fireworks/models/deepseek-v3p2": {
|
||||
id: "accounts/fireworks/models/deepseek-v3p2",
|
||||
name: "DeepSeek V3.2",
|
||||
api: "anthropic-messages",
|
||||
provider: "fireworks",
|
||||
baseUrl: "https://api.fireworks.ai/inference",
|
||||
reasoning: true,
|
||||
input: ["text"],
|
||||
cost: {
|
||||
input: 0.56,
|
||||
output: 1.68,
|
||||
cacheRead: 0.28,
|
||||
cacheWrite: 0,
|
||||
},
|
||||
contextWindow: 160000,
|
||||
maxTokens: 160000,
|
||||
} satisfies Model<"anthropic-messages">,
|
||||
"accounts/fireworks/models/glm-4p5": {
|
||||
id: "accounts/fireworks/models/glm-4p5",
|
||||
name: "GLM 4.5",
|
||||
api: "anthropic-messages",
|
||||
provider: "fireworks",
|
||||
baseUrl: "https://api.fireworks.ai/inference",
|
||||
reasoning: true,
|
||||
input: ["text"],
|
||||
cost: {
|
||||
input: 0.55,
|
||||
output: 2.19,
|
||||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
},
|
||||
contextWindow: 131072,
|
||||
maxTokens: 131072,
|
||||
} satisfies Model<"anthropic-messages">,
|
||||
"accounts/fireworks/models/glm-4p5-air": {
|
||||
id: "accounts/fireworks/models/glm-4p5-air",
|
||||
name: "GLM 4.5 Air",
|
||||
api: "anthropic-messages",
|
||||
provider: "fireworks",
|
||||
baseUrl: "https://api.fireworks.ai/inference",
|
||||
reasoning: true,
|
||||
input: ["text"],
|
||||
cost: {
|
||||
input: 0.22,
|
||||
output: 0.88,
|
||||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
},
|
||||
contextWindow: 131072,
|
||||
maxTokens: 131072,
|
||||
} satisfies Model<"anthropic-messages">,
|
||||
"accounts/fireworks/models/glm-4p7": {
|
||||
id: "accounts/fireworks/models/glm-4p7",
|
||||
name: "GLM 4.7",
|
||||
api: "anthropic-messages",
|
||||
provider: "fireworks",
|
||||
baseUrl: "https://api.fireworks.ai/inference",
|
||||
reasoning: true,
|
||||
input: ["text"],
|
||||
cost: {
|
||||
input: 0.6,
|
||||
output: 2.2,
|
||||
cacheRead: 0.3,
|
||||
cacheWrite: 0,
|
||||
},
|
||||
contextWindow: 198000,
|
||||
maxTokens: 198000,
|
||||
} satisfies Model<"anthropic-messages">,
|
||||
"accounts/fireworks/models/glm-5": {
|
||||
id: "accounts/fireworks/models/glm-5",
|
||||
name: "GLM 5",
|
||||
api: "anthropic-messages",
|
||||
provider: "fireworks",
|
||||
baseUrl: "https://api.fireworks.ai/inference",
|
||||
reasoning: true,
|
||||
input: ["text"],
|
||||
cost: {
|
||||
input: 1,
|
||||
output: 3.2,
|
||||
cacheRead: 0.5,
|
||||
cacheWrite: 0,
|
||||
},
|
||||
contextWindow: 202752,
|
||||
maxTokens: 131072,
|
||||
} satisfies Model<"anthropic-messages">,
|
||||
"accounts/fireworks/models/glm-5p1": {
|
||||
id: "accounts/fireworks/models/glm-5p1",
|
||||
name: "GLM 5.1",
|
||||
api: "anthropic-messages",
|
||||
provider: "fireworks",
|
||||
baseUrl: "https://api.fireworks.ai/inference",
|
||||
reasoning: true,
|
||||
input: ["text"],
|
||||
cost: {
|
||||
input: 1.4,
|
||||
output: 4.4,
|
||||
cacheRead: 0.26,
|
||||
cacheWrite: 0,
|
||||
},
|
||||
contextWindow: 202800,
|
||||
maxTokens: 131072,
|
||||
} satisfies Model<"anthropic-messages">,
|
||||
"accounts/fireworks/models/gpt-oss-120b": {
|
||||
id: "accounts/fireworks/models/gpt-oss-120b",
|
||||
name: "GPT OSS 120B",
|
||||
api: "anthropic-messages",
|
||||
provider: "fireworks",
|
||||
baseUrl: "https://api.fireworks.ai/inference",
|
||||
reasoning: true,
|
||||
input: ["text"],
|
||||
cost: {
|
||||
input: 0.15,
|
||||
output: 0.6,
|
||||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
},
|
||||
contextWindow: 131072,
|
||||
maxTokens: 32768,
|
||||
} satisfies Model<"anthropic-messages">,
|
||||
"accounts/fireworks/models/gpt-oss-20b": {
|
||||
id: "accounts/fireworks/models/gpt-oss-20b",
|
||||
name: "GPT OSS 20B",
|
||||
api: "anthropic-messages",
|
||||
provider: "fireworks",
|
||||
baseUrl: "https://api.fireworks.ai/inference",
|
||||
reasoning: true,
|
||||
input: ["text"],
|
||||
cost: {
|
||||
input: 0.05,
|
||||
output: 0.2,
|
||||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
},
|
||||
contextWindow: 131072,
|
||||
maxTokens: 32768,
|
||||
} satisfies Model<"anthropic-messages">,
|
||||
"accounts/fireworks/models/kimi-k2-instruct": {
|
||||
id: "accounts/fireworks/models/kimi-k2-instruct",
|
||||
name: "Kimi K2 Instruct",
|
||||
api: "anthropic-messages",
|
||||
provider: "fireworks",
|
||||
baseUrl: "https://api.fireworks.ai/inference",
|
||||
reasoning: false,
|
||||
input: ["text"],
|
||||
cost: {
|
||||
input: 1,
|
||||
output: 3,
|
||||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
},
|
||||
contextWindow: 128000,
|
||||
maxTokens: 16384,
|
||||
} satisfies Model<"anthropic-messages">,
|
||||
"accounts/fireworks/models/kimi-k2-thinking": {
|
||||
id: "accounts/fireworks/models/kimi-k2-thinking",
|
||||
name: "Kimi K2 Thinking",
|
||||
api: "anthropic-messages",
|
||||
provider: "fireworks",
|
||||
baseUrl: "https://api.fireworks.ai/inference",
|
||||
reasoning: true,
|
||||
input: ["text"],
|
||||
cost: {
|
||||
input: 0.6,
|
||||
output: 2.5,
|
||||
cacheRead: 0.3,
|
||||
cacheWrite: 0,
|
||||
},
|
||||
contextWindow: 256000,
|
||||
maxTokens: 256000,
|
||||
} satisfies Model<"anthropic-messages">,
|
||||
"accounts/fireworks/models/kimi-k2p5": {
|
||||
id: "accounts/fireworks/models/kimi-k2p5",
|
||||
name: "Kimi K2.5",
|
||||
api: "anthropic-messages",
|
||||
provider: "fireworks",
|
||||
baseUrl: "https://api.fireworks.ai/inference",
|
||||
reasoning: true,
|
||||
input: ["text", "image"],
|
||||
cost: {
|
||||
input: 0.6,
|
||||
output: 3,
|
||||
cacheRead: 0.1,
|
||||
cacheWrite: 0,
|
||||
},
|
||||
contextWindow: 256000,
|
||||
maxTokens: 256000,
|
||||
} satisfies Model<"anthropic-messages">,
|
||||
"accounts/fireworks/models/kimi-k2p6": {
|
||||
id: "accounts/fireworks/models/kimi-k2p6",
|
||||
name: "Kimi K2.6",
|
||||
api: "anthropic-messages",
|
||||
provider: "fireworks",
|
||||
baseUrl: "https://api.fireworks.ai/inference",
|
||||
reasoning: true,
|
||||
input: ["text", "image"],
|
||||
cost: {
|
||||
input: 0.95,
|
||||
output: 4,
|
||||
cacheRead: 0.16,
|
||||
cacheWrite: 0,
|
||||
},
|
||||
contextWindow: 262000,
|
||||
maxTokens: 262000,
|
||||
} satisfies Model<"anthropic-messages">,
|
||||
"accounts/fireworks/models/minimax-m2p1": {
|
||||
id: "accounts/fireworks/models/minimax-m2p1",
|
||||
name: "MiniMax-M2.1",
|
||||
api: "anthropic-messages",
|
||||
provider: "fireworks",
|
||||
baseUrl: "https://api.fireworks.ai/inference",
|
||||
reasoning: true,
|
||||
input: ["text"],
|
||||
cost: {
|
||||
input: 0.3,
|
||||
output: 1.2,
|
||||
cacheRead: 0.03,
|
||||
cacheWrite: 0,
|
||||
},
|
||||
contextWindow: 200000,
|
||||
maxTokens: 200000,
|
||||
} satisfies Model<"anthropic-messages">,
|
||||
"accounts/fireworks/models/minimax-m2p5": {
|
||||
id: "accounts/fireworks/models/minimax-m2p5",
|
||||
name: "MiniMax-M2.5",
|
||||
api: "anthropic-messages",
|
||||
provider: "fireworks",
|
||||
baseUrl: "https://api.fireworks.ai/inference",
|
||||
reasoning: true,
|
||||
input: ["text"],
|
||||
cost: {
|
||||
input: 0.3,
|
||||
output: 1.2,
|
||||
cacheRead: 0.03,
|
||||
cacheWrite: 0,
|
||||
},
|
||||
contextWindow: 196608,
|
||||
maxTokens: 196608,
|
||||
} satisfies Model<"anthropic-messages">,
|
||||
"accounts/fireworks/models/minimax-m2p7": {
|
||||
id: "accounts/fireworks/models/minimax-m2p7",
|
||||
name: "MiniMax-M2.7",
|
||||
api: "anthropic-messages",
|
||||
provider: "fireworks",
|
||||
baseUrl: "https://api.fireworks.ai/inference",
|
||||
reasoning: true,
|
||||
input: ["text"],
|
||||
cost: {
|
||||
input: 0.3,
|
||||
output: 1.2,
|
||||
cacheRead: 0.03,
|
||||
cacheWrite: 0,
|
||||
},
|
||||
contextWindow: 196608,
|
||||
maxTokens: 196608,
|
||||
} satisfies Model<"anthropic-messages">,
|
||||
"accounts/fireworks/models/qwen3p6-plus": {
|
||||
id: "accounts/fireworks/models/qwen3p6-plus",
|
||||
name: "Qwen 3.6 Plus",
|
||||
api: "anthropic-messages",
|
||||
provider: "fireworks",
|
||||
baseUrl: "https://api.fireworks.ai/inference",
|
||||
reasoning: true,
|
||||
input: ["text", "image"],
|
||||
cost: {
|
||||
input: 0.5,
|
||||
output: 3,
|
||||
cacheRead: 0.1,
|
||||
cacheWrite: 0,
|
||||
},
|
||||
contextWindow: 128000,
|
||||
maxTokens: 8192,
|
||||
} satisfies Model<"anthropic-messages">,
|
||||
"accounts/fireworks/routers/kimi-k2p5-turbo": {
|
||||
id: "accounts/fireworks/routers/kimi-k2p5-turbo",
|
||||
name: "Kimi K2.5 Turbo (firepass)",
|
||||
api: "anthropic-messages",
|
||||
provider: "fireworks",
|
||||
baseUrl: "https://api.fireworks.ai/inference",
|
||||
reasoning: true,
|
||||
input: ["text", "image"],
|
||||
cost: {
|
||||
input: 0,
|
||||
output: 0,
|
||||
cacheRead: 0,
|
||||
cacheWrite: 0,
|
||||
},
|
||||
contextWindow: 256000,
|
||||
maxTokens: 256000,
|
||||
} satisfies Model<"anthropic-messages">,
|
||||
},
|
||||
"github-copilot": {
|
||||
"claude-haiku-4.5": {
|
||||
id: "claude-haiku-4.5",
|
||||
|
||||
@@ -37,6 +37,7 @@ export type KnownProvider =
|
||||
| "minimax"
|
||||
| "minimax-cn"
|
||||
| "huggingface"
|
||||
| "fireworks"
|
||||
| "opencode"
|
||||
| "opencode-go"
|
||||
| "kimi-coding";
|
||||
|
||||
49
packages/ai/test/fireworks-models.test.ts
Normal file
49
packages/ai/test/fireworks-models.test.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { afterEach, describe, expect, it } from "vitest";
|
||||
import { getEnvApiKey } from "../src/env-api-keys.js";
|
||||
import { getModel } from "../src/models.js";
|
||||
|
||||
const originalFireworksApiKey = process.env.FIREWORKS_API_KEY;
|
||||
|
||||
afterEach(() => {
|
||||
if (originalFireworksApiKey === undefined) {
|
||||
delete process.env.FIREWORKS_API_KEY;
|
||||
} else {
|
||||
process.env.FIREWORKS_API_KEY = originalFireworksApiKey;
|
||||
}
|
||||
});
|
||||
|
||||
describe("Fireworks models", () => {
|
||||
it("registers the default Kimi K2.6 model via Anthropic-compatible Messages API", () => {
|
||||
const model = getModel("fireworks", "accounts/fireworks/models/kimi-k2p6");
|
||||
|
||||
expect(model).toBeDefined();
|
||||
expect(model.api).toBe("anthropic-messages");
|
||||
expect(model.provider).toBe("fireworks");
|
||||
expect(model.baseUrl).toBe("https://api.fireworks.ai/inference");
|
||||
expect(model.reasoning).toBe(true);
|
||||
expect(model.input).toEqual(["text", "image"]);
|
||||
expect(model.contextWindow).toBe(262000);
|
||||
expect(model.maxTokens).toBe(262000);
|
||||
expect(model.cost).toEqual({
|
||||
input: 0.95,
|
||||
output: 4,
|
||||
cacheRead: 0.16,
|
||||
cacheWrite: 0,
|
||||
});
|
||||
});
|
||||
|
||||
it("registers the Fire Pass turbo router model", () => {
|
||||
const model = getModel("fireworks", "accounts/fireworks/routers/kimi-k2p5-turbo");
|
||||
|
||||
expect(model).toBeDefined();
|
||||
expect(model.api).toBe("anthropic-messages");
|
||||
expect(model.baseUrl).toBe("https://api.fireworks.ai/inference");
|
||||
expect(model.input).toEqual(["text", "image"]);
|
||||
});
|
||||
|
||||
it("resolves FIREWORKS_API_KEY from the environment", () => {
|
||||
process.env.FIREWORKS_API_KEY = "test-fireworks-key";
|
||||
|
||||
expect(getEnvApiKey("fireworks")).toBe("test-fireworks-key");
|
||||
});
|
||||
});
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
- Added built-in Fireworks provider support, including `FIREWORKS_API_KEY` setup/docs and the default Fireworks model `accounts/fireworks/models/kimi-k2p6` ([#3519](https://github.com/badlogic/pi-mono/issues/3519))
|
||||
|
||||
### Fixed
|
||||
|
||||
- Added configurable interactive inline image width via `terminal.imageWidthCells` and `/settings`, so tool-output images are no longer hard-capped to 60 terminal cells ([#3508](https://github.com/badlogic/pi-mono/issues/3508))
|
||||
|
||||
@@ -120,6 +120,7 @@ For each built-in provider, pi maintains a list of tool-capable models, updated
|
||||
- OpenCode Zen
|
||||
- OpenCode Go
|
||||
- Hugging Face
|
||||
- Fireworks
|
||||
- Kimi For Coding
|
||||
- MiniMax
|
||||
|
||||
|
||||
@@ -67,6 +67,7 @@ pi
|
||||
| OpenCode Zen | `OPENCODE_API_KEY` | `opencode` |
|
||||
| OpenCode Go | `OPENCODE_API_KEY` | `opencode-go` |
|
||||
| Hugging Face | `HF_TOKEN` | `huggingface` |
|
||||
| Fireworks | `FIREWORKS_API_KEY` | `fireworks` |
|
||||
| Kimi For Coding | `KIMI_API_KEY` | `kimi-coding` |
|
||||
| MiniMax | `MINIMAX_API_KEY` | `minimax` |
|
||||
| MiniMax (China) | `MINIMAX_CN_API_KEY` | `minimax-cn` |
|
||||
|
||||
@@ -303,6 +303,7 @@ ${chalk.bold("Environment Variables:")}
|
||||
GROQ_API_KEY - Groq API key
|
||||
CEREBRAS_API_KEY - Cerebras API key
|
||||
XAI_API_KEY - xAI Grok API key
|
||||
FIREWORKS_API_KEY - Fireworks API key
|
||||
OPENROUTER_API_KEY - OpenRouter API key
|
||||
AI_GATEWAY_API_KEY - Vercel AI Gateway API key
|
||||
ZAI_API_KEY - ZAI API key
|
||||
|
||||
@@ -32,6 +32,7 @@ export const defaultModelPerProvider: Record<KnownProvider, string> = {
|
||||
minimax: "MiniMax-M2.7",
|
||||
"minimax-cn": "MiniMax-M2.7",
|
||||
huggingface: "moonshotai/Kimi-K2.5",
|
||||
fireworks: "accounts/fireworks/models/kimi-k2p6",
|
||||
opencode: "claude-opus-4-6",
|
||||
"opencode-go": "kimi-k2.5",
|
||||
"kimi-coding": "kimi-for-coding",
|
||||
|
||||
Reference in New Issue
Block a user