@@ -96,9 +96,9 @@ describe.skipIf(!isDashScopeE2EReady())("e2e: advisor recommend (DashScope)", ()
9696 expect ( data . result ?. recommendations ?. [ 0 ] ?. highlights ?. length ) . toBeGreaterThan ( 0 ) ;
9797 } , 120_000 ) ;
9898
99- // ---- Model preference: positive cases ----
99+ // ---- Mode coverage: all 4 modes ----
100100
101- test ( "scoped preference — intent contains modelPreference. mode=scoped when family is specified " , async ( ) => {
101+ test ( "mode: scoped — family-scoped query sets mode=scoped with targets " , async ( ) => {
102102 const { stdout, stderr, exitCode } = await runCommandE2e ( ADVISOR_ROUTES , [
103103 "advisor" ,
104104 "recommend" ,
@@ -112,39 +112,62 @@ describe.skipIf(!isDashScopeE2EReady())("e2e: advisor recommend (DashScope)", ()
112112 const data = parseStdoutJson < {
113113 intent ?: { modelPreference ?: { mode ?: string ; targets ?: string [ ] } } ;
114114 } > ( stdout ) ;
115- // Model preference detection depends on LLM interpretation
116- // Accept either "scoped" or "unconstrained" as valid
117- const mode = data . intent ?. modelPreference ?. mode ;
118- expect ( mode === "scoped" || mode === "unconstrained" || mode === undefined ) . toBe ( true ) ;
115+ const pref = data . intent ?. modelPreference ;
116+ expect ( pref ?. mode ) . toBe ( "scoped" ) ;
117+ expect ( pref ?. targets ?. length ) . toBeGreaterThan ( 0 ) ;
118+ // Should contain "deepseek" (case-insensitive substring)
119+ expect ( pref ?. targets ?. some ( ( t ) => t . toLowerCase ( ) . includes ( "deepseek" ) ) ) . toBe ( true ) ;
119120 } , 60_000 ) ;
120121
121- test ( "comparison preference — intent contains modelPreference. mode=comparison when comparing models " , async ( ) => {
122+ test ( "mode: comparison — comparing two models sets mode=comparison with both targets " , async ( ) => {
122123 const { stdout, stderr, exitCode } = await runCommandE2e ( ADVISOR_ROUTES , [
123124 "advisor" ,
124125 "recommend" ,
125126 "--dry-run" ,
126127 "--message" ,
127- "Which is better for code generation, qwen-max or deepseek-v3? " ,
128+ "Compare qwen-max and deepseek-v3 for legal contract review, high precision required " ,
128129 "--output" ,
129130 "json" ,
130131 ] ) ;
131132 expect ( exitCode , stderr ) . toBe ( 0 ) ;
132133 const data = parseStdoutJson < {
133134 intent ?: { modelPreference ?: { mode ?: string ; targets ?: string [ ] } } ;
134135 } > ( stdout ) ;
135- // Model preference detection depends on LLM interpretation
136- // Accept either "comparison" or "unconstrained" as valid
137- const mode = data . intent ?. modelPreference ?. mode ;
138- expect ( mode === "comparison" || mode === "unconstrained" || mode === undefined ) . toBe ( true ) ;
136+ const pref = data . intent ?. modelPreference ;
137+ expect ( pref ?. mode ) . toBe ( "comparison" ) ;
138+ expect ( pref ?. targets ?. length ) . toBeGreaterThanOrEqual ( 2 ) ;
139+ const targetsLower = pref ?. targets ?. map ( ( t ) => t . toLowerCase ( ) ) ?? [ ] ;
140+ expect ( targetsLower . some ( ( t ) => t . includes ( "qwen" ) ) ) . toBe ( true ) ;
141+ expect ( targetsLower . some ( ( t ) => t . includes ( "deepseek" ) ) ) . toBe ( true ) ;
142+ } , 60_000 ) ;
143+
144+ test ( "mode: alternative — reference model query sets mode=alternative with target" , async ( ) => {
145+ const { stdout, stderr, exitCode } = await runCommandE2e ( ADVISOR_ROUTES , [
146+ "advisor" ,
147+ "recommend" ,
148+ "--dry-run" ,
149+ "--message" ,
150+ "Something like qwen-max but cheaper, for text summarization" ,
151+ "--output" ,
152+ "json" ,
153+ ] ) ;
154+ expect ( exitCode , stderr ) . toBe ( 0 ) ;
155+ const data = parseStdoutJson < {
156+ intent ?: { modelPreference ?: { mode ?: string ; targets ?: string [ ] } } ;
157+ } > ( stdout ) ;
158+ const pref = data . intent ?. modelPreference ;
159+ expect ( pref ?. mode ) . toBe ( "alternative" ) ;
160+ expect ( pref ?. targets ?. length ) . toBeGreaterThan ( 0 ) ;
161+ expect ( pref ?. targets ?. some ( ( t ) => t . toLowerCase ( ) . includes ( "qwen" ) ) ) . toBe ( true ) ;
139162 } , 60_000 ) ;
140163
141- test ( "excludes preference — intent detects modelPreference when excluding models " , async ( ) => {
164+ test ( "mode: excludes — excluding a family populates excludes array " , async ( ) => {
142165 const { stdout, stderr, exitCode } = await runCommandE2e ( ADVISOR_ROUTES , [
143166 "advisor" ,
144167 "recommend" ,
145168 "--dry-run" ,
146169 "--message" ,
147- "Not qwen, recommend a model suitable for text generation" ,
170+ "Recommend a model for text generation, but not qwen " ,
148171 "--output" ,
149172 "json" ,
150173 ] ) ;
@@ -157,18 +180,12 @@ describe.skipIf(!isDashScopeE2EReady())("e2e: advisor recommend (DashScope)", ()
157180 } ;
158181 } ;
159182 } > ( stdout ) ;
160- // Model preference detection depends on LLM interpretation
161- // If excludes is detected, verify it contains qwen; otherwise accept as valid
162183 const pref = data . intent ?. modelPreference ;
163- if ( pref ?. excludes && pref . excludes . length > 0 ) {
164- expect ( pref . excludes . some ( ( e ) => e . toLowerCase ( ) . includes ( "qwen" ) ) ) . toBe ( true ) ;
165- }
166- // Test passes if exit code is 0, regardless of whether excludes was detected
184+ expect ( pref ?. excludes ?. length ) . toBeGreaterThan ( 0 ) ;
185+ expect ( pref ?. excludes ?. some ( ( e ) => e . toLowerCase ( ) . includes ( "qwen" ) ) ) . toBe ( true ) ;
167186 } , 60_000 ) ;
168187
169- // ---- Model preference: negative cases ----
170-
171- test ( "no preference — intent has no modelPreference or mode=unconstrained for generic queries" , async ( ) => {
188+ test ( "mode: unconstrained — generic query has no modelPreference" , async ( ) => {
172189 const { stdout, stderr, exitCode } = await runCommandE2e ( ADVISOR_ROUTES , [
173190 "advisor" ,
174191 "recommend" ,
0 commit comments