Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ export function ModelSelector() {
{ effort: effortChoice ?? undefined, modelSelection: selection },
advanced
)
const options = advanced
const options = modelSelection
? mothershipEffortOptions(modelSelection.model)
: MOTHERSHIP_SIMPLE_EFFORT_OPTIONS
const setEffort = (choice: MothershipEffort) => {
Expand All @@ -59,13 +59,10 @@ export function ModelSelector() {
}, [chatId])

const effortLabel = options.find((option) => option.value === effort)?.label ?? effort
const modelLabel =
MOTHERSHIP_MODEL_OPTIONS.find((option) => option.value === modelSelection.model)?.label ??
modelSelection.model

return (
<div className='flex items-center gap-[inherit]'>
{advanced && (
{modelSelection && (
<>
{modelSelection.model !== 'claude-opus-5-5' && (
<FastModeToggle
Expand All @@ -77,7 +74,10 @@ export function ModelSelector() {
<DropdownMenu modal={false}>
<ModelSettingTrigger
label='Model'
valueLabel={modelLabel}
valueLabel={
MOTHERSHIP_MODEL_OPTIONS.find((option) => option.value === modelSelection.model)
?.label ?? modelSelection.model
}
icon={Sparkles}
showChevron
/>
Expand Down
11 changes: 4 additions & 7 deletions apps/sim/lib/mothership/chat/post.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -816,7 +816,7 @@ describe('handleUnifiedChatPost', () => {
['low', 'low'],
['none', 'high'],
])(
'enforces the default model and effort range on submitted %s effort',
'leaves the model to the worker and enforces the effort range on submitted %s effort',
async (effort, expected) => {
flags.models.mockResolvedValue(false)
const response = await handleUnifiedChatPost(
Expand All @@ -831,12 +831,9 @@ describe('handleUnifiedChatPost', () => {
})
)
expect(response.status).toBe(200)
expect(buildCopilotRequestPayload).toHaveBeenCalledWith(
expect.objectContaining({
effort: expected,
modelSelection: { model: 'gpt-6-astra', fastMode: false },
})
)
const { requestPayload } = createSSEStream.mock.lastCall?.[0] ?? {}
expect(requestPayload?.effort).toBe(expected)
expect(JSON.stringify(requestPayload)).not.toContain('modelSelection')
}
)

Expand Down
25 changes: 16 additions & 9 deletions apps/sim/lib/mothership/model-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,22 +38,29 @@ export function mothershipEffortOptions(model: ModelSelection['model']) {
: MOTHERSHIP_EFFORT_OPTIONS
}

/** Shared by the visible controls, send path and server admission so hidden preferences cannot leak. */
/**
* Shared by the visible controls, send path and server admission so hidden preferences cannot leak.
* Without the model picker no selection is sent, so the worker routes every model role itself.
*/
export function resolveMothershipModelSettings(
settings: { effort?: MothershipEffort; modelSelection?: ModelSelection },
advanced: boolean
): { effort: MothershipEffort; modelSelection: ModelSelection } {
): { effort: MothershipEffort; modelSelection: ModelSelection | undefined } {
let effort = settings.effort ?? DEFAULT_MOTHERSHIP_EFFORT
if (!advanced) {
if (effort === 'none') effort = DEFAULT_MOTHERSHIP_EFFORT
if (effort === 'max') effort = 'xhigh'
return { effort, modelSelection: { model: 'gpt-6-astra', fastMode: false } }
return { effort, modelSelection: undefined }
}
const stored = settings.modelSelection ?? { model: 'gpt-6-astra', fastMode: false }
const modelSelection = normalizeModelSelection(
settings.modelSelection ?? { model: 'gpt-6-astra', fastMode: false }
)
if (effort === 'none' && modelSelection.model !== 'gpt-6-sol') effort = DEFAULT_MOTHERSHIP_EFFORT
return { effort, modelSelection }
}

/** A stored pick on the current catalog: Opus 5 reads as Opus 5.5, which has no Fast mode. */
export function normalizeModelSelection(stored: ModelSelection): ModelSelection {
const model = stored.model === 'claude-opus-5' ? 'claude-opus-5-5' : stored.model
if (effort === 'none' && model !== 'gpt-6-sol') effort = DEFAULT_MOTHERSHIP_EFFORT
return {
effort,
modelSelection: { model, fastMode: model === 'claude-opus-5-5' ? false : stored.fastMode },
}
return { model, fastMode: model === 'claude-opus-5-5' ? false : stored.fastMode }
}
7 changes: 2 additions & 5 deletions apps/sim/stores/mothership-effort/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@ import { omit, toRecord } from '@sim/utils/object'
import { create } from 'zustand'
import { devtools, persist } from 'zustand/middleware'
import { type ModelSelection, ModelSelectionSchema } from '@/lib/mothership/generated/protocol'
import {
type MothershipEffort,
resolveMothershipModelSettings,
} from '@/lib/mothership/model-options'
import { type MothershipEffort, normalizeModelSelection } from '@/lib/mothership/model-options'

/** A chat's effort pick and the token that tells it apart from other picks of the same value. */
interface ChatEffortPick {
Expand Down Expand Up @@ -52,7 +49,7 @@ let lastChatEffortPick = 0
function withModelSelection(
modelSelection: ModelSelection
): Pick<MothershipEffortState, 'modelSelection'> {
return { modelSelection: resolveMothershipModelSettings({ modelSelection }, true).modelSelection }
return { modelSelection: normalizeModelSelection(modelSelection) }
}

export const useMothershipEffortStore = create<MothershipEffortState>()(
Expand Down
Loading