@@ -19,7 +19,8 @@ vi.mock('@/providers/registry', () => ({
1919vi . mock ( '@/lib/billing/core/usage-gate-cache' , ( ) => billingUsageGateCacheMock )
2020vi . mock ( '@/tools' , ( ) => toolsMock )
2121vi . mock ( '@/lib/billing/core/billing' , ( ) => billingCoreMock )
22- vi . mock ( '@/providers/cost-policy' , ( ) => ( {
22+ vi . mock ( '@/providers/cost-policy' , async ( importOriginal ) => ( {
23+ ...( await importOriginal < typeof import ( '@/providers/cost-policy' ) > ( ) ) ,
2324 resolveModelCostPolicy : ( _model : string , isBYOK = false ) => ( {
2425 billable : ! isBYOK ,
2526 multiplier : 1 ,
@@ -298,6 +299,63 @@ describe('run usage limit', () => {
298299 expect ( readTrustedExecutionCost ( failure ) ) . toEqual ( { input : 1 , output : 1 , total : 2.25 } )
299300 } )
300301
302+ it ( "charges a continued Function call's failure once, through the journal that records it" , async ( ) => {
303+ mockCheckExecutionUsageLimits . mockResolvedValue ( {
304+ isExceeded : false ,
305+ payerUsage : { currentUsage : 0 , limit : 5 } ,
306+ } )
307+ const tool : ProviderToolConfig = {
308+ id : 'function_execute' ,
309+ description : 'run code' ,
310+ params : { } ,
311+ parameters : { type : 'object' , properties : { } , required : [ ] } ,
312+ }
313+ const session = new AgentTurnStateMachine (
314+ { save : vi . fn ( ) } ,
315+ {
316+ version : 1 ,
317+ steps : [
318+ {
319+ id : 'interrupted-step' ,
320+ assistant : { role : 'assistant' , content : '' } ,
321+ calls : [
322+ {
323+ invocationId : 'call-1' ,
324+ toolId : 'function_execute' ,
325+ arguments : '{}' ,
326+ configuredToolBinding : getConfiguredConversationToolBinding ( tool ) ,
327+ } ,
328+ ] ,
329+ results : [ ] ,
330+ } ,
331+ ] ,
332+ }
333+ )
334+ mockExecuteTool . mockResolvedValueOnce ( {
335+ success : false ,
336+ output : { cost : { total : 0.5 } } ,
337+ error : 'Function failed' ,
338+ } )
339+ mockExecuteRequest . mockResolvedValue ( {
340+ content : 'Done' ,
341+ model : 'model-a' ,
342+ cost : { input : 0 , output : 0 , total : 0 } ,
343+ } )
344+
345+ const response = await executeProviderRequest (
346+ 'anthropic' ,
347+ {
348+ model : 'model-a' ,
349+ workflowId : 'workflow-1' ,
350+ messages : [ { role : 'user' , content : 'Continue' } ] ,
351+ tools : [ tool ] ,
352+ } ,
353+ { executionContext : startRun ( ) , agentConversation : session }
354+ )
355+
356+ expect ( response ) . toMatchObject ( { cost : { total : 0.5 } } )
357+ } )
358+
301359 it ( 'counts every block of the run, not only the request that is running' , async ( ) => {
302360 const run = startRun ( )
303361 await modelStep ( startRequest ( run ) , 1 , 1 )
0 commit comments