88const test = require ( 'node:test' )
99const assert = require ( 'node:assert' )
1010
11+ const { tspl } = require ( '@matteo.collina/tspl' )
12+
1113const { removeModules } = require ( '../../lib/cache-buster' )
1214const { assertSegments, match } = require ( '../../lib/custom-assertions' )
1315const createOpenAIMockServer = require ( '../openai/mock-server' )
@@ -70,9 +72,9 @@ test.beforeEach(async (ctx) => {
7072 } )
7173
7274 // Create a simple LangGraph agent.
73- // Ignore the deprecation warning; LangGraph just wants
74- // us to require from "langchain" directly, but
75- // the function works the same .
75+ // Ignore the deprecation warning; LangGraph just
76+ // wants us to require from "langchain" directly,
77+ // but the function is still valid .
7678 ctx . nr . langgraphAgent = createReactAgent ( {
7779 llm : mockLLM ,
7880 // must define tools even if empty
@@ -302,6 +304,7 @@ test('should add subcomponent attribute to span', async (t) => {
302304
303305test ( 'should create LlmError event when given bad input' , async ( t ) => {
304306 const { agent, langgraphAgent } = t . nr
307+ const plan = tspl ( t , { plan : 8 } )
305308
306309 await helper . runInTransaction ( agent , async ( tx ) => {
307310 try {
@@ -312,23 +315,67 @@ test('should create LlmError event when given bad input', async (t) => {
312315 consumeChunk ( chunk )
313316 }
314317 } catch ( error ) {
315- assert . ok ( error , 'should catch an error' )
318+ plan . ok ( error , 'should catch an error' )
319+ }
320+
321+ // Check for LlmAgent event with error flag
322+ const events = agent . customEventAggregator . events . toArray ( )
323+ const agentEvent = events . find ( ( e ) => e [ 0 ] . type === 'LlmAgent' ) ?. [ 1 ]
324+ plan . ok ( agentEvent , 'should have LlmAgent event' )
325+ plan . equal ( agentEvent . error , true , 'should set LlmAgent event `error` to true' )
326+
327+ // Check for LlmError in transaction exceptions.
328+ // 2 will be created, first one for LangChain RunnableSequence.stream
329+ // failure, second one for LangGraph agent failure
330+ const exceptions = tx . exceptions
331+ plan . equal ( exceptions . length , 2 )
332+ const lgException = exceptions [ 1 ]
333+ const str = Object . prototype . toString . call ( lgException . customAttributes )
334+ plan . equal ( str , '[object LlmErrorMessage]' , 'should be a LlmErrorMessage' )
335+ plan . equal ( lgException . customAttributes . agent_id , agentEvent . id , 'ai agent_id should match' )
336+ plan . equal ( lgException . customAttributes [ 'error.code' ] , lgException . error [ 'lc_error_code' ] , 'error codes should match' )
337+ plan . equal ( lgException . customAttributes [ 'error.message' ] , lgException . error [ 'message' ] , 'error messages should match' )
338+
339+ tx . end ( )
340+ } )
341+ } )
342+
343+ test ( 'should create LlmError event when stream fails in the middle' , async ( t ) => {
344+ const { agent, langgraphAgent } = t . nr
345+ const plan = tspl ( t , { plan : 8 } )
346+
347+ await helper . runInTransaction ( agent , async ( tx ) => {
348+ try {
349+ // Starts off with a valid request...
350+ const stream = await langgraphAgent . stream (
351+ { messages : [ { role : 'user' , content : 'You are a scientist.' } ] }
352+ )
353+ for await ( const chunk of stream ) {
354+ consumeChunk ( chunk )
355+ // then abruptly abort the stream
356+ stream . cancel ( 'abort' )
357+ }
358+ } catch ( error ) {
359+ plan . ok ( error , 'should catch an error' )
316360 }
317361
318362 // Check for LlmAgent event with error flag
319363 const events = agent . customEventAggregator . events . toArray ( )
320364 const agentEvent = events . find ( ( e ) => e [ 0 ] . type === 'LlmAgent' ) ?. [ 1 ]
321- assert . ok ( agentEvent , 'should have LlmAgent event' )
322- assert . equal ( agentEvent . error , true )
365+ plan . ok ( agentEvent , 'should have LlmAgent event' )
366+ plan . equal ( agentEvent . error , true , 'should set LlmAgent event `error` to true' )
323367
324368 // Check for LlmError in transaction exceptions.
325369 // 2 will be created, first one for LangChain RunnableSequence.stream
326370 // failure, second one for LangGraph agent failure
327371 const exceptions = tx . exceptions
328- assert . equal ( exceptions . length , 2 )
329- const str = Object . prototype . toString . call ( exceptions [ 1 ] . customAttributes )
330- assert . equal ( str , '[object LlmErrorMessage]' )
331- assert . equal ( exceptions [ 1 ] . customAttributes . agent_id , agentEvent . id )
372+ plan . equal ( exceptions . length , 2 )
373+ const lgException = exceptions [ 1 ]
374+ const str = Object . prototype . toString . call ( lgException . customAttributes )
375+ plan . equal ( str , '[object LlmErrorMessage]' , 'should be a LlmErrorMessage' )
376+ plan . equal ( lgException . customAttributes . agent_id , agentEvent . id , 'ai agent_id should match' )
377+ plan . equal ( lgException . customAttributes [ 'error.code' ] , lgException . error [ 'code' ] , 'error codes should match' )
378+ plan . equal ( lgException . customAttributes [ 'error.message' ] , lgException . error [ 'message' ] , 'error messages should match' )
332379
333380 tx . end ( )
334381 } )
0 commit comments