Skip to content

Commit 8827346

Browse files
committed
goddammit
1 parent 194bb9a commit 8827346

14 files changed

Lines changed: 555 additions & 76 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
@@ -362,6 +362,38 @@ describe('AgentBlockHandler', () => {
362362
})
363363
})
364364

365+
it('keeps the Fireworks deployment separate from the catalog billing model', async () => {
366+
mockContext.userId = 'super-user'
367+
mockExecuteProviderRequest.mockResolvedValue({
368+
content: 'On-demand response',
369+
model: 'fireworks/qwen3.7-max',
370+
tokens: { input: 100, output: 20, total: 120 },
371+
})
372+
373+
await handler.execute(mockContext, mockBlock, {
374+
model: CUSTOM_MODEL_ID,
375+
customModelConfig: {
376+
provider: 'fireworks',
377+
model: 'fireworks/qwen3.7-max',
378+
deployment: 'accounts/acme/deployments/qwen-prod',
379+
credentials: { mode: 'explicit', apiKey: 'fw-secret' },
380+
},
381+
userPrompt: 'Hello',
382+
})
383+
384+
expect(mockExecuteProviderRequest).toHaveBeenCalledWith(
385+
'fireworks',
386+
expect.objectContaining({
387+
model: 'fireworks/qwen3.7-max',
388+
providerModel: 'accounts/acme/deployments/qwen-prod',
389+
apiKey: 'fw-secret',
390+
credentialMode: 'explicit',
391+
capabilityPolicy: 'passthrough',
392+
}),
393+
expect.anything()
394+
)
395+
})
396+
365397
it('rejects custom model execution when Super User mode is off', async () => {
366398
mockContext.userId = 'admin-with-toggle-off'
367399
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
@@ -145,6 +145,25 @@ describe('fireworksProvider', () => {
145145
})
146146
})
147147

148+
it('reports cached prompts separately from uncached input', async () => {
149+
mockCreate.mockResolvedValueOnce({
150+
...textResponse('cached'),
151+
usage: {
152+
prompt_tokens: 10,
153+
completion_tokens: 5,
154+
total_tokens: 15,
155+
prompt_tokens_details: { cached_tokens: 4 },
156+
},
157+
})
158+
159+
const result = await fireworksProvider.executeRequest(baseRequest)
160+
161+
expect(result).toMatchObject({
162+
tokens: { input: 6, cacheRead: 4, output: 5, total: 15 },
163+
cost: expect.objectContaining({ input: expect.any(Number), output: expect.any(Number) }),
164+
})
165+
})
166+
148167
it('sends the resolved wire model name while reporting the catalog id', async () => {
149168
mockCreate.mockResolvedValueOnce(textResponse('ok'))
150169
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
@@ -187,6 +187,8 @@ export function enrichLastModelSegmentFromChatCompletions(
187187
errorMessage?: string
188188
/** Override the automatically derived cost. */
189189
cost?: { input?: number; output?: number; total?: number }
190+
/** Override normalized token buckets when provider semantics differ from OpenAI. */
191+
tokens?: BlockTokens
190192
}
191193
): void {
192194
const choice = response.choices[0]
@@ -228,15 +230,17 @@ export function enrichLastModelSegmentFromChatCompletions(
228230
thinkingContent: thinkingText,
229231
toolCalls: toolCalls.length > 0 ? toolCalls : undefined,
230232
finishReason: choice?.finish_reason ?? undefined,
231-
tokens: usage
232-
? {
233-
input: promptTokens,
234-
output: completionTokens,
235-
total: usage.total_tokens ?? undefined,
236-
...(cacheRead > 0 && { cacheRead }),
237-
...(reasoning > 0 && { reasoning }),
238-
}
239-
: undefined,
233+
tokens:
234+
extras?.tokens ??
235+
(usage
236+
? {
237+
input: promptTokens === undefined ? undefined : Math.max(0, promptTokens - cacheRead),
238+
output: completionTokens,
239+
total: usage.total_tokens ?? undefined,
240+
...(cacheRead > 0 && { cacheRead }),
241+
...(reasoning > 0 && { reasoning }),
242+
}
243+
: undefined),
240244
cost: derivedCost,
241245
ttft: extras?.ttft,
242246
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
@@ -940,7 +940,9 @@ export function calculateCost(
940940

941941
const inputCost =
942942
promptTokens *
943-
(useCachedInput && cachedInputRate ? cachedInputRate / 1_000_000 : inputRate / 1_000_000)
943+
(useCachedInput && cachedInputRate !== undefined
944+
? cachedInputRate / 1_000_000
945+
: inputRate / 1_000_000)
944946

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

0 commit comments

Comments
 (0)