|
1 | 1 | import { ToolMessage } from "@langchain/core/messages"; |
2 | 2 | import { createMiddleware } from "langchain"; |
3 | 3 | import { logger } from "adminforth"; |
4 | | -import type { ApiBasedTool } from "../../apiBasedTools.js"; |
| 4 | +import { type ApiBasedTool } from "../../apiBasedTools.js"; |
| 5 | +import { |
| 6 | + createToolCallTracker, |
| 7 | + type ToolCallEventSink, |
| 8 | +} from "../toolCallEvents.js"; |
5 | 9 | import { ALWAYS_AVAILABLE_API_TOOL_NAMES } from "../tools/index.js"; |
6 | 10 | import { createApiTool } from "../tools/apiTool.js"; |
7 | 11 |
|
@@ -67,37 +71,54 @@ export function createApiBasedToolsMiddleware( |
67 | 71 | async wrapToolCall(request, handler) { |
68 | 72 | const startedAt = Date.now(); |
69 | 73 | const toolInput = JSON.stringify(request.toolCall.args ?? {}); |
| 74 | + const { emitToolCallEvent } = request.runtime.context as { |
| 75 | + emitToolCallEvent: ToolCallEventSink; |
| 76 | + }; |
| 77 | + const toolCallTracker = createToolCallTracker({ |
| 78 | + emit: emitToolCallEvent, |
| 79 | + toolCallId: request.toolCall.id, |
| 80 | + toolName: request.toolCall.name, |
| 81 | + input: (request.toolCall.args ?? {}) as Record<string, unknown>, |
| 82 | + startedAt, |
| 83 | + }); |
| 84 | + toolCallTracker.start(); |
70 | 85 | logger.info( |
71 | 86 | `Invoking tool "${request.toolCall.name}" with input: ${toolInput}`, |
72 | 87 | ); |
73 | 88 |
|
74 | 89 | try { |
75 | | - if (request.tool) { |
76 | | - return await handler(request); |
77 | | - } |
| 90 | + let result; |
78 | 91 |
|
79 | | - const enabledApiToolNames = getEnabledApiToolNames(request.state.messages); |
| 92 | + if (request.tool) { |
| 93 | + result = await handler(request); |
| 94 | + } else { |
| 95 | + const enabledApiToolNames = getEnabledApiToolNames(request.state.messages); |
80 | 96 |
|
81 | | - if (enabledApiToolNames.has(request.toolCall.name)) { |
82 | | - return await handler({ |
83 | | - ...request, |
84 | | - tool: dynamicTools[request.toolCall.name], |
85 | | - }); |
| 97 | + if (enabledApiToolNames.has(request.toolCall.name)) { |
| 98 | + result = await handler({ |
| 99 | + ...request, |
| 100 | + tool: dynamicTools[request.toolCall.name], |
| 101 | + }); |
| 102 | + } else { |
| 103 | + result = new ToolMessage({ |
| 104 | + content: `Tool "${request.toolCall.name}" is not loaded. Call fetch_tool_schema first.`, |
| 105 | + tool_call_id: request.toolCall.id ?? "", |
| 106 | + name: request.toolCall.name, |
| 107 | + status: "error", |
| 108 | + }); |
| 109 | + } |
86 | 110 | } |
87 | 111 |
|
88 | | - return new ToolMessage({ |
89 | | - content: `Tool "${request.toolCall.name}" is not loaded. Call fetch_tool_schema first.`, |
90 | | - tool_call_id: request.toolCall.id ?? "", |
91 | | - name: request.toolCall.name, |
92 | | - status: "error", |
93 | | - }); |
| 112 | + toolCallTracker.finishSuccess(result); |
| 113 | + return result; |
94 | 114 | } catch (error) { |
95 | 115 | const errorDetails = |
96 | 116 | error instanceof Error ? error.stack ?? error.message : String(error); |
97 | 117 |
|
98 | 118 | logger.error( |
99 | 119 | `Tool "${request.toolCall.name}" failed after ${Date.now() - startedAt}ms with input: ${toolInput}\n${errorDetails}`, |
100 | 120 | ); |
| 121 | + toolCallTracker.finishError(error); |
101 | 122 | throw error; |
102 | 123 | } finally { |
103 | 124 | logger.info( |
|
0 commit comments