Skip to content

Commit a810211

Browse files
committed
All models
1 parent eac09a5 commit a810211

20 files changed

Lines changed: 856 additions & 53 deletions

File tree

apps/sim/blocks/blocks/agent.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -718,6 +718,11 @@ Return ONLY the JSON array.`,
718718
description: 'Provider timing information',
719719
},
720720
cost: { type: 'json', description: 'Cost of the API call' },
721+
estimatedProviderCost: {
722+
type: 'json',
723+
description:
724+
'Informational vendor list-price estimate for explicit Custom credentials; never charged by Sim. GPU-time models report why a per-request estimate is unavailable.',
725+
},
721726
interactionId: {
722727
type: 'string',
723728
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',
@@ -308,6 +317,7 @@ describe('getModelOptions', () => {
308317
it('always includes the static Fireworks catalog and preserves dynamic BYOK models', () => {
309318
mockProviders.value.fireworks.models = [
310319
'fireworks/kimi-k3',
320+
'fireworks/accounts/fireworks/models/minimax-m2p7',
311321
'fireworks/accounts/acme/models/custom',
312322
]
313323

@@ -322,6 +332,8 @@ describe('getModelOptions', () => {
322332
])
323333
)
324334
expect(ids.filter((id) => id === 'fireworks/kimi-k3')).toHaveLength(1)
335+
expect(ids).not.toContain('fireworks/minimax-m2.7')
336+
expect(ids).not.toContain('fireworks/accounts/fireworks/models/minimax-m2p7')
325337
})
326338

327339
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
@@ -19,6 +19,7 @@ import {
1919
getProviderIcon,
2020
getProviderModels,
2121
isAutoModel,
22+
isModelVisibleInStandardAuthoring,
2223
orderModelIdsByReleaseDate,
2324
SIM_AUTO_MODEL_ID,
2425
} from '@/providers/models'
@@ -84,6 +85,7 @@ export function getModelOptions() {
8485
)
8586

8687
const options = allModels
88+
.filter(isModelVisibleInStandardAuthoring)
8789
.filter((model) => getModelSunsetStatus(model) !== 'deprecated')
8890
.map((model) => {
8991
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
@@ -298,8 +298,22 @@ describe('AgentBlockHandler', () => {
298298

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

302-
await handler.execute(mockContext, mockBlock, {
316+
const result = await handler.execute(mockContext, mockBlock, {
303317
model: CUSTOM_MODEL_ID,
304318
customModelConfig: {
305319
provider: 'xai',
@@ -336,6 +350,16 @@ describe('AgentBlockHandler', () => {
336350
}),
337351
expect.anything()
338352
)
353+
expect(result).toMatchObject({
354+
tokens: { input: 1200, output: 300, total: 1500 },
355+
cost: { input: 0, output: 0, total: 0 },
356+
estimatedProviderCost: {
357+
available: true,
358+
input: 0.0024,
359+
output: 0.0018,
360+
total: 0.0042,
361+
},
362+
})
339363
})
340364

341365
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
@@ -1532,6 +1532,7 @@ export class AgentBlockHandler implements BlockHandler {
15321532
toolCalls?: Array<any>
15331533
timing?: any
15341534
cost?: any
1535+
estimatedProviderCost?: any
15351536
}) {
15361537
return {
15371538
model: result.model,
@@ -1546,6 +1547,9 @@ export class AgentBlockHandler implements BlockHandler {
15461547
},
15471548
providerTiming: result.timing,
15481549
cost: result.cost,
1550+
...(result.estimatedProviderCost && {
1551+
estimatedProviderCost: result.estimatedProviderCost,
1552+
}),
15491553
}
15501554
}
15511555

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
@@ -415,6 +415,20 @@ describe('validateInputsForBlock', () => {
415415
expect(result.validInputs.model).toBe('sim-custom')
416416
})
417417

418+
it('rejects JSON-only Fireworks models in ordinary Agent and Router model fields', () => {
419+
for (const [blockType, model] of [
420+
['agent', 'fireworks/minimax-m2.7'],
421+
['router_v2', 'fireworks/accounts/fireworks/models/minimax-m2p7'],
422+
]) {
423+
const result = validateInputsForBlock(blockType, { model }, `${blockType}-1`)
424+
425+
expect(result.validInputs.model).toBeUndefined()
426+
expect(result.errors).toHaveLength(1)
427+
expect(result.errors[0]?.error).toContain('Super User Custom model configuration')
428+
expect(result.errors[0]?.error).toContain('sim-custom')
429+
}
430+
})
431+
418432
it('rejects hallucinated agent model ids that match a static provider pattern', () => {
419433
const result = validateInputsForBlock('agent', { model: 'claude-sonnet-4.6' }, 'agent-1')
420434

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
@@ -20,7 +20,12 @@ import { getAgentModelOptions, getModelOptions } from '@/blocks/utils'
2020
import { overlayVisibility } from '@/blocks/visibility/context'
2121
import { BlockType, EDGE, normalizeName } from '@/executor/constants'
2222
import { isCustomModel } from '@/providers/custom-model'
23-
import { isAutoModel, isKnownModelId, suggestModelIdsForUnknownModel } from '@/providers/models'
23+
import {
24+
isAutoModel,
25+
isCustomJsonOnlyModel,
26+
isKnownModelId,
27+
suggestModelIdsForUnknownModel,
28+
} from '@/providers/models'
2429
import { isPiByokOnlyMode } from '@/providers/pi-providers'
2530
import { getTool } from '@/tools/utils'
2631
import { TRIGGER_RUNTIME_SUBBLOCK_IDS, TRIGGER_WEBHOOK_URL_FIELD } from '@/triggers/constants'
@@ -603,6 +608,18 @@ export function validateValueForSubBlockType(
603608
if (trimmed !== '' && isAutoModel(trimmed) && isHostedDeployment) {
604609
return { valid: true, value: trimmed.toLowerCase() }
605610
}
611+
if (trimmed !== '' && isCustomJsonOnlyModel(trimmed)) {
612+
return {
613+
valid: false,
614+
error: {
615+
blockId,
616+
blockType,
617+
field: fieldName,
618+
value,
619+
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.`,
620+
},
621+
}
622+
}
606623
if (trimmed !== '' && !isKnownModelId(trimmed)) {
607624
const suggestions = suggestModelIdsForUnknownModel(trimmed)
608625
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)