@adminforth/agent 1.5.0 → 1.6.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/sequenceDebug.ts +65 -7
- package/agent/systemPrompt.ts +9 -3
- package/build.log +2 -2
- package/custom/skills/mutate_data/SKILL.md +10 -5
- package/dist/agent/middleware/sequenceDebug.js +44 -6
- package/dist/agent/systemPrompt.js +9 -3
- package/dist/custom/skills/mutate_data/SKILL.md +10 -5
- package/dist/index.js +3 -0
- package/index.ts +4 -0
- package/package.json +1 -1
|
@@ -21,8 +21,11 @@ export type SequenceDebug = {
|
|
|
21
21
|
sequenceId: number;
|
|
22
22
|
startedAt: string;
|
|
23
23
|
prompt: string;
|
|
24
|
+
promptTokens: number;
|
|
24
25
|
reasoning: string;
|
|
26
|
+
reasoningTokens: number;
|
|
25
27
|
text: string;
|
|
28
|
+
textTokens: number;
|
|
26
29
|
cachedTokens: number;
|
|
27
30
|
responseId: string | null;
|
|
28
31
|
toolCalls: SequenceDebugToolCall[];
|
|
@@ -37,14 +40,18 @@ type PendingSequenceDebug = Omit<SequenceDebug, "toolCalls" | "endedAt" | "resul
|
|
|
37
40
|
};
|
|
38
41
|
|
|
39
42
|
type SequenceDebugModelCall = {
|
|
43
|
+
promptTokens: number;
|
|
40
44
|
reasoning: string;
|
|
45
|
+
reasoningTokens: number;
|
|
41
46
|
text: string;
|
|
47
|
+
textTokens: number;
|
|
42
48
|
cachedTokens: number;
|
|
43
49
|
responseId: string | null;
|
|
44
50
|
resultType: SequenceDebugResultType;
|
|
45
51
|
};
|
|
46
52
|
|
|
47
53
|
type OpenAiUsageMetadata = {
|
|
54
|
+
input_tokens?: number;
|
|
48
55
|
input_token_details?: {
|
|
49
56
|
cache_read?: number;
|
|
50
57
|
};
|
|
@@ -70,8 +77,11 @@ function createPendingSequenceDebug(sequenceId: number): PendingSequenceDebug {
|
|
|
70
77
|
sequenceId,
|
|
71
78
|
startedAt: new Date().toISOString(),
|
|
72
79
|
prompt: "",
|
|
80
|
+
promptTokens: 0,
|
|
73
81
|
reasoning: "",
|
|
82
|
+
reasoningTokens: 0,
|
|
74
83
|
text: "",
|
|
84
|
+
textTokens: 0,
|
|
75
85
|
cachedTokens: 0,
|
|
76
86
|
responseId: null,
|
|
77
87
|
toolCalls: [],
|
|
@@ -97,8 +107,11 @@ function finalizeSequenceDebug(sequence: PendingSequenceDebug): SequenceDebug {
|
|
|
97
107
|
sequenceId: sequence.sequenceId,
|
|
98
108
|
startedAt: sequence.startedAt,
|
|
99
109
|
prompt: sequence.prompt,
|
|
110
|
+
promptTokens: sequence.promptTokens,
|
|
100
111
|
reasoning: sequence.reasoning,
|
|
112
|
+
reasoningTokens: sequence.reasoningTokens,
|
|
101
113
|
text: sequence.text,
|
|
114
|
+
textTokens: sequence.textTokens,
|
|
102
115
|
cachedTokens: sequence.cachedTokens,
|
|
103
116
|
responseId: sequence.responseId,
|
|
104
117
|
toolCalls: sequence.toolCalls.map(({ completed: _completed, ...toolCall }) => toolCall),
|
|
@@ -176,6 +189,29 @@ function hasToolCallSignal(message: {
|
|
|
176
189
|
);
|
|
177
190
|
}
|
|
178
191
|
|
|
192
|
+
function hasTokenCounter(model: unknown): model is {
|
|
193
|
+
getNumTokens: (content: string) => Promise<number>;
|
|
194
|
+
} {
|
|
195
|
+
return (
|
|
196
|
+
typeof model === "object" &&
|
|
197
|
+
model !== null &&
|
|
198
|
+
"getNumTokens" in model &&
|
|
199
|
+
typeof model.getNumTokens === "function"
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
async function countTokens(model: unknown, content: string) {
|
|
204
|
+
if (!content) {
|
|
205
|
+
return 0;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (!hasTokenCounter(model)) {
|
|
209
|
+
return 0;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
return await model.getNumTokens(content);
|
|
213
|
+
}
|
|
214
|
+
|
|
179
215
|
function extractSequenceResponseDebug(message: AIMessage): SequenceDebugModelCall {
|
|
180
216
|
const blocks = getMessageBlocks(message);
|
|
181
217
|
const reasoning = blocks
|
|
@@ -188,8 +224,13 @@ function extractSequenceResponseDebug(message: AIMessage): SequenceDebugModelCal
|
|
|
188
224
|
.join("");
|
|
189
225
|
|
|
190
226
|
return {
|
|
227
|
+
promptTokens:
|
|
228
|
+
(message.usage_metadata as OpenAiUsageMetadata | undefined)?.input_tokens ??
|
|
229
|
+
0,
|
|
191
230
|
reasoning,
|
|
231
|
+
reasoningTokens: 0,
|
|
192
232
|
text: textFromBlocks || (typeof message.content === "string" ? message.content : ""),
|
|
233
|
+
textTokens: 0,
|
|
193
234
|
cachedTokens:
|
|
194
235
|
(message.usage_metadata as OpenAiUsageMetadata | undefined)
|
|
195
236
|
?.input_token_details?.cache_read ?? 0,
|
|
@@ -238,8 +279,11 @@ export function createSequenceDebugCollector(): SequenceDebugCollector {
|
|
|
238
279
|
},
|
|
239
280
|
handleModelCallComplete(params) {
|
|
240
281
|
const sequenceDebug = ensureSequenceDebug();
|
|
282
|
+
sequenceDebug.promptTokens = params.promptTokens;
|
|
241
283
|
sequenceDebug.reasoning = params.reasoning;
|
|
284
|
+
sequenceDebug.reasoningTokens = params.reasoningTokens;
|
|
242
285
|
sequenceDebug.text = params.text;
|
|
286
|
+
sequenceDebug.textTokens = params.textTokens;
|
|
243
287
|
sequenceDebug.cachedTokens = params.cachedTokens;
|
|
244
288
|
sequenceDebug.responseId = params.responseId;
|
|
245
289
|
sequenceDebug.resultType = params.resultType;
|
|
@@ -311,17 +355,31 @@ export function createSequenceDebugMiddleware(
|
|
|
311
355
|
return createMiddleware({
|
|
312
356
|
name: "SequenceDebugMiddleware",
|
|
313
357
|
async wrapModelCall(request, handler) {
|
|
358
|
+
const prompt = stringifyPromptForDebug({
|
|
359
|
+
systemMessage: request.systemMessage,
|
|
360
|
+
messages: request.messages,
|
|
361
|
+
tools: request.tools,
|
|
362
|
+
modelSettings: request.modelSettings,
|
|
363
|
+
});
|
|
364
|
+
|
|
314
365
|
sink.handleModelCallStart(
|
|
315
|
-
|
|
316
|
-
systemMessage: request.systemMessage,
|
|
317
|
-
messages: request.messages,
|
|
318
|
-
tools: request.tools,
|
|
319
|
-
modelSettings: request.modelSettings,
|
|
320
|
-
}),
|
|
366
|
+
prompt,
|
|
321
367
|
);
|
|
322
368
|
|
|
323
369
|
const response = await handler(request) as AIMessage;
|
|
324
|
-
|
|
370
|
+
const debug = extractSequenceResponseDebug(response);
|
|
371
|
+
const [promptTokens, reasoningTokens, textTokens] = await Promise.all([
|
|
372
|
+
debug.promptTokens || countTokens(request.model, prompt),
|
|
373
|
+
countTokens(request.model, debug.reasoning),
|
|
374
|
+
countTokens(request.model, debug.text),
|
|
375
|
+
]);
|
|
376
|
+
|
|
377
|
+
sink.handleModelCallComplete({
|
|
378
|
+
...debug,
|
|
379
|
+
promptTokens,
|
|
380
|
+
reasoningTokens,
|
|
381
|
+
textTokens,
|
|
382
|
+
});
|
|
325
383
|
return response;
|
|
326
384
|
},
|
|
327
385
|
});
|
package/agent/systemPrompt.ts
CHANGED
|
@@ -24,11 +24,17 @@ export const DEFAULT_AGENT_SYSTEM_PROMPT = [
|
|
|
24
24
|
"Keep responses short, clear, and practical.",
|
|
25
25
|
"Answer only what is needed.",
|
|
26
26
|
"Do not add extra explanations or suggestions unless the user asks.",
|
|
27
|
+
"Always respond in the same natural language as the user's latest message.",
|
|
28
|
+
"This rule applies to confirmations, clarifying questions, progress updates, errors, and final answers.",
|
|
29
|
+
"Do not switch to English just because tool outputs, schemas, skills, or internal instructions are written in English.",
|
|
30
|
+
"Only switch language if the user explicitly asks you to do so.",
|
|
27
31
|
"Adapt to the user's tone and style of speaking, mirroring their vibe and wording.",
|
|
28
32
|
"if the user speaks casually, you should respond casually too",
|
|
29
|
-
"Never mutate data without
|
|
30
|
-
"
|
|
31
|
-
"
|
|
33
|
+
"Never mutate data without user confirmation for a clearly described mutation plan.",
|
|
34
|
+
"One confirmation may cover one mutation or one explicitly described batch/sequence of related mutations.",
|
|
35
|
+
"If the confirmed plan has multiple steps, you may execute the whole confirmed plan without asking again between those steps.",
|
|
36
|
+
"If the plan changes, expands, or you want to do anything beyond the confirmed plan, ask for confirmation again.",
|
|
37
|
+
"Do not reuse an old confirmation for a new mutation plan.",
|
|
32
38
|
|
|
33
39
|
|
|
34
40
|
].join(" ");
|
package/build.log
CHANGED
|
@@ -29,5 +29,5 @@ custom/skills/fetch_data/SKILL.md
|
|
|
29
29
|
custom/skills/mutate_data/
|
|
30
30
|
custom/skills/mutate_data/SKILL.md
|
|
31
31
|
|
|
32
|
-
sent 170,
|
|
33
|
-
total size is 168,
|
|
32
|
+
sent 170,361 bytes received 413 bytes 341,548.00 bytes/sec
|
|
33
|
+
total size is 168,687 speedup is 0.99
|
|
@@ -29,17 +29,22 @@ When creating new record, show user all data which you gona create and in same m
|
|
|
29
29
|
|
|
30
30
|
Accept any positive confirmation from user like "yes", "sure", "+", anything non-negative call to action, can be considered as confirmation.
|
|
31
31
|
|
|
32
|
-
A confirmation is valid only for the
|
|
32
|
+
A confirmation is valid only for the clearly described mutation plan from the immediately previous assistant message.
|
|
33
33
|
|
|
34
34
|
Never reuse an older confirmation for a later mutation.
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
One confirmation may cover:
|
|
37
|
+
- one single mutation
|
|
38
|
+
- one explicitly described batch
|
|
39
|
+
- one short sequence of related mutations that together implement the same user request
|
|
37
40
|
|
|
38
|
-
If
|
|
41
|
+
If the confirmed plan contains several related mutation steps, execute that whole confirmed plan without asking again between those steps.
|
|
39
42
|
|
|
40
|
-
|
|
43
|
+
Ask for confirmation again if the plan changes in any way: different record, different fields, different values, different number of records, different action, or any extra mutation that was not listed in the confirmation message.
|
|
41
44
|
|
|
42
|
-
If you are creating or deleting multiple records in one batch, you may ask once
|
|
45
|
+
If you are creating or deleting multiple records in one batch, you may ask once for that exact batch, but list the whole batch explicitly in the confirmation message. Any extra record outside that described batch requires a new confirmation.
|
|
46
|
+
|
|
47
|
+
After the confirmed plan is finished, do not treat that confirmation as still active for later requests.
|
|
43
48
|
|
|
44
49
|
# Calling actions
|
|
45
50
|
|
|
@@ -25,8 +25,11 @@ function createPendingSequenceDebug(sequenceId) {
|
|
|
25
25
|
sequenceId,
|
|
26
26
|
startedAt: new Date().toISOString(),
|
|
27
27
|
prompt: "",
|
|
28
|
+
promptTokens: 0,
|
|
28
29
|
reasoning: "",
|
|
30
|
+
reasoningTokens: 0,
|
|
29
31
|
text: "",
|
|
32
|
+
textTokens: 0,
|
|
30
33
|
cachedTokens: 0,
|
|
31
34
|
responseId: null,
|
|
32
35
|
toolCalls: [],
|
|
@@ -47,8 +50,11 @@ function finalizeSequenceDebug(sequence) {
|
|
|
47
50
|
sequenceId: sequence.sequenceId,
|
|
48
51
|
startedAt: sequence.startedAt,
|
|
49
52
|
prompt: sequence.prompt,
|
|
53
|
+
promptTokens: sequence.promptTokens,
|
|
50
54
|
reasoning: sequence.reasoning,
|
|
55
|
+
reasoningTokens: sequence.reasoningTokens,
|
|
51
56
|
text: sequence.text,
|
|
57
|
+
textTokens: sequence.textTokens,
|
|
52
58
|
cachedTokens: sequence.cachedTokens,
|
|
53
59
|
responseId: sequence.responseId,
|
|
54
60
|
toolCalls: sequence.toolCalls.map((_a) => {
|
|
@@ -98,8 +104,25 @@ function hasToolCallSignal(message) {
|
|
|
98
104
|
(Array.isArray((_a = message.additional_kwargs) === null || _a === void 0 ? void 0 : _a.tool_calls) &&
|
|
99
105
|
message.additional_kwargs.tool_calls.length > 0));
|
|
100
106
|
}
|
|
107
|
+
function hasTokenCounter(model) {
|
|
108
|
+
return (typeof model === "object" &&
|
|
109
|
+
model !== null &&
|
|
110
|
+
"getNumTokens" in model &&
|
|
111
|
+
typeof model.getNumTokens === "function");
|
|
112
|
+
}
|
|
113
|
+
function countTokens(model, content) {
|
|
114
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
115
|
+
if (!content) {
|
|
116
|
+
return 0;
|
|
117
|
+
}
|
|
118
|
+
if (!hasTokenCounter(model)) {
|
|
119
|
+
return 0;
|
|
120
|
+
}
|
|
121
|
+
return yield model.getNumTokens(content);
|
|
122
|
+
});
|
|
123
|
+
}
|
|
101
124
|
function extractSequenceResponseDebug(message) {
|
|
102
|
-
var _a, _b, _c, _d, _e;
|
|
125
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
103
126
|
const blocks = getMessageBlocks(message);
|
|
104
127
|
const reasoning = blocks
|
|
105
128
|
.filter((block) => (block === null || block === void 0 ? void 0 : block.type) === "reasoning")
|
|
@@ -110,10 +133,13 @@ function extractSequenceResponseDebug(message) {
|
|
|
110
133
|
.map((block) => { var _a; return String((_a = block.text) !== null && _a !== void 0 ? _a : ""); })
|
|
111
134
|
.join("");
|
|
112
135
|
return {
|
|
136
|
+
promptTokens: (_b = (_a = message.usage_metadata) === null || _a === void 0 ? void 0 : _a.input_tokens) !== null && _b !== void 0 ? _b : 0,
|
|
113
137
|
reasoning,
|
|
138
|
+
reasoningTokens: 0,
|
|
114
139
|
text: textFromBlocks || (typeof message.content === "string" ? message.content : ""),
|
|
115
|
-
|
|
116
|
-
|
|
140
|
+
textTokens: 0,
|
|
141
|
+
cachedTokens: (_e = (_d = (_c = message.usage_metadata) === null || _c === void 0 ? void 0 : _c.input_token_details) === null || _d === void 0 ? void 0 : _d.cache_read) !== null && _e !== void 0 ? _e : 0,
|
|
142
|
+
responseId: (_g = (_f = message.response_metadata) === null || _f === void 0 ? void 0 : _f.id) !== null && _g !== void 0 ? _g : null,
|
|
117
143
|
resultType: hasToolCallSignal(message) ? "tool_calls" : "final_text",
|
|
118
144
|
};
|
|
119
145
|
}
|
|
@@ -147,8 +173,11 @@ export function createSequenceDebugCollector() {
|
|
|
147
173
|
},
|
|
148
174
|
handleModelCallComplete(params) {
|
|
149
175
|
const sequenceDebug = ensureSequenceDebug();
|
|
176
|
+
sequenceDebug.promptTokens = params.promptTokens;
|
|
150
177
|
sequenceDebug.reasoning = params.reasoning;
|
|
178
|
+
sequenceDebug.reasoningTokens = params.reasoningTokens;
|
|
151
179
|
sequenceDebug.text = params.text;
|
|
180
|
+
sequenceDebug.textTokens = params.textTokens;
|
|
152
181
|
sequenceDebug.cachedTokens = params.cachedTokens;
|
|
153
182
|
sequenceDebug.responseId = params.responseId;
|
|
154
183
|
sequenceDebug.resultType = params.resultType;
|
|
@@ -204,14 +233,23 @@ export function createSequenceDebugMiddleware(sink) {
|
|
|
204
233
|
name: "SequenceDebugMiddleware",
|
|
205
234
|
wrapModelCall(request, handler) {
|
|
206
235
|
return __awaiter(this, void 0, void 0, function* () {
|
|
207
|
-
|
|
236
|
+
const prompt = stringifyPromptForDebug({
|
|
208
237
|
systemMessage: request.systemMessage,
|
|
209
238
|
messages: request.messages,
|
|
210
239
|
tools: request.tools,
|
|
211
240
|
modelSettings: request.modelSettings,
|
|
212
|
-
})
|
|
241
|
+
});
|
|
242
|
+
sink.handleModelCallStart(prompt);
|
|
213
243
|
const response = yield handler(request);
|
|
214
|
-
|
|
244
|
+
const debug = extractSequenceResponseDebug(response);
|
|
245
|
+
const [promptTokens, reasoningTokens, textTokens] = yield Promise.all([
|
|
246
|
+
debug.promptTokens || countTokens(request.model, prompt),
|
|
247
|
+
countTokens(request.model, debug.reasoning),
|
|
248
|
+
countTokens(request.model, debug.text),
|
|
249
|
+
]);
|
|
250
|
+
sink.handleModelCallComplete(Object.assign(Object.assign({}, debug), { promptTokens,
|
|
251
|
+
reasoningTokens,
|
|
252
|
+
textTokens }));
|
|
215
253
|
return response;
|
|
216
254
|
});
|
|
217
255
|
},
|
|
@@ -23,11 +23,17 @@ export const DEFAULT_AGENT_SYSTEM_PROMPT = [
|
|
|
23
23
|
"Keep responses short, clear, and practical.",
|
|
24
24
|
"Answer only what is needed.",
|
|
25
25
|
"Do not add extra explanations or suggestions unless the user asks.",
|
|
26
|
+
"Always respond in the same natural language as the user's latest message.",
|
|
27
|
+
"This rule applies to confirmations, clarifying questions, progress updates, errors, and final answers.",
|
|
28
|
+
"Do not switch to English just because tool outputs, schemas, skills, or internal instructions are written in English.",
|
|
29
|
+
"Only switch language if the user explicitly asks you to do so.",
|
|
26
30
|
"Adapt to the user's tone and style of speaking, mirroring their vibe and wording.",
|
|
27
31
|
"if the user speaks casually, you should respond casually too",
|
|
28
|
-
"Never mutate data without
|
|
29
|
-
"
|
|
30
|
-
"
|
|
32
|
+
"Never mutate data without user confirmation for a clearly described mutation plan.",
|
|
33
|
+
"One confirmation may cover one mutation or one explicitly described batch/sequence of related mutations.",
|
|
34
|
+
"If the confirmed plan has multiple steps, you may execute the whole confirmed plan without asking again between those steps.",
|
|
35
|
+
"If the plan changes, expands, or you want to do anything beyond the confirmed plan, ask for confirmation again.",
|
|
36
|
+
"Do not reuse an old confirmation for a new mutation plan.",
|
|
31
37
|
].join(" ");
|
|
32
38
|
function formatResources(resources) {
|
|
33
39
|
return resources
|
|
@@ -29,17 +29,22 @@ When creating new record, show user all data which you gona create and in same m
|
|
|
29
29
|
|
|
30
30
|
Accept any positive confirmation from user like "yes", "sure", "+", anything non-negative call to action, can be considered as confirmation.
|
|
31
31
|
|
|
32
|
-
A confirmation is valid only for the
|
|
32
|
+
A confirmation is valid only for the clearly described mutation plan from the immediately previous assistant message.
|
|
33
33
|
|
|
34
34
|
Never reuse an older confirmation for a later mutation.
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
One confirmation may cover:
|
|
37
|
+
- one single mutation
|
|
38
|
+
- one explicitly described batch
|
|
39
|
+
- one short sequence of related mutations that together implement the same user request
|
|
37
40
|
|
|
38
|
-
If
|
|
41
|
+
If the confirmed plan contains several related mutation steps, execute that whole confirmed plan without asking again between those steps.
|
|
39
42
|
|
|
40
|
-
|
|
43
|
+
Ask for confirmation again if the plan changes in any way: different record, different fields, different values, different number of records, different action, or any extra mutation that was not listed in the confirmation message.
|
|
41
44
|
|
|
42
|
-
If you are creating or deleting multiple records in one batch, you may ask once
|
|
45
|
+
If you are creating or deleting multiple records in one batch, you may ask once for that exact batch, but list the whole batch explicitly in the confirmation message. Any extra record outside that described batch requires a new confirmation.
|
|
46
|
+
|
|
47
|
+
After the confirmed plan is finished, do not treat that confirmation as still active for later requests.
|
|
43
48
|
|
|
44
49
|
# Calling actions
|
|
45
50
|
|
package/dist/index.js
CHANGED
|
@@ -159,6 +159,9 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
159
159
|
res.write(`data: ${JSON.stringify(obj)}\n\n`);
|
|
160
160
|
};
|
|
161
161
|
const emitToolCallEvent = (event) => {
|
|
162
|
+
if (event.phase === "start") {
|
|
163
|
+
endActiveBlock();
|
|
164
|
+
}
|
|
162
165
|
sequenceDebugCollector.handleToolCallEvent(event);
|
|
163
166
|
send({
|
|
164
167
|
type: "data-tool-call",
|
package/index.ts
CHANGED
|
@@ -177,6 +177,10 @@ export default class AdminForthAgentPlugin extends AdminForthPlugin {
|
|
|
177
177
|
};
|
|
178
178
|
|
|
179
179
|
const emitToolCallEvent = (event: ToolCallEvent) => {
|
|
180
|
+
if (event.phase === "start") {
|
|
181
|
+
endActiveBlock();
|
|
182
|
+
}
|
|
183
|
+
|
|
180
184
|
sequenceDebugCollector.handleToolCallEvent(event);
|
|
181
185
|
|
|
182
186
|
send({
|