@@ -5,8 +5,7 @@ import { describe, expect, it } from 'vitest';
55import { validateAxMessageArray } from '../ai/base.js' ;
66import { AxMockAIService } from '../ai/mock/api.js' ;
77import type { AxChatResponse } from '../ai/types.js' ;
8- import { AxStopFunctionCallException } from './functions.js' ;
9- import { AxGen , type AxGenerateError } from './generate.js' ;
8+ import { AxGen } from './generate.js' ;
109import { AxSignature } from './sig.js' ;
1110import type { AxProgramForwardOptions } from './types.js' ;
1211
@@ -142,7 +141,7 @@ describe('AxGen forward and streamingForward', () => {
142141} ) ;
143142
144143describe ( 'stopFunction behavior' , ( ) => {
145- it ( 'throws AxStopFunctionCallException for string stopFunction' , async ( ) => {
144+ it ( 'stops gracefully when stopFunction is called with string stopFunction' , async ( ) => {
146145 const ai = new AxMockAIService ( {
147146 features : { functions : true , streaming : false } ,
148147 chatResponse : {
@@ -175,27 +174,17 @@ describe('stopFunction behavior', () => {
175174 }
176175 ) ;
177176
178- try {
179- await gen . forward (
180- ai as any ,
181- { userQuestion : 'call tool' } ,
182- { stopFunction : 'getTime' }
183- ) ;
184- throw new Error ( 'Expected AxStopFunctionCallException' ) ;
185- } catch ( e ) {
186- const ex =
187- e instanceof AxStopFunctionCallException
188- ? e
189- : ( e as AxGenerateError ) . cause ;
190- expect ( ex ) . toBeInstanceOf ( AxStopFunctionCallException ) ;
191- const stop = ex as AxStopFunctionCallException ;
192- expect ( stop . calls ?. length ) . toBe ( 1 ) ;
193- expect ( stop . calls ?. [ 0 ] ?. func . name ) . toBe ( 'getTime' ) ;
194- expect ( stop . calls ?. [ 0 ] ?. result ) . toBe ( 'NOW' ) ;
195- }
177+ // With the new behavior, stopFunction should complete without throwing
178+ const result = await gen . forward (
179+ ai as any ,
180+ { userQuestion : 'call tool' } ,
181+ { stopFunction : 'getTime' }
182+ ) ;
183+ // The function should have been called and the generator should stop gracefully
184+ expect ( result ) . toBeDefined ( ) ;
196185 } ) ;
197186
198- it ( 'throws AxStopFunctionCallException for any match in string[]' , async ( ) => {
187+ it ( 'stops gracefully when stopFunction matches any function in string[]' , async ( ) => {
199188 const ai = new AxMockAIService ( {
200189 features : { functions : true , streaming : false } ,
201190 chatResponse : {
@@ -225,27 +214,17 @@ describe('stopFunction behavior', () => {
225214 }
226215 ) ;
227216
228- try {
229- await gen . forward (
230- ai as any ,
231- { userQuestion : 'call B' } ,
232- { stopFunction : [ 'toolA' , 'toolB' ] }
233- ) ;
234- throw new Error ( 'Expected AxStopFunctionCallException' ) ;
235- } catch ( e ) {
236- const ex =
237- e instanceof AxStopFunctionCallException
238- ? e
239- : ( e as AxGenerateError ) . cause ;
240- expect ( ex ) . toBeInstanceOf ( AxStopFunctionCallException ) ;
241- const stop = ex as AxStopFunctionCallException ;
242- expect ( stop . calls ?. length ) . toBeGreaterThanOrEqual ( 1 ) ;
243- expect ( stop . calls ?. [ 0 ] ?. func . name ) . toBe ( 'toolB' ) ;
244- expect ( stop . calls ?. [ 0 ] ?. result ) . toBe ( 'B' ) ;
245- }
217+ // With the new behavior, stopFunction should complete without throwing
218+ const result = await gen . forward (
219+ ai as any ,
220+ { userQuestion : 'call B' } ,
221+ { stopFunction : [ 'toolA' , 'toolB' ] }
222+ ) ;
223+ // The function should have been called and the generator should stop gracefully
224+ expect ( result ) . toBeDefined ( ) ;
246225 } ) ;
247226
248- it ( 'aggregates multiple parallel stop function matches' , async ( ) => {
227+ it ( 'stops gracefully with multiple parallel stop function matches' , async ( ) => {
249228 const ai = new AxMockAIService ( {
250229 features : { functions : true , streaming : false } ,
251230 chatResponse : {
@@ -280,26 +259,14 @@ describe('stopFunction behavior', () => {
280259 }
281260 ) ;
282261
283- try {
284- await gen . forward (
285- ai as any ,
286- { userQuestion : 'call both' } ,
287- { stopFunction : [ 'toolA' , 'toolB' ] }
288- ) ;
289- throw new Error ( 'Expected AxStopFunctionCallException' ) ;
290- } catch ( e ) {
291- const ex =
292- e instanceof AxStopFunctionCallException
293- ? e
294- : ( e as AxGenerateError ) . cause ;
295- expect ( ex ) . toBeInstanceOf ( AxStopFunctionCallException ) ;
296- const stop = ex as AxStopFunctionCallException ;
297- expect ( stop . calls ?. length ) . toBe ( 2 ) ;
298- const names = ( stop . calls ?? [ ] ) . map ( ( c ) => c . func . name ) . sort ( ) ;
299- expect ( names ) . toEqual ( [ 'toolA' , 'toolB' ] ) ;
300- const results = ( stop . calls ?? [ ] ) . map ( ( c ) => c . result ) . sort ( ) ;
301- expect ( results ) . toEqual ( [ 'A' , 'B' ] ) ;
302- }
262+ // With the new behavior, stopFunction should complete without throwing
263+ const result = await gen . forward (
264+ ai as any ,
265+ { userQuestion : 'call both' } ,
266+ { stopFunction : [ 'toolA' , 'toolB' ] }
267+ ) ;
268+ // Both functions should have been called and the generator should stop gracefully
269+ expect ( result ) . toBeDefined ( ) ;
303270 } ) ;
304271} ) ;
305272
0 commit comments