diff --git a/langchain-core/src/callbacks/manager.ts b/langchain-core/src/callbacks/manager.ts index 1f9ffebfae9a..dcabf602b838 100644 --- a/langchain-core/src/callbacks/manager.ts +++ b/langchain-core/src/callbacks/manager.ts @@ -1273,10 +1273,10 @@ export class CallbackManager handler = contextVarValue; } else if (createIfNotInContext) { // eslint-disable-next-line @typescript-eslint/no-explicit-any - handler = new (handlerClass as any)(); + handler = new (handlerClass as any)({}); } if (handler !== undefined) { - if (!callbackManager?.handlers.some((h) => h.name === handler.name)) { + if (!callbackManager?.handlers.some((h) => h.name === handler!.name)) { callbackManager?.addHandler(handler, inheritable); } } diff --git a/langchain-core/src/singletons/async_local_storage/context.ts b/langchain-core/src/singletons/async_local_storage/context.ts index d9dbdf74a469..af08cd0d41d4 100644 --- a/langchain-core/src/singletons/async_local_storage/context.ts +++ b/langchain-core/src/singletons/async_local_storage/context.ts @@ -1,9 +1,9 @@ import { isRunTree, RunTree } from "langsmith/run_trees"; +import { BaseCallbackHandler } from "../../callbacks/base.js"; import { _CONTEXT_VARIABLES_KEY, - AsyncLocalStorageProviderSingleton, -} from "./index.js"; -import { BaseCallbackHandler } from "../../callbacks/base.js"; + getGlobalAsyncLocalStorageInstance, +} from "./globals.js"; /** * Set a context variable. Context variables are scoped to any @@ -57,7 +57,14 @@ import { BaseCallbackHandler } from "../../callbacks/base.js"; */ // eslint-disable-next-line @typescript-eslint/no-explicit-any export function setContextVariable(name: PropertyKey, value: T): void { - const runTree = AsyncLocalStorageProviderSingleton.getInstance().getStore(); + // Avoid using global singleton due to circuluar dependency issues + const asyncLocalStorageInstance = getGlobalAsyncLocalStorageInstance(); + if (asyncLocalStorageInstance === undefined) { + throw new Error( + `Internal error: Global shared async local storage instance has not been initialized.` + ); + } + const runTree = asyncLocalStorageInstance.getStore(); const contextVars = { ...runTree?.[_CONTEXT_VARIABLES_KEY] }; contextVars[name] = value; let newValue = {}; @@ -66,7 +73,7 @@ export function setContextVariable(name: PropertyKey, value: T): void { } // eslint-disable-next-line @typescript-eslint/no-explicit-any (newValue as any)[_CONTEXT_VARIABLES_KEY] = contextVars; - AsyncLocalStorageProviderSingleton.getInstance().enterWith(newValue); + asyncLocalStorageInstance.enterWith(newValue); } /** @@ -120,7 +127,12 @@ export function setContextVariable(name: PropertyKey, value: T): void { */ // eslint-disable-next-line @typescript-eslint/no-explicit-any export function getContextVariable(name: PropertyKey): T | undefined { - const runTree = AsyncLocalStorageProviderSingleton.getInstance().getStore(); + // Avoid using global singleton due to circuluar dependency issues + const asyncLocalStorageInstance = getGlobalAsyncLocalStorageInstance(); + if (asyncLocalStorageInstance === undefined) { + return undefined; + } + const runTree = asyncLocalStorageInstance.getStore(); return runTree?.[_CONTEXT_VARIABLES_KEY]?.[name]; } diff --git a/langchain-core/src/singletons/async_local_storage/globals.ts b/langchain-core/src/singletons/async_local_storage/globals.ts index c3428989b8c4..d7c661aac79e 100644 --- a/langchain-core/src/singletons/async_local_storage/globals.ts +++ b/langchain-core/src/singletons/async_local_storage/globals.ts @@ -9,12 +9,16 @@ export interface AsyncLocalStorageInterface { export const TRACING_ALS_KEY = Symbol.for("ls:tracing_async_local_storage"); +export const _CONTEXT_VARIABLES_KEY = Symbol.for("lc:context_variables"); + export const setGlobalAsyncLocalStorageInstance = ( instance: AsyncLocalStorageInterface ) => { (globalThis as any)[TRACING_ALS_KEY] = instance; }; -export const getGlobalAsyncLocalStorageInstance = () => { +export const getGlobalAsyncLocalStorageInstance = (): + | AsyncLocalStorageInterface + | undefined => { return (globalThis as any)[TRACING_ALS_KEY]; }; diff --git a/langchain-core/src/singletons/async_local_storage/index.ts b/langchain-core/src/singletons/async_local_storage/index.ts index f89c9fbce50a..3dc1341d92e2 100644 --- a/langchain-core/src/singletons/async_local_storage/index.ts +++ b/langchain-core/src/singletons/async_local_storage/index.ts @@ -4,6 +4,7 @@ import { AsyncLocalStorageInterface, getGlobalAsyncLocalStorageInstance, setGlobalAsyncLocalStorageInstance, + _CONTEXT_VARIABLES_KEY, } from "./globals.js"; import { CallbackManager } from "../../callbacks/manager.js"; import { LangChainTracer } from "../../tracers/tracer_langchain.js"; @@ -26,8 +27,6 @@ const mockAsyncLocalStorage = new MockAsyncLocalStorage(); const LC_CHILD_KEY = Symbol.for("lc:child_config"); -export const _CONTEXT_VARIABLES_KEY = Symbol.for("lc:context_variables"); - class AsyncLocalStorageProvider { getInstance(): AsyncLocalStorageInterface { return getGlobalAsyncLocalStorageInstance() ?? mockAsyncLocalStorage; diff --git a/langchain-core/src/singletons/callbacks.ts b/langchain-core/src/singletons/callbacks.ts index 681d770ba96d..5c881bfcbab6 100644 --- a/langchain-core/src/singletons/callbacks.ts +++ b/langchain-core/src/singletons/callbacks.ts @@ -37,20 +37,18 @@ export async function consumeCallback( if (wait === true) { // Clear config since callbacks are not part of the root run // Avoid using global singleton due to circuluar dependency issues - if (getGlobalAsyncLocalStorageInstance() !== undefined) { - await getGlobalAsyncLocalStorageInstance().run(undefined, async () => - promiseFn() - ); + const asyncLocalStorageInstance = getGlobalAsyncLocalStorageInstance(); + if (asyncLocalStorageInstance !== undefined) { + await asyncLocalStorageInstance.run(undefined, async () => promiseFn()); } else { await promiseFn(); } } else { queue = getQueue(); void queue.add(async () => { - if (getGlobalAsyncLocalStorageInstance() !== undefined) { - await getGlobalAsyncLocalStorageInstance().run(undefined, async () => - promiseFn() - ); + const asyncLocalStorageInstance = getGlobalAsyncLocalStorageInstance(); + if (asyncLocalStorageInstance !== undefined) { + await asyncLocalStorageInstance.run(undefined, async () => promiseFn()); } else { await promiseFn(); } diff --git a/langchain-core/src/singletons/index.ts b/langchain-core/src/singletons/index.ts index bee8320abaec..4f5d6ba29605 100644 --- a/langchain-core/src/singletons/index.ts +++ b/langchain-core/src/singletons/index.ts @@ -2,13 +2,11 @@ import { type AsyncLocalStorageInterface, AsyncLocalStorageProviderSingleton, - _CONTEXT_VARIABLES_KEY, MockAsyncLocalStorage, } from "./async_local_storage/index.js"; export { type AsyncLocalStorageInterface, AsyncLocalStorageProviderSingleton, - _CONTEXT_VARIABLES_KEY, MockAsyncLocalStorage, };