@sebastiantuyu/agest 0.3.3-next.6 → 0.3.3-next.7
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/adapters/tracing.js +71 -17
- package/dist/preview.js +1 -0
- package/dist/reporter.js +3 -0
- package/dist/reports.d.ts +1 -0
- package/dist/reports.js +3 -0
- package/dist/types.d.ts +2 -0
- package/dist/waterfall.js +2 -1
- package/package.json +1 -1
package/dist/adapters/tracing.js
CHANGED
|
@@ -48,6 +48,7 @@ export async function createTracingHandle(baselineMs) {
|
|
|
48
48
|
const endMs = now() - baselineMs;
|
|
49
49
|
const tokens = extractTokensFromLLMOutput(output);
|
|
50
50
|
const providerCost = extractProviderCost(output);
|
|
51
|
+
const cachedInputTokens = extractCachedTokens(output);
|
|
51
52
|
const name = open.name ?? extractModelNameFromOutput(output) ?? "model";
|
|
52
53
|
if (name && name !== "model")
|
|
53
54
|
lastModelName = name;
|
|
@@ -64,6 +65,7 @@ export async function createTracingHandle(baselineMs) {
|
|
|
64
65
|
endMs,
|
|
65
66
|
durationMs: Math.max(0, endMs - open.startMs),
|
|
66
67
|
tokens,
|
|
68
|
+
cachedInputTokens,
|
|
67
69
|
cost: stripCostIfEmpty(cost),
|
|
68
70
|
});
|
|
69
71
|
}
|
|
@@ -82,10 +84,10 @@ export async function createTracingHandle(baselineMs) {
|
|
|
82
84
|
error: err?.message ?? String(err),
|
|
83
85
|
});
|
|
84
86
|
}
|
|
85
|
-
handleToolStart(tool, _input, runId) {
|
|
87
|
+
handleToolStart(tool, _input, runId, _parentRunId, _tags, _metadata, runName) {
|
|
86
88
|
openTools.set(runId, {
|
|
87
89
|
startMs: now() - baselineMs,
|
|
88
|
-
name: extractToolName(tool) ?? "tool",
|
|
90
|
+
name: extractToolName(tool, runName) ?? "tool",
|
|
89
91
|
});
|
|
90
92
|
}
|
|
91
93
|
handleToolEnd(_output, runId) {
|
|
@@ -252,30 +254,82 @@ function extractTokensFromLLMOutput(output) {
|
|
|
252
254
|
return undefined;
|
|
253
255
|
return { input, output: out };
|
|
254
256
|
}
|
|
257
|
+
/** Collect the usage-bearing objects LangChain/OpenRouter may attach to an LLM result. */
|
|
258
|
+
function usageObjects(output) {
|
|
259
|
+
const msg = output?.generations?.[0]?.[0]?.message;
|
|
260
|
+
return [
|
|
261
|
+
output?.llmOutput?.usage,
|
|
262
|
+
output?.llmOutput?.tokenUsage,
|
|
263
|
+
output?.llmOutput?.estimatedTokenUsage,
|
|
264
|
+
output?.llmOutput,
|
|
265
|
+
msg?.usage_metadata,
|
|
266
|
+
msg?.response_metadata?.usage,
|
|
267
|
+
msg?.response_metadata?.tokenUsage,
|
|
268
|
+
msg?.response_metadata?.estimatedTokenUsage,
|
|
269
|
+
msg?.response_metadata,
|
|
270
|
+
msg?.additional_kwargs?.usage,
|
|
271
|
+
].filter((u) => u && typeof u === "object");
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* OpenRouter (with `usage: { include: true }`) reports real USD cost. LangChain
|
|
275
|
+
* surfaces it inconsistently across versions, so scan the known usage objects
|
|
276
|
+
* for a numeric `cost` / `total_cost`.
|
|
277
|
+
*/
|
|
255
278
|
function extractProviderCost(output) {
|
|
256
|
-
const
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
for (const c of candidates) {
|
|
279
|
+
for (const u of usageObjects(output)) {
|
|
280
|
+
const c = (typeof u.cost === "number" ? u.cost : undefined) ??
|
|
281
|
+
(typeof u.total_cost === "number" ? u.total_cost : undefined) ??
|
|
282
|
+
(typeof u.cost_usd === "number" ? u.cost_usd : undefined) ??
|
|
283
|
+
(typeof u.cost_details?.upstream_inference_cost === "number"
|
|
284
|
+
? u.cost_details.upstream_inference_cost
|
|
285
|
+
: undefined);
|
|
264
286
|
if (typeof c === "number" && Number.isFinite(c))
|
|
265
287
|
return c;
|
|
266
288
|
}
|
|
267
289
|
return undefined;
|
|
268
290
|
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
291
|
+
/**
|
|
292
|
+
* Cached (prompt-cache hit) input tokens, when the provider reports them.
|
|
293
|
+
* Charged at a fraction of the normal input rate, so surfacing them lets the
|
|
294
|
+
* report explain why provider cost is below the flat-table estimate.
|
|
295
|
+
*/
|
|
296
|
+
function extractCachedTokens(output) {
|
|
297
|
+
for (const u of usageObjects(output)) {
|
|
298
|
+
const cached = u.input_token_details?.cache_read ??
|
|
299
|
+
u.prompt_tokens_details?.cached_tokens ??
|
|
300
|
+
u.cache_read_input_tokens ??
|
|
301
|
+
u.cached_tokens;
|
|
302
|
+
if (typeof cached === "number" && cached > 0)
|
|
303
|
+
return cached;
|
|
276
304
|
}
|
|
277
305
|
return undefined;
|
|
278
306
|
}
|
|
307
|
+
const TOOL_CLASS_NAMES = new Set([
|
|
308
|
+
"DynamicStructuredTool",
|
|
309
|
+
"DynamicTool",
|
|
310
|
+
"StructuredTool",
|
|
311
|
+
"Tool",
|
|
312
|
+
]);
|
|
313
|
+
function extractToolName(tool, runName) {
|
|
314
|
+
// `runName` is the actual tool name LangChain assigns the run (e.g.
|
|
315
|
+
// "search_recipes"); prefer it over the serialized class name.
|
|
316
|
+
if (runName && !TOOL_CLASS_NAMES.has(runName))
|
|
317
|
+
return runName;
|
|
318
|
+
if (tool) {
|
|
319
|
+
if (typeof tool.name === "string" && !TOOL_CLASS_NAMES.has(tool.name))
|
|
320
|
+
return tool.name;
|
|
321
|
+
if (typeof tool.kwargs?.name === "string")
|
|
322
|
+
return tool.kwargs.name;
|
|
323
|
+
if (Array.isArray(tool.id) && tool.id.length > 0) {
|
|
324
|
+
const last = String(tool.id[tool.id.length - 1]);
|
|
325
|
+
if (!TOOL_CLASS_NAMES.has(last))
|
|
326
|
+
return last;
|
|
327
|
+
}
|
|
328
|
+
if (typeof tool.name === "string")
|
|
329
|
+
return tool.name;
|
|
330
|
+
}
|
|
331
|
+
return runName;
|
|
332
|
+
}
|
|
279
333
|
function stripCostIfEmpty(cost) {
|
|
280
334
|
if (cost.source === "unavailable" && cost.totalUsd == null)
|
|
281
335
|
return undefined;
|
package/dist/preview.js
CHANGED
|
@@ -222,6 +222,7 @@ function renderWaterfallHtml(report) {
|
|
|
222
222
|
`${e.kind}: ${e.name}`,
|
|
223
223
|
`start ${Math.round(e.startMs)}ms · ${Math.round(e.durationMs)}ms`,
|
|
224
224
|
e.tokens ? `${e.tokens.input}→${e.tokens.output} tok` : "",
|
|
225
|
+
e.cachedInputTokens ? `${e.cachedInputTokens} cached` : "",
|
|
225
226
|
e.costUsd != null ? fmtUsdHtml(e.costUsd) : "",
|
|
226
227
|
e.error ? `error: ${e.error}` : "",
|
|
227
228
|
]
|
package/dist/reporter.js
CHANGED
|
@@ -126,6 +126,9 @@ function renderTimelineEvent(e) {
|
|
|
126
126
|
if (e.tokens) {
|
|
127
127
|
out.push(` tokens: { input: ${e.tokens.input}, output: ${e.tokens.output} }`);
|
|
128
128
|
}
|
|
129
|
+
if (e.cachedInputTokens != null && e.cachedInputTokens > 0) {
|
|
130
|
+
out.push(` cached_input_tokens: ${e.cachedInputTokens}`);
|
|
131
|
+
}
|
|
129
132
|
if (e.cost?.totalUsd != null) {
|
|
130
133
|
out.push(` cost_usd: ${formatUsd(e.cost.totalUsd)}`);
|
|
131
134
|
out.push(` cost_source: ${e.cost.source}`);
|
package/dist/reports.d.ts
CHANGED
package/dist/reports.js
CHANGED
|
@@ -205,6 +205,9 @@ export function parseScenes(content) {
|
|
|
205
205
|
case "tokens":
|
|
206
206
|
event.tokens = parseTokens(value);
|
|
207
207
|
break;
|
|
208
|
+
case "cached_input_tokens":
|
|
209
|
+
event.cachedInputTokens = parseInt(value, 10);
|
|
210
|
+
break;
|
|
208
211
|
case "cost_usd":
|
|
209
212
|
event.costUsd = parseFloat(value);
|
|
210
213
|
break;
|
package/dist/types.d.ts
CHANGED
|
@@ -21,6 +21,8 @@ export interface TimelineEvent {
|
|
|
21
21
|
input: number;
|
|
22
22
|
output: number;
|
|
23
23
|
};
|
|
24
|
+
/** Prompt-cache-hit input tokens (subset of tokens.input), when reported by the provider */
|
|
25
|
+
cachedInputTokens?: number;
|
|
24
26
|
cost?: CostBreakdown;
|
|
25
27
|
/** Index of the run this event belongs to (only set when aggregating across multi-run scenes) */
|
|
26
28
|
runIndex?: number;
|
package/dist/waterfall.js
CHANGED
|
@@ -39,7 +39,8 @@ export function renderTerminalWaterfall(events, opts = {}) {
|
|
|
39
39
|
const nameLabel = truncate(e.name, nameWidth).padEnd(nameWidth);
|
|
40
40
|
const dur = `${Math.round(e.durationMs)}ms`.padStart(7);
|
|
41
41
|
const cost = e.cost?.totalUsd != null ? ` ${fmtUsd(e.cost.totalUsd)}` : "";
|
|
42
|
+
const cached = e.cachedInputTokens ? ` ${c.dim(`(${e.cachedInputTokens} cached)`)}` : "";
|
|
42
43
|
const err = e.error ? ` ${c.red("✗ " + truncate(e.error, 40))}` : "";
|
|
43
|
-
return `${indent}${c.dim(kindLabel)} ${nameLabel} ${bar} ${c.dim(dur)}${c.dim(cost)}${err}`;
|
|
44
|
+
return `${indent}${c.dim(kindLabel)} ${nameLabel} ${bar} ${c.dim(dur)}${c.dim(cost)}${cached}${err}`;
|
|
44
45
|
});
|
|
45
46
|
}
|