@zhixuan92/multi-model-agent-core 3.12.2 → 3.12.4
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/README.md +1 -1
- package/dist/provider.d.ts.map +1 -1
- package/dist/provider.js +10 -2
- package/dist/provider.js.map +1 -1
- package/dist/run-tasks/reviewed-lifecycle.d.ts.map +1 -1
- package/dist/run-tasks/reviewed-lifecycle.js +33 -13
- package/dist/run-tasks/reviewed-lifecycle.js.map +1 -1
- package/dist/runners/claude-runner.d.ts.map +1 -1
- package/dist/runners/claude-runner.js +56 -8
- package/dist/runners/claude-runner.js.map +1 -1
- package/dist/runners/openai-runner.d.ts +8 -1
- package/dist/runners/openai-runner.d.ts.map +1 -1
- package/dist/runners/openai-runner.js +102 -67
- package/dist/runners/openai-runner.js.map +1 -1
- package/dist/runners/openai-usage-interceptor.d.ts +25 -0
- package/dist/runners/openai-usage-interceptor.d.ts.map +1 -0
- package/dist/runners/openai-usage-interceptor.js +121 -0
- package/dist/runners/openai-usage-interceptor.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
// HTTP-level usage capture for openai-compatible providers, independent of
|
|
2
|
+
// @openai/agents' internal aggregation.
|
|
3
|
+
//
|
|
4
|
+
// Why this exists: the @openai/agents SDK's stream consumer
|
|
5
|
+
// (node_modules/@openai/agents-openai/dist/openaiChatCompletionsStreaming.js
|
|
6
|
+
// line ~38) overwrites its local `usage` variable on every SSE chunk:
|
|
7
|
+
//
|
|
8
|
+
// usage = chunk.usage || undefined;
|
|
9
|
+
//
|
|
10
|
+
// For OpenAI proper this is benign — only the final `[DONE]`-adjacent chunk
|
|
11
|
+
// carries usage, so the last write is the right one. For DeepSeek (and
|
|
12
|
+
// likely other openai-compatible providers) with multi-turn tool-use, later
|
|
13
|
+
// chunks can have `usage:undefined` AFTER an earlier chunk reported real
|
|
14
|
+
// numbers, wiping the captured usage. The SDK then ends with
|
|
15
|
+
// `state.usage.inputTokens=0`, costUSD=0, despite real tokens having
|
|
16
|
+
// flowed. mma 3.12.2 telemetry showed every DeepSeek-as-reviewer call
|
|
17
|
+
// logging 21+ turns and zero tokens, untraceable until the SDK source was
|
|
18
|
+
// inspected.
|
|
19
|
+
//
|
|
20
|
+
// Fix: wrap the OpenAI client's `chat.completions.create` so we see the
|
|
21
|
+
// raw HTTP response (or the raw SSE stream) BEFORE the SDK consumes it, and
|
|
22
|
+
// accumulate usage into an out-of-band counter the runner can read at
|
|
23
|
+
// result-time as a source-of-truth fallback. The wrapper does not modify
|
|
24
|
+
// SDK-visible behavior — it observes only.
|
|
25
|
+
function addUsage(acc, u) {
|
|
26
|
+
return {
|
|
27
|
+
promptTokens: acc.promptTokens + (u.prompt_tokens ?? 0),
|
|
28
|
+
completionTokens: acc.completionTokens + (u.completion_tokens ?? 0),
|
|
29
|
+
cachedReadTokens: acc.cachedReadTokens + (u.prompt_tokens_details?.cached_tokens ?? 0),
|
|
30
|
+
reasoningTokens: acc.reasoningTokens + (u.completion_tokens_details?.reasoning_tokens ?? 0),
|
|
31
|
+
responses: acc.responses + 1,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
/** Wrap an OpenAI client so usage from every chat.completions.create response
|
|
35
|
+
* is captured into the returned UsageAccumulator.
|
|
36
|
+
*
|
|
37
|
+
* Mutates `client.chat.completions.create` in place. The wrapped client is
|
|
38
|
+
* otherwise unchanged — same return shapes, same error behavior. The SDK
|
|
39
|
+
* sees the same response stream byte-for-byte (we tee, we don't transform).
|
|
40
|
+
*/
|
|
41
|
+
export function wrapClientForUsageCapture(client) {
|
|
42
|
+
let acc = {
|
|
43
|
+
promptTokens: 0,
|
|
44
|
+
completionTokens: 0,
|
|
45
|
+
cachedReadTokens: 0,
|
|
46
|
+
reasoningTokens: 0,
|
|
47
|
+
responses: 0,
|
|
48
|
+
};
|
|
49
|
+
let observedNonZero = false;
|
|
50
|
+
const observe = (u) => {
|
|
51
|
+
if (!u)
|
|
52
|
+
return;
|
|
53
|
+
const before = acc;
|
|
54
|
+
acc = addUsage(acc, u);
|
|
55
|
+
// hasObservedUsage flips true the first time a response carries real
|
|
56
|
+
// input or output tokens. Counters can stay at 0 for empty responses
|
|
57
|
+
// (auth failures, immediate refusals) — those don't count as evidence
|
|
58
|
+
// that the provider IS reporting usage.
|
|
59
|
+
if ((u.prompt_tokens ?? 0) > 0 || (u.completion_tokens ?? 0) > 0) {
|
|
60
|
+
observedNonZero = true;
|
|
61
|
+
}
|
|
62
|
+
void before;
|
|
63
|
+
};
|
|
64
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
65
|
+
const completions = client.chat.completions;
|
|
66
|
+
const originalCreate = completions.create.bind(completions);
|
|
67
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
68
|
+
completions.create = async function wrappedCreate(...args) {
|
|
69
|
+
const result = await originalCreate(...args);
|
|
70
|
+
// Non-streaming branch: result is a ChatCompletion object with usage.
|
|
71
|
+
// Detect via the presence of `choices` (eagerly resolved) and `usage`.
|
|
72
|
+
if (result && typeof result === 'object' && Array.isArray(result.choices)) {
|
|
73
|
+
observe(result.usage);
|
|
74
|
+
return result;
|
|
75
|
+
}
|
|
76
|
+
// Streaming branch: result is an AsyncIterable of chunks. We wrap the
|
|
77
|
+
// iterator so each yielded chunk's usage feeds the accumulator without
|
|
78
|
+
// changing what the SDK sees.
|
|
79
|
+
if (result && typeof result[Symbol.asyncIterator] === 'function') {
|
|
80
|
+
const inner = result;
|
|
81
|
+
// Build a thin wrapper that preserves the original async-iterable
|
|
82
|
+
// identity (so the SDK can still .controller-abort if it does), but
|
|
83
|
+
// observes usage on every chunk that has it. Note: we accumulate the
|
|
84
|
+
// FIRST non-null usage we see from each request (chunks-with-usage are
|
|
85
|
+
// typically only 1 per stream, sent on the [DONE] frame). If a
|
|
86
|
+
// provider sends multiple usage frames, we add each one — that matches
|
|
87
|
+
// the cumulative semantics.
|
|
88
|
+
const wrapped = {
|
|
89
|
+
async *[Symbol.asyncIterator]() {
|
|
90
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
91
|
+
let lastUsage;
|
|
92
|
+
for await (const chunk of inner) {
|
|
93
|
+
if (chunk && chunk.usage) {
|
|
94
|
+
lastUsage = chunk.usage;
|
|
95
|
+
}
|
|
96
|
+
yield chunk;
|
|
97
|
+
}
|
|
98
|
+
// Accumulate at end-of-stream so multiple intra-stream `usage`
|
|
99
|
+
// chunks (DeepSeek behavior) don't double-count. The last seen
|
|
100
|
+
// usage is the cumulative-for-this-request value the provider
|
|
101
|
+
// wanted us to see.
|
|
102
|
+
observe(lastUsage);
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
// Some SDK call sites poke at `.controller` on the stream object for
|
|
106
|
+
// abort handling. Forward it if present.
|
|
107
|
+
const ctrl = result.controller;
|
|
108
|
+
if (ctrl !== undefined)
|
|
109
|
+
wrapped.controller = ctrl;
|
|
110
|
+
return wrapped;
|
|
111
|
+
}
|
|
112
|
+
// Unknown shape (shouldn't happen for chat.completions.create) — pass
|
|
113
|
+
// through unmodified.
|
|
114
|
+
return result;
|
|
115
|
+
};
|
|
116
|
+
return {
|
|
117
|
+
snapshot: () => ({ ...acc }),
|
|
118
|
+
hasObservedUsage: () => observedNonZero,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=openai-usage-interceptor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openai-usage-interceptor.js","sourceRoot":"","sources":["../../src/runners/openai-usage-interceptor.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zhixuan92/multi-model-agent-core",
|
|
3
|
-
"version": "3.12.
|
|
3
|
+
"version": "3.12.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"description": "Core library for multi-model-agent: provider runners (Claude, Codex, OpenAI-compatible), routing logic, config schema, and tool/sandbox primitives.",
|