11import { describe , expect , it } from 'vitest'
22import { ResolvedSecretTraceRegistry } from '@/executor/utils/resolved-secret-trace-registry'
33import { jevChoiceTool , jevEvaluateTool , jevNoulTool , jevScoreTool } from '@/tools/jev'
4+ import type { JevEvaluateParams } from '@/tools/jev/types'
45import { prepareToolRequest } from '@/tools/request-transport'
56
67const BASE = { apiKey : 'test-key' , state : 'My payouts have been failing for three days.' }
@@ -19,6 +20,22 @@ const SCORE = {
1920 confidence : 0.92 ,
2021} as const
2122const NOUL = { type : 'noul' , noul : 0.95 } as const
23+ const BATCH_PARAMS : JevEvaluateParams = {
24+ ...BASE ,
25+ questions : {
26+ department : {
27+ type : 'choice' ,
28+ instructions : 'Which team?' ,
29+ criteria : { billing : null , technical : null } ,
30+ } ,
31+ frustration : {
32+ type : 'score' ,
33+ instructions : 'How frustrated?' ,
34+ criteria : [ 'Calm' , 'Frustrated' , 'Very angry' ] ,
35+ } ,
36+ is_urgent : { type : 'noul' , instructions : 'Is this urgent?' } ,
37+ } ,
38+ }
2239
2340function response ( answers : Record < string , unknown > ) {
2441 return Response . json ( { model : 'jev-1.13.0' , answers, usage : USAGE } )
@@ -142,12 +159,44 @@ describe('Jev tools', () => {
142159 } )
143160 } )
144161
145- it ( 'preserves all mixed batch answer types and question IDs' , async ( ) => {
146- const answers = { department : CHOICE , frustration : SCORE , is_urgent : NOUL }
147- expect ( await jevEvaluateTool . transformResponse ! ( response ( answers ) ) ) . toEqual ( {
148- success : true ,
149- output : { model : 'jev-1.13.0' , usage : USAGE , answers } ,
150- } )
162+ it . each ( [ false , true ] ) (
163+ 'matches mixed batch answers regardless of order, serialized=%s' ,
164+ async ( serialized ) => {
165+ const answers = { department : CHOICE , frustration : SCORE , is_urgent : NOUL }
166+ const params = {
167+ ...BATCH_PARAMS ,
168+ questions : serialized ? JSON . stringify ( BATCH_PARAMS . questions ) : BATCH_PARAMS . questions ,
169+ }
170+ expect (
171+ await jevEvaluateTool . transformResponse ! (
172+ response ( { is_urgent : NOUL , frustration : SCORE , department : CHOICE } ) ,
173+ params
174+ )
175+ ) . toEqual ( {
176+ success : true ,
177+ output : { model : 'jev-1.13.0' , usage : USAGE , answers } ,
178+ } )
179+ }
180+ )
181+
182+ it . each ( [
183+ [ 'empty' , { } ] ,
184+ [ 'partial' , { department : CHOICE , frustration : SCORE } ] ,
185+ [ 'mismatched type' , { department : NOUL , frustration : SCORE , is_urgent : NOUL } ] ,
186+ [ 'unexpected ID' , { department : CHOICE , frustration : SCORE , other : NOUL } ] ,
187+ [ 'extra answer' , { department : CHOICE , frustration : SCORE , is_urgent : NOUL , other : NOUL } ] ,
188+ ] ) ( 'rejects a %s batch response' , async ( _label , answers ) => {
189+ await expect (
190+ jevEvaluateTool . transformResponse ! ( response ( answers ) , BATCH_PARAMS )
191+ ) . rejects . toThrow (
192+ 'TypeSafe returned Jev answers that do not match the requested question IDs and types'
193+ )
194+ } )
195+
196+ it ( 'requires request context to validate batch answers' , async ( ) => {
197+ await expect (
198+ jevEvaluateTool . transformResponse ! ( response ( { department : CHOICE } ) )
199+ ) . rejects . toThrow ( 'Jev batch response validation requires request parameters' )
151200 } )
152201
153202 it ( 'supports structured Score legends documented by the TypeSafe SDK' , async ( ) => {
0 commit comments