@adminforth/agent 1.0.0 → 1.1.0
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/agent/middleware/apiBasedTools.ts +37 -16
- package/agent/toolCallEvents.ts +46 -0
- package/agent/tools/apiTool.ts +4 -39
- package/build.log +1 -1
- package/dist/agent/middleware/apiBasedTools.js +30 -12
- package/dist/agent/toolCallEvents.js +39 -1
- package/dist/agent/tools/apiTool.js +4 -37
- package/package.json +1 -1
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { ToolMessage } from "@langchain/core/messages";
|
|
2
2
|
import { createMiddleware } from "langchain";
|
|
3
3
|
import { logger } from "adminforth";
|
|
4
|
-
import type
|
|
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,30 +71,46 @@ 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
|
-
|
|
76
|
-
return await handler(request);
|
|
77
|
-
}
|
|
90
|
+
let result;
|
|
78
91
|
|
|
79
|
-
|
|
92
|
+
if (request.tool) {
|
|
93
|
+
result = await handler(request);
|
|
94
|
+
} else {
|
|
95
|
+
const enabledApiToolNames = getEnabledApiToolNames(request.state.messages);
|
|
80
96
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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
|
-
|
|
89
|
-
|
|
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);
|
|
@@ -98,6 +118,7 @@ export function createApiBasedToolsMiddleware(
|
|
|
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(
|
package/agent/toolCallEvents.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import { randomUUID } from "crypto";
|
|
2
|
+
import YAML from "yaml";
|
|
3
|
+
import { serializeUnknownError } from "../apiBasedTools.js";
|
|
4
|
+
|
|
1
5
|
export type ToolCallEvent =
|
|
2
6
|
| {
|
|
3
7
|
toolCallId: string;
|
|
@@ -15,3 +19,45 @@ export type ToolCallEvent =
|
|
|
15
19
|
};
|
|
16
20
|
|
|
17
21
|
export type ToolCallEventSink = (event: ToolCallEvent) => void;
|
|
22
|
+
|
|
23
|
+
export function createToolCallTracker(params: {
|
|
24
|
+
emit: ToolCallEventSink;
|
|
25
|
+
toolCallId?: string;
|
|
26
|
+
toolName: string;
|
|
27
|
+
input?: Record<string, unknown>;
|
|
28
|
+
startedAt?: number;
|
|
29
|
+
}) {
|
|
30
|
+
const toolCallId = params.toolCallId ?? randomUUID();
|
|
31
|
+
const startedAt = params.startedAt ?? Date.now();
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
start() {
|
|
35
|
+
params.emit({
|
|
36
|
+
toolCallId,
|
|
37
|
+
toolName: params.toolName,
|
|
38
|
+
phase: "start",
|
|
39
|
+
input: YAML.stringify(params.input ?? {}),
|
|
40
|
+
});
|
|
41
|
+
},
|
|
42
|
+
finishSuccess(output: unknown) {
|
|
43
|
+
params.emit({
|
|
44
|
+
toolCallId,
|
|
45
|
+
toolName: params.toolName,
|
|
46
|
+
phase: "end",
|
|
47
|
+
durationMs: Date.now() - startedAt,
|
|
48
|
+
output: YAML.stringify(output).trimEnd(),
|
|
49
|
+
error: null,
|
|
50
|
+
});
|
|
51
|
+
},
|
|
52
|
+
finishError(error: unknown) {
|
|
53
|
+
params.emit({
|
|
54
|
+
toolCallId,
|
|
55
|
+
toolName: params.toolName,
|
|
56
|
+
phase: "end",
|
|
57
|
+
durationMs: Date.now() - startedAt,
|
|
58
|
+
output: null,
|
|
59
|
+
error: YAML.stringify(serializeUnknownError(error)).trimEnd(),
|
|
60
|
+
});
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}
|
package/agent/tools/apiTool.ts
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
1
|
import { tool } from "langchain";
|
|
2
|
-
import { randomUUID } from "crypto";
|
|
3
|
-
import YAML from "yaml";
|
|
4
2
|
import type { ApiBasedTool } from "../../apiBasedTools.js";
|
|
5
|
-
import { serializeUnknownError } from "../../apiBasedTools.js";
|
|
6
3
|
|
|
7
4
|
const emptyToolSchema = {
|
|
8
5
|
type: "object",
|
|
@@ -52,43 +49,11 @@ export function createApiTool(toolName: string, apiBasedTool: ApiBasedTool) {
|
|
|
52
49
|
return tool(
|
|
53
50
|
async (input, runtime) => {
|
|
54
51
|
const normalizedInput = (input ?? {}) as Record<string, unknown>;
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
toolName,
|
|
60
|
-
phase: "start",
|
|
61
|
-
input: YAML.stringify(normalizedInput),
|
|
52
|
+
return apiBasedTool.call({
|
|
53
|
+
adminUser: runtime.context.adminUser,
|
|
54
|
+
inputs: normalizedInput,
|
|
55
|
+
userTimeZone: runtime.context.userTimeZone,
|
|
62
56
|
});
|
|
63
|
-
|
|
64
|
-
try {
|
|
65
|
-
const output = await apiBasedTool.call({
|
|
66
|
-
adminUser: runtime.context.adminUser,
|
|
67
|
-
inputs: normalizedInput,
|
|
68
|
-
userTimeZone: runtime.context.userTimeZone,
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
runtime.context.emitToolCallEvent({
|
|
72
|
-
toolCallId,
|
|
73
|
-
toolName,
|
|
74
|
-
phase: "end",
|
|
75
|
-
durationMs: Date.now() - startedAt,
|
|
76
|
-
output,
|
|
77
|
-
error: null,
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
return output;
|
|
81
|
-
} catch (error) {
|
|
82
|
-
runtime.context.emitToolCallEvent({
|
|
83
|
-
toolCallId,
|
|
84
|
-
toolName,
|
|
85
|
-
phase: "end",
|
|
86
|
-
durationMs: Date.now() - startedAt,
|
|
87
|
-
output: null,
|
|
88
|
-
error: YAML.stringify(serializeUnknownError(error)),
|
|
89
|
-
});
|
|
90
|
-
throw error;
|
|
91
|
-
}
|
|
92
57
|
},
|
|
93
58
|
{
|
|
94
59
|
name: toolName,
|
package/build.log
CHANGED
|
@@ -26,5 +26,5 @@ custom/skills/fetch_data/SKILL.md
|
|
|
26
26
|
custom/skills/mutate_data/
|
|
27
27
|
custom/skills/mutate_data/SKILL.md
|
|
28
28
|
|
|
29
|
-
sent 136,
|
|
29
|
+
sent 136,273 bytes received 367 bytes 273,280.00 bytes/sec
|
|
30
30
|
total size is 134,767 speedup is 0.99
|
|
@@ -10,6 +10,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
10
10
|
import { ToolMessage } from "@langchain/core/messages";
|
|
11
11
|
import { createMiddleware } from "langchain";
|
|
12
12
|
import { logger } from "adminforth";
|
|
13
|
+
import { createToolCallTracker, } from "../toolCallEvents.js";
|
|
13
14
|
import { ALWAYS_AVAILABLE_API_TOOL_NAMES } from "../tools/index.js";
|
|
14
15
|
import { createApiTool } from "../tools/apiTool.js";
|
|
15
16
|
function getEnabledApiToolNames(messages) {
|
|
@@ -58,28 +59,45 @@ export function createApiBasedToolsMiddleware(apiBasedTools) {
|
|
|
58
59
|
},
|
|
59
60
|
wrapToolCall(request, handler) {
|
|
60
61
|
return __awaiter(this, void 0, void 0, function* () {
|
|
61
|
-
var _a, _b, _c;
|
|
62
|
+
var _a, _b, _c, _d;
|
|
62
63
|
const startedAt = Date.now();
|
|
63
64
|
const toolInput = JSON.stringify((_a = request.toolCall.args) !== null && _a !== void 0 ? _a : {});
|
|
65
|
+
const { emitToolCallEvent } = request.runtime.context;
|
|
66
|
+
const toolCallTracker = createToolCallTracker({
|
|
67
|
+
emit: emitToolCallEvent,
|
|
68
|
+
toolCallId: request.toolCall.id,
|
|
69
|
+
toolName: request.toolCall.name,
|
|
70
|
+
input: ((_b = request.toolCall.args) !== null && _b !== void 0 ? _b : {}),
|
|
71
|
+
startedAt,
|
|
72
|
+
});
|
|
73
|
+
toolCallTracker.start();
|
|
64
74
|
logger.info(`Invoking tool "${request.toolCall.name}" with input: ${toolInput}`);
|
|
65
75
|
try {
|
|
76
|
+
let result;
|
|
66
77
|
if (request.tool) {
|
|
67
|
-
|
|
78
|
+
result = yield handler(request);
|
|
68
79
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
80
|
+
else {
|
|
81
|
+
const enabledApiToolNames = getEnabledApiToolNames(request.state.messages);
|
|
82
|
+
if (enabledApiToolNames.has(request.toolCall.name)) {
|
|
83
|
+
result = yield handler(Object.assign(Object.assign({}, request), { tool: dynamicTools[request.toolCall.name] }));
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
result = new ToolMessage({
|
|
87
|
+
content: `Tool "${request.toolCall.name}" is not loaded. Call fetch_tool_schema first.`,
|
|
88
|
+
tool_call_id: (_c = request.toolCall.id) !== null && _c !== void 0 ? _c : "",
|
|
89
|
+
name: request.toolCall.name,
|
|
90
|
+
status: "error",
|
|
91
|
+
});
|
|
92
|
+
}
|
|
72
93
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
tool_call_id: (_b = request.toolCall.id) !== null && _b !== void 0 ? _b : "",
|
|
76
|
-
name: request.toolCall.name,
|
|
77
|
-
status: "error",
|
|
78
|
-
});
|
|
94
|
+
toolCallTracker.finishSuccess(result);
|
|
95
|
+
return result;
|
|
79
96
|
}
|
|
80
97
|
catch (error) {
|
|
81
|
-
const errorDetails = error instanceof Error ? (
|
|
98
|
+
const errorDetails = error instanceof Error ? (_d = error.stack) !== null && _d !== void 0 ? _d : error.message : String(error);
|
|
82
99
|
logger.error(`Tool "${request.toolCall.name}" failed after ${Date.now() - startedAt}ms with input: ${toolInput}\n${errorDetails}`);
|
|
100
|
+
toolCallTracker.finishError(error);
|
|
83
101
|
throw error;
|
|
84
102
|
}
|
|
85
103
|
finally {
|
|
@@ -1 +1,39 @@
|
|
|
1
|
-
|
|
1
|
+
import { randomUUID } from "crypto";
|
|
2
|
+
import YAML from "yaml";
|
|
3
|
+
import { serializeUnknownError } from "../apiBasedTools.js";
|
|
4
|
+
export function createToolCallTracker(params) {
|
|
5
|
+
var _a, _b;
|
|
6
|
+
const toolCallId = (_a = params.toolCallId) !== null && _a !== void 0 ? _a : randomUUID();
|
|
7
|
+
const startedAt = (_b = params.startedAt) !== null && _b !== void 0 ? _b : Date.now();
|
|
8
|
+
return {
|
|
9
|
+
start() {
|
|
10
|
+
var _a;
|
|
11
|
+
params.emit({
|
|
12
|
+
toolCallId,
|
|
13
|
+
toolName: params.toolName,
|
|
14
|
+
phase: "start",
|
|
15
|
+
input: YAML.stringify((_a = params.input) !== null && _a !== void 0 ? _a : {}),
|
|
16
|
+
});
|
|
17
|
+
},
|
|
18
|
+
finishSuccess(output) {
|
|
19
|
+
params.emit({
|
|
20
|
+
toolCallId,
|
|
21
|
+
toolName: params.toolName,
|
|
22
|
+
phase: "end",
|
|
23
|
+
durationMs: Date.now() - startedAt,
|
|
24
|
+
output: YAML.stringify(output).trimEnd(),
|
|
25
|
+
error: null,
|
|
26
|
+
});
|
|
27
|
+
},
|
|
28
|
+
finishError(error) {
|
|
29
|
+
params.emit({
|
|
30
|
+
toolCallId,
|
|
31
|
+
toolName: params.toolName,
|
|
32
|
+
phase: "end",
|
|
33
|
+
durationMs: Date.now() - startedAt,
|
|
34
|
+
output: null,
|
|
35
|
+
error: YAML.stringify(serializeUnknownError(error)).trimEnd(),
|
|
36
|
+
});
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
}
|
|
@@ -8,9 +8,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
10
|
import { tool } from "langchain";
|
|
11
|
-
import { randomUUID } from "crypto";
|
|
12
|
-
import YAML from "yaml";
|
|
13
|
-
import { serializeUnknownError } from "../../apiBasedTools.js";
|
|
14
11
|
const emptyToolSchema = {
|
|
15
12
|
type: "object",
|
|
16
13
|
properties: {},
|
|
@@ -50,41 +47,11 @@ export function createApiTool(toolName, apiBasedTool) {
|
|
|
50
47
|
var _a;
|
|
51
48
|
return tool((input, runtime) => __awaiter(this, void 0, void 0, function* () {
|
|
52
49
|
const normalizedInput = (input !== null && input !== void 0 ? input : {});
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
toolName,
|
|
58
|
-
phase: "start",
|
|
59
|
-
input: YAML.stringify(normalizedInput),
|
|
50
|
+
return apiBasedTool.call({
|
|
51
|
+
adminUser: runtime.context.adminUser,
|
|
52
|
+
inputs: normalizedInput,
|
|
53
|
+
userTimeZone: runtime.context.userTimeZone,
|
|
60
54
|
});
|
|
61
|
-
try {
|
|
62
|
-
const output = yield apiBasedTool.call({
|
|
63
|
-
adminUser: runtime.context.adminUser,
|
|
64
|
-
inputs: normalizedInput,
|
|
65
|
-
userTimeZone: runtime.context.userTimeZone,
|
|
66
|
-
});
|
|
67
|
-
runtime.context.emitToolCallEvent({
|
|
68
|
-
toolCallId,
|
|
69
|
-
toolName,
|
|
70
|
-
phase: "end",
|
|
71
|
-
durationMs: Date.now() - startedAt,
|
|
72
|
-
output,
|
|
73
|
-
error: null,
|
|
74
|
-
});
|
|
75
|
-
return output;
|
|
76
|
-
}
|
|
77
|
-
catch (error) {
|
|
78
|
-
runtime.context.emitToolCallEvent({
|
|
79
|
-
toolCallId,
|
|
80
|
-
toolName,
|
|
81
|
-
phase: "end",
|
|
82
|
-
durationMs: Date.now() - startedAt,
|
|
83
|
-
output: null,
|
|
84
|
-
error: YAML.stringify(serializeUnknownError(error)),
|
|
85
|
-
});
|
|
86
|
-
throw error;
|
|
87
|
-
}
|
|
88
55
|
}), {
|
|
89
56
|
name: toolName,
|
|
90
57
|
description: (_a = apiBasedTool.description) !== null && _a !== void 0 ? _a : `${toolName} tool`,
|