Skip to content

Commit 06d840a

Browse files
committed
goddammit
1 parent 66b36b6 commit 06d840a

14 files changed

Lines changed: 555 additions & 80 deletions

File tree

apps/sim/executor/handlers/agent/agent-handler.test.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,38 @@ describe('AgentBlockHandler', () => {
357357
})
358358
})
359359

360+
it('keeps the Fireworks deployment separate from the catalog billing model', async () => {
361+
mockContext.userId = 'super-user'
362+
mockExecuteProviderRequest.mockResolvedValue({
363+
content: 'On-demand response',
364+
model: 'fireworks/qwen3.7-max',
365+
tokens: { input: 100, output: 20, total: 120 },
366+
})
367+
368+
await handler.execute(mockContext, mockBlock, {
369+
model: CUSTOM_MODEL_ID,
370+
customModelConfig: {
371+
provider: 'fireworks',
372+
model: 'fireworks/qwen3.7-max',
373+
deployment: 'accounts/acme/deployments/qwen-prod',
374+
credentials: { mode: 'explicit', apiKey: 'fw-secret' },
375+
},
376+
userPrompt: 'Hello',
377+
})
378+
379+
expect(mockExecuteProviderRequest).toHaveBeenCalledWith(
380+
'fireworks',
381+
expect.objectContaining({
382+
model: 'fireworks/qwen3.7-max',
383+
providerModel: 'accounts/acme/deployments/qwen-prod',
384+
apiKey: 'fw-secret',
385+
credentialMode: 'explicit',
386+
capabilityPolicy: 'passthrough',
387+
}),
388+
expect.anything()
389+
)
390+
})
391+
360392
it('rejects custom model execution when Super User mode is off', async () => {
361393
mockContext.userId = 'admin-with-toggle-off'
362394
mockVerifyEffectiveSuperUser.mockResolvedValue({

apps/sim/executor/types.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,21 @@ interface BlockCost {
172172
cachedInput?: number
173173
output: number
174174
}
175+
serviceTiers?: {
176+
priority?:
177+
| { multiplier: number }
178+
| {
179+
input: number
180+
cachedInput?: number
181+
output: number
182+
longContext?: {
183+
threshold: number
184+
input: number
185+
cachedInput?: number
186+
output: number
187+
}
188+
}
189+
}
175190
}
176191
}
177192

apps/sim/providers/cost-policy.test.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,14 @@ describe('withoutToolCost', () => {
253253
const cost = { input: 1, output: 2, total: 3 }
254254
expect(withoutToolCost(cost)).toBe(cost)
255255
})
256+
257+
it('preserves an exact vendor model total that differs from the displayed split', () => {
258+
expect(withoutToolCost({ input: 0.3, output: 0.6, total: 1.05, toolCost: 0.05 })).toEqual({
259+
input: 0.3,
260+
output: 0.6,
261+
total: 1,
262+
})
263+
})
256264
})
257265

258266
describe('calculateBillableModelCost', () => {

apps/sim/providers/cost-policy.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export interface ModelCost {
3131
/** Cost that always carries pricing, as `ProviderResponse['cost']` requires. */
3232
export type PricedModelCost = ModelCost & { pricing: ModelPricing }
3333

34-
/** Vendor list-price information shown for explicit custom credentials only. */
34+
/** Vendor list-price information shown for Custom JSON calls resolved to user credentials. */
3535
export interface EstimatedProviderCost {
3636
available: boolean
3737
input?: number
@@ -294,8 +294,8 @@ export function calculateBillableModelCost(
294294
export function withoutToolCost(cost: ModelCost): ModelCost {
295295
if (cost.toolCost === undefined) return cost
296296

297-
const { toolCost: _toolCost, ...model } = cost
298-
return { ...model, total: roundCost(model.input + model.output) }
297+
const { toolCost, ...model } = cost
298+
return { ...model, total: roundCost(Math.max(0, model.total - toolCost)) }
299299
}
300300

301301
/** Rejects NaN, Infinity, and negatives — a negative would credit the run. */

apps/sim/providers/fireworks/index.test.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,25 @@ describe('fireworksProvider', () => {
140140
})
141141
})
142142

143+
it('reports cached prompts separately from uncached input', async () => {
144+
mockCreate.mockResolvedValueOnce({
145+
...textResponse('cached'),
146+
usage: {
147+
prompt_tokens: 10,
148+
completion_tokens: 5,
149+
total_tokens: 15,
150+
prompt_tokens_details: { cached_tokens: 4 },
151+
},
152+
})
153+
154+
const result = await fireworksProvider.executeRequest(baseRequest)
155+
156+
expect(result).toMatchObject({
157+
tokens: { input: 6, cacheRead: 4, output: 5, total: 15 },
158+
cost: expect.objectContaining({ input: expect.any(Number), output: expect.any(Number) }),
159+
})
160+
})
161+
143162
it('sends the resolved wire model name while reporting the catalog id', async () => {
144163
mockCreate.mockResolvedValueOnce(textResponse('ok'))
145164
mockResolveFireworksWireModel.mockReturnValueOnce('accounts/fireworks/models/glm-5p2')
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { describe, expect, it } from 'vitest'
2+
import { enrichLastModelSegmentFromChatCompletions } from '@/providers/trace-enrichment'
3+
4+
describe('chat-completions trace enrichment', () => {
5+
it('separates cached input and accepts provider-normalized token overrides', () => {
6+
const segments: any[] = [
7+
{ type: 'model', name: 'model', startTime: 1, endTime: 2, duration: 1 },
8+
]
9+
const response = {
10+
choices: [{ message: { content: 'ok' }, finish_reason: 'stop' }],
11+
usage: {
12+
prompt_tokens: 100,
13+
completion_tokens: 20,
14+
total_tokens: 120,
15+
prompt_tokens_details: { cached_tokens: 40 },
16+
completion_tokens_details: { reasoning_tokens: 5 },
17+
},
18+
}
19+
20+
enrichLastModelSegmentFromChatCompletions(segments, response, undefined, {
21+
cost: { input: 1, output: 2, total: 3 },
22+
})
23+
expect(segments[0].tokens).toEqual({
24+
input: 60,
25+
cacheRead: 40,
26+
output: 20,
27+
reasoning: 5,
28+
total: 120,
29+
})
30+
31+
enrichLastModelSegmentFromChatCompletions(segments, response, undefined, {
32+
tokens: { input: 60, cacheRead: 40, output: 25, reasoning: 5, total: 125 },
33+
cost: { input: 1, output: 2, total: 3 },
34+
})
35+
expect(segments[0].tokens).toEqual({
36+
input: 60,
37+
cacheRead: 40,
38+
output: 25,
39+
reasoning: 5,
40+
total: 125,
41+
})
42+
})
43+
})

apps/sim/providers/trace-enrichment.ts

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,8 @@ export function enrichLastModelSegmentFromChatCompletions(
172172
errorMessage?: string
173173
/** Override the automatically derived cost. */
174174
cost?: { input?: number; output?: number; total?: number }
175+
/** Override normalized token buckets when provider semantics differ from OpenAI. */
176+
tokens?: BlockTokens
175177
}
176178
): void {
177179
const choice = response.choices[0]
@@ -213,15 +215,17 @@ export function enrichLastModelSegmentFromChatCompletions(
213215
thinkingContent: thinkingText,
214216
toolCalls: toolCalls.length > 0 ? toolCalls : undefined,
215217
finishReason: choice?.finish_reason ?? undefined,
216-
tokens: usage
217-
? {
218-
input: promptTokens,
219-
output: completionTokens,
220-
total: usage.total_tokens ?? undefined,
221-
...(cacheRead > 0 && { cacheRead }),
222-
...(reasoning > 0 && { reasoning }),
223-
}
224-
: undefined,
218+
tokens:
219+
extras?.tokens ??
220+
(usage
221+
? {
222+
input: promptTokens === undefined ? undefined : Math.max(0, promptTokens - cacheRead),
223+
output: completionTokens,
224+
total: usage.total_tokens ?? undefined,
225+
...(cacheRead > 0 && { cacheRead }),
226+
...(reasoning > 0 && { reasoning }),
227+
}
228+
: undefined),
225229
cost: derivedCost,
226230
ttft: extras?.ttft,
227231
provider: extras?.provider,

apps/sim/providers/types.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -150,9 +150,9 @@ export interface ProviderResponse {
150150
pricing: ModelPricing
151151
}
152152
/**
153-
* Vendor list-price estimate for explicit custom credentials. This is
154-
* informational only and never enters Sim's usage ledger; `cost` remains the
155-
* authoritative amount Sim charges.
153+
* Vendor list-price estimate for Custom JSON calls resolved to explicit or
154+
* workspace BYOK credentials. This is informational only and never enters
155+
* Sim's usage ledger; `cost` remains the authoritative amount Sim charges.
156156
*/
157157
estimatedProviderCost?: {
158158
available: boolean

apps/sim/providers/utils.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -939,7 +939,9 @@ export function calculateCost(
939939

940940
const inputCost =
941941
promptTokens *
942-
(useCachedInput && cachedInputRate ? cachedInputRate / 1_000_000 : inputRate / 1_000_000)
942+
(useCachedInput && cachedInputRate !== undefined
943+
? cachedInputRate / 1_000_000
944+
: inputRate / 1_000_000)
943945

944946
const outputCost = completionTokens * (outputRate / 1_000_000)
945947
const finalInputCost = inputCost * (inputMultiplier ?? 1)

0 commit comments

Comments
 (0)