@@ -3,6 +3,10 @@ import '@sim/testing/mocks/executor'
33import { authOAuthUtilsMock , authOAuthUtilsMockFns } from '@sim/testing'
44import { beforeEach , describe , expect , it , type Mock , vi } from 'vitest'
55
6+ const { mockResolveAutoModel } = vi . hoisted ( ( ) => ( {
7+ mockResolveAutoModel : vi . fn ( ) ,
8+ } ) )
9+
610vi . mock ( '@/app/api/auth/oauth/utils' , ( ) => authOAuthUtilsMock )
711
812vi . mock ( '@/lib/credentials/access' , ( ) => ( {
@@ -20,6 +24,13 @@ vi.mock('@/lib/credentials/access', () => ({
2024 } ) ,
2125} ) )
2226
27+ vi . mock ( '@/lib/model-router/resolve' , ( ) => ( {
28+ addAutoRoutingCost : ( cost : Record < string , number > , routingCost : number ) =>
29+ routingCost > 0 ? { ...cost , routing : routingCost , total : cost . total + routingCost } : cost ,
30+ resolveAutoModel : mockResolveAutoModel ,
31+ SIM_AUTO_SYSTEM_PREAMBLE : 'Sim auto system preamble' ,
32+ } ) )
33+
2334import { BlockType } from '@/executor/constants'
2435import { EvaluatorBlockHandler } from '@/executor/handlers/evaluator/evaluator-handler'
2536import type { ExecutionContext } from '@/executor/types'
@@ -82,6 +93,12 @@ describe('EvaluatorBlockHandler', () => {
8293 refreshed : false ,
8394 } )
8495 mockGetProviderFromModel . mockReturnValue ( 'openai' )
96+ mockResolveAutoModel . mockResolvedValue ( {
97+ model : 'fireworks/glm-5.2' ,
98+ tier : '2' ,
99+ decidedBy : 'llm' ,
100+ billableRoutingCost : 0.002 ,
101+ } )
85102
86103 // Set up fetch mock to return a successful response
87104 mockFetch . mockImplementation ( ( ) => {
@@ -101,16 +118,27 @@ describe('EvaluatorBlockHandler', () => {
101118
102119 it ( 'should handle evaluator blocks' , ( ) => {
103120 expect ( handler . canHandle ( mockBlock ) ) . toBe ( true )
104- const nonEvalBlock : SerializedBlock = { ...mockBlock , metadata : { id : 'other' } }
121+ const nonEvalBlock : SerializedBlock = {
122+ ...mockBlock ,
123+ metadata : { id : 'other' } ,
124+ }
105125 expect ( handler . canHandle ( nonEvalBlock ) ) . toBe ( false )
106126 } )
107127
108128 it ( 'should execute evaluator block correctly with basic inputs' , async ( ) => {
109129 const inputs = {
110130 content : 'This is the content to evaluate.' ,
111131 metrics : [
112- { name : 'score1' , description : 'First score' , range : { min : 0 , max : 10 } } ,
113- { name : 'score2' , description : 'Second score' , range : { min : 0 , max : 10 } } ,
132+ {
133+ name : 'score1' ,
134+ description : 'First score' ,
135+ range : { min : 0 , max : 10 } ,
136+ } ,
137+ {
138+ name : 'score2' ,
139+ description : 'Second score' ,
140+ range : { min : 0 , max : 10 } ,
141+ } ,
114142 ] ,
115143 model : 'gpt-4o' ,
116144 apiKey : 'test-api-key' ,
@@ -163,6 +191,64 @@ describe('EvaluatorBlockHandler', () => {
163191 } )
164192 } )
165193
194+ it ( 'resolves sim-auto before executing evaluator and preserves its public identity' , async ( ) => {
195+ const inputs = {
196+ content : 'A clear and accurate answer.' ,
197+ metrics : [
198+ {
199+ name : 'quality' ,
200+ description : 'Overall answer quality' ,
201+ range : { min : 1 , max : 5 } ,
202+ } ,
203+ ] ,
204+ model : 'sim-auto' ,
205+ }
206+
207+ mockFetch . mockResolvedValueOnce ( {
208+ ok : true ,
209+ json : ( ) =>
210+ Promise . resolve ( {
211+ content : JSON . stringify ( { quality : 5 } ) ,
212+ model : 'fireworks/glm-5.2' ,
213+ tokens : { input : 80 , output : 10 , total : 90 } ,
214+ cost : { input : 0.001 , output : 0.0005 , total : 0.0015 } ,
215+ } ) ,
216+ } )
217+
218+ const result = await handler . execute ( mockContext , mockBlock , inputs )
219+
220+ expect ( mockResolveAutoModel ) . toHaveBeenCalledWith ( {
221+ ctx : mockContext ,
222+ blockId : mockBlock . id ,
223+ signals : expect . objectContaining ( {
224+ lastMessage : inputs . content ,
225+ messageCount : 1 ,
226+ toolNames : [ ] ,
227+ mediaKind : 'none' ,
228+ hasResponseFormat : true ,
229+ } ) ,
230+ fallbackModel : 'claude-sonnet-5' ,
231+ } )
232+ expect ( mockGetProviderFromModel ) . toHaveBeenCalledWith ( 'fireworks/glm-5.2' )
233+
234+ const requestBody = JSON . parse ( mockFetch . mock . calls [ 0 ] [ 1 ] . body )
235+ expect ( requestBody ) . toMatchObject ( {
236+ provider : 'openai' ,
237+ model : 'fireworks/glm-5.2' ,
238+ systemPrompt : expect . stringMatching ( / ^ S i m a u t o s y s t e m p r e a m b l e \n \n / ) ,
239+ } )
240+ expect ( result ) . toMatchObject ( {
241+ model : 'sim-auto' ,
242+ quality : 5 ,
243+ cost : {
244+ input : 0.001 ,
245+ output : 0.0005 ,
246+ routing : 0.002 ,
247+ total : 0.0035 ,
248+ } ,
249+ } )
250+ } )
251+
166252 it ( 'bills the cost the provider proxy decided rather than recomputing it' , async ( ) => {
167253 // The proxy already resolved key provenance and the margin; recomputing
168254 // here would re-charge a BYOK caller the proxy correctly zeroed.
@@ -195,7 +281,13 @@ describe('EvaluatorBlockHandler', () => {
195281 const contentObj = { text : 'Evaluate this JSON.' , value : 42 }
196282 const inputs = {
197283 content : JSON . stringify ( contentObj ) ,
198- metrics : [ { name : 'clarity' , description : 'Clarity score' , range : { min : 1 , max : 5 } } ] ,
284+ metrics : [
285+ {
286+ name : 'clarity' ,
287+ description : 'Clarity score' ,
288+ range : { min : 1 , max : 5 } ,
289+ } ,
290+ ] ,
199291 apiKey : 'test-api-key' ,
200292 }
201293
@@ -227,7 +319,11 @@ describe('EvaluatorBlockHandler', () => {
227319 const inputs = {
228320 content : contentObj ,
229321 metrics : [
230- { name : 'completeness' , description : 'Data completeness' , range : { min : 0 , max : 1 } } ,
322+ {
323+ name : 'completeness' ,
324+ description : 'Data completeness' ,
325+ range : { min : 0 , max : 1 } ,
326+ } ,
231327 ] ,
232328 apiKey : 'test-api-key' ,
233329 }
@@ -258,7 +354,13 @@ describe('EvaluatorBlockHandler', () => {
258354 it ( 'should parse valid JSON response correctly' , async ( ) => {
259355 const inputs = {
260356 content : 'Test content' ,
261- metrics : [ { name : 'quality' , description : 'Quality score' , range : { min : 1 , max : 10 } } ] ,
357+ metrics : [
358+ {
359+ name : 'quality' ,
360+ description : 'Quality score' ,
361+ range : { min : 1 , max : 10 } ,
362+ } ,
363+ ] ,
262364 apiKey : 'test-api-key' ,
263365 }
264366
@@ -339,7 +441,13 @@ describe('EvaluatorBlockHandler', () => {
339441 it ( 'should extract metric scores ignoring case' , async ( ) => {
340442 const inputs = {
341443 content : 'Test' ,
342- metrics : [ { name : 'CamelCaseScore' , description : 'Desc' , range : { min : 0 , max : 10 } } ] ,
444+ metrics : [
445+ {
446+ name : 'CamelCaseScore' ,
447+ description : 'Desc' ,
448+ range : { min : 0 , max : 10 } ,
449+ } ,
450+ ] ,
343451 apiKey : 'test-api-key' ,
344452 }
345453
@@ -366,8 +474,16 @@ describe('EvaluatorBlockHandler', () => {
366474 const inputs = {
367475 content : 'Test' ,
368476 metrics : [
369- { name : 'presentScore' , description : 'Desc1' , range : { min : 0 , max : 5 } } ,
370- { name : 'missingScore' , description : 'Desc2' , range : { min : 0 , max : 5 } } ,
477+ {
478+ name : 'presentScore' ,
479+ description : 'Desc1' ,
480+ range : { min : 0 , max : 5 } ,
481+ } ,
482+ {
483+ name : 'missingScore' ,
484+ description : 'Desc2' ,
485+ range : { min : 0 , max : 5 } ,
486+ } ,
371487 ] ,
372488 apiKey : 'test-api-key' ,
373489 }
@@ -410,7 +526,13 @@ describe('EvaluatorBlockHandler', () => {
410526 it ( 'should handle Azure OpenAI models with endpoint and API version' , async ( ) => {
411527 const inputs = {
412528 content : 'Test content to evaluate' ,
413- metrics : [ { name : 'quality' , description : 'Quality score' , range : { min : 1 , max : 10 } } ] ,
529+ metrics : [
530+ {
531+ name : 'quality' ,
532+ description : 'Quality score' ,
533+ range : { min : 1 , max : 10 } ,
534+ } ,
535+ ] ,
414536 model : 'gpt-4o' ,
415537 apiKey : 'test-azure-key' ,
416538 azureEndpoint : 'https://test.openai.azure.com' ,
@@ -450,7 +572,13 @@ describe('EvaluatorBlockHandler', () => {
450572 it ( 'should handle Vertex AI models with OAuth credential' , async ( ) => {
451573 const inputs = {
452574 content : 'Test content to evaluate' ,
453- metrics : [ { name : 'quality' , description : 'Quality score' , range : { min : 1 , max : 10 } } ] ,
575+ metrics : [
576+ {
577+ name : 'quality' ,
578+ description : 'Quality score' ,
579+ range : { min : 1 , max : 10 } ,
580+ } ,
581+ ] ,
454582 model : 'gemini-2.0-flash-exp' ,
455583 vertexCredential : 'test-vertex-credential-id' ,
456584 vertexProject : 'test-gcp-project' ,
0 commit comments