From 8f4f24ffa734398b6d1fb4ba2ada9f9ffca9707e Mon Sep 17 00:00:00 2001 From: Siddharth Ganesan Date: Wed, 29 Jul 2026 19:14:44 -0700 Subject: [PATCH 1/4] Add sim-auto: automatic model routing for the agent block (hosted) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 'Auto' model option (Sim wordmark icon, hosted-only, new-block default; runtime fallback for unset models stays claude-sonnet-5) - Resolver (lib/model-router): classifies each execution via mothership's /api/model-router and routes over two ladders — text: fireworks/glm-5.2 -> fireworks/kimi-k3 (new static hosted Fireworks catalog entries on the platform FIREWORKS_API_KEY); attachments: claude-haiku-4-5 -> gpt-5.5 (Fireworks OSS endpoints reject images). Trivial tasks skip the router; 5-min decision cache; 2s timeout; never fails the workflow - Hidden identity preamble on every auto run (English by default, don't volunteer the underlying model) - Fireworks executor: wire-name map for catalog ids (glm-5.2 -> glm-5p2), pricing keyed on the full catalog id - Billing: routing cost applied to non-streaming output cost only when mothership marks the call billable (bill-model-router flag, default off) - sim-auto special-cases: API-key condition, serialization tool lookup, edit-workflow validation (hosted), VFS model options projection --- apps/sim/blocks/blocks/agent.ts | 12 +- apps/sim/blocks/utils.test.ts | 2 + apps/sim/blocks/utils.ts | 14 +- apps/sim/components/icons.tsx | 25 ++ .../executor/handlers/agent/agent-handler.ts | 88 +++++- .../workflow/edit-workflow/validation.ts | 9 +- apps/sim/lib/copilot/vfs/serializers.ts | 18 +- apps/sim/lib/model-router/resolve.test.ts | 208 +++++++++++++ apps/sim/lib/model-router/resolve.ts | 291 ++++++++++++++++++ apps/sim/providers/fireworks/index.test.ts | 1 + apps/sim/providers/fireworks/index.ts | 10 +- apps/sim/providers/fireworks/utils.ts | 20 ++ apps/sim/providers/models.ts | 59 +++- .../providers/settled-tool-streams.test.ts | 1 + 14 files changed, 749 insertions(+), 9 deletions(-) create mode 100644 apps/sim/lib/model-router/resolve.test.ts create mode 100644 apps/sim/lib/model-router/resolve.ts diff --git a/apps/sim/blocks/blocks/agent.ts b/apps/sim/blocks/blocks/agent.ts index fb2e9756c38..95db56a9a87 100644 --- a/apps/sim/blocks/blocks/agent.ts +++ b/apps/sim/blocks/blocks/agent.ts @@ -1,5 +1,6 @@ import { createLogger } from '@sim/logger' import { AgentIcon } from '@/components/icons' +import { isHosted } from '@/lib/core/config/env-flags' import type { BlockConfig } from '@/blocks/types' import { AuthMode, IntegrationType } from '@/blocks/types' import { @@ -20,6 +21,8 @@ import { getReasoningEffortValuesForModel, getThinkingLevelsForModel, getVerbosityValuesForModel, + isAutoModel, + SIM_AUTO_MODEL_ID, supportsTemperature, } from '@/providers/models' import { useWorkflowRegistry } from '@/stores/workflows/registry/store' @@ -132,7 +135,9 @@ Return ONLY the JSON array.`, type: 'combobox', placeholder: 'Type or select a model...', required: true, - defaultValue: 'claude-sonnet-5', + // Hosted Sim defaults new agent blocks to the automatic model; the + // runtime fallback for blocks with no model set stays AGENT.DEFAULT_MODEL. + defaultValue: isHosted ? SIM_AUTO_MODEL_ID : 'claude-sonnet-5', options: getModelOptions, commandSearchable: true, }, @@ -522,6 +527,11 @@ Return ONLY the JSON array.`, if (!model) { throw new Error('No model selected') } + // sim-auto resolves to a concrete model at execution time; this + // serialization-time lookup only needs a stable provider tool id. + if (isAutoModel(model)) { + return 'anthropic_chat' + } const tool = getBaseModelProviders()[model] if (!tool) { throw new Error(`Invalid model selected: ${model}`) diff --git a/apps/sim/blocks/utils.test.ts b/apps/sim/blocks/utils.test.ts index bf90c25d656..9d5a3adf24f 100644 --- a/apps/sim/blocks/utils.test.ts +++ b/apps/sim/blocks/utils.test.ts @@ -40,6 +40,8 @@ vi.mock('@/providers/models', () => ({ getProviderModels: mockGetProviderModels, getProviderIcon: mockGetProviderIcon, getBaseModelProviders: mockGetBaseModelProviders, + SIM_AUTO_MODEL_ID: 'sim-auto', + isAutoModel: (model: string) => model.trim().toLowerCase() === 'sim-auto', })) vi.mock('@/providers/utils', () => ({ diff --git a/apps/sim/blocks/utils.ts b/apps/sim/blocks/utils.ts index f0c7d11f13a..8c9d8894225 100644 --- a/apps/sim/blocks/utils.ts +++ b/apps/sim/blocks/utils.ts @@ -1,4 +1,5 @@ import { toError } from '@sim/utils/errors' +import { SimAutoIcon } from '@/components/icons' import { isAzureConfigured, isCohereConfigured, @@ -14,7 +15,9 @@ import { getModelSunsetStatus, getProviderIcon, getProviderModels, + isAutoModel, orderModelIdsByReleaseDate, + SIM_AUTO_MODEL_ID, } from '@/providers/models' import { isPiSupportedModel } from '@/providers/pi-providers' import { getProviderFromModel } from '@/providers/utils' @@ -75,12 +78,18 @@ export function getModelOptions() { ]) ) - return allModels + const options = allModels .filter((model) => getModelSunsetStatus(model) !== 'deprecated') .map((model) => { const icon = getProviderIcon(model) return { label: model, id: model, ...(icon && { icon }) } }) + + if (isHosted) { + options.unshift({ label: 'Auto', id: SIM_AUTO_MODEL_ID, icon: SimAutoIcon }) + } + + return options } /** @@ -183,6 +192,9 @@ function shouldRequireApiKeyForModel(model: string): boolean { const normalizedModel = model.trim().toLowerCase() if (!normalizedModel) return false + // The auto pseudo-model resolves server-side to a hosted pool model. + if (isAutoModel(normalizedModel)) return false + if (isHosted) { const hostedModels = getHostedModels() if (hostedModels.some((m) => m.toLowerCase() === normalizedModel)) return false diff --git a/apps/sim/components/icons.tsx b/apps/sim/components/icons.tsx index efedba2000d..5a2e8424b26 100644 --- a/apps/sim/components/icons.tsx +++ b/apps/sim/components/icons.tsx @@ -7558,6 +7558,31 @@ export function SixtyfourIcon(props: SVGProps) { ) } +/** + * The "sim" brand wordmark (v1.0 brand guide simLogotype paths — the same mark + * the navbar/login header renders), inked with the theme-adaptive + * `--text-body`. Used as the icon for the Auto model option; the wide viewBox + * letterboxes itself inside square icon slots. + */ +export function SimAutoIcon(props: SVGProps) { + return ( + + ) +} + export function SimTriggerIcon(props: SVGProps) { return (