refactor: enhance team model selection logic and improve test setup

- Introduced a new utility function, computeEffectiveTeamModel, to streamline the logic for determining the effective model based on selected options and context.
- Updated CreateTeamDialog and LaunchTeamDialog components to utilize the new utility for cleaner code.
- Refactored TeamProvisioningService test to improve type handling for child processes, ensuring compatibility with vitest.

Made-with: Cursor
This commit is contained in:
iliya 2026-03-03 13:39:02 +02:00
parent 6e0fdad612
commit 71e88fd267
5 changed files with 102 additions and 47 deletions

View file

@ -160,7 +160,9 @@
"supports_pdf_input": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_vision": true
"supports_vision": true,
"cache_read_input_token_cost": 2.5e-8,
"cache_creation_input_token_cost": 3.125e-7
},
"anthropic.claude-3-opus-20240229-v1:0": {
"input_cost_per_token": 0.000015,
@ -173,7 +175,9 @@
"supports_function_calling": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_vision": true
"supports_vision": true,
"cache_read_input_token_cost": 0.0000015,
"cache_creation_input_token_cost": 0.00001875
},
"anthropic.claude-3-sonnet-20240229-v1:0": {
"input_cost_per_token": 0.000003,
@ -187,7 +191,9 @@
"supports_pdf_input": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_vision": true
"supports_vision": true,
"cache_read_input_token_cost": 3e-7,
"cache_creation_input_token_cost": 0.00000375
},
"anthropic.claude-instant-v1": {
"input_cost_per_token": 8e-7,
@ -668,7 +674,9 @@
"supports_pdf_input": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_vision": true
"supports_vision": true,
"cache_read_input_token_cost": 3e-7,
"cache_creation_input_token_cost": 0.00000375
},
"apac.anthropic.claude-3-5-sonnet-20241022-v2:0": {
"cache_creation_input_token_cost": 0.00000375,
@ -701,7 +709,9 @@
"supports_pdf_input": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_vision": true
"supports_vision": true,
"cache_read_input_token_cost": 2.5e-8,
"cache_creation_input_token_cost": 3.125e-7
},
"apac.anthropic.claude-haiku-4-5-20251001-v1:0": {
"cache_creation_input_token_cost": 0.000001375,
@ -737,7 +747,9 @@
"supports_pdf_input": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_vision": true
"supports_vision": true,
"cache_read_input_token_cost": 3e-7,
"cache_creation_input_token_cost": 0.00000375
},
"apac.anthropic.claude-sonnet-4-20250514-v1:0": {
"cache_creation_input_token_cost": 0.00000375,
@ -1005,7 +1017,9 @@
"supports_function_calling": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_vision": true
"supports_vision": true,
"cache_read_input_token_cost": 3e-7,
"cache_creation_input_token_cost": 0.00000375
},
"bedrock/us-east-1/anthropic.claude-instant-v1": {
"input_cost_per_token": 8e-7,
@ -1049,7 +1063,9 @@
"supports_pdf_input": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_vision": true
"supports_vision": true,
"cache_read_input_token_cost": 3.6e-7,
"cache_creation_input_token_cost": 0.0000045
},
"bedrock/us-gov-east-1/anthropic.claude-3-haiku-20240307-v1:0": {
"input_cost_per_token": 3e-7,
@ -1063,7 +1079,9 @@
"supports_pdf_input": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_vision": true
"supports_vision": true,
"cache_read_input_token_cost": 3e-8,
"cache_creation_input_token_cost": 3.75e-7
},
"bedrock/us-gov-east-1/claude-sonnet-4-5-20250929-v1:0": {
"input_cost_per_token": 0.0000033,
@ -1081,7 +1099,9 @@
"supports_reasoning": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_vision": true
"supports_vision": true,
"cache_read_input_token_cost": 3.3e-7,
"cache_creation_input_token_cost": 0.000004125
},
"bedrock/us-gov-west-1/anthropic.claude-3-7-sonnet-20250219-v1:0": {
"cache_creation_input_token_cost": 0.0000045,
@ -1115,7 +1135,9 @@
"supports_pdf_input": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_vision": true
"supports_vision": true,
"cache_read_input_token_cost": 3.6e-7,
"cache_creation_input_token_cost": 0.0000045
},
"bedrock/us-gov-west-1/anthropic.claude-3-haiku-20240307-v1:0": {
"input_cost_per_token": 3e-7,
@ -1129,7 +1151,9 @@
"supports_pdf_input": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_vision": true
"supports_vision": true,
"cache_read_input_token_cost": 3e-8,
"cache_creation_input_token_cost": 3.75e-7
},
"bedrock/us-gov-west-1/claude-sonnet-4-5-20250929-v1:0": {
"input_cost_per_token": 0.0000033,
@ -1147,7 +1171,9 @@
"supports_reasoning": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_vision": true
"supports_vision": true,
"cache_read_input_token_cost": 3.3e-7,
"cache_creation_input_token_cost": 0.000004125
},
"bedrock/us-west-2/anthropic.claude-instant-v1": {
"input_cost_per_token": 8e-7,
@ -2093,7 +2119,9 @@
"supports_pdf_input": true,
"supports_prompt_caching": true,
"supports_response_schema": true,
"supports_tool_choice": true
"supports_tool_choice": true,
"cache_read_input_token_cost": 2.5e-8,
"cache_creation_input_token_cost": 3.125e-7
},
"eu.anthropic.claude-haiku-4-5-20251001-v1:0": {
"cache_creation_input_token_cost": 0.000001375,
@ -2130,7 +2158,9 @@
"supports_pdf_input": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_vision": true
"supports_vision": true,
"cache_read_input_token_cost": 3e-7,
"cache_creation_input_token_cost": 0.00000375
},
"eu.anthropic.claude-3-5-sonnet-20241022-v2:0": {
"input_cost_per_token": 0.000003,
@ -2147,7 +2177,9 @@
"supports_prompt_caching": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_vision": true
"supports_vision": true,
"cache_read_input_token_cost": 3e-7,
"cache_creation_input_token_cost": 0.00000375
},
"eu.anthropic.claude-3-7-sonnet-20250219-v1:0": {
"input_cost_per_token": 0.000003,
@ -2165,7 +2197,9 @@
"supports_reasoning": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_vision": true
"supports_vision": true,
"cache_read_input_token_cost": 3e-7,
"cache_creation_input_token_cost": 0.00000375
},
"eu.anthropic.claude-3-haiku-20240307-v1:0": {
"input_cost_per_token": 2.5e-7,
@ -2179,7 +2213,9 @@
"supports_pdf_input": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_vision": true
"supports_vision": true,
"cache_read_input_token_cost": 2.5e-8,
"cache_creation_input_token_cost": 3.125e-7
},
"eu.anthropic.claude-3-opus-20240229-v1:0": {
"input_cost_per_token": 0.000015,
@ -2192,7 +2228,9 @@
"supports_function_calling": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_vision": true
"supports_vision": true,
"cache_read_input_token_cost": 0.0000015,
"cache_creation_input_token_cost": 0.00001875
},
"eu.anthropic.claude-3-sonnet-20240229-v1:0": {
"input_cost_per_token": 0.000003,
@ -2206,7 +2244,9 @@
"supports_pdf_input": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_vision": true
"supports_vision": true,
"cache_read_input_token_cost": 3e-7,
"cache_creation_input_token_cost": 0.00000375
},
"eu.anthropic.claude-opus-4-1-20250805-v1:0": {
"cache_creation_input_token_cost": 0.00001875,
@ -2872,7 +2912,9 @@
"supports_pdf_input": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_vision": true
"supports_vision": true,
"cache_read_input_token_cost": 3e-7,
"cache_creation_input_token_cost": 0.00000375
},
"us.anthropic.claude-3-5-sonnet-20241022-v2:0": {
"cache_creation_input_token_cost": 0.00000375,
@ -2925,7 +2967,9 @@
"supports_pdf_input": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_vision": true
"supports_vision": true,
"cache_read_input_token_cost": 2.5e-8,
"cache_creation_input_token_cost": 3.125e-7
},
"us.anthropic.claude-3-opus-20240229-v1:0": {
"input_cost_per_token": 0.000015,
@ -2938,7 +2982,9 @@
"supports_function_calling": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_vision": true
"supports_vision": true,
"cache_read_input_token_cost": 0.0000015,
"cache_creation_input_token_cost": 0.00001875
},
"us.anthropic.claude-3-sonnet-20240229-v1:0": {
"input_cost_per_token": 0.000003,
@ -2952,7 +2998,9 @@
"supports_pdf_input": true,
"supports_response_schema": true,
"supports_tool_choice": true,
"supports_vision": true
"supports_vision": true,
"cache_read_input_token_cost": 3e-7,
"cache_creation_input_token_cost": 0.00000375
},
"us.anthropic.claude-opus-4-1-20250805-v1:0": {
"cache_creation_input_token_cost": 0.00001875,

View file

@ -31,7 +31,7 @@ import { AlertTriangle, CheckCircle2, Loader2 } from 'lucide-react';
import { ExtendedContextCheckbox } from './ExtendedContextCheckbox';
import { ProjectPathSelector } from './ProjectPathSelector';
import { TeamModelSelector } from './TeamModelSelector';
import { computeEffectiveTeamModel, TeamModelSelector } from './TeamModelSelector';
import type { MemberDraft } from '@renderer/components/team/members/membersEditorTypes';
@ -445,13 +445,10 @@ export const CreateTeamDialog = ({
[members]
);
const effectiveModel = useMemo(() => {
const base = selectedModel || undefined;
if (!extendedContext) return base;
// 1M context is only supported for opus and sonnet
if (base === 'haiku') return base;
return base ? `${base}[1m]` : 'sonnet[1m]';
}, [selectedModel, extendedContext]);
const effectiveModel = useMemo(
() => computeEffectiveTeamModel(selectedModel, extendedContext),
[selectedModel, extendedContext]
);
const sanitizedTeamName = sanitizeTeamName(teamName.trim());

View file

@ -22,7 +22,7 @@ import { normalizePath } from '@renderer/utils/pathNormalize';
import { AlertTriangle, CheckCircle2, Loader2, RotateCcw } from 'lucide-react';
import { ProjectPathSelector } from './ProjectPathSelector';
import { TeamModelSelector } from './TeamModelSelector';
import { computeEffectiveTeamModel, TeamModelSelector } from './TeamModelSelector';
import type { ActiveTeamRef } from './CreateTeamDialog';
import type { MentionSuggestion } from '@renderer/types/mention';
@ -256,12 +256,7 @@ export const LaunchTeamDialog = ({
teamName,
cwd: effectiveCwd,
prompt: promptDraft.value.trim() || undefined,
model: (() => {
if (!extendedContext) return selectedModel || undefined;
// 1M context is only supported for opus and sonnet
if (selectedModel === 'haiku') return selectedModel;
return selectedModel ? `${selectedModel}[1m]` : 'sonnet[1m]';
})(),
model: computeEffectiveTeamModel(selectedModel, extendedContext),
clearContext: clearContext || undefined,
});
resetFormState();

View file

@ -10,6 +10,21 @@ const MODEL_OPTIONS = [
{ value: 'haiku', label: 'Haiku 4.5' },
] as const;
/**
* Computes the effective model string for team provisioning.
* - Without extended context: returns base model or undefined.
* - With extended context: haiku stays as-is; opus/sonnet get [1m] suffix; default sonnet[1m].
*/
export function computeEffectiveTeamModel(
selectedModel: string,
extendedContext: boolean
): string | undefined {
const base = selectedModel || undefined;
if (!extendedContext) return base;
if (base === 'haiku') return base;
return base ? `${base}[1m]` : 'sonnet[1m]';
}
export interface TeamModelSelectorProps {
value: string;
onValueChange: (value: string) => void;

View file

@ -1,3 +1,4 @@
import type { ChildProcess } from 'child_process';
import { EventEmitter } from 'events';
import { beforeEach, describe, expect, it, vi } from 'vitest';
@ -19,13 +20,12 @@ function allowConsoleLogs() {
vi.spyOn(console, 'warn').mockImplementation(() => {});
}
function createFakeChild(exitCode: number): NodeJS.Process {
const child = new EventEmitter() as NodeJS.Process & {
stdout: EventEmitter;
stderr: EventEmitter;
};
child.stdout = new EventEmitter();
child.stderr = new EventEmitter();
function createFakeChild(exitCode: number): ChildProcess {
const child = Object.assign(new EventEmitter(), {
stdout: null,
stderr: null,
stdin: null,
}) as unknown as ChildProcess;
setImmediate(() => child.emit('close', exitCode));
return child;
}
@ -45,7 +45,7 @@ describe('TeamProvisioningService', () => {
if (callCount === 1) {
throw new Error('spawn EINVAL');
}
return createFakeChild(0) as ReturnType<typeof spawnCli>;
return createFakeChild(0);
});
const svc = new TeamProvisioningService();