Compare commits

...
8 changed files with 167 additions and 55 deletions
+6 -6
View File
@@ -18,18 +18,18 @@ Fireworks AI is a leading infrastructure platform for generative AI that focuses
Cline supports the following Fireworks AI models:
- `accounts/fireworks/models/kimi-k2-instruct-0905` (Default) - Kimi K2 with 262K context, prompt caching ($0.60/$2.50 per 1M tokens)
- `accounts/fireworks/models/qwen3-235b-a22b-instruct-2507` - Latest Qwen3 thinking model (256K context, $0.22/$0.88 per 1M tokens)
- `accounts/fireworks/models/qwen3-coder-480b-a35b-instruct` - Qwen3's most agentic code model (256K context, $0.45/$1.80 per 1M tokens)
- `accounts/fireworks/models/deepseek-r1-0528` - DeepSeek R1 reasoning model (160K context, $3.00/$8.00 per 1M tokens)
- `accounts/fireworks/models/deepseek-v3` - DeepSeek V3 general-purpose model (128K context, $0.90/$0.90 per 1M tokens)
- `accounts/fireworks/models/kimi-k2p5` (Default) - Kimi K2.5 flagship agentic model with multimodal support (262K context, prompt caching, $0.60/$3.00 per 1M tokens)
- `accounts/fireworks/models/qwen3-vl-30b-a3b-thinking` - Qwen3-VL reasoning model with image support (262K context, prompt caching, $0.15/$0.60 per 1M tokens)
- `accounts/fireworks/models/qwen3-vl-30b-a3b-instruct` - Qwen3-VL instruct model with image support (262K context, $0.15/$0.60 per 1M tokens)
- `accounts/fireworks/models/deepseek-v3p2` - Latest DeepSeek V3.2 model (164K context, prompt caching, $0.56/$1.68 per 1M tokens)
- `accounts/fireworks/models/deepseek-v3p1` - DeepSeek V3.1 long-context model (164K context, prompt caching, $0.56/$1.68 per 1M tokens)
### Configuration in Cline
1. **Open Cline Settings:** Click the settings icon (⚙️) in the Cline panel.
2. **Select Provider:** Choose "Fireworks" from the "API Provider" dropdown.
3. **Enter API Key:** Paste your Fireworks API key into the "Fireworks API Key" field.
4. **Enter Model ID:** Specify the model you want to use (e.g., "accounts/fireworks/models/llama-v3p1-70b-instruct").
4. **Enter Model ID:** Specify the model you want to use (e.g., "accounts/fireworks/models/kimi-k2p5").
5. **Configure Tokens:** Optionally set max completion tokens and context window size.
### Fireworks AI's Performance Focus
@@ -52,4 +52,46 @@ describe("FireworksHandler", () => {
},
])
})
it("should read cache hits from prompt_tokens_details when hit tokens are not present", async () => {
const handler = new FireworksHandler({
fireworksApiKey: "test-api-key",
fireworksModelId: "accounts/fireworks/models/llama-v3p1-8b-instruct",
})
const fakeClient = {
chat: {
completions: {
create: sinon.stub().resolves(
createAsyncIterable([
{
choices: [{}],
usage: {
prompt_tokens: 60,
completion_tokens: 12,
prompt_tokens_details: { cached_tokens: 20 },
prompt_cache_miss_tokens: 40,
},
},
]),
),
},
},
}
sinon.stub(handler as any, "ensureClient").returns(fakeClient as any)
const chunks: any[] = []
for await (const chunk of handler.createMessage("system", [{ role: "user", content: "hi" }])) {
chunks.push(chunk)
}
chunks.should.deepEqual([
{
type: "usage",
inputTokens: 60,
outputTokens: 12,
cacheReadTokens: 20,
cacheWriteTokens: 40,
},
])
})
})
+12 -6
View File
@@ -83,14 +83,20 @@ export class FireworksHandler implements ApiHandler {
}
if (chunk.usage) {
const usage = chunk.usage as OpenAI.CompletionUsage & {
prompt_cache_hit_tokens?: number
prompt_cache_miss_tokens?: number
prompt_tokens_details?: {
cached_tokens?: number
}
}
yield {
type: "usage",
inputTokens: chunk.usage.prompt_tokens || 0, // (deepseek reports total input AND cache reads/writes, see context caching: https://api-docs.deepseek.com/guides/kv_cache) where the input tokens is the sum of the cache hits/misses, while anthropic reports them as separate tokens. This is important to know for 1) context management truncation algorithm, and 2) cost calculation (NOTE: we report both input and cache stats but for now set input price to 0 since all the cost calculation will be done using cache hits/misses)
outputTokens: chunk.usage.completion_tokens || 0,
// @ts-expect-error-next-line
cacheReadTokens: chunk.usage.prompt_cache_hit_tokens || 0,
// @ts-expect-error-next-line
cacheWriteTokens: chunk.usage.prompt_cache_miss_tokens || 0,
inputTokens: usage.prompt_tokens || 0,
outputTokens: usage.completion_tokens || 0,
// Fireworks can return cache hits either as prompt_cache_hit_tokens or prompt_tokens_details.cached_tokens.
cacheReadTokens: usage.prompt_cache_hit_tokens ?? usage.prompt_tokens_details?.cached_tokens ?? 0,
cacheWriteTokens: usage.prompt_cache_miss_tokens || 0,
}
}
}
@@ -2,6 +2,7 @@ import assert from "node:assert/strict"
import { EventEmitter } from "events"
import { describe, it } from "mocha"
import { orchestrateCommandExecution } from "./CommandOrchestrator"
import { MAX_BYTES_BEFORE_FILE } from "./constants"
import type {
CommandExecutorCallbacks,
ITerminalManager,
@@ -111,4 +112,26 @@ describe("CommandOrchestrator exit status messaging", () => {
assert.equal(result.exitCode, 0)
assert.match(result.result as string, /^Command executed successfully \(exit code 0\)\./)
})
it("truncates huge single-line summaries when file logging is triggered by bytes", async () => {
const process = new FakeTerminalProcess()
const orchestrationPromise = orchestrateCommandExecution(
process.asResultPromise(),
createTerminalManager(),
createCallbacks(),
{ command: "echo huge" },
)
const hugeLine = `START-${"x".repeat(MAX_BYTES_BEFORE_FILE + 32 * 1024)}-END`
process.emit("line", hugeLine)
await new Promise((resolve) => setTimeout(resolve, 0))
process.complete({ exitCode: 0, signal: null })
const result: OrchestrationResult = await orchestrationPromise
const resultText = result.result as string
assert.equal(result.completed, true)
assert.match(resultText, /output truncated by size/)
assert.match(resultText, /Full output saved to:/)
assert.equal(resultText.includes(hugeLine), false)
})
})
@@ -29,6 +29,7 @@ import {
COMPLETION_TIMEOUT_MS,
MAX_BYTES_BEFORE_FILE,
MAX_LINES_BEFORE_FILE,
SUMMARY_BYTES_TO_KEEP,
SUMMARY_LINES_TO_KEEP,
} from "./constants"
import type {
@@ -529,10 +530,19 @@ export async function orchestrateCommandExecution(
let resultOutputLines: string[]
if (isWritingToFile) {
// Build summary from first and last lines
const skippedLines = totalLineCount - firstLines.length - lastLines.length
const summaryLines = [...firstLines, `\n... (${skippedLines} lines written to ${largeOutputLogPath}) ...\n`, ...lastLines]
result = terminalManager.processOutput(summaryLines)
// Build summary from first and last lines, avoiding overlap when output is small.
const overlappingLines = Math.max(0, firstLines.length + lastLines.length - totalLineCount)
const nonOverlappingLastLines = overlappingLines > 0 ? lastLines.slice(overlappingLines) : lastLines
const skippedLines = Math.max(0, totalLineCount - firstLines.length - nonOverlappingLastLines.length)
const summaryLines =
skippedLines > 0
? [
...firstLines,
`\n... (${skippedLines} lines written to ${largeOutputLogPath ?? "output log file"}) ...\n`,
...nonOverlappingLastLines,
]
: [...firstLines, ...nonOverlappingLastLines]
result = truncateSummaryByBytes(terminalManager.processOutput(summaryLines), SUMMARY_BYTES_TO_KEEP)
resultOutputLines = summaryLines
} else {
result = terminalManager.processOutput(outputLines)
@@ -626,3 +636,25 @@ export function findLastIndex<T>(array: T[], predicate: (item: T) => boolean): n
}
return -1
}
function truncateSummaryByBytes(output: string, maxBytes: number): string {
if (Buffer.byteLength(output, "utf8") <= maxBytes) {
return output
}
const marker = "\n... (output truncated by size) ...\n"
const markerBytes = Buffer.byteLength(marker, "utf8")
if (maxBytes <= markerBytes) {
return marker.trim()
}
const bytesForContent = maxBytes - markerBytes
const startBytes = Math.ceil(bytesForContent / 2)
const endBytes = Math.floor(bytesForContent / 2)
const buffer = Buffer.from(output, "utf8")
const start = buffer.subarray(0, startBytes).toString("utf8").replace(/\uFFFD+$/u, "")
const end = buffer.subarray(buffer.length - endBytes).toString("utf8").replace(/^\uFFFD+/u, "")
return `${start}${marker}${end}`.trim()
}
+3
View File
@@ -51,6 +51,9 @@ export const MAX_BYTES_BEFORE_FILE = 512 * 1024 // 512KB
/** Lines to keep at start/end for summary when truncating */
export const SUMMARY_LINES_TO_KEEP = 100
/** Maximum bytes to keep in large-output summaries returned to AI */
export const SUMMARY_BYTES_TO_KEEP = 64 * 1024 // 64KB
/** Maximum size for fullOutput storage (memory protection) */
export const MAX_FULL_OUTPUT_SIZE = 1024 * 1024 // 1MB
+44 -38
View File
@@ -4587,56 +4587,62 @@ export const mainlandZAiModels = {
// Fireworks AI
export type FireworksModelId = keyof typeof fireworksModels
export const fireworksDefaultModelId: FireworksModelId = "accounts/fireworks/models/kimi-k2-instruct-0905"
export const fireworksDefaultModelId: FireworksModelId = "accounts/fireworks/models/kimi-k2p5"
export const fireworksModels = {
"accounts/fireworks/models/kimi-k2-instruct-0905": {
"accounts/fireworks/models/kimi-k2p5": {
maxTokens: 16384,
contextWindow: 262144,
supportsImages: false,
supportsImages: true,
supportsPromptCache: true,
inputPrice: 0.6,
outputPrice: 2.5,
cacheReadsPrice: 0.15,
outputPrice: 3,
cacheWritesPrice: 0.6,
cacheReadsPrice: 0.1,
description:
"Kimi K2 model gets a new version update: Agentic coding: more accurate, better generalization across scaffolds. Frontend coding: improved aesthetics and functionalities on web, 3d, and other tasks. Context length: extended from 128k to 256k, providing better long-horizon support.",
"Moonshot's flagship open agentic model. Kimi K2.5 unifies vision and text, thinking and non-thinking modes, and single-agent and multi-agent execution.",
},
"accounts/fireworks/models/qwen3-235b-a22b-instruct-2507": {
"accounts/fireworks/models/qwen3-vl-30b-a3b-thinking": {
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,
contextWindow: 262144,
supportsImages: true,
supportsPromptCache: true,
inputPrice: 0.15,
outputPrice: 0.6,
cacheWritesPrice: 0.15,
cacheReadsPrice: 0.07,
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.",
"Reasoning-enabled Qwen3-VL model with strong multimodal understanding, long context support, and function calling.",
},
"accounts/fireworks/models/deepseek-v3": {
"accounts/fireworks/models/qwen3-vl-30b-a3b-instruct": {
maxTokens: 32768,
contextWindow: 262144,
supportsImages: true,
supportsPromptCache: false,
inputPrice: 0.15,
outputPrice: 0.6,
description: "Qwen3-VL instruct model with strong multimodal reasoning, long context support, and function calling.",
},
"accounts/fireworks/models/deepseek-v3p2": {
maxTokens: 16384,
contextWindow: 128000,
contextWindow: 163840,
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.",
supportsPromptCache: true,
inputPrice: 0.56,
outputPrice: 1.68,
cacheWritesPrice: 0.56,
cacheReadsPrice: 0.28,
description: "DeepSeek V3.2 model tuned for high computational efficiency and strong reasoning and agent performance.",
},
"accounts/fireworks/models/deepseek-v3p1": {
maxTokens: 16384,
contextWindow: 163840,
supportsImages: false,
supportsPromptCache: true,
inputPrice: 0.56,
outputPrice: 1.68,
cacheWritesPrice: 0.56,
cacheReadsPrice: 0.28,
description: "DeepSeek V3.1 long-context model with improved reasoning, agentic behavior, and function calling.",
},
} as const satisfies Record<string, ModelInfo>
@@ -138,7 +138,7 @@ describe("ApiOptions Component", () => {
)
const modelIdSelect = screen.getByLabelText("Model")
expect(modelIdSelect).toBeInTheDocument()
expect(modelIdSelect).toHaveValue("accounts/fireworks/models/kimi-k2-instruct-0905")
expect(modelIdSelect).toHaveValue("accounts/fireworks/models/kimi-k2p5")
})
})