@librechat/agents 3.1.45 → 3.1.51
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/events.cjs +9 -4
- package/dist/cjs/events.cjs.map +1 -1
- package/dist/cjs/graphs/Graph.cjs +142 -106
- package/dist/cjs/graphs/Graph.cjs.map +1 -1
- package/dist/cjs/messages/format.cjs +8 -1
- package/dist/cjs/messages/format.cjs.map +1 -1
- package/dist/cjs/run.cjs +0 -4
- package/dist/cjs/run.cjs.map +1 -1
- package/dist/cjs/tools/ToolNode.cjs +100 -1
- package/dist/cjs/tools/ToolNode.cjs.map +1 -1
- package/dist/cjs/tools/handlers.cjs.map +1 -1
- package/dist/esm/events.mjs +9 -4
- package/dist/esm/events.mjs.map +1 -1
- package/dist/esm/graphs/Graph.mjs +138 -102
- package/dist/esm/graphs/Graph.mjs.map +1 -1
- package/dist/esm/messages/format.mjs +8 -1
- package/dist/esm/messages/format.mjs.map +1 -1
- package/dist/esm/run.mjs +1 -5
- package/dist/esm/run.mjs.map +1 -1
- package/dist/esm/tools/ToolNode.mjs +100 -1
- package/dist/esm/tools/ToolNode.mjs.map +1 -1
- package/dist/esm/tools/handlers.mjs.map +1 -1
- package/dist/types/events.d.ts +10 -3
- package/dist/types/graphs/Graph.d.ts +0 -2
- package/dist/types/tools/ToolNode.d.ts +13 -1
- package/dist/types/tools/handlers.d.ts +2 -2
- package/package.json +1 -1
- package/src/events.ts +11 -14
- package/src/graphs/Graph.ts +181 -144
- package/src/messages/format.ts +12 -1
- package/src/messages/formatAgentMessages.test.ts +184 -0
- package/src/run.ts +0 -6
- package/src/scripts/simple.ts +1 -1
- package/src/specs/anthropic.simple.test.ts +3 -4
- package/src/specs/azure.simple.test.ts +1 -2
- package/src/specs/cache.simple.test.ts +1 -2
- package/src/specs/custom-event-await.test.ts +2 -4
- package/src/specs/deepseek.simple.test.ts +1 -2
- package/src/specs/moonshot.simple.test.ts +1 -2
- package/src/specs/openai.simple.test.ts +1 -2
- package/src/specs/openrouter.simple.test.ts +1 -2
- package/src/specs/reasoning.test.ts +1 -2
- package/src/specs/tool-error.test.ts +1 -2
- package/src/tools/ToolNode.ts +130 -1
- package/src/tools/handlers.ts +2 -2
package/dist/cjs/events.cjs
CHANGED
|
@@ -47,12 +47,18 @@ class ModelEndHandler {
|
|
|
47
47
|
class ToolEndHandler {
|
|
48
48
|
callback;
|
|
49
49
|
logger;
|
|
50
|
-
|
|
51
|
-
constructor(callback, logger, omitOutput) {
|
|
50
|
+
constructor(callback, logger) {
|
|
52
51
|
this.callback = callback;
|
|
53
52
|
this.logger = logger;
|
|
54
|
-
this.omitOutput = omitOutput;
|
|
55
53
|
}
|
|
54
|
+
/**
|
|
55
|
+
* Handles on_tool_end events from the for-await stream consumer.
|
|
56
|
+
*
|
|
57
|
+
* This handler is now purely a consumer callback — tool completion
|
|
58
|
+
* (ON_RUN_STEP_COMPLETED dispatch + session context storage) is handled
|
|
59
|
+
* in graph context by ToolNode directly, eliminating the race between
|
|
60
|
+
* the stream consumer and graph execution.
|
|
61
|
+
*/
|
|
56
62
|
async handle(event, data, metadata, graph) {
|
|
57
63
|
try {
|
|
58
64
|
if (!graph || !metadata) {
|
|
@@ -80,7 +86,6 @@ class ToolEndHandler {
|
|
|
80
86
|
if (this.callback) {
|
|
81
87
|
await this.callback(toolEndData, metadata);
|
|
82
88
|
}
|
|
83
|
-
await graph.handleToolCallCompleted({ input: toolEndData.input, output: toolEndData.output }, metadata, this.omitOutput?.(toolEndData.output?.name));
|
|
84
89
|
}
|
|
85
90
|
catch (error) {
|
|
86
91
|
if (this.logger) {
|
package/dist/cjs/events.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"events.cjs","sources":["../../src/events.ts"],"sourcesContent":["/* eslint-disable no-console */\n// src/events.ts\nimport type {\n ToolMessage,\n UsageMetadata,\n BaseMessageFields,\n} from '@langchain/core/messages';\nimport type { MultiAgentGraph, StandardGraph } from '@/graphs';\nimport type { Logger } from 'winston';\nimport type * as t from '@/types';\nimport { handleToolCalls } from '@/tools/handlers';\nimport { Constants, Providers } from '@/common';\n\nexport class HandlerRegistry {\n private handlers: Map<string, t.EventHandler> = new Map();\n\n register(eventType: string, handler: t.EventHandler): void {\n this.handlers.set(eventType, handler);\n }\n\n getHandler(eventType: string): t.EventHandler | undefined {\n return this.handlers.get(eventType);\n }\n}\n\nexport class ModelEndHandler implements t.EventHandler {\n collectedUsage?: UsageMetadata[];\n constructor(collectedUsage?: UsageMetadata[]) {\n if (collectedUsage && !Array.isArray(collectedUsage)) {\n throw new Error('collectedUsage must be an array');\n }\n this.collectedUsage = collectedUsage;\n }\n\n async handle(\n event: string,\n data: t.ModelEndData,\n metadata?: Record<string, unknown>,\n graph?: StandardGraph | MultiAgentGraph\n ): Promise<void> {\n if (!graph || !metadata) {\n console.warn(`Graph or metadata not found in ${event} event`);\n return;\n }\n\n const usage = data?.output?.usage_metadata;\n if (usage != null && this.collectedUsage != null) {\n this.collectedUsage.push(usage);\n }\n\n if (metadata.ls_provider === 'FakeListChatModel') {\n return handleToolCalls(data?.output?.tool_calls, metadata, graph);\n }\n\n console.log(`====== ${event.toUpperCase()} ======`);\n console.dir(\n {\n usage,\n },\n { depth: null }\n );\n\n const agentContext = graph.getAgentContext(metadata);\n\n if (\n agentContext.provider !== Providers.GOOGLE &&\n agentContext.provider !== Providers.BEDROCK\n ) {\n return;\n }\n\n await handleToolCalls(data?.output?.tool_calls, metadata, graph);\n }\n}\n\nexport class ToolEndHandler implements t.EventHandler {\n private callback?: t.ToolEndCallback;\n private logger?: Logger;\n private omitOutput?: (name?: string) => boolean;\n constructor(\n callback?: t.ToolEndCallback,\n logger?: Logger,\n omitOutput?: (name?: string) => boolean\n ) {\n this.callback = callback;\n this.logger = logger;\n this.omitOutput = omitOutput;\n }\n async handle(\n event: string,\n data: t.StreamEventData | undefined,\n metadata?: Record<string, unknown>,\n graph?: StandardGraph | MultiAgentGraph\n ): Promise<void> {\n try {\n if (!graph || !metadata) {\n if (this.logger) {\n this.logger.warn(`Graph or metadata not found in ${event} event`);\n } else {\n console.warn(`Graph or metadata not found in ${event} event`);\n }\n return;\n }\n\n const toolEndData = data as t.ToolEndData | undefined;\n if (!toolEndData?.output) {\n if (this.logger) {\n this.logger.warn('No output found in tool_end event');\n } else {\n console.warn('No output found in tool_end event');\n }\n return;\n }\n\n if (metadata[Constants.PROGRAMMATIC_TOOL_CALLING] === true) {\n return;\n }\n\n if (this.callback) {\n await this.callback(toolEndData, metadata);\n }\n await graph.handleToolCallCompleted(\n { input: toolEndData.input, output: toolEndData.output },\n metadata,\n this.omitOutput?.((toolEndData.output as ToolMessage | undefined)?.name)\n );\n } catch (error) {\n if (this.logger) {\n this.logger.error('Error handling tool_end event:', error);\n } else {\n console.error('Error handling tool_end event:', error);\n }\n }\n }\n}\n\nexport class TestLLMStreamHandler implements t.EventHandler {\n handle(event: string, data: t.StreamEventData | undefined): void {\n const chunk = data?.chunk;\n const isMessageChunk = !!(chunk && 'message' in chunk);\n const msg = isMessageChunk ? chunk.message : undefined;\n if (msg && msg.tool_call_chunks && msg.tool_call_chunks.length > 0) {\n console.log(msg.tool_call_chunks);\n } else if (msg && msg.content) {\n if (typeof msg.content === 'string') {\n process.stdout.write(msg.content);\n }\n }\n }\n}\n\nexport class TestChatStreamHandler implements t.EventHandler {\n handle(event: string, data: t.StreamEventData | undefined): void {\n const chunk = data?.chunk;\n const isContentChunk = !!(chunk && 'content' in chunk);\n const content = isContentChunk && chunk.content;\n\n if (!content || !isContentChunk) {\n return;\n }\n\n if (chunk.tool_call_chunks && chunk.tool_call_chunks.length > 0) {\n console.dir(chunk.tool_call_chunks, { depth: null });\n }\n\n if (typeof content === 'string') {\n process.stdout.write(content);\n } else {\n console.dir(content, { depth: null });\n }\n }\n}\n\nexport class LLMStreamHandler implements t.EventHandler {\n handle(\n event: string,\n data: t.StreamEventData | undefined,\n metadata?: Record<string, unknown>\n ): void {\n const chunk = data?.chunk;\n const isMessageChunk = !!(chunk && 'message' in chunk);\n const msg = isMessageChunk && chunk.message;\n if (metadata) {\n console.log(metadata);\n }\n if (msg && msg.tool_call_chunks && msg.tool_call_chunks.length > 0) {\n console.log(msg.tool_call_chunks);\n } else if (msg && msg.content) {\n if (typeof msg.content === 'string') {\n // const text_delta = msg.content;\n // dispatchCustomEvent(GraphEvents.CHAT_MODEL_STREAM, { chunk }, config);\n process.stdout.write(msg.content);\n }\n }\n }\n}\n\nexport const createMetadataAggregator = (\n _collected?: Record<\n string,\n NonNullable<BaseMessageFields['response_metadata']>\n >[]\n): t.MetadataAggregatorResult => {\n const collected = _collected || [];\n\n const handleLLMEnd: t.HandleLLMEnd = (output) => {\n const { generations } = output;\n const lastMessageOutput = (\n generations[generations.length - 1] as\n | (t.StreamGeneration | undefined)[]\n | undefined\n )?.[0];\n if (!lastMessageOutput) {\n return;\n }\n const { message } = lastMessageOutput;\n if (message?.response_metadata) {\n collected.push(message.response_metadata);\n }\n };\n\n return { handleLLMEnd, collected };\n};\n"],"names":["handleToolCalls","Providers","Constants"],"mappings":";;;;;MAaa,eAAe,CAAA;AAClB,IAAA,QAAQ,GAAgC,IAAI,GAAG,EAAE;IAEzD,QAAQ,CAAC,SAAiB,EAAE,OAAuB,EAAA;QACjD,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC;;AAGvC,IAAA,UAAU,CAAC,SAAiB,EAAA;QAC1B,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,SAAS,CAAC;;AAEtC;MAEY,eAAe,CAAA;AAC1B,IAAA,cAAc;AACd,IAAA,WAAA,CAAY,cAAgC,EAAA;QAC1C,IAAI,cAAc,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,cAAc,CAAC,EAAE;AACpD,YAAA,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC;;AAEpD,QAAA,IAAI,CAAC,cAAc,GAAG,cAAc;;IAGtC,MAAM,MAAM,CACV,KAAa,EACb,IAAoB,EACpB,QAAkC,EAClC,KAAuC,EAAA;AAEvC,QAAA,IAAI,CAAC,KAAK,IAAI,CAAC,QAAQ,EAAE;AACvB,YAAA,OAAO,CAAC,IAAI,CAAC,kCAAkC,KAAK,CAAA,MAAA,CAAQ,CAAC;YAC7D;;AAGF,QAAA,MAAM,KAAK,GAAG,IAAI,EAAE,MAAM,EAAE,cAAc;QAC1C,IAAI,KAAK,IAAI,IAAI,IAAI,IAAI,CAAC,cAAc,IAAI,IAAI,EAAE;AAChD,YAAA,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC;;AAGjC,QAAA,IAAI,QAAQ,CAAC,WAAW,KAAK,mBAAmB,EAAE;AAChD,YAAA,OAAOA,wBAAe,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,KAAK,CAAC;;QAGnE,OAAO,CAAC,GAAG,CAAC,CAAU,OAAA,EAAA,KAAK,CAAC,WAAW,EAAE,CAAS,OAAA,CAAA,CAAC;QACnD,OAAO,CAAC,GAAG,CACT;YACE,KAAK;AACN,SAAA,EACD,EAAE,KAAK,EAAE,IAAI,EAAE,CAChB;QAED,MAAM,YAAY,GAAG,KAAK,CAAC,eAAe,CAAC,QAAQ,CAAC;AAEpD,QAAA,IACE,YAAY,CAAC,QAAQ,KAAKC,eAAS,CAAC,MAAM;AAC1C,YAAA,YAAY,CAAC,QAAQ,KAAKA,eAAS,CAAC,OAAO,EAC3C;YACA;;AAGF,QAAA,MAAMD,wBAAe,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,KAAK,CAAC;;AAEnE;MAEY,cAAc,CAAA;AACjB,IAAA,QAAQ;AACR,IAAA,MAAM;AACN,IAAA,UAAU;AAClB,IAAA,WAAA,CACE,QAA4B,EAC5B,MAAe,EACf,UAAuC,EAAA;AAEvC,QAAA,IAAI,CAAC,QAAQ,GAAG,QAAQ;AACxB,QAAA,IAAI,CAAC,MAAM,GAAG,MAAM;AACpB,QAAA,IAAI,CAAC,UAAU,GAAG,UAAU;;IAE9B,MAAM,MAAM,CACV,KAAa,EACb,IAAmC,EACnC,QAAkC,EAClC,KAAuC,EAAA;AAEvC,QAAA,IAAI;AACF,YAAA,IAAI,CAAC,KAAK,IAAI,CAAC,QAAQ,EAAE;AACvB,gBAAA,IAAI,IAAI,CAAC,MAAM,EAAE;oBACf,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAkC,+BAAA,EAAA,KAAK,CAAQ,MAAA,CAAA,CAAC;;qBAC5D;AACL,oBAAA,OAAO,CAAC,IAAI,CAAC,kCAAkC,KAAK,CAAA,MAAA,CAAQ,CAAC;;gBAE/D;;YAGF,MAAM,WAAW,GAAG,IAAiC;AACrD,YAAA,IAAI,CAAC,WAAW,EAAE,MAAM,EAAE;AACxB,gBAAA,IAAI,IAAI,CAAC,MAAM,EAAE;AACf,oBAAA,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,mCAAmC,CAAC;;qBAChD;AACL,oBAAA,OAAO,CAAC,IAAI,CAAC,mCAAmC,CAAC;;gBAEnD;;YAGF,IAAI,QAAQ,CAACE,eAAS,CAAC,yBAAyB,CAAC,KAAK,IAAI,EAAE;gBAC1D;;AAGF,YAAA,IAAI,IAAI,CAAC,QAAQ,EAAE;gBACjB,MAAM,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,QAAQ,CAAC;;AAE5C,YAAA,MAAM,KAAK,CAAC,uBAAuB,CACjC,EAAE,KAAK,EAAE,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,EACxD,QAAQ,EACR,IAAI,CAAC,UAAU,GAAI,WAAW,CAAC,MAAkC,EAAE,IAAI,CAAC,CACzE;;QACD,OAAO,KAAK,EAAE;AACd,YAAA,IAAI,IAAI,CAAC,MAAM,EAAE;gBACf,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,gCAAgC,EAAE,KAAK,CAAC;;iBACrD;AACL,gBAAA,OAAO,CAAC,KAAK,CAAC,gCAAgC,EAAE,KAAK,CAAC;;;;AAI7D;MAEY,oBAAoB,CAAA;IAC/B,MAAM,CAAC,KAAa,EAAE,IAAmC,EAAA;AACvD,QAAA,MAAM,KAAK,GAAG,IAAI,EAAE,KAAK;QACzB,MAAM,cAAc,GAAG,CAAC,EAAE,KAAK,IAAI,SAAS,IAAI,KAAK,CAAC;AACtD,QAAA,MAAM,GAAG,GAAG,cAAc,GAAG,KAAK,CAAC,OAAO,GAAG,SAAS;AACtD,QAAA,IAAI,GAAG,IAAI,GAAG,CAAC,gBAAgB,IAAI,GAAG,CAAC,gBAAgB,CAAC,MAAM,GAAG,CAAC,EAAE;AAClE,YAAA,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,gBAAgB,CAAC;;AAC5B,aAAA,IAAI,GAAG,IAAI,GAAG,CAAC,OAAO,EAAE;AAC7B,YAAA,IAAI,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ,EAAE;gBACnC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC;;;;AAIxC;MAEY,qBAAqB,CAAA;IAChC,MAAM,CAAC,KAAa,EAAE,IAAmC,EAAA;AACvD,QAAA,MAAM,KAAK,GAAG,IAAI,EAAE,KAAK;QACzB,MAAM,cAAc,GAAG,CAAC,EAAE,KAAK,IAAI,SAAS,IAAI,KAAK,CAAC;AACtD,QAAA,MAAM,OAAO,GAAG,cAAc,IAAI,KAAK,CAAC,OAAO;AAE/C,QAAA,IAAI,CAAC,OAAO,IAAI,CAAC,cAAc,EAAE;YAC/B;;AAGF,QAAA,IAAI,KAAK,CAAC,gBAAgB,IAAI,KAAK,CAAC,gBAAgB,CAAC,MAAM,GAAG,CAAC,EAAE;AAC/D,YAAA,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,gBAAgB,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;;AAGtD,QAAA,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE;AAC/B,YAAA,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC;;aACxB;YACL,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;;;AAG1C;MAEY,gBAAgB,CAAA;AAC3B,IAAA,MAAM,CACJ,KAAa,EACb,IAAmC,EACnC,QAAkC,EAAA;AAElC,QAAA,MAAM,KAAK,GAAG,IAAI,EAAE,KAAK;QACzB,MAAM,cAAc,GAAG,CAAC,EAAE,KAAK,IAAI,SAAS,IAAI,KAAK,CAAC;AACtD,QAAA,MAAM,GAAG,GAAG,cAAc,IAAI,KAAK,CAAC,OAAO;QAC3C,IAAI,QAAQ,EAAE;AACZ,YAAA,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC;;AAEvB,QAAA,IAAI,GAAG,IAAI,GAAG,CAAC,gBAAgB,IAAI,GAAG,CAAC,gBAAgB,CAAC,MAAM,GAAG,CAAC,EAAE;AAClE,YAAA,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,gBAAgB,CAAC;;AAC5B,aAAA,IAAI,GAAG,IAAI,GAAG,CAAC,OAAO,EAAE;AAC7B,YAAA,IAAI,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ,EAAE;;;gBAGnC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC;;;;AAIxC;AAEY,MAAA,wBAAwB,GAAG,CACtC,UAGG,KAC2B;AAC9B,IAAA,MAAM,SAAS,GAAG,UAAU,IAAI,EAAE;AAElC,IAAA,MAAM,YAAY,GAAmB,CAAC,MAAM,KAAI;AAC9C,QAAA,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM;AAC9B,QAAA,MAAM,iBAAiB,GACrB,WAAW,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,CAGnC,GAAG,CAAC,CAAC;QACN,IAAI,CAAC,iBAAiB,EAAE;YACtB;;AAEF,QAAA,MAAM,EAAE,OAAO,EAAE,GAAG,iBAAiB;AACrC,QAAA,IAAI,OAAO,EAAE,iBAAiB,EAAE;AAC9B,YAAA,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,iBAAiB,CAAC;;AAE7C,KAAC;AAED,IAAA,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE;AACpC;;;;;;;;;;"}
|
|
1
|
+
{"version":3,"file":"events.cjs","sources":["../../src/events.ts"],"sourcesContent":["/* eslint-disable no-console */\n// src/events.ts\nimport type {\n BaseMessageFields,\n UsageMetadata,\n} from '@langchain/core/messages';\nimport type { MultiAgentGraph, StandardGraph } from '@/graphs';\nimport type { Logger } from 'winston';\nimport type * as t from '@/types';\nimport { handleToolCalls } from '@/tools/handlers';\nimport { Constants, Providers } from '@/common';\n\nexport class HandlerRegistry {\n private handlers: Map<string, t.EventHandler> = new Map();\n\n register(eventType: string, handler: t.EventHandler): void {\n this.handlers.set(eventType, handler);\n }\n\n getHandler(eventType: string): t.EventHandler | undefined {\n return this.handlers.get(eventType);\n }\n}\n\nexport class ModelEndHandler implements t.EventHandler {\n collectedUsage?: UsageMetadata[];\n constructor(collectedUsage?: UsageMetadata[]) {\n if (collectedUsage && !Array.isArray(collectedUsage)) {\n throw new Error('collectedUsage must be an array');\n }\n this.collectedUsage = collectedUsage;\n }\n\n async handle(\n event: string,\n data: t.ModelEndData,\n metadata?: Record<string, unknown>,\n graph?: StandardGraph | MultiAgentGraph\n ): Promise<void> {\n if (!graph || !metadata) {\n console.warn(`Graph or metadata not found in ${event} event`);\n return;\n }\n\n const usage = data?.output?.usage_metadata;\n if (usage != null && this.collectedUsage != null) {\n this.collectedUsage.push(usage);\n }\n\n if (metadata.ls_provider === 'FakeListChatModel') {\n return handleToolCalls(data?.output?.tool_calls, metadata, graph);\n }\n\n console.log(`====== ${event.toUpperCase()} ======`);\n console.dir(\n {\n usage,\n },\n { depth: null }\n );\n\n const agentContext = graph.getAgentContext(metadata);\n\n if (\n agentContext.provider !== Providers.GOOGLE &&\n agentContext.provider !== Providers.BEDROCK\n ) {\n return;\n }\n\n await handleToolCalls(data?.output?.tool_calls, metadata, graph);\n }\n}\n\nexport class ToolEndHandler implements t.EventHandler {\n private callback?: t.ToolEndCallback;\n private logger?: Logger;\n constructor(callback?: t.ToolEndCallback, logger?: Logger) {\n this.callback = callback;\n this.logger = logger;\n }\n\n /**\n * Handles on_tool_end events from the for-await stream consumer.\n *\n * This handler is now purely a consumer callback — tool completion\n * (ON_RUN_STEP_COMPLETED dispatch + session context storage) is handled\n * in graph context by ToolNode directly, eliminating the race between\n * the stream consumer and graph execution.\n */\n async handle(\n event: string,\n data: t.StreamEventData | undefined,\n metadata?: Record<string, unknown>,\n graph?: StandardGraph | MultiAgentGraph\n ): Promise<void> {\n try {\n if (!graph || !metadata) {\n if (this.logger) {\n this.logger.warn(`Graph or metadata not found in ${event} event`);\n } else {\n console.warn(`Graph or metadata not found in ${event} event`);\n }\n return;\n }\n\n const toolEndData = data as t.ToolEndData | undefined;\n if (!toolEndData?.output) {\n if (this.logger) {\n this.logger.warn('No output found in tool_end event');\n } else {\n console.warn('No output found in tool_end event');\n }\n return;\n }\n\n if (metadata[Constants.PROGRAMMATIC_TOOL_CALLING] === true) {\n return;\n }\n\n if (this.callback) {\n await this.callback(toolEndData, metadata);\n }\n } catch (error) {\n if (this.logger) {\n this.logger.error('Error handling tool_end event:', error);\n } else {\n console.error('Error handling tool_end event:', error);\n }\n }\n }\n}\n\nexport class TestLLMStreamHandler implements t.EventHandler {\n handle(event: string, data: t.StreamEventData | undefined): void {\n const chunk = data?.chunk;\n const isMessageChunk = !!(chunk && 'message' in chunk);\n const msg = isMessageChunk ? chunk.message : undefined;\n if (msg && msg.tool_call_chunks && msg.tool_call_chunks.length > 0) {\n console.log(msg.tool_call_chunks);\n } else if (msg && msg.content) {\n if (typeof msg.content === 'string') {\n process.stdout.write(msg.content);\n }\n }\n }\n}\n\nexport class TestChatStreamHandler implements t.EventHandler {\n handle(event: string, data: t.StreamEventData | undefined): void {\n const chunk = data?.chunk;\n const isContentChunk = !!(chunk && 'content' in chunk);\n const content = isContentChunk && chunk.content;\n\n if (!content || !isContentChunk) {\n return;\n }\n\n if (chunk.tool_call_chunks && chunk.tool_call_chunks.length > 0) {\n console.dir(chunk.tool_call_chunks, { depth: null });\n }\n\n if (typeof content === 'string') {\n process.stdout.write(content);\n } else {\n console.dir(content, { depth: null });\n }\n }\n}\n\nexport class LLMStreamHandler implements t.EventHandler {\n handle(\n event: string,\n data: t.StreamEventData | undefined,\n metadata?: Record<string, unknown>\n ): void {\n const chunk = data?.chunk;\n const isMessageChunk = !!(chunk && 'message' in chunk);\n const msg = isMessageChunk && chunk.message;\n if (metadata) {\n console.log(metadata);\n }\n if (msg && msg.tool_call_chunks && msg.tool_call_chunks.length > 0) {\n console.log(msg.tool_call_chunks);\n } else if (msg && msg.content) {\n if (typeof msg.content === 'string') {\n // const text_delta = msg.content;\n // dispatchCustomEvent(GraphEvents.CHAT_MODEL_STREAM, { chunk }, config);\n process.stdout.write(msg.content);\n }\n }\n }\n}\n\nexport const createMetadataAggregator = (\n _collected?: Record<\n string,\n NonNullable<BaseMessageFields['response_metadata']>\n >[]\n): t.MetadataAggregatorResult => {\n const collected = _collected || [];\n\n const handleLLMEnd: t.HandleLLMEnd = (output) => {\n const { generations } = output;\n const lastMessageOutput = (\n generations[generations.length - 1] as\n | (t.StreamGeneration | undefined)[]\n | undefined\n )?.[0];\n if (!lastMessageOutput) {\n return;\n }\n const { message } = lastMessageOutput;\n if (message?.response_metadata) {\n collected.push(message.response_metadata);\n }\n };\n\n return { handleLLMEnd, collected };\n};\n"],"names":["handleToolCalls","Providers","Constants"],"mappings":"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"}
|
|
@@ -1,20 +1,23 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var nanoid = require('nanoid');
|
|
4
|
-
var stream = require('@langchain/core/utils/stream');
|
|
4
|
+
var stream$1 = require('@langchain/core/utils/stream');
|
|
5
5
|
var googleVertexai = require('@langchain/google-vertexai');
|
|
6
6
|
var langgraph = require('@langchain/langgraph');
|
|
7
7
|
var runnables = require('@langchain/core/runnables');
|
|
8
8
|
var messages = require('@langchain/core/messages');
|
|
9
|
-
var _enum = require('../common/enum.cjs');
|
|
10
9
|
var core = require('../messages/core.cjs');
|
|
10
|
+
var ids = require('../messages/ids.cjs');
|
|
11
11
|
var prune = require('../messages/prune.cjs');
|
|
12
12
|
var format = require('../messages/format.cjs');
|
|
13
13
|
var cache = require('../messages/cache.cjs');
|
|
14
14
|
var content = require('../messages/content.cjs');
|
|
15
15
|
var tools = require('../messages/tools.cjs');
|
|
16
|
+
var _enum = require('../common/enum.cjs');
|
|
16
17
|
var graph = require('../utils/graph.cjs');
|
|
17
18
|
var llm = require('../utils/llm.cjs');
|
|
19
|
+
var stream = require('../stream.cjs');
|
|
20
|
+
var handlers = require('../tools/handlers.cjs');
|
|
18
21
|
var run = require('../utils/run.cjs');
|
|
19
22
|
require('js-tiktoken/lite');
|
|
20
23
|
require('zod-to-json-schema');
|
|
@@ -85,7 +88,13 @@ class StandardGraph extends Graph {
|
|
|
85
88
|
this.contentIndexMap = graph.resetIfNotEmpty(this.contentIndexMap, new Map());
|
|
86
89
|
}
|
|
87
90
|
this.stepKeyIds = graph.resetIfNotEmpty(this.stepKeyIds, new Map());
|
|
88
|
-
|
|
91
|
+
/**
|
|
92
|
+
* Clear in-place instead of replacing with a new Map to preserve the
|
|
93
|
+
* shared reference held by ToolNode (passed at construction time).
|
|
94
|
+
* Using resetIfNotEmpty would create a new Map, leaving ToolNode with
|
|
95
|
+
* a stale reference on 2nd+ processStream calls.
|
|
96
|
+
*/
|
|
97
|
+
this.toolCallStepIds.clear();
|
|
89
98
|
this.messageIdsByStepKey = graph.resetIfNotEmpty(this.messageIdsByStepKey, new Map());
|
|
90
99
|
this.messageStepHasToolCalls = graph.resetIfNotEmpty(this.messageStepHasToolCalls, new Map());
|
|
91
100
|
this.prelimMessageIdsByStepKey = graph.resetIfNotEmpty(this.prelimMessageIdsByStepKey, new Map());
|
|
@@ -378,25 +387,50 @@ class StandardGraph extends Graph {
|
|
|
378
387
|
}
|
|
379
388
|
}
|
|
380
389
|
/** Execute model invocation with streaming support */
|
|
381
|
-
async attemptInvoke({ currentModel, finalMessages, provider, tools, }, config) {
|
|
390
|
+
async attemptInvoke({ currentModel, finalMessages, provider, tools: _tools, }, config) {
|
|
382
391
|
const model = this.overrideModel ?? currentModel;
|
|
383
392
|
if (!model) {
|
|
384
393
|
throw new Error('No model found');
|
|
385
394
|
}
|
|
386
|
-
if (
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
395
|
+
if (model.stream) {
|
|
396
|
+
/**
|
|
397
|
+
* Process all model output through a local ChatModelStreamHandler in the
|
|
398
|
+
* graph execution context. Each chunk is awaited before the next one is
|
|
399
|
+
* consumed, so by the time the stream is exhausted every run step
|
|
400
|
+
* (MESSAGE_CREATION, TOOL_CALLS) has been created and toolCallStepIds is
|
|
401
|
+
* fully populated — the graph will not transition to ToolNode until this
|
|
402
|
+
* is done.
|
|
403
|
+
*
|
|
404
|
+
* This replaces the previous pattern where ChatModelStreamHandler lived
|
|
405
|
+
* in the for-await stream consumer (handler registry). That consumer
|
|
406
|
+
* runs concurrently with graph execution, so the graph could advance to
|
|
407
|
+
* ToolNode before the consumer had processed all events. By handling
|
|
408
|
+
* chunks here, inside the agent node, the race is eliminated.
|
|
409
|
+
*
|
|
410
|
+
* The for-await consumer no longer needs a ChatModelStreamHandler; its
|
|
411
|
+
* on_chat_model_stream events are simply ignored (no handler registered).
|
|
412
|
+
* The dispatched custom events (ON_RUN_STEP, ON_MESSAGE_DELTA, etc.)
|
|
413
|
+
* still reach the content aggregator and SSE handlers through the custom
|
|
414
|
+
* event callback in Run.createCustomEventCallback.
|
|
415
|
+
*/
|
|
416
|
+
const metadata = config?.metadata;
|
|
417
|
+
const streamHandler = new stream.ChatModelStreamHandler();
|
|
418
|
+
const stream$2 = await model.stream(finalMessages, config);
|
|
391
419
|
let finalChunk;
|
|
392
|
-
for await (const chunk of stream$
|
|
393
|
-
await
|
|
394
|
-
finalChunk = finalChunk ? stream.concat(finalChunk, chunk) : chunk;
|
|
420
|
+
for await (const chunk of stream$2) {
|
|
421
|
+
await streamHandler.handle(_enum.GraphEvents.CHAT_MODEL_STREAM, { chunk }, metadata, this);
|
|
422
|
+
finalChunk = finalChunk ? stream$1.concat(finalChunk, chunk) : chunk;
|
|
423
|
+
}
|
|
424
|
+
if (providers.manualToolStreamProviders.has(provider)) {
|
|
425
|
+
finalChunk = core.modifyDeltaProperties(provider, finalChunk);
|
|
426
|
+
}
|
|
427
|
+
if ((finalChunk?.tool_calls?.length ?? 0) > 0) {
|
|
428
|
+
finalChunk.tool_calls = finalChunk.tool_calls?.filter((tool_call) => !!tool_call.name);
|
|
395
429
|
}
|
|
396
|
-
finalChunk = core.modifyDeltaProperties(provider, finalChunk);
|
|
397
430
|
return { messages: [finalChunk] };
|
|
398
431
|
}
|
|
399
432
|
else {
|
|
433
|
+
/** Fallback for models without stream support. */
|
|
400
434
|
const finalMessage = await model.invoke(finalMessages, config);
|
|
401
435
|
if ((finalMessage.tool_calls?.length ?? 0) > 0) {
|
|
402
436
|
finalMessage.tool_calls = finalMessage.tool_calls?.filter((tool_call) => !!tool_call.name);
|
|
@@ -599,6 +633,101 @@ class StandardGraph extends Graph {
|
|
|
599
633
|
if (!result) {
|
|
600
634
|
throw new Error('No result after model invocation');
|
|
601
635
|
}
|
|
636
|
+
/**
|
|
637
|
+
* Fallback: populate toolCallStepIds in the graph execution context.
|
|
638
|
+
*
|
|
639
|
+
* When model.stream() is available (the common case), attemptInvoke
|
|
640
|
+
* processes all chunks through a local ChatModelStreamHandler which
|
|
641
|
+
* creates run steps and populates toolCallStepIds before returning.
|
|
642
|
+
* The code below is a fallback for the rare case where model.stream
|
|
643
|
+
* is unavailable and model.invoke() was used instead.
|
|
644
|
+
*
|
|
645
|
+
* Text content is dispatched FIRST so that MESSAGE_CREATION is the
|
|
646
|
+
* current step when handleToolCalls runs. handleToolCalls then creates
|
|
647
|
+
* TOOL_CALLS on top of it. The dedup in getMessageId and
|
|
648
|
+
* toolCallStepIds.has makes this safe when attemptInvoke already
|
|
649
|
+
* handled everything — both paths become no-ops.
|
|
650
|
+
*/
|
|
651
|
+
const responseMessage = result.messages?.[0];
|
|
652
|
+
const toolCalls = responseMessage
|
|
653
|
+
?.tool_calls;
|
|
654
|
+
const hasToolCalls = Array.isArray(toolCalls) && toolCalls.length > 0;
|
|
655
|
+
if (hasToolCalls) {
|
|
656
|
+
const metadata = config.metadata;
|
|
657
|
+
const stepKey = this.getStepKey(metadata);
|
|
658
|
+
const content = responseMessage?.content;
|
|
659
|
+
const hasTextContent = content != null &&
|
|
660
|
+
(typeof content === 'string'
|
|
661
|
+
? content !== ''
|
|
662
|
+
: Array.isArray(content) && content.length > 0);
|
|
663
|
+
/**
|
|
664
|
+
* Dispatch text content BEFORE creating TOOL_CALLS steps.
|
|
665
|
+
* getMessageId returns a new ID only on the first call for a step key;
|
|
666
|
+
* if the for-await consumer already claimed it, this is a no-op.
|
|
667
|
+
*/
|
|
668
|
+
if (hasTextContent) {
|
|
669
|
+
const messageId = ids.getMessageId(stepKey, this) ?? '';
|
|
670
|
+
if (messageId) {
|
|
671
|
+
await this.dispatchRunStep(stepKey, {
|
|
672
|
+
type: _enum.StepTypes.MESSAGE_CREATION,
|
|
673
|
+
message_creation: { message_id: messageId },
|
|
674
|
+
}, metadata);
|
|
675
|
+
const stepId = this.getStepIdByKey(stepKey);
|
|
676
|
+
if (typeof content === 'string') {
|
|
677
|
+
await this.dispatchMessageDelta(stepId, {
|
|
678
|
+
content: [{ type: _enum.ContentTypes.TEXT, text: content }],
|
|
679
|
+
});
|
|
680
|
+
}
|
|
681
|
+
else if (Array.isArray(content) &&
|
|
682
|
+
content.every((c) => typeof c === 'object' &&
|
|
683
|
+
'type' in c &&
|
|
684
|
+
typeof c.type === 'string' &&
|
|
685
|
+
c.type.startsWith('text'))) {
|
|
686
|
+
await this.dispatchMessageDelta(stepId, {
|
|
687
|
+
content: content,
|
|
688
|
+
});
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
await handlers.handleToolCalls(toolCalls, metadata, this);
|
|
693
|
+
}
|
|
694
|
+
/**
|
|
695
|
+
* When streaming is disabled, on_chat_model_stream events are never
|
|
696
|
+
* emitted so ChatModelStreamHandler never fires. Dispatch the text
|
|
697
|
+
* content as MESSAGE_CREATION + MESSAGE_DELTA here.
|
|
698
|
+
*/
|
|
699
|
+
const disableStreaming = agentContext.clientOptions
|
|
700
|
+
?.disableStreaming === true;
|
|
701
|
+
if (disableStreaming &&
|
|
702
|
+
!hasToolCalls &&
|
|
703
|
+
responseMessage != null &&
|
|
704
|
+
responseMessage.content != null) {
|
|
705
|
+
const metadata = config.metadata;
|
|
706
|
+
const stepKey = this.getStepKey(metadata);
|
|
707
|
+
const messageId = ids.getMessageId(stepKey, this) ?? '';
|
|
708
|
+
if (messageId) {
|
|
709
|
+
await this.dispatchRunStep(stepKey, {
|
|
710
|
+
type: _enum.StepTypes.MESSAGE_CREATION,
|
|
711
|
+
message_creation: { message_id: messageId },
|
|
712
|
+
}, metadata);
|
|
713
|
+
}
|
|
714
|
+
const stepId = this.getStepIdByKey(stepKey);
|
|
715
|
+
const content = responseMessage.content;
|
|
716
|
+
if (typeof content === 'string') {
|
|
717
|
+
await this.dispatchMessageDelta(stepId, {
|
|
718
|
+
content: [{ type: _enum.ContentTypes.TEXT, text: content }],
|
|
719
|
+
});
|
|
720
|
+
}
|
|
721
|
+
else if (Array.isArray(content) &&
|
|
722
|
+
content.every((c) => typeof c === 'object' &&
|
|
723
|
+
'type' in c &&
|
|
724
|
+
typeof c.type === 'string' &&
|
|
725
|
+
c.type.startsWith('text'))) {
|
|
726
|
+
await this.dispatchMessageDelta(stepId, {
|
|
727
|
+
content: content,
|
|
728
|
+
});
|
|
729
|
+
}
|
|
730
|
+
}
|
|
602
731
|
agentContext.currentUsage = this.getUsageMetadata(result.messages?.[0]);
|
|
603
732
|
this.cleanupSignalListener();
|
|
604
733
|
return result;
|
|
@@ -731,99 +860,6 @@ class StandardGraph extends Graph {
|
|
|
731
860
|
await events.safeDispatchCustomEvent(_enum.GraphEvents.ON_RUN_STEP, runStep, this.config);
|
|
732
861
|
return stepId;
|
|
733
862
|
}
|
|
734
|
-
async handleToolCallCompleted(data, metadata, omitOutput) {
|
|
735
|
-
if (!this.config) {
|
|
736
|
-
throw new Error('No config provided');
|
|
737
|
-
}
|
|
738
|
-
if (!data.output) {
|
|
739
|
-
return;
|
|
740
|
-
}
|
|
741
|
-
const { input, output: _output } = data;
|
|
742
|
-
if (_output?.lg_name === 'Command') {
|
|
743
|
-
return;
|
|
744
|
-
}
|
|
745
|
-
const output = _output;
|
|
746
|
-
const { tool_call_id } = output;
|
|
747
|
-
const stepId = this.toolCallStepIds.get(tool_call_id) ?? '';
|
|
748
|
-
if (!stepId) {
|
|
749
|
-
throw new Error(`No stepId found for tool_call_id ${tool_call_id}`);
|
|
750
|
-
}
|
|
751
|
-
const runStep = this.getRunStep(stepId);
|
|
752
|
-
if (!runStep) {
|
|
753
|
-
throw new Error(`No run step found for stepId ${stepId}`);
|
|
754
|
-
}
|
|
755
|
-
/**
|
|
756
|
-
* Extract and store code execution session context from artifacts.
|
|
757
|
-
* Each file is stamped with its source session_id to support multi-session file tracking.
|
|
758
|
-
* When the same filename appears in a later execution, the newer version replaces the old.
|
|
759
|
-
*/
|
|
760
|
-
const toolName = output.name;
|
|
761
|
-
if (toolName === _enum.Constants.EXECUTE_CODE ||
|
|
762
|
-
toolName === _enum.Constants.PROGRAMMATIC_TOOL_CALLING) {
|
|
763
|
-
const artifact = output.artifact;
|
|
764
|
-
if (artifact?.session_id != null && artifact.session_id !== '') {
|
|
765
|
-
const newFiles = artifact.files ?? [];
|
|
766
|
-
const existingSession = this.sessions.get(_enum.Constants.EXECUTE_CODE);
|
|
767
|
-
const existingFiles = existingSession?.files ?? [];
|
|
768
|
-
if (newFiles.length > 0) {
|
|
769
|
-
/**
|
|
770
|
-
* Stamp each new file with its source session_id.
|
|
771
|
-
* This enables files from different executions (parallel or sequential)
|
|
772
|
-
* to be tracked and passed to subsequent calls.
|
|
773
|
-
*/
|
|
774
|
-
const filesWithSession = newFiles.map((file) => ({
|
|
775
|
-
...file,
|
|
776
|
-
session_id: artifact.session_id,
|
|
777
|
-
}));
|
|
778
|
-
/**
|
|
779
|
-
* Merge files, preferring latest versions by name.
|
|
780
|
-
* If a file with the same name exists, replace it with the new version.
|
|
781
|
-
* This handles cases where files are edited/recreated in subsequent executions.
|
|
782
|
-
*/
|
|
783
|
-
const newFileNames = new Set(filesWithSession.map((f) => f.name));
|
|
784
|
-
const filteredExisting = existingFiles.filter((f) => !newFileNames.has(f.name));
|
|
785
|
-
this.sessions.set(_enum.Constants.EXECUTE_CODE, {
|
|
786
|
-
session_id: artifact.session_id,
|
|
787
|
-
files: [...filteredExisting, ...filesWithSession],
|
|
788
|
-
lastUpdated: Date.now(),
|
|
789
|
-
});
|
|
790
|
-
}
|
|
791
|
-
else {
|
|
792
|
-
/**
|
|
793
|
-
* Store session_id even without new files for session continuity.
|
|
794
|
-
* The CodeExecutor can fall back to the /files endpoint to discover
|
|
795
|
-
* session files not explicitly returned in the exec response.
|
|
796
|
-
*/
|
|
797
|
-
this.sessions.set(_enum.Constants.EXECUTE_CODE, {
|
|
798
|
-
session_id: artifact.session_id,
|
|
799
|
-
files: existingFiles,
|
|
800
|
-
lastUpdated: Date.now(),
|
|
801
|
-
});
|
|
802
|
-
}
|
|
803
|
-
}
|
|
804
|
-
}
|
|
805
|
-
const dispatchedOutput = typeof output.content === 'string'
|
|
806
|
-
? output.content
|
|
807
|
-
: JSON.stringify(output.content);
|
|
808
|
-
const args = typeof input === 'string' ? input : input.input;
|
|
809
|
-
const tool_call = {
|
|
810
|
-
args: typeof args === 'string' ? args : JSON.stringify(args),
|
|
811
|
-
name: output.name ?? '',
|
|
812
|
-
id: output.tool_call_id,
|
|
813
|
-
output: omitOutput === true ? '' : dispatchedOutput,
|
|
814
|
-
progress: 1,
|
|
815
|
-
};
|
|
816
|
-
await this.handlerRegistry
|
|
817
|
-
?.getHandler(_enum.GraphEvents.ON_RUN_STEP_COMPLETED)
|
|
818
|
-
?.handle(_enum.GraphEvents.ON_RUN_STEP_COMPLETED, {
|
|
819
|
-
result: {
|
|
820
|
-
id: stepId,
|
|
821
|
-
index: runStep.index,
|
|
822
|
-
type: 'tool_call',
|
|
823
|
-
tool_call,
|
|
824
|
-
},
|
|
825
|
-
}, metadata, this);
|
|
826
|
-
}
|
|
827
863
|
/**
|
|
828
864
|
* Static version of handleToolCallError to avoid creating strong references
|
|
829
865
|
* that prevent garbage collection
|