@@ -3,14 +3,9 @@ import {
33 fetchModelInfo ,
44 fetchOpencodeConfig ,
55 fetchProviders ,
6- filterProviders ,
7- flattenProviders ,
86 parseFullModelID ,
9- pickDocsModel ,
10- pickFastModel ,
11- pickVisionModel ,
12- resolveModelRef ,
137} from "./catalog" ;
8+ import { resolveFallbackModel , resolveWorkerModel } from "./resolve" ;
149
1510export type ProfileModelHydrationChange = {
1611 profileId : string ;
@@ -34,88 +29,24 @@ export async function hydrateProfileModelsFromOpencode(input: {
3429 ] ) ;
3530
3631 const providersAll = providersRes . providers ;
37- // For auto-selection (node:vision, node:fast, etc.), prefer configured providers.
38- // But allow ALL providers for explicit model references since the user chose them.
39- const providersUsable = filterProviders ( providersAll , "configured" ) ;
40- const catalog = flattenProviders ( providersUsable ) ;
41-
42- // Collect provider IDs explicitly referenced in profile models (user intent = use them)
43- const explicitlyReferencedProviders = new Set < string > ( ) ;
44- for ( const profile of Object . values ( input . profiles ) ) {
45- const model = profile . model . trim ( ) ;
46- if ( model . includes ( "/" ) && ! model . startsWith ( "auto" ) && ! model . startsWith ( "node" ) ) {
47- const providerID = model . split ( "/" ) [ 0 ] ;
48- explicitlyReferencedProviders . add ( providerID ) ;
49- }
50- }
51-
52- const fallbackCandidate =
53- cfg ?. model ||
54- ( providersRes . defaults ?. opencode ? `opencode/${ providersRes . defaults . opencode } ` : undefined ) ||
55- "opencode/gpt-5-nano" ;
56-
57- const resolvedFallback = resolveModelRef ( fallbackCandidate , providersAll ) ;
58- const fallbackModel = "error" in resolvedFallback ? fallbackCandidate : resolvedFallback . full ;
32+ const fallbackModel = resolveFallbackModel ( {
33+ config : cfg ,
34+ providers : providersAll ,
35+ providerDefaults : providersRes . defaults ,
36+ } ) ;
5937
6038 const changes : ProfileModelHydrationChange [ ] = [ ] ;
6139
62- const resolveAuto = ( profile : WorkerProfile ) : { model : string ; reason : string } => {
63- const tag = profile . model ;
64- const isVision = profile . supportsVision || / (?: a u t o | n o d e ) : v i s i o n / i. test ( tag ) ;
65- const isDocs = / (?: a u t o | n o d e ) : d o c s / i. test ( tag ) ;
66- const isFast = / (?: a u t o | n o d e ) : f a s t / i. test ( tag ) ;
67-
68- if ( isFast && cfg ?. small_model ) {
69- const resolvedSmall = resolveModelRef ( cfg . small_model , providersAll ) ;
70- if ( ! ( "error" in resolvedSmall ) ) {
71- return { model : resolvedSmall . full , reason : `auto-selected from small_model (${ tag } )` } ;
72- }
73- }
74-
75- const picked = isVision
76- ? pickVisionModel ( catalog )
77- : isDocs
78- ? pickDocsModel ( catalog )
79- : isFast
80- ? pickFastModel ( catalog )
81- : undefined ;
82-
83- if ( picked ) {
84- return { model : picked . full , reason : `auto-selected from configured models (${ tag } )` } ;
85- }
86-
87- // Vision workers should never silently downgrade to a text-only model.
88- if ( isVision ) {
89- throw new Error (
90- `No vision-capable models found for "${ profile . id } " (model tag: "${ tag } "). ` +
91- `Configure a vision model in OpenCode or set the profile model explicitly.`
92- ) ;
93- }
94-
95- return { model : fallbackModel , reason : `fallback to default model (${ tag } )` } ;
96- } ;
97-
9840 const next : Record < string , WorkerProfile > = { } ;
9941 for ( const [ id , profile ] of Object . entries ( input . profiles ) ) {
100- let desired = profile . model ;
101- let reason = "" ;
102-
103- const modelSpec = profile . model . trim ( ) ;
104- const isNodeTag = modelSpec . startsWith ( "auto" ) || modelSpec . startsWith ( "node" ) ;
105-
106- if ( isNodeTag ) {
107- const resolved = resolveAuto ( profile ) ;
108- desired = resolved . model ;
109- reason = resolved . reason ;
110- } else {
111- // User explicitly specified a model - trust their choice and use ALL providers
112- const resolved = resolveModelRef ( profile . model , providersAll ) ;
113- if ( "error" in resolved ) {
114- const suffix = resolved . suggestions ?. length ? `\nSuggestions:\n- ${ resolved . suggestions . join ( "\n- " ) } ` : "" ;
115- throw new Error ( `Invalid model for profile "${ profile . id } ": ${ resolved . error } ${ suffix } ` ) ;
116- }
117- desired = resolved . full ;
118- }
42+ const resolved = resolveWorkerModel ( {
43+ profile,
44+ config : cfg ,
45+ providers : providersAll ,
46+ providerDefaults : providersRes . defaults ,
47+ } ) ;
48+ const desired = resolved . resolvedModel ;
49+ const reason = resolved . reason ;
11950
12051 if ( profile . supportsVision ) {
12152 const parsed = parseFullModelID ( desired ) ;
0 commit comments