@migor/agentia 0.4.2 → 0.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/README.md +1 -1
- package/dist/AGENTS.md +145 -10
- package/dist/core/message.d.ts +148 -0
- package/dist/core/message.js +25 -0
- package/dist/core/message.js.map +1 -0
- package/dist/core/tool.d.ts +10 -9
- package/dist/core/trace.d.ts +21 -0
- package/dist/core/trace.js +9 -1
- package/dist/core/trace.js.map +1 -1
- package/dist/engine/errors.d.ts +12 -0
- package/dist/engine/errors.js +46 -13
- package/dist/engine/errors.js.map +1 -1
- package/dist/engine/loop.d.ts +17 -11
- package/dist/engine/loop.js +46 -433
- package/dist/engine/loop.js.map +1 -1
- package/dist/engine/replay.d.ts +18 -3
- package/dist/engine/replay.js +49 -8
- package/dist/engine/replay.js.map +1 -1
- package/dist/engine/retry.d.ts +5 -4
- package/dist/engine/retry.js.map +1 -1
- package/dist/engine/spec.d.ts +5 -5
- package/dist/engine/spec.js.map +1 -1
- package/dist/engine/trace-diff.d.ts +57 -0
- package/dist/engine/trace-diff.js +188 -0
- package/dist/engine/trace-diff.js.map +1 -0
- package/dist/engine/trimming.d.ts +7 -7
- package/dist/engine/trimming.js +6 -5
- package/dist/engine/trimming.js.map +1 -1
- package/dist/engine/turn.d.ts +157 -0
- package/dist/engine/turn.js +500 -0
- package/dist/engine/turn.js.map +1 -0
- package/dist/engine/types.d.ts +18 -8
- package/dist/engine/usage.d.ts +2 -2
- package/dist/engine/usage.js.map +1 -1
- package/dist/eval/defineEval.d.ts +1 -1
- package/dist/eval/defineEval.js +12 -0
- package/dist/eval/defineEval.js.map +1 -1
- package/dist/eval/harvest.d.ts +37 -0
- package/dist/eval/harvest.js +132 -0
- package/dist/eval/harvest.js.map +1 -0
- package/dist/eval/scripted.js.map +1 -1
- package/dist/index.d.ts +8 -2
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/integrations/anthropic.d.ts +41 -37
- package/dist/integrations/anthropic.js +408 -17
- package/dist/integrations/anthropic.js.map +1 -1
- package/dist/integrations/metrics.d.ts +13 -0
- package/dist/integrations/metrics.js +65 -0
- package/dist/integrations/metrics.js.map +1 -1
- package/dist/integrations/openai.d.ts +1 -1
- package/dist/integrations/openai.js +3 -3
- package/dist/integrations/openai.js.map +1 -1
- package/dist/integrations/otlp.d.ts +10 -0
- package/dist/integrations/otlp.js +59 -0
- package/dist/integrations/otlp.js.map +1 -1
- package/dist/runtime/run.js +2 -0
- package/dist/runtime/run.js.map +1 -1
- package/dist/runtime/session.d.ts +5 -5
- package/dist/runtime/session.js.map +1 -1
- package/dist/toolkit/module.d.ts +8 -2
- package/dist/toolkit/module.js +56 -8
- package/dist/toolkit/module.js.map +1 -1
- package/dist/toolkit/prompt.d.ts +20 -0
- package/dist/toolkit/prompt.js +18 -4
- package/dist/toolkit/prompt.js.map +1 -1
- package/dist/toolkit/skill.d.ts +3 -3
- package/dist/toolkit/subagent.d.ts +2 -2
- package/dist/toolkit/subagent.js +1 -1
- package/dist/transport/async.d.ts +2 -2
- package/package.json +2 -4
|
@@ -1,24 +1,415 @@
|
|
|
1
|
-
import Anthropic from '@anthropic-ai/sdk';
|
|
2
1
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* 白盒探查 SDK 内部不现实,但这一步的输入/输出可以钉死(见 `tests/integrations/anthropic.test.ts`)。
|
|
2
|
+
* Anthropic API 错误:非 2xx 响应(读响应体截断进 message)或 200 流内嵌 error 事件。
|
|
3
|
+
* 带数值 `status` —— `engine/errors.ts` 的鸭子类型分类靠它(429 → rate_limit、
|
|
4
|
+
* 5xx → server、其余 4xx → api)。
|
|
7
5
|
*/
|
|
8
|
-
export
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
export class AnthropicApiError extends Error {
|
|
7
|
+
status;
|
|
8
|
+
constructor(status, message) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.name = 'AnthropicApiError';
|
|
11
|
+
this.status = status;
|
|
12
|
+
}
|
|
11
13
|
}
|
|
12
|
-
/** 创建默认 ModelClient(Anthropic
|
|
14
|
+
/** 创建默认 ModelClient(Anthropic)。不传则完全走环境变量 + 官方端点。 */
|
|
13
15
|
export function createAnthropicClient(options = {}) {
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
const apiKey = options.apiKey ?? process.env.ANTHROPIC_API_KEY;
|
|
17
|
+
const baseURL = (options.baseURL ??
|
|
18
|
+
process.env.ANTHROPIC_BASE_URL ??
|
|
19
|
+
'https://api.anthropic.com').replace(/\/+$/, '');
|
|
20
|
+
const maxRetries = typeof options.maxRetries === 'number' ? options.maxRetries : 2;
|
|
21
|
+
const timeoutMs = typeof options.timeout === 'number' ? options.timeout : undefined;
|
|
22
|
+
return {
|
|
23
|
+
messages: {
|
|
24
|
+
stream(params) {
|
|
25
|
+
const textCallbacks = [];
|
|
26
|
+
// **立即发起请求**(eager,与 SDK 的 stream() 语义一致):调用方可以先等
|
|
27
|
+
// on('text') 分片、后 await finalMessage()(scripts/e2e-live.ts 步骤 ⑤
|
|
28
|
+
// 就是这个形态)—— 若惰性到 finalMessage() 才发请求,那种形态永远等不到
|
|
29
|
+
// 第一个分片。同步注册的回调不会漏分片:网络 I/O 不可能同步完成。
|
|
30
|
+
const work = (async () => {
|
|
31
|
+
// signal 是契约字段,不是 API 字段:摘出后 body 才序列化(残留会污染请求体)
|
|
32
|
+
const { signal: paramsSignal, ...bodyParams } = params;
|
|
33
|
+
const { signal, cleanup } = composeSignal(paramsSignal, timeoutMs);
|
|
34
|
+
try {
|
|
35
|
+
const res = await postWithRetries(`${baseURL}/v1/messages`, {
|
|
36
|
+
'content-type': 'application/json',
|
|
37
|
+
'anthropic-version': '2023-06-01',
|
|
38
|
+
...(apiKey ? { 'x-api-key': apiKey } : {}),
|
|
39
|
+
}, JSON.stringify({ ...bodyParams, stream: true }), signal, maxRetries);
|
|
40
|
+
// 内容协商(与 openai.ts 同款):个别兼容端点会忽略 stream:true 直接回整份 JSON
|
|
41
|
+
const ctype = res.headers.get('content-type') ?? '';
|
|
42
|
+
if (!ctype.includes('event-stream')) {
|
|
43
|
+
const message = (await res.json());
|
|
44
|
+
const full = textOf(message);
|
|
45
|
+
if (full)
|
|
46
|
+
for (const cb of textCallbacks)
|
|
47
|
+
cb(full);
|
|
48
|
+
return message;
|
|
49
|
+
}
|
|
50
|
+
if (!res.body) {
|
|
51
|
+
throw new Error('Anthropic 流式响应没有 body(端点声明了 event-stream 却没给流)');
|
|
52
|
+
}
|
|
53
|
+
return await readAnthropicStream(res.body, params.model, textCallbacks);
|
|
54
|
+
}
|
|
55
|
+
finally {
|
|
56
|
+
cleanup();
|
|
57
|
+
}
|
|
58
|
+
})();
|
|
59
|
+
// 调用方可能只收分片、从不 await finalMessage():挂个空 catch 防 unhandled rejection
|
|
60
|
+
// (finalMessage 返回的仍是原 promise,await 它的调用方照常拿到 rejection)
|
|
61
|
+
work.catch(() => { });
|
|
62
|
+
return {
|
|
63
|
+
on(event, cb) {
|
|
64
|
+
if (event === 'text')
|
|
65
|
+
textCallbacks.push(cb);
|
|
66
|
+
},
|
|
67
|
+
finalMessage: () => work,
|
|
68
|
+
};
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
// —— 请求与重试(SDK 缺省语义的手写等价)——
|
|
74
|
+
/**
|
|
75
|
+
* 可重试的 HTTP 状态:408 / 409 / 429 / 5xx —— 与 SDK 缺省一致。
|
|
76
|
+
* 注意这是 **client 内重试**这一层;重试耗尽(或本就不重试)后抛出的
|
|
77
|
+
* `AnthropicApiError` 由 `engine/errors.ts` 的鸭子分类另行归类(408/409 落
|
|
78
|
+
* api/不可重试、429 落 rate_limit、5xx 落 server)—— 那是「这次失败怎么记账、
|
|
79
|
+
* 引擎外层要不要再来一轮」的语义,两层各司其职,不互相替代。
|
|
80
|
+
*/
|
|
81
|
+
function isRetryableStatus(status) {
|
|
82
|
+
return status === 408 || status === 409 || status === 429 || status >= 500;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* 带重试的 POST。返回首个 2xx 响应;不可重试错误与重试耗尽都抛
|
|
86
|
+
* `AnthropicApiError`(HTTP 错误)或原始网络错误(fetch reject)。
|
|
87
|
+
* abort(外部 signal 或 timeout 合成信号)原样向上 —— 引擎靠 `name === 'AbortError'` 判。
|
|
88
|
+
*/
|
|
89
|
+
async function postWithRetries(url, headers, body, signal, maxRetries) {
|
|
90
|
+
let retryAfter = null;
|
|
91
|
+
for (let attempt = 0;; attempt++) {
|
|
92
|
+
if (attempt > 0) {
|
|
93
|
+
// 退避期间被中止:以 AbortError 收场(与引擎的 sleep 同语义)
|
|
94
|
+
await interruptibleSleep(backoffMs(attempt, retryAfter), signal);
|
|
95
|
+
retryAfter = null;
|
|
96
|
+
}
|
|
97
|
+
let res;
|
|
98
|
+
try {
|
|
99
|
+
res = await fetch(url, {
|
|
100
|
+
method: 'POST',
|
|
101
|
+
headers,
|
|
102
|
+
body,
|
|
103
|
+
...(signal ? { signal } : {}),
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
catch (e) {
|
|
107
|
+
// abort 原样向上;signal 已中止时 fetch 的 reject 必为 abort 语义
|
|
108
|
+
if (e?.name === 'AbortError' || signal?.aborted)
|
|
109
|
+
throw e;
|
|
110
|
+
// 其余 reject 是网络失败(undici 的 TypeError: fetch failed,cause 带 errno)
|
|
111
|
+
if (attempt >= maxRetries)
|
|
112
|
+
throw e;
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
if (res.ok)
|
|
116
|
+
return res;
|
|
117
|
+
const text = (await res.text().catch(() => '')).slice(0, 300);
|
|
118
|
+
if (isRetryableStatus(res.status) && attempt < maxRetries) {
|
|
119
|
+
// 429 的 retry-after 尤其要尊重(限流窗口是上游说了算)
|
|
120
|
+
retryAfter = res.headers.get('retry-after');
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
throw new AnthropicApiError(res.status, `Anthropic 请求失败(HTTP ${res.status}):${text}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* 退避毫秒:`retry-after`(秒数或 HTTP-date)优先;否则指数退避
|
|
128
|
+
* `min(500 × 2^(attempt-1), 8000)` ±25% 抖动(attempt 从 1 起 = 第一次重试)。
|
|
129
|
+
*/
|
|
130
|
+
function backoffMs(attempt, retryAfter) {
|
|
131
|
+
if (retryAfter) {
|
|
132
|
+
const secs = Number(retryAfter);
|
|
133
|
+
if (Number.isFinite(secs))
|
|
134
|
+
return Math.max(0, Math.round(secs * 1000));
|
|
135
|
+
const at = Date.parse(retryAfter);
|
|
136
|
+
if (Number.isFinite(at))
|
|
137
|
+
return Math.max(0, at - Date.now());
|
|
138
|
+
// 解析不了就回落指数退避
|
|
139
|
+
}
|
|
140
|
+
const base = Math.min(500 * 2 ** Math.max(0, attempt - 1), 8000);
|
|
141
|
+
return Math.round(base * (0.75 + Math.random() * 0.5));
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* 可被 signal 中断的 sleep。
|
|
145
|
+
* 与 `engine/retry.ts` 的 sleep 同语义但不复用它 —— 分层约束:integrations 只能依赖 core。
|
|
146
|
+
*/
|
|
147
|
+
function interruptibleSleep(ms, signal) {
|
|
148
|
+
if (ms <= 0)
|
|
149
|
+
return Promise.resolve();
|
|
150
|
+
return new Promise((resolve, reject) => {
|
|
151
|
+
const abortError = () => Object.assign(new Error('请求已被取消'), { name: 'AbortError' });
|
|
152
|
+
if (signal?.aborted) {
|
|
153
|
+
reject(abortError());
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
function onAbort() {
|
|
157
|
+
clearTimeout(timer);
|
|
158
|
+
reject(abortError());
|
|
159
|
+
}
|
|
160
|
+
const timer = setTimeout(() => {
|
|
161
|
+
signal?.removeEventListener('abort', onAbort);
|
|
162
|
+
resolve();
|
|
163
|
+
}, ms);
|
|
164
|
+
signal?.addEventListener('abort', onAbort, { once: true });
|
|
21
165
|
});
|
|
22
|
-
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* 合成 params.signal 与 timeout(任一触发即中止)。
|
|
169
|
+
* 不用 `AbortSignal.any`:它 Node 20+ 才有,本包 `engines` 承诺 >=18。
|
|
170
|
+
* cleanup 由调用方在请求结束后调用(清计时器与监听器,防泄漏)。
|
|
171
|
+
*/
|
|
172
|
+
function composeSignal(paramsSignal, timeoutMs) {
|
|
173
|
+
if (timeoutMs == null)
|
|
174
|
+
return { signal: paramsSignal, cleanup: () => { } };
|
|
175
|
+
const controller = new AbortController();
|
|
176
|
+
const timer = setTimeout(() => {
|
|
177
|
+
// 超时以 TimeoutError 收场:分类按连接错误(可重试),与 SDK 的
|
|
178
|
+
// APIConnectionTimeoutError → connection 同语义
|
|
179
|
+
controller.abort(new DOMException(`Anthropic 请求超过 ${timeoutMs}ms`, 'TimeoutError'));
|
|
180
|
+
}, timeoutMs);
|
|
181
|
+
timer.unref(); // 兜底计时器不得拽住进程(spec 2026-09-14 ④ 同条教训)
|
|
182
|
+
const onAbort = () => controller.abort(paramsSignal?.reason);
|
|
183
|
+
if (paramsSignal) {
|
|
184
|
+
if (paramsSignal.aborted)
|
|
185
|
+
controller.abort(paramsSignal.reason);
|
|
186
|
+
else
|
|
187
|
+
paramsSignal.addEventListener('abort', onAbort, { once: true });
|
|
188
|
+
}
|
|
189
|
+
return {
|
|
190
|
+
signal: controller.signal,
|
|
191
|
+
cleanup: () => {
|
|
192
|
+
clearTimeout(timer);
|
|
193
|
+
paramsSignal?.removeEventListener('abort', onAbort);
|
|
194
|
+
},
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* 消费 `text/event-stream` 并组装成 Message。事件处理:
|
|
199
|
+
*
|
|
200
|
+
* | 事件 | 动作 |
|
|
201
|
+
* |---|---|
|
|
202
|
+
* | message_start | 拿 id / model / 初始 usage |
|
|
203
|
+
* | content_block_start | 按 index 建块累积器:text / tool_use / thinking 收拼;redacted_thinking 与未知块型原样携带(透出时不丢) |
|
|
204
|
+
* | content_block_delta | text_delta → 拼文本并触发 on('text');input_json_delta → 拼 partialJson;thinking_delta → 拼 thinking;signature_delta → 累积进 thinking 块的 signature |
|
|
205
|
+
* | content_block_stop | 无动作(块按 index 累积,无需收尾) |
|
|
206
|
+
* | message_delta | stop_reason / stop_sequence + 累计 usage 合并(后者覆盖前者出现的字段) |
|
|
207
|
+
* | message_stop / ping | 结束 / 忽略 |
|
|
208
|
+
* | error | 抛 AnthropicApiError(类型映射 status:rate_limit→429、overloaded→529、其余→500) |
|
|
209
|
+
*
|
|
210
|
+
* thinking 块只做「收拼 + signature 累积 + redacted/未知块原样透传」——
|
|
211
|
+
* 框架**从不主动请求 extended thinking**,这些是端点自己开了之后的兜底不丢。
|
|
212
|
+
* 与非流式 `toAnthropicMessage`(openai.ts)的空 choices 守卫同款:
|
|
213
|
+
* 整条流走完都没见 message_start = 上游故障,**抛出**而不是组装成
|
|
214
|
+
* 「stop_reason=null、content=[]、usage 全 0」的假成功。
|
|
215
|
+
*/
|
|
216
|
+
async function readAnthropicStream(body, fallbackModel, textCallbacks) {
|
|
217
|
+
const blocks = [];
|
|
218
|
+
let started = false;
|
|
219
|
+
let id = '';
|
|
220
|
+
let model = '';
|
|
221
|
+
let stopReason = null;
|
|
222
|
+
let stopSequence = null;
|
|
223
|
+
let usage = {};
|
|
224
|
+
for await (const line of sseLines(body)) {
|
|
225
|
+
if (!line.startsWith('data:'))
|
|
226
|
+
continue; // 忽略 event: / 注释 / 空行
|
|
227
|
+
const payload = line.slice('data:'.length).trim();
|
|
228
|
+
if (!payload)
|
|
229
|
+
continue;
|
|
230
|
+
let event;
|
|
231
|
+
try {
|
|
232
|
+
event = JSON.parse(payload);
|
|
233
|
+
}
|
|
234
|
+
catch {
|
|
235
|
+
continue; // 半截/畸形分片不该毁掉整个流(与 openai.ts 同款)
|
|
236
|
+
}
|
|
237
|
+
switch (event.type) {
|
|
238
|
+
case 'message_start': {
|
|
239
|
+
started = true;
|
|
240
|
+
id = event.message?.id ?? '';
|
|
241
|
+
model = event.message?.model ?? '';
|
|
242
|
+
usage = { ...event.message?.usage };
|
|
243
|
+
break;
|
|
244
|
+
}
|
|
245
|
+
case 'content_block_start': {
|
|
246
|
+
const cb = event.content_block ?? {};
|
|
247
|
+
const type = cb.type ?? 'text';
|
|
248
|
+
const acc = {
|
|
249
|
+
type,
|
|
250
|
+
text: typeof cb.text === 'string' ? cb.text : '',
|
|
251
|
+
thinking: typeof cb.thinking === 'string' ? cb.thinking : '',
|
|
252
|
+
signature: typeof cb.signature === 'string' ? cb.signature : '',
|
|
253
|
+
id: typeof cb.id === 'string' ? cb.id : '',
|
|
254
|
+
name: typeof cb.name === 'string' ? cb.name : '',
|
|
255
|
+
partialJson: '',
|
|
256
|
+
};
|
|
257
|
+
// redacted_thinking(数据在 `data` 字段)与一切未知块型:原始块对象
|
|
258
|
+
// 原样携带 —— 不丢成空文本块,finalMessage 原样透出
|
|
259
|
+
if (type !== 'text' && type !== 'tool_use' && type !== 'thinking') {
|
|
260
|
+
acc.raw = { ...cb, type };
|
|
261
|
+
}
|
|
262
|
+
blocks[event.index ?? blocks.length] = acc;
|
|
263
|
+
break;
|
|
264
|
+
}
|
|
265
|
+
case 'content_block_delta': {
|
|
266
|
+
const b = blocks[event.index ?? -1];
|
|
267
|
+
const d = event.delta;
|
|
268
|
+
if (!b || !d)
|
|
269
|
+
break;
|
|
270
|
+
if (d.type === 'text_delta' && typeof d.text === 'string') {
|
|
271
|
+
b.text += d.text;
|
|
272
|
+
// 逐 token 下发 —— 「打字机」效果的全部来源
|
|
273
|
+
for (const cb of textCallbacks)
|
|
274
|
+
cb(d.text);
|
|
275
|
+
}
|
|
276
|
+
else if (d.type === 'input_json_delta' && typeof d.partial_json === 'string') {
|
|
277
|
+
b.partialJson += d.partial_json;
|
|
278
|
+
}
|
|
279
|
+
else if (d.type === 'thinking_delta' && typeof d.thinking === 'string') {
|
|
280
|
+
b.thinking += d.thinking;
|
|
281
|
+
}
|
|
282
|
+
else if (d.type === 'signature_delta' && typeof d.signature === 'string') {
|
|
283
|
+
b.signature += d.signature;
|
|
284
|
+
}
|
|
285
|
+
break;
|
|
286
|
+
}
|
|
287
|
+
case 'message_delta': {
|
|
288
|
+
const d = event.delta;
|
|
289
|
+
if (d?.stop_reason)
|
|
290
|
+
stopReason = d.stop_reason;
|
|
291
|
+
if (d?.stop_sequence !== undefined)
|
|
292
|
+
stopSequence = d.stop_sequence;
|
|
293
|
+
// message_delta 的 usage 是累计口径(output_tokens 为累计值,cache 计量在此回报)
|
|
294
|
+
if (event.usage)
|
|
295
|
+
usage = { ...usage, ...event.usage };
|
|
296
|
+
break;
|
|
297
|
+
}
|
|
298
|
+
case 'error': {
|
|
299
|
+
// 上游故障以错误事件下发(HTTP 仍是 200)—— 不抛出就会被组装成假成功,
|
|
300
|
+
// run 结论与真实相反(openai.ts 处理过同款形态)
|
|
301
|
+
const err = event.error ?? {};
|
|
302
|
+
throw new AnthropicApiError(statusOfStreamError(err.type), `Anthropic 流内错误(${err.type ?? 'unknown'}):${err.message ?? payload.slice(0, 300)}`);
|
|
303
|
+
}
|
|
304
|
+
default:
|
|
305
|
+
break; // content_block_stop / message_stop / ping 及未知事件:忽略
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
if (!started) {
|
|
309
|
+
throw new Error('Anthropic 流式响应为空(未见 message_start);响应无可用补全,按上游故障处理');
|
|
310
|
+
}
|
|
311
|
+
const content = [];
|
|
312
|
+
for (const b of blocks) {
|
|
313
|
+
if (!b)
|
|
314
|
+
continue; // index 跳号留的洞
|
|
315
|
+
if (b.type === 'tool_use') {
|
|
316
|
+
content.push({
|
|
317
|
+
type: 'tool_use',
|
|
318
|
+
id: b.id,
|
|
319
|
+
name: b.name,
|
|
320
|
+
input: parseToolInput(b.partialJson),
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
else if (b.type === 'thinking') {
|
|
324
|
+
content.push({
|
|
325
|
+
type: 'thinking',
|
|
326
|
+
thinking: b.thinking,
|
|
327
|
+
signature: b.signature,
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
else if (b.raw) {
|
|
331
|
+
// redacted_thinking / 未知块型:原样透出(UnknownContentBlock 兜底成员承载,
|
|
332
|
+
// 无需为它新增具体类型 —— `data` 等字段跟着原始对象走,消费方自行收窄读取)
|
|
333
|
+
content.push(b.raw);
|
|
334
|
+
}
|
|
335
|
+
else {
|
|
336
|
+
content.push({ type: 'text', text: b.text });
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
return {
|
|
340
|
+
id: id || 'msg-unknown',
|
|
341
|
+
type: 'message',
|
|
342
|
+
role: 'assistant',
|
|
343
|
+
model: model || fallbackModel,
|
|
344
|
+
content,
|
|
345
|
+
stop_reason: stopReason,
|
|
346
|
+
stop_sequence: stopSequence,
|
|
347
|
+
usage: {
|
|
348
|
+
input_tokens: usage.input_tokens ?? 0,
|
|
349
|
+
output_tokens: usage.output_tokens ?? 0,
|
|
350
|
+
cache_creation_input_tokens: usage.cache_creation_input_tokens ?? 0,
|
|
351
|
+
cache_read_input_tokens: usage.cache_read_input_tokens ?? 0,
|
|
352
|
+
},
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
/** 流内 error 事件的类型 → HTTP status(让引擎的错误分类与重试语义照常工作) */
|
|
356
|
+
function statusOfStreamError(type) {
|
|
357
|
+
switch (type) {
|
|
358
|
+
case 'rate_limit_error':
|
|
359
|
+
return 429;
|
|
360
|
+
case 'overloaded_error':
|
|
361
|
+
return 529;
|
|
362
|
+
default:
|
|
363
|
+
return 500;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
/** tool_use 的 input 是分片拼出的 JSON 字符串;非法时原样交给下游 schema 校验(同 openai.ts) */
|
|
367
|
+
function parseToolInput(raw) {
|
|
368
|
+
if (!raw)
|
|
369
|
+
return {};
|
|
370
|
+
try {
|
|
371
|
+
return JSON.parse(raw);
|
|
372
|
+
}
|
|
373
|
+
catch {
|
|
374
|
+
return raw;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
/** 逐行读 SSE(每条事件都是一行 `data:`,不需要处理多行 payload;与 openai.ts 同款) */
|
|
378
|
+
async function* sseLines(body) {
|
|
379
|
+
const reader = body.getReader();
|
|
380
|
+
const decoder = new TextDecoder();
|
|
381
|
+
let buf = '';
|
|
382
|
+
try {
|
|
383
|
+
for (;;) {
|
|
384
|
+
const { done, value } = await reader.read();
|
|
385
|
+
if (done)
|
|
386
|
+
break;
|
|
387
|
+
buf += decoder.decode(value, { stream: true });
|
|
388
|
+
let idx = buf.indexOf('\n');
|
|
389
|
+
while (idx >= 0) {
|
|
390
|
+
yield buf.slice(0, idx).replace(/\r$/, '');
|
|
391
|
+
buf = buf.slice(idx + 1);
|
|
392
|
+
idx = buf.indexOf('\n');
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
if (buf)
|
|
396
|
+
yield buf.replace(/\r$/, '');
|
|
397
|
+
}
|
|
398
|
+
finally {
|
|
399
|
+
// 提前 break(abort 等)时释放读锁,否则流不会被回收
|
|
400
|
+
try {
|
|
401
|
+
await reader.cancel();
|
|
402
|
+
}
|
|
403
|
+
catch {
|
|
404
|
+
/* 已结束/已取消 */
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
/** 取消息里的全部文本块(非流式回落路径一次性回调用) */
|
|
409
|
+
function textOf(message) {
|
|
410
|
+
return message.content
|
|
411
|
+
.filter((b) => b.type === 'text')
|
|
412
|
+
.map((b) => b.text)
|
|
413
|
+
.join('');
|
|
23
414
|
}
|
|
24
415
|
//# sourceMappingURL=anthropic.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"anthropic.js","sourceRoot":"","sources":["../../src/integrations/anthropic.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,mBAAmB,CAAC;AAuC1C;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,MAA+D;IAIzF,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,MAAM,CAAC;IACnC,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;AACrD,CAAC;AAED,sDAAsD;AACtD,MAAM,UAAU,qBAAqB,CAAC,OAAO,GAA2B,EAAE;IACxE,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,OAAqD,CAAC,CAAC;IACpF,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;IACjC,MAAM,YAAY,GAAG,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACpD,sDAAsD;IACtD,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,IAA6B,EAAE,cAAwC,EAAE,EAAE;QAC7F,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,aAAa,EAAE,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;QACtE,OAAO,YAAY,CAAC,SAAkB,EAAE,EAAE,GAAG,cAAc,EAAE,GAAG,aAAa,EAAE,CAAC,CAAC;IACnF,CAAC,CAA2B,CAAC;IAC7B,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
1
|
+
{"version":3,"file":"anthropic.js","sourceRoot":"","sources":["../../src/integrations/anthropic.ts"],"names":[],"mappings":"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"}
|
|
@@ -12,6 +12,8 @@ import type { TraceSink } from '../core/trace.js';
|
|
|
12
12
|
* `skill` / `subagent` 来自 `capability` span(tracer 已把子孙 llm.turn 的 usage 聚合上去);
|
|
13
13
|
* `@Prompt` 不建 span、无独立耗时,**不产出**能力指标(如实缺省,不硬凑);
|
|
14
14
|
* - **模型级**(E3):来自 `llm.turn` span(其 `name` 即模型 id)。
|
|
15
|
+
* - **评分级**(R7 质量闭环):来自 run 根 span 的 `score` 事件(`attachScore` 写入)——
|
|
16
|
+
* gauge 记**最近一次**值(分数不是累加量),counter 记条数;label 为 `name` × `source`。
|
|
15
17
|
*
|
|
16
18
|
* 时长同时给两种口径(histogram 与分位 gauge 必须用**不同指标名**,见 `render()` 注释):
|
|
17
19
|
* - **histogram**(`*_bucket` / `*_sum` / `*_count`,累积语义)—— 抓取端可跨实例任意聚合;
|
|
@@ -46,6 +48,15 @@ export interface ModelMetrics {
|
|
|
46
48
|
latencyP50: number;
|
|
47
49
|
latencyP95: number;
|
|
48
50
|
}
|
|
51
|
+
/** 评分维度指标(`snapshot().scores[key]`,key 为 `name@source`,source 缺省时裸 name) */
|
|
52
|
+
export interface ScoreMetrics {
|
|
53
|
+
/** 最近一次评分值(gauge 语义 —— 分数不是累加量) */
|
|
54
|
+
value: number;
|
|
55
|
+
/** 评分条数 */
|
|
56
|
+
count: number;
|
|
57
|
+
/** 评分值合计(平均 = sum / count) */
|
|
58
|
+
sum: number;
|
|
59
|
+
}
|
|
49
60
|
/** 进程内累计快照(`snapshot()` 返回) */
|
|
50
61
|
export interface MetricsSnapshot {
|
|
51
62
|
/** 投递过 trace 的 run 总数(= `export` 被调用次数) */
|
|
@@ -66,6 +77,8 @@ export interface MetricsSnapshot {
|
|
|
66
77
|
capabilities: Record<string, CapabilityMetrics>;
|
|
67
78
|
/** 模型维度 */
|
|
68
79
|
models: Record<string, ModelMetrics>;
|
|
80
|
+
/** 评分维度(key 为 `name@source`,source 缺省时裸 name;无评分时为空对象) */
|
|
81
|
+
scores: Record<string, ScoreMetrics>;
|
|
69
82
|
/** 因 `maxCapabilities` 上限被归入 `__other__` 的不同能力数(未开启上限时为 0) */
|
|
70
83
|
droppedCapabilities: number;
|
|
71
84
|
}
|
|
@@ -134,6 +134,8 @@ export function metricsSink(opts = {}) {
|
|
|
134
134
|
const runStat = new DurationStat(windowSize, buckets);
|
|
135
135
|
const capabilities = new Map();
|
|
136
136
|
const models = new Map();
|
|
137
|
+
/** 评分累加器:key = `${name}\t${source}`(source 缺省 '';\t 不会出现在正常评分名里,做天然分隔符) */
|
|
138
|
+
const scores = new Map();
|
|
137
139
|
/** 已分配独立标签的能力键(超 maxCapabilities 后新键归 __other__) */
|
|
138
140
|
const assignedCapabilities = new Set();
|
|
139
141
|
/** 被归入 __other__ 的不同能力键 */
|
|
@@ -175,6 +177,29 @@ export function metricsSink(opts = {}) {
|
|
|
175
177
|
const d = runDurationMs(trace);
|
|
176
178
|
if (d !== undefined)
|
|
177
179
|
runStat.add(d);
|
|
180
|
+
// —— 评分(R7):只认根 span 的 score 事件(attachScore 的写入位置);
|
|
181
|
+
// body 宽容读取 —— name 不是 string / value 不是有限 number 就跳过,观测不击穿业务
|
|
182
|
+
const rootSpan = trace.spans.find((s) => s.spanId === trace.rootSpanId);
|
|
183
|
+
if (rootSpan) {
|
|
184
|
+
for (const e of rootSpan.events) {
|
|
185
|
+
if (e.name !== 'score')
|
|
186
|
+
continue;
|
|
187
|
+
const body = e.body;
|
|
188
|
+
if (!body || typeof body !== 'object')
|
|
189
|
+
continue;
|
|
190
|
+
if (typeof body.name !== 'string')
|
|
191
|
+
continue;
|
|
192
|
+
if (typeof body.value !== 'number' || !Number.isFinite(body.value))
|
|
193
|
+
continue;
|
|
194
|
+
const source = typeof body.source === 'string' ? body.source : '';
|
|
195
|
+
const key = `${body.name}\t${source}`;
|
|
196
|
+
const acc = scores.get(key) ?? { value: 0, count: 0, sum: 0 };
|
|
197
|
+
acc.value = body.value; // gauge 语义:覆盖为最近一次
|
|
198
|
+
acc.count++;
|
|
199
|
+
acc.sum += body.value;
|
|
200
|
+
scores.set(key, acc);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
178
203
|
for (const span of trace.spans) {
|
|
179
204
|
if (span.kind === 'capability') {
|
|
180
205
|
const label = labelFor(capabilityKindOf(span), span.name);
|
|
@@ -269,6 +294,13 @@ export function metricsSink(opts = {}) {
|
|
|
269
294
|
latencyP95: acc.stat.percentile(0.95),
|
|
270
295
|
};
|
|
271
296
|
}
|
|
297
|
+
const scoreOut = {};
|
|
298
|
+
for (const [key, acc] of scores) {
|
|
299
|
+
const tab = key.indexOf('\t');
|
|
300
|
+
const name = key.slice(0, tab);
|
|
301
|
+
const source = key.slice(tab + 1);
|
|
302
|
+
scoreOut[source === '' ? name : `${name}@${source}`] = { ...acc };
|
|
303
|
+
}
|
|
272
304
|
return {
|
|
273
305
|
runs,
|
|
274
306
|
failed,
|
|
@@ -278,6 +310,7 @@ export function metricsSink(opts = {}) {
|
|
|
278
310
|
costUsd,
|
|
279
311
|
capabilities: capabilityOut,
|
|
280
312
|
models: modelOut,
|
|
313
|
+
scores: scoreOut,
|
|
281
314
|
droppedCapabilities: dropped.size,
|
|
282
315
|
};
|
|
283
316
|
};
|
|
@@ -392,6 +425,20 @@ export function metricsSink(opts = {}) {
|
|
|
392
425
|
out.push(family(`${p}model_duration_ms`, 'histogram', '模型往返耗时(毫秒)', durations));
|
|
393
426
|
out.push(family(`${p}model_duration_ms_last`, 'gauge', '模型往返耗时分位(窗口内精确值)', durationQuantiles));
|
|
394
427
|
}
|
|
428
|
+
// —— 评分维度(R7):gauge 记最近一次值、counter 记条数,label 为 name × source ——
|
|
429
|
+
if (scores.size > 0) {
|
|
430
|
+
const gaugeSamples = [];
|
|
431
|
+
const counterSamples = [];
|
|
432
|
+
for (const key of [...scores.keys()].sort()) {
|
|
433
|
+
const acc = scores.get(key);
|
|
434
|
+
const tab = key.indexOf('\t');
|
|
435
|
+
const l = `{name="${escLabel(key.slice(0, tab))}",source="${escLabel(key.slice(tab + 1))}"}`;
|
|
436
|
+
gaugeSamples.push(`${p}score${l} ${acc.value}`);
|
|
437
|
+
counterSamples.push(`${p}score_total${l} ${acc.count}`);
|
|
438
|
+
}
|
|
439
|
+
out.push(family(`${p}score`, 'gauge', '最近一次评分(label 为评分维度与来源)', gaugeSamples));
|
|
440
|
+
out.push(family(`${p}score_total`, 'counter', '评分条数', counterSamples));
|
|
441
|
+
}
|
|
395
442
|
return out.join('');
|
|
396
443
|
};
|
|
397
444
|
// —— OTLP/JSON 导出(E5)——
|
|
@@ -426,6 +473,16 @@ export function metricsSink(opts = {}) {
|
|
|
426
473
|
],
|
|
427
474
|
},
|
|
428
475
|
});
|
|
476
|
+
// gauge 没有理由只收 int:评分值是浮点,统一走 asDouble
|
|
477
|
+
const gauge = (name, value, help, attrs) => ({
|
|
478
|
+
name,
|
|
479
|
+
description: help,
|
|
480
|
+
gauge: {
|
|
481
|
+
dataPoints: [
|
|
482
|
+
{ attributes: attrs, startTimeUnixNano: start, timeUnixNano: now, asDouble: value },
|
|
483
|
+
],
|
|
484
|
+
},
|
|
485
|
+
});
|
|
429
486
|
const hist = (name, stat, help, attrs) => ({
|
|
430
487
|
name,
|
|
431
488
|
description: help,
|
|
@@ -481,6 +538,13 @@ export function metricsSink(opts = {}) {
|
|
|
481
538
|
}
|
|
482
539
|
metrics.push(hist(`${p}model_duration_ms`, acc.stat, '模型往返耗时(毫秒)', attrs));
|
|
483
540
|
}
|
|
541
|
+
for (const key of [...scores.keys()].sort()) {
|
|
542
|
+
const acc = scores.get(key);
|
|
543
|
+
const tab = key.indexOf('\t');
|
|
544
|
+
const attrs = [strAttr('name', key.slice(0, tab)), strAttr('source', key.slice(tab + 1))];
|
|
545
|
+
metrics.push(gauge(`${p}score`, acc.value, '最近一次评分', attrs));
|
|
546
|
+
metrics.push(sum(`${p}score_total`, acc.count, '评分条数', attrs));
|
|
547
|
+
}
|
|
484
548
|
const resourceAttrs = [
|
|
485
549
|
strAttr('service.name', serviceName),
|
|
486
550
|
...Object.entries(opts.resourceAttributes ?? {}).map(([k, v]) => strAttr(k, v)),
|
|
@@ -554,6 +618,7 @@ export function metricsSink(opts = {}) {
|
|
|
554
618
|
runStat.reset();
|
|
555
619
|
capabilities.clear();
|
|
556
620
|
models.clear();
|
|
621
|
+
scores.clear();
|
|
557
622
|
assignedCapabilities.clear();
|
|
558
623
|
dropped.clear();
|
|
559
624
|
},
|