diff --git a/.changeset/four-comics-attend.md b/.changeset/four-comics-attend.md new file mode 100644 index 0000000000..c7e3375e08 --- /dev/null +++ b/.changeset/four-comics-attend.md @@ -0,0 +1,5 @@ +--- +"claude-dev": patch +--- + +Fix the fireworks provider diff --git a/docs/docs.json b/docs/docs.json index e87811b82d..31e80cc553 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -166,6 +166,7 @@ "provider-config/xai-grok", "provider-config/mistral-ai", "provider-config/deepseek", + "provider-config/fireworks-ai", "provider-config/ollama", "provider-config/openai", "provider-config/openai-compatible", diff --git a/docs/provider-config/fireworks-ai.mdx b/docs/provider-config/fireworks-ai.mdx new file mode 100644 index 0000000000..66075c7c1e --- /dev/null +++ b/docs/provider-config/fireworks-ai.mdx @@ -0,0 +1,51 @@ +--- +title: "Fireworks AI" +description: "Learn how to configure and use Fireworks AI models with Cline. Access high-performance open-source language models with fast, cost-effective APIs." +--- + +Cline supports accessing models through the Fireworks AI platform, which offers fast, cost-effective access to a wide range of state-of-the-art open-source language models. Built for speed and reliability, Fireworks AI provides serverless deployment options with OpenAI-compatible APIs and context windows up to 256,000 tokens. + +**Website:** [https://fireworks.ai/](https://fireworks.ai/) + +### Getting an API Key + +1. **Sign Up/Sign In:** Go to [Fireworks AI](https://fireworks.ai/) and create an account or sign in. +2. **Navigate to API Keys:** After logging in, go to the [API Keys page](https://app.fireworks.ai/settings/users/api-keys) in the account settings. +3. **Create a Key:** Click "Create API key" and give your key a descriptive name (e.g., "Cline"). +4. **Copy the Key:** **Important:** Copy the API key _immediately_. You will not be able to see it again. Store it securely. + +### Supported Models + +Cline supports the following Fireworks AI models: + +- `accounts/fireworks/models/kimi-k2-instruct` (Default) +- `accounts/fireworks/models/qwen3-235b-a22b-instruct-2507` +- `accounts/fireworks/models/qwen3-coder-480b-a35b-instruct` +- `accounts/fireworks/models/deepseek-r1-0528` +- `accounts/fireworks/models/deepseek-v3` + +**Model Details:** + +| Model | Context Window | Best For | Pricing (per 1M tokens) | +|-------|----------------|----------|-------------------------| +| Kimi K2 | 128K | General tasks, agentic capabilities | \$0.60 input, \$2.50 output | +| Qwen3 235B | 256K | Cost-effective general use | \$0.22 input, \$0.88 output | +| Qwen3 Coder | 256K | Code generation and debugging | \$0.45 input, \$1.80 output | +| DeepSeek R1 | 160K | Complex reasoning, function calling | \$3.00 input, \$8.00 output | +| DeepSeek V3 | 128K | Strong general performance | \$0.90 input, \$0.90 output | + +### Configuration in Cline + +1. **Open Cline Settings:** Click the settings icon (⚙️) in the Cline panel. +2. **Select Provider:** Choose "Fireworks AI" from the "API Provider" dropdown. +3. **Enter API Key:** Paste your Fireworks AI API key into the "Fireworks AI API Key" field. +4. **Select Model:** Choose your desired model from the "Model" dropdown. The default model is Kimi K2. + +### Tips and Notes + +- **Cost-Effective:** Fireworks AI offers significantly lower pricing than proprietary models while maintaining competitive performance. +- **Large Context Windows:** Most models support 128K-256K tokens, suitable for processing large documents and maintaining extended conversations. +- **OpenAI Compatibility:** The provider uses an OpenAI-compatible API format with streaming support and usage tracking. +- **Rate Limits:** Fireworks AI has usage-based rate limits. Monitor your usage in the dashboard and consider upgrading your plan if needed. +- **API Keys:** Stored locally on your machine for security. +- **Pricing:** See the [Fireworks AI pricing page](https://fireworks.ai/pricing) for current rates. Prices shown are per million tokens. diff --git a/src/core/api/index.ts b/src/core/api/index.ts index 6c94670bd0..3117be0849 100644 --- a/src/core/api/index.ts +++ b/src/core/api/index.ts @@ -166,8 +166,6 @@ function createHandlerForProvider( return new FireworksHandler({ fireworksApiKey: options.fireworksApiKey, fireworksModelId: mode === "plan" ? options.planModeFireworksModelId : options.actModeFireworksModelId, - fireworksModelMaxCompletionTokens: options.fireworksModelMaxCompletionTokens, - fireworksModelMaxTokens: options.fireworksModelMaxTokens, }) case "together": return new TogetherHandler({ diff --git a/src/core/api/providers/fireworks.ts b/src/core/api/providers/fireworks.ts index f4c77eac61..a314d8f61a 100644 --- a/src/core/api/providers/fireworks.ts +++ b/src/core/api/providers/fireworks.ts @@ -1,5 +1,5 @@ import { Anthropic } from "@anthropic-ai/sdk" -import { ModelInfo, openAiModelInfoSaneDefaults } from "@shared/api" +import { FireworksModelId, fireworksDefaultModelId, fireworksModels, ModelInfo } from "@shared/api" import OpenAI from "openai" import { ApiHandler } from ".." import { withRetry } from "../retry" @@ -50,10 +50,6 @@ export class FireworksHandler implements ApiHandler { const stream = await client.chat.completions.create({ model: modelId, - ...(this.options.fireworksModelMaxCompletionTokens - ? { max_completion_tokens: this.options.fireworksModelMaxCompletionTokens } - : {}), - ...(this.options.fireworksModelMaxTokens ? { max_tokens: this.options.fireworksModelMaxTokens } : {}), messages: openAiMessages, stream: true, stream_options: { include_usage: true }, @@ -99,10 +95,15 @@ export class FireworksHandler implements ApiHandler { } } - getModel(): { id: string; info: ModelInfo } { + getModel(): { id: FireworksModelId; info: ModelInfo } { + const modelId = this.options.fireworksModelId + if (modelId && modelId in fireworksModels) { + const id = modelId as FireworksModelId + return { id, info: fireworksModels[id] } + } return { - id: this.options.fireworksModelId ?? "", - info: openAiModelInfoSaneDefaults, + id: fireworksDefaultModelId, + info: fireworksModels[fireworksDefaultModelId], } } } diff --git a/src/core/storage/CacheService.ts b/src/core/storage/CacheService.ts index 9d4ab9d6b3..5aac2a77f3 100644 --- a/src/core/storage/CacheService.ts +++ b/src/core/storage/CacheService.ts @@ -1,4 +1,4 @@ -import { ApiConfiguration } from "@shared/api" +import { ApiConfiguration, fireworksDefaultModelId } from "@shared/api" import { DEFAULT_FOCUS_CHAIN_SETTINGS } from "@shared/FocusChainSettings" import type { ExtensionContext } from "vscode" import { DEFAULT_AUTO_APPROVAL_SETTINGS } from "@/shared/AutoApprovalSettings" @@ -1008,7 +1008,7 @@ export class CacheService { planModeRequestyModelId: this.globalStateCache["planModeRequestyModelId"], planModeRequestyModelInfo: this.globalStateCache["planModeRequestyModelInfo"], planModeTogetherModelId: this.globalStateCache["planModeTogetherModelId"], - planModeFireworksModelId: this.globalStateCache["planModeFireworksModelId"], + planModeFireworksModelId: this.globalStateCache["planModeFireworksModelId"] || fireworksDefaultModelId, planModeSapAiCoreModelId: this.globalStateCache["planModeSapAiCoreModelId"], planModeGroqModelId: this.globalStateCache["planModeGroqModelId"], planModeGroqModelInfo: this.globalStateCache["planModeGroqModelInfo"], @@ -1038,7 +1038,7 @@ export class CacheService { actModeRequestyModelId: this.globalStateCache["actModeRequestyModelId"], actModeRequestyModelInfo: this.globalStateCache["actModeRequestyModelInfo"], actModeTogetherModelId: this.globalStateCache["actModeTogetherModelId"], - actModeFireworksModelId: this.globalStateCache["actModeFireworksModelId"], + actModeFireworksModelId: this.globalStateCache["actModeFireworksModelId"] || fireworksDefaultModelId, actModeSapAiCoreModelId: this.globalStateCache["actModeSapAiCoreModelId"], actModeGroqModelId: this.globalStateCache["actModeGroqModelId"], actModeGroqModelInfo: this.globalStateCache["actModeGroqModelInfo"], diff --git a/src/shared/api.ts b/src/shared/api.ts index afb8285131..e632fe8f7f 100644 --- a/src/shared/api.ts +++ b/src/shared/api.ts @@ -3303,3 +3303,57 @@ export const mainlandZAiModels = { ], }, } as const satisfies Record + +// Fireworks AI +export type FireworksModelId = keyof typeof fireworksModels +export const fireworksDefaultModelId: FireworksModelId = "accounts/fireworks/models/kimi-k2-instruct" +export const fireworksModels = { + "accounts/fireworks/models/kimi-k2-instruct": { + maxTokens: 16384, + contextWindow: 128000, + supportsImages: false, + supportsPromptCache: false, + inputPrice: 0.6, + outputPrice: 2.5, + description: + "Kimi K2 is a state-of-the-art mixture-of-experts (MoE) language model with 32 billion activated parameters and 1 trillion total parameters. Trained with the Muon optimizer, Kimi K2 achieves exceptional performance across frontier knowledge, reasoning, and coding tasks while being meticulously optimized for agentic capabilities.", + }, + "accounts/fireworks/models/qwen3-235b-a22b-instruct-2507": { + maxTokens: 32768, + contextWindow: 256000, + supportsImages: false, + supportsPromptCache: false, + inputPrice: 0.22, + outputPrice: 0.88, + description: "Latest Qwen3 thinking model, competitive against the best closed source models in Jul 2025.", + }, + "accounts/fireworks/models/qwen3-coder-480b-a35b-instruct": { + maxTokens: 32768, + contextWindow: 256000, + supportsImages: false, + supportsPromptCache: false, + inputPrice: 0.45, + outputPrice: 1.8, + description: "Qwen3's most agentic code model to date.", + }, + "accounts/fireworks/models/deepseek-r1-0528": { + maxTokens: 20480, + contextWindow: 160000, + supportsImages: false, + supportsPromptCache: false, + inputPrice: 3, + outputPrice: 8, + description: + "05/28 updated checkpoint of Deepseek R1. Its overall performance is now approaching that of leading models, such as O3 and Gemini 2.5 Pro. Compared to the previous version, the upgraded model shows significant improvements in handling complex reasoning tasks, and this version also offers a reduced hallucination rate, enhanced support for function calling, and better experience for vibe coding. Note that fine-tuning for this model is only available through contacting fireworks at https://fireworks.ai/company/contact-us.", + }, + "accounts/fireworks/models/deepseek-v3": { + maxTokens: 16384, + contextWindow: 128000, + supportsImages: false, + supportsPromptCache: false, + inputPrice: 0.9, + outputPrice: 0.9, + description: + "A strong Mixture-of-Experts (MoE) language model with 671B total parameters with 37B activated for each token from Deepseek. Note that fine-tuning for this model is only available through contacting fireworks at https://fireworks.ai/company/contact-us.", + }, +} as const satisfies Record diff --git a/webview-ui/src/components/chat/ChatTextArea.tsx b/webview-ui/src/components/chat/ChatTextArea.tsx index 1610447a31..8ffa3c120d 100644 --- a/webview-ui/src/components/chat/ChatTextArea.tsx +++ b/webview-ui/src/components/chat/ChatTextArea.tsx @@ -1140,8 +1140,6 @@ const ChatTextArea = forwardRef( return `vscode-lm:${vsCodeLmModelSelector ? `${vsCodeLmModelSelector.vendor ?? ""}/${vsCodeLmModelSelector.family ?? ""}` : unknownModel}` case "together": return `${selectedProvider}:${togetherModelId}` - case "fireworks": - return `fireworks:${fireworksModelId}` case "lmstudio": return `${selectedProvider}:${lmStudioModelId}` case "ollama": diff --git a/webview-ui/src/components/settings/ApiOptions.tsx b/webview-ui/src/components/settings/ApiOptions.tsx index 570293edbe..f4fef13460 100644 --- a/webview-ui/src/components/settings/ApiOptions.tsx +++ b/webview-ui/src/components/settings/ApiOptions.tsx @@ -154,7 +154,7 @@ const ApiOptions = ({ showModelOptions, apiErrorMessage, modelIdErrorMessage, is VS Code LM API Mistral Requesty - Fireworks + Fireworks AI Together Alibaba Qwen Bytedance Doubao diff --git a/webview-ui/src/components/settings/__tests__/APIOptions.spec.tsx b/webview-ui/src/components/settings/__tests__/APIOptions.spec.tsx index 401d870a64..7c5038e457 100644 --- a/webview-ui/src/components/settings/__tests__/APIOptions.spec.tsx +++ b/webview-ui/src/components/settings/__tests__/APIOptions.spec.tsx @@ -132,34 +132,15 @@ describe("ApiOptions Component", () => { expect(apiKeyInput).toBeInTheDocument() }) - it("renders Fireworks Model ID input", () => { + it("renders Fireworks Model Select", () => { render( , ) - const modelIdInput = screen.getByPlaceholderText("Enter Model ID...") - expect(modelIdInput).toBeInTheDocument() - }) - - it("renders Fireworks Max Completion Tokens input", () => { - render( - - - , - ) - const maxCompletionTokensInput = screen.getByPlaceholderText("2000") - expect(maxCompletionTokensInput).toBeInTheDocument() - }) - - it("renders Fireworks Max Tokens input", () => { - render( - - - , - ) - const maxTokensInput = screen.getByPlaceholderText("4000") - expect(maxTokensInput).toBeInTheDocument() + const modelIdSelect = screen.getByLabelText("Model") + expect(modelIdSelect).toBeInTheDocument() + expect(modelIdSelect).toHaveValue("accounts/fireworks/models/kimi-k2-instruct") }) }) diff --git a/webview-ui/src/components/settings/providers/FireworksProvider.tsx b/webview-ui/src/components/settings/providers/FireworksProvider.tsx index ca40043d4d..ca26126fd7 100644 --- a/webview-ui/src/components/settings/providers/FireworksProvider.tsx +++ b/webview-ui/src/components/settings/providers/FireworksProvider.tsx @@ -1,40 +1,29 @@ -import { ApiConfiguration } from "@shared/api" +import { fireworksModels } from "@shared/api" import { Mode } from "@shared/storage/types" import { useExtensionState } from "@/context/ExtensionStateContext" import { ApiKeyField } from "../common/ApiKeyField" -import { DebouncedTextField } from "../common/DebouncedTextField" -import { getModeSpecificFields } from "../utils/providerUtils" +import { ModelInfoView } from "../common/ModelInfoView" +import { ModelSelector } from "../common/ModelSelector" +import { normalizeApiConfiguration } from "../utils/providerUtils" import { useApiConfigurationHandlers } from "../utils/useApiConfigurationHandlers" /** * Props for the FireworksProvider component */ interface FireworksProviderProps { - showModelOptions: boolean - isPopup?: boolean currentMode: Mode + isPopup?: boolean + showModelOptions: boolean } /** * The Fireworks provider configuration component */ -export const FireworksProvider = ({ showModelOptions, isPopup, currentMode }: FireworksProviderProps) => { +export const FireworksProvider = ({ currentMode, isPopup, showModelOptions }: FireworksProviderProps) => { const { apiConfiguration } = useExtensionState() const { handleModeFieldChange, handleFieldChange } = useApiConfigurationHandlers() - const { fireworksModelId } = getModeSpecificFields(apiConfiguration, currentMode) - - // Handler for number input fields with validation - const handleNumberInputChange = (field: keyof ApiConfiguration, value: string) => { - if (!value) { - return - } - const num = parseInt(value, 10) - if (Number.isNaN(num)) { - return - } - handleFieldChange(field, num) - } + const { selectedModelId, selectedModelInfo } = normalizeApiConfiguration(apiConfiguration, currentMode) return (
@@ -42,51 +31,25 @@ export const FireworksProvider = ({ showModelOptions, isPopup, currentMode }: Fi initialValue={apiConfiguration?.fireworksApiKey || ""} onChange={(value) => handleFieldChange("fireworksApiKey", value)} providerName="Fireworks" - signupUrl="https://fireworks.ai/settings/users/api-keys" + signupUrl="https://fireworks.ai/" + /> + { + handleModeFieldChange( + { + plan: "planModeFireworksModelId", + act: "actModeFireworksModelId", + }, + e.target.value, + currentMode, + ) + }} + selectedModelId={selectedModelId} /> - {showModelOptions && ( - <> - - handleModeFieldChange( - { plan: "planModeFireworksModelId", act: "actModeFireworksModelId" }, - value, - currentMode, - ) - } - placeholder={"Enter Model ID..."} - style={{ width: "100%" }}> - Model ID - -

- - (Note: Cline uses complex prompts and works best with Claude - models. Less capable models may not work as expected.) - -

- handleNumberInputChange("fireworksModelMaxCompletionTokens", value)} - placeholder={"2000"} - style={{ width: "100%", marginBottom: 8 }}> - Max Completion Tokens - - handleNumberInputChange("fireworksModelMaxTokens", value)} - placeholder={"4000"} - style={{ width: "100%", marginBottom: 8 }}> - Max Context Tokens - - - )} +
) } diff --git a/webview-ui/src/components/settings/utils/providerUtils.ts b/webview-ui/src/components/settings/utils/providerUtils.ts index e3f4efd891..33b61ee502 100644 --- a/webview-ui/src/components/settings/utils/providerUtils.ts +++ b/webview-ui/src/components/settings/utils/providerUtils.ts @@ -17,6 +17,8 @@ import { deepSeekModels, doubaoDefaultModelId, doubaoModels, + fireworksDefaultModelId, + fireworksModels, geminiDefaultModelId, geminiModels, groqDefaultModelId, @@ -273,7 +275,9 @@ export function normalizeApiConfiguration( selectedModelId: finalBasetenModelId, selectedModelInfo: basetenModelInfo || basetenModels[finalBasetenModelId as keyof typeof basetenModels] || - basetenModels[basetenDefaultModelId] || { description: "Baseten model" }, + basetenModels[basetenDefaultModelId] || { + description: "Baseten model", + }, } case "sapaicore": return getProviderData(sapAiCoreModels, sapAiCoreDefaultModelId) @@ -296,6 +300,17 @@ export function normalizeApiConfiguration( const zaiDefaultId = apiConfiguration?.zaiApiLine === "china" ? mainlandZAiDefaultModelId : internationalZAiDefaultModelId return getProviderData(zaiModels, zaiDefaultId) + case "fireworks": + const fireworksModelId = + currentMode === "plan" ? apiConfiguration?.planModeFireworksModelId : apiConfiguration?.actModeFireworksModelId + return { + selectedProvider: provider, + selectedModelId: fireworksModelId || fireworksDefaultModelId, + selectedModelInfo: + fireworksModelId && fireworksModelId in fireworksModels + ? fireworksModels[fireworksModelId as keyof typeof fireworksModels] + : fireworksModels[fireworksDefaultModelId], + } default: return getProviderData(anthropicModels, anthropicDefaultModelId) } @@ -419,16 +434,12 @@ export async function syncModeConfigurations( sourceMode: Mode, handleFieldsChange: (updates: Partial) => Promise, ): Promise { - if (!apiConfiguration) { - return - } + if (!apiConfiguration) return const sourceFields = getModeSpecificFields(apiConfiguration, sourceMode) const { apiProvider } = sourceFields - if (!apiProvider) { - return - } + if (!apiProvider) return // Build the complete update object with both plan and act mode fields const updates: Partial = { diff --git a/webview-ui/src/utils/validate.ts b/webview-ui/src/utils/validate.ts index 0c54a8304f..24bdcdbfe4 100644 --- a/webview-ui/src/utils/validate.ts +++ b/webview-ui/src/utils/validate.ts @@ -8,7 +8,6 @@ export function validateApiConfiguration(currentMode: Mode, apiConfiguration?: A apiProvider, openAiModelId, requestyModelId, - fireworksModelId, togetherModelId, ollamaModelId, lmStudioModelId, @@ -87,7 +86,7 @@ export function validateApiConfiguration(currentMode: Mode, apiConfiguration?: A } break case "fireworks": - if (!apiConfiguration.fireworksApiKey || !fireworksModelId) { + if (!apiConfiguration.fireworksApiKey) { return "You must provide a valid API key or choose a different provider." } break