4
4
5
5
import { test , expect , afterEach } from "@jest/globals" ;
6
6
import { z } from "zod" ;
7
+ import { AsyncLocalStorage } from "node:async_hooks" ;
7
8
import {
8
9
RunnableLambda ,
9
10
RunnableMap ,
@@ -28,8 +29,9 @@ import { DynamicStructuredTool, DynamicTool, tool } from "../../tools/index.js";
28
29
import { Document } from "../../documents/document.js" ;
29
30
import { PromptTemplate } from "../../prompts/prompt.js" ;
30
31
import { GenerationChunk } from "../../outputs.js" ;
31
- // Import from web to avoid side-effects from AsyncLocalStorage
32
+ // Import from web to avoid top-level side-effects from AsyncLocalStorage
32
33
import { dispatchCustomEvent } from "../../callbacks/dispatch/web.js" ;
34
+ import { AsyncLocalStorageProviderSingleton } from "../../singletons/index.js" ;
33
35
34
36
function reverse ( s : string ) {
35
37
// Reverse a string.
@@ -138,6 +140,73 @@ test("Runnable streamEvents method on a chat model", async () => {
138
140
] ) ;
139
141
} ) ;
140
142
143
+ test ( "Runnable streamEvents call nested in another runnable + passed callbacks should still work" , async ( ) => {
144
+ AsyncLocalStorageProviderSingleton . initializeGlobalInstance (
145
+ new AsyncLocalStorage ( )
146
+ ) ;
147
+
148
+ const model = new FakeListChatModel ( {
149
+ responses : [ "abc" ] ,
150
+ } ) ;
151
+
152
+ const events : any [ ] = [ ] ;
153
+ const container = RunnableLambda . from ( async ( _ ) => {
154
+ const eventStream = model . streamEvents ( "hello" , { version : "v2" } ) ;
155
+ for await ( const event of eventStream ) {
156
+ events . push ( event ) ;
157
+ }
158
+ return events ;
159
+ } ) ;
160
+
161
+ await container . invoke ( { } , { callbacks : [ { handleLLMStart : ( ) => { } } ] } ) ;
162
+
163
+ // used here to avoid casting every ID
164
+ const anyString = expect . any ( String ) as unknown as string ;
165
+
166
+ expect ( events ) . toMatchObject ( [
167
+ {
168
+ data : { input : "hello" } ,
169
+ event : "on_chat_model_start" ,
170
+ name : "FakeListChatModel" ,
171
+ metadata : expect . any ( Object ) ,
172
+ run_id : expect . any ( String ) ,
173
+ tags : [ ] ,
174
+ } ,
175
+ {
176
+ data : { chunk : new AIMessageChunk ( { id : anyString , content : "a" } ) } ,
177
+ event : "on_chat_model_stream" ,
178
+ name : "FakeListChatModel" ,
179
+ metadata : expect . any ( Object ) ,
180
+ run_id : expect . any ( String ) ,
181
+ tags : [ ] ,
182
+ } ,
183
+ {
184
+ data : { chunk : new AIMessageChunk ( { id : anyString , content : "b" } ) } ,
185
+ event : "on_chat_model_stream" ,
186
+ name : "FakeListChatModel" ,
187
+ metadata : expect . any ( Object ) ,
188
+ run_id : expect . any ( String ) ,
189
+ tags : [ ] ,
190
+ } ,
191
+ {
192
+ data : { chunk : new AIMessageChunk ( { id : anyString , content : "c" } ) } ,
193
+ event : "on_chat_model_stream" ,
194
+ name : "FakeListChatModel" ,
195
+ metadata : expect . any ( Object ) ,
196
+ run_id : expect . any ( String ) ,
197
+ tags : [ ] ,
198
+ } ,
199
+ {
200
+ data : { output : new AIMessageChunk ( { id : anyString , content : "abc" } ) } ,
201
+ event : "on_chat_model_end" ,
202
+ name : "FakeListChatModel" ,
203
+ metadata : expect . any ( Object ) ,
204
+ run_id : expect . any ( String ) ,
205
+ tags : [ ] ,
206
+ } ,
207
+ ] ) ;
208
+ } ) ;
209
+
141
210
test ( "Runnable streamEvents method with three runnables" , async ( ) => {
142
211
const r = RunnableLambda . from ( reverse ) ;
143
212
0 commit comments