Skip to content

Commit 0508acf

Browse files
committed
All models
1 parent 1e3f324 commit 0508acf

20 files changed

Lines changed: 856 additions & 52 deletions

File tree

apps/sim/blocks/blocks/agent.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -726,6 +726,11 @@ Return ONLY the JSON array.`,
726726
description: 'Provider timing information',
727727
},
728728
cost: { type: 'json', description: 'Cost of the API call' },
729+
estimatedProviderCost: {
730+
type: 'json',
731+
description:
732+
'Informational vendor list-price estimate for explicit Custom credentials; never charged by Sim. GPU-time models report why a per-request estimate is unavailable.',
733+
},
729734
interactionId: {
730735
type: 'string',
731736
description: 'Interaction ID for multi-turn deep research follow-ups',

apps/sim/blocks/utils.test.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,24 @@ const {
1212
mockGetProviderModels,
1313
mockGetProviderIcon,
1414
mockGetBaseModelProviders,
15+
mockIsModelVisibleInStandardAuthoring,
1516
} = vi.hoisted(() => ({
1617
mockGetHostedModels: vi.fn(() => []),
1718
mockGetHostedFireworksModels: vi.fn(() => [
1819
'fireworks/glm-5.2',
1920
'fireworks/kimi-k3',
2021
'fireworks/deepseek-v4-pro',
22+
'fireworks/minimax-m2.7',
2123
]),
2224
mockGetProviderModels: vi.fn(() => []),
2325
mockGetProviderIcon: vi.fn(() => null),
2426
mockGetBaseModelProviders: vi.fn(() => ({})),
27+
mockIsModelVisibleInStandardAuthoring: vi.fn(
28+
(model: string) =>
29+
!['fireworks/minimax-m2.7', 'fireworks/accounts/fireworks/models/minimax-m2p7'].includes(
30+
model
31+
)
32+
),
2533
}))
2634

2735
const { mockProviders } = vi.hoisted(() => ({
@@ -50,6 +58,7 @@ vi.mock('@/providers/models', () => ({
5058
getProviderModels: mockGetProviderModels,
5159
getProviderIcon: mockGetProviderIcon,
5260
getBaseModelProviders: mockGetBaseModelProviders,
61+
isModelVisibleInStandardAuthoring: mockIsModelVisibleInStandardAuthoring,
5362
getModelSunsetStatus: vi.fn(() => undefined),
5463
orderModelIdsByReleaseDate: vi.fn((models: string[]) => models),
5564
SIM_AUTO_MODEL_ID: 'sim-auto',
@@ -303,6 +312,7 @@ describe('getModelOptions', () => {
303312
it('always includes the static Fireworks catalog and preserves dynamic BYOK models', () => {
304313
mockProviders.value.fireworks.models = [
305314
'fireworks/kimi-k3',
315+
'fireworks/accounts/fireworks/models/minimax-m2p7',
306316
'fireworks/accounts/acme/models/custom',
307317
]
308318

@@ -317,6 +327,8 @@ describe('getModelOptions', () => {
317327
])
318328
)
319329
expect(ids.filter((id) => id === 'fireworks/kimi-k3')).toHaveLength(1)
330+
expect(ids).not.toContain('fireworks/minimax-m2.7')
331+
expect(ids).not.toContain('fireworks/accounts/fireworks/models/minimax-m2p7')
320332
})
321333

322334
it('adds the super-user custom option only to the Agent model options', () => {

apps/sim/blocks/utils.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
getProviderIcon,
1919
getProviderModels,
2020
isAutoModel,
21+
isModelVisibleInStandardAuthoring,
2122
orderModelIdsByReleaseDate,
2223
SIM_AUTO_MODEL_ID,
2324
} from '@/providers/models'
@@ -83,6 +84,7 @@ export function getModelOptions() {
8384
)
8485

8586
const options = allModels
87+
.filter(isModelVisibleInStandardAuthoring)
8688
.filter((model) => getModelSunsetStatus(model) !== 'deprecated')
8789
.map((model) => {
8890
const icon = getProviderIcon(model)

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

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,8 +293,22 @@ describe('AgentBlockHandler', () => {
293293

294294
it('routes a Super User custom model through the explicit provider contract', async () => {
295295
mockContext.userId = 'super-user'
296+
mockExecuteProviderRequest.mockResolvedValue({
297+
content: 'Custom response',
298+
model: 'grok-4.5',
299+
tokens: { input: 1200, output: 300, total: 1500 },
300+
toolCalls: [],
301+
cost: { input: 0, output: 0, total: 0 },
302+
estimatedProviderCost: {
303+
available: true,
304+
input: 0.0024,
305+
output: 0.0018,
306+
total: 0.0042,
307+
pricing: { input: 2, cachedInput: 0.3, output: 6, updatedAt: '2026-07-08' },
308+
},
309+
})
296310

297-
await handler.execute(mockContext, mockBlock, {
311+
const result = await handler.execute(mockContext, mockBlock, {
298312
model: CUSTOM_MODEL_ID,
299313
customModelConfig: {
300314
provider: 'xai',
@@ -331,6 +345,16 @@ describe('AgentBlockHandler', () => {
331345
}),
332346
expect.anything()
333347
)
348+
expect(result).toMatchObject({
349+
tokens: { input: 1200, output: 300, total: 1500 },
350+
cost: { input: 0, output: 0, total: 0 },
351+
estimatedProviderCost: {
352+
available: true,
353+
input: 0.0024,
354+
output: 0.0018,
355+
total: 0.0042,
356+
},
357+
})
334358
})
335359

336360
it('rejects custom model execution when Super User mode is off', async () => {

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1480,6 +1480,7 @@ export class AgentBlockHandler implements BlockHandler {
14801480
toolCalls?: Array<any>
14811481
timing?: any
14821482
cost?: any
1483+
estimatedProviderCost?: any
14831484
}) {
14841485
return {
14851486
model: result.model,
@@ -1494,6 +1495,9 @@ export class AgentBlockHandler implements BlockHandler {
14941495
},
14951496
providerTiming: result.timing,
14961497
cost: result.cost,
1498+
...(result.estimatedProviderCost && {
1499+
estimatedProviderCost: result.estimatedProviderCost,
1500+
}),
14971501
}
14981502
}
14991503

apps/sim/executor/types.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,9 +159,32 @@ interface BlockCost {
159159
output: number
160160
cachedInput?: number
161161
updatedAt: string
162+
billingMode?: 'per_token' | 'gpu_time'
163+
gpuHourlyRates?: {
164+
h100?: number
165+
h200?: number
166+
b200?: number
167+
b300?: number
168+
}
169+
longContext?: {
170+
threshold: number
171+
input: number
172+
cachedInput?: number
173+
output: number
174+
}
162175
}
163176
}
164177

178+
/** Informational vendor estimate that is deliberately excluded from Sim billing. */
179+
interface EstimatedProviderCost {
180+
available: boolean
181+
input?: number
182+
output?: number
183+
total?: number
184+
pricing: NonNullable<BlockCost['pricing']>
185+
unavailableReason?: string
186+
}
187+
165188
/** Token usage from provider. `prompt`/`completion` are legacy aliases. */
166189
export interface BlockTokens {
167190
input?: number
@@ -204,6 +227,7 @@ export interface NormalizedBlockOutput {
204227
toolCalls?: BlockToolCalls
205228
providerTiming?: BlockProviderTiming
206229
cost?: BlockCost
230+
estimatedProviderCost?: EstimatedProviderCost
207231
files?: UserFile[]
208232
selectedPath?: {
209233
blockId: string

apps/sim/lib/copilot/tools/server/workflow/edit-workflow/validation.test.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,20 @@ describe('validateInputsForBlock', () => {
400400
expect(result.validInputs.model).toBe('sim-custom')
401401
})
402402

403+
it('rejects JSON-only Fireworks models in ordinary Agent and Router model fields', () => {
404+
for (const [blockType, model] of [
405+
['agent', 'fireworks/minimax-m2.7'],
406+
['router_v2', 'fireworks/accounts/fireworks/models/minimax-m2p7'],
407+
]) {
408+
const result = validateInputsForBlock(blockType, { model }, `${blockType}-1`)
409+
410+
expect(result.validInputs.model).toBeUndefined()
411+
expect(result.errors).toHaveLength(1)
412+
expect(result.errors[0]?.error).toContain('Super User Custom model configuration')
413+
expect(result.errors[0]?.error).toContain('sim-custom')
414+
}
415+
})
416+
403417
it('rejects hallucinated agent model ids that match a static provider pattern', () => {
404418
const result = validateInputsForBlock('agent', { model: 'claude-sonnet-4.6' }, 'agent-1')
405419

apps/sim/lib/copilot/tools/server/workflow/edit-workflow/validation.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,12 @@ import type { SubBlockConfig } from '@/blocks/types'
1818
import { getAgentModelOptions, getModelOptions } from '@/blocks/utils'
1919
import { BlockType, EDGE, normalizeName } from '@/executor/constants'
2020
import { isCustomModel } from '@/providers/custom-model'
21-
import { isAutoModel, isKnownModelId, suggestModelIdsForUnknownModel } from '@/providers/models'
21+
import {
22+
isAutoModel,
23+
isCustomJsonOnlyModel,
24+
isKnownModelId,
25+
suggestModelIdsForUnknownModel,
26+
} from '@/providers/models'
2227
import { isPiByokOnlyMode } from '@/providers/pi-providers'
2328
import { getTool } from '@/tools/utils'
2429
import { TRIGGER_RUNTIME_SUBBLOCK_IDS, TRIGGER_WEBHOOK_URL_FIELD } from '@/triggers/constants'
@@ -598,6 +603,18 @@ export function validateValueForSubBlockType(
598603
if (trimmed !== '' && isAutoModel(trimmed) && isHostedDeployment) {
599604
return { valid: true, value: trimmed.toLowerCase() }
600605
}
606+
if (trimmed !== '' && isCustomJsonOnlyModel(trimmed)) {
607+
return {
608+
valid: false,
609+
error: {
610+
blockId,
611+
blockType,
612+
field: fieldName,
613+
value,
614+
error: `Model "${trimmed}" is available only through an Agent block's Super User Custom model configuration. Set model to "sim-custom" and provide customModelConfig instead.`,
615+
},
616+
}
617+
}
601618
if (trimmed !== '' && !isKnownModelId(trimmed)) {
602619
const suggestions = suggestModelIdsForUnknownModel(trimmed)
603620
const suggestionText =

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

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import type { NormalizedBlockOutput } from '@/executor/types'
77
import {
88
applyModelCostPolicy,
99
applySegmentCostPolicy,
10+
buildEstimatedProviderCost,
1011
calculateBillableModelCost,
1112
installStreamingCostPolicy,
1213
LIST_PRICE_POLICY,
@@ -65,6 +66,51 @@ describe('applyModelCostPolicy', () => {
6566
})
6667
})
6768

69+
describe('buildEstimatedProviderCost', () => {
70+
it('returns a list-price estimate for a known per-token model', () => {
71+
expect(
72+
buildEstimatedProviderCost('fireworks/minimax-m2.7', {
73+
input: 0.0003,
74+
output: 0.0012,
75+
total: 0.0015,
76+
})
77+
).toMatchObject({
78+
available: true,
79+
input: 0.0003,
80+
output: 0.0012,
81+
total: 0.0015,
82+
pricing: { input: 0.3, cachedInput: 0.059, output: 1.2 },
83+
})
84+
})
85+
86+
it('reports GPU-time billing without fabricating a request cost', () => {
87+
expect(
88+
buildEstimatedProviderCost('fireworks/qwen3.7-max', {
89+
input: 0,
90+
output: 0,
91+
total: 0,
92+
})
93+
).toMatchObject({
94+
available: false,
95+
pricing: {
96+
billingMode: 'gpu_time',
97+
gpuHourlyRates: { h100: 7, h200: 7, b200: 10, b300: 12 },
98+
},
99+
unavailableReason: expect.stringContaining('active GPU time'),
100+
})
101+
})
102+
103+
it('omits estimates for uncataloged arbitrary custom models', () => {
104+
expect(
105+
buildEstimatedProviderCost('fireworks/accounts/acme/models/private', {
106+
input: 1,
107+
output: 2,
108+
total: 3,
109+
})
110+
).toBeUndefined()
111+
})
112+
})
113+
68114
describe('priceModelUsage', () => {
69115
/** Claude Sonnet 5: $2/MTok input, $0.20/MTok cached, $10/MTok output. */
70116
const PRICED_MODEL = 'claude-sonnet-5'
@@ -234,6 +280,21 @@ describe('installStreamingCostPolicy', () => {
234280

235281
expect(output.cost).toMatchObject({ input: 0, output: 0, total: 0 })
236282
})
283+
284+
it('keeps a late list-price estimate separate from the zero explicit-key charge', () => {
285+
const output = { cost: { input: 0, output: 0, total: 0 } } as NormalizedBlockOutput
286+
installStreamingCostPolicy(output, { billable: false, multiplier: 0 }, 'fireworks/minimax-m2.7')
287+
288+
output.cost = { input: 0.0003, output: 0.0012, total: 0.0015 }
289+
290+
expect(output.cost).toMatchObject({ input: 0, output: 0, total: 0 })
291+
expect(output.estimatedProviderCost).toMatchObject({
292+
available: true,
293+
input: 0.0003,
294+
output: 0.0012,
295+
total: 0.0015,
296+
})
297+
})
237298
})
238299

239300
describe('applySegmentCostPolicy', () => {

0 commit comments

Comments
 (0)