@migor/agentia 0.7.1 → 0.8.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.
Files changed (98) hide show
  1. package/CHANGELOG.md +115 -1
  2. package/README.md +10 -6
  3. package/dist/AGENTS.md +27 -6
  4. package/dist/engine/loop-result.d.ts +72 -0
  5. package/dist/engine/loop-result.js +80 -0
  6. package/dist/engine/loop-result.js.map +1 -0
  7. package/dist/engine/loop.d.ts +3 -40
  8. package/dist/engine/loop.js +31 -125
  9. package/dist/engine/loop.js.map +1 -1
  10. package/dist/engine/resume-input.d.ts +26 -0
  11. package/dist/engine/resume-input.js +39 -0
  12. package/dist/engine/resume-input.js.map +1 -0
  13. package/dist/engine/retry.d.ts +8 -0
  14. package/dist/engine/retry.js +10 -0
  15. package/dist/engine/retry.js.map +1 -1
  16. package/dist/engine/run-config.d.ts +28 -0
  17. package/dist/engine/run-config.js +75 -0
  18. package/dist/engine/run-config.js.map +1 -0
  19. package/dist/engine/stop-reason.d.ts +27 -0
  20. package/dist/engine/stop-reason.js +76 -0
  21. package/dist/engine/stop-reason.js.map +1 -0
  22. package/dist/engine/text.d.ts +10 -0
  23. package/dist/engine/text.js +6 -0
  24. package/dist/engine/text.js.map +1 -0
  25. package/dist/engine/tool-context.d.ts +32 -0
  26. package/dist/engine/tool-context.js +18 -0
  27. package/dist/engine/tool-context.js.map +1 -0
  28. package/dist/engine/tool-events.d.ts +42 -0
  29. package/dist/engine/tool-events.js +64 -0
  30. package/dist/engine/tool-events.js.map +1 -0
  31. package/dist/engine/turn-request.d.ts +24 -0
  32. package/dist/engine/turn-request.js +12 -0
  33. package/dist/engine/turn-request.js.map +1 -0
  34. package/dist/engine/turn.d.ts +3 -25
  35. package/dist/engine/turn.js +43 -155
  36. package/dist/engine/turn.js.map +1 -1
  37. package/dist/index.d.ts +3 -2
  38. package/dist/index.js +3 -2
  39. package/dist/index.js.map +1 -1
  40. package/dist/integrations/anthropic.d.ts +5 -0
  41. package/dist/integrations/anthropic.js +29 -2
  42. package/dist/integrations/anthropic.js.map +1 -1
  43. package/dist/integrations/mcp-http.d.ts +43 -0
  44. package/dist/integrations/mcp-http.js +250 -0
  45. package/dist/integrations/mcp-http.js.map +1 -0
  46. package/dist/integrations/mcp-stdio.d.ts +46 -0
  47. package/dist/integrations/mcp-stdio.js +230 -0
  48. package/dist/integrations/mcp-stdio.js.map +1 -0
  49. package/dist/integrations/mcp.d.ts +37 -72
  50. package/dist/integrations/mcp.js +9 -468
  51. package/dist/integrations/mcp.js.map +1 -1
  52. package/dist/integrations/metrics-otlp.d.ts +42 -0
  53. package/dist/integrations/metrics-otlp.js +178 -0
  54. package/dist/integrations/metrics-otlp.js.map +1 -0
  55. package/dist/integrations/metrics-render.d.ts +2 -0
  56. package/dist/integrations/metrics-render.js +145 -0
  57. package/dist/integrations/metrics-render.js.map +1 -0
  58. package/dist/integrations/metrics-state.d.ts +191 -0
  59. package/dist/integrations/metrics-state.js +363 -0
  60. package/dist/integrations/metrics-state.js.map +1 -0
  61. package/dist/integrations/metrics.d.ts +3 -86
  62. package/dist/integrations/metrics.js +28 -648
  63. package/dist/integrations/metrics.js.map +1 -1
  64. package/dist/integrations/openai.js +4 -3
  65. package/dist/integrations/openai.js.map +1 -1
  66. package/dist/store/fsStore.d.ts +1 -1
  67. package/dist/store/fsStore.js +5 -2
  68. package/dist/store/fsStore.js.map +1 -1
  69. package/dist/toolkit/tool.js +1 -0
  70. package/dist/toolkit/tool.js.map +1 -1
  71. package/dist/transport/approval-policy.d.ts +30 -0
  72. package/dist/transport/approval-policy.js +37 -0
  73. package/dist/transport/approval-policy.js.map +1 -0
  74. package/dist/transport/async.d.ts +13 -5
  75. package/dist/transport/async.js +143 -150
  76. package/dist/transport/async.js.map +1 -1
  77. package/dist/transport/drain-gate.d.ts +28 -0
  78. package/dist/transport/drain-gate.js +62 -0
  79. package/dist/transport/drain-gate.js.map +1 -0
  80. package/dist/transport/http-route.d.ts +55 -0
  81. package/dist/transport/http-route.js +72 -0
  82. package/dist/transport/http-route.js.map +1 -0
  83. package/dist/transport/http-shapes.d.ts +57 -0
  84. package/dist/transport/http-shapes.js +56 -0
  85. package/dist/transport/http-shapes.js.map +1 -0
  86. package/dist/transport/http.d.ts +5 -21
  87. package/dist/transport/http.js +176 -232
  88. package/dist/transport/http.js.map +1 -1
  89. package/dist/transport/resume-policy.d.ts +40 -0
  90. package/dist/transport/resume-policy.js +25 -0
  91. package/dist/transport/resume-policy.js.map +1 -0
  92. package/dist/transport/slot-pool.d.ts +24 -0
  93. package/dist/transport/slot-pool.js +43 -0
  94. package/dist/transport/slot-pool.js.map +1 -0
  95. package/dist/transport/task-waiters.d.ts +25 -0
  96. package/dist/transport/task-waiters.js +63 -0
  97. package/dist/transport/task-waiters.js.map +1 -0
  98. package/package.json +5 -4
@@ -0,0 +1,178 @@
1
+ import { scoreLabels } from './metrics-state.js';
2
+ /**
3
+ * metricsSink 的 **OTLP/JSON 导出**(E5):从 `MetricsState` 组装 payload + POST 到
4
+ * `${endpoint}/v1/metrics`。只读状态、不改账。module 级 export,不进公共面(`src/index.ts`)。
5
+ */
6
+ /**
7
+ * OTLP metrics 导出失败(带数值 `status`)。
8
+ *
9
+ * 为什么不能抛裸 `Error`:`engine/errors.ts` 的分类是鸭子类型,只看数据属性。
10
+ * 裸 Error 会被判 `unknown` + `retryable:false` —— 宿主在 `onExportError` 里拿不到
11
+ * status,也就没法区分「collector 拒收(4xx,改配置)」与「collector 挂了(5xx,等它回来)」。
12
+ * 形状与 `otlp.ts` 的 `OtlpExportError` 一致(同层两处导出器不该有两种错误形状)。
13
+ *
14
+ * 注:本处抛错由 `onExportError` 接住并按「观测失败不得击穿业务」吞掉 —— 带 status 是为了
15
+ * **可判断**,不是为了让谁重试。
16
+ */
17
+ export class MetricsExportError extends Error {
18
+ status;
19
+ constructor(status, message) {
20
+ super(message);
21
+ this.name = 'MetricsExportError';
22
+ this.status = status;
23
+ }
24
+ }
25
+ // —— OTLP/JSON 导出(E5)——
26
+ // epoch 毫秒 ×1e6 ≈ 1.7e18 > 2^53,double 直接乘会丢精度 —— 必须先取整再转 BigInt
27
+ const nanos = (ms) => String(BigInt(Math.round(ms)) * 1000000n);
28
+ const strAttr = (key, v) => ({ key, value: { stringValue: v } });
29
+ export function buildOtlpPayload(state, opts) {
30
+ const p = opts.prefix;
31
+ const now = nanos(Date.now());
32
+ const start = nanos(opts.startedAtMs);
33
+ const s = state.snapshot();
34
+ const metricTable = new Map();
35
+ /** 查表取已建 Metric,没有则建;同名只保留首次的 description(冲突描述本身是 semantic error) */
36
+ const takeMetric = (name, help) => {
37
+ let m = metricTable.get(name);
38
+ if (!m) {
39
+ m = { name, description: help };
40
+ metricTable.set(name, m);
41
+ }
42
+ return m;
43
+ };
44
+ const sum = (name, value, help, attrs, monotonic = true) => {
45
+ const m = takeMetric(name, help);
46
+ m.sum ??= { aggregationTemporality: 2, isMonotonic: monotonic, dataPoints: [] };
47
+ m.sum.dataPoints.push({
48
+ attributes: attrs,
49
+ startTimeUnixNano: start,
50
+ timeUnixNano: now,
51
+ asInt: String(value),
52
+ });
53
+ };
54
+ // 浮点指标(成本)必须走 asDouble —— OTLP 的 asInt 是 string 编码 int64,
55
+ // 塞浮点(如 0.25)会让 collector 直接拒收整批数据
56
+ const sumDouble = (name, value, help, attrs) => {
57
+ const m = takeMetric(name, help);
58
+ m.sum ??= { aggregationTemporality: 2, isMonotonic: true, dataPoints: [] };
59
+ m.sum.dataPoints.push({
60
+ attributes: attrs,
61
+ startTimeUnixNano: start,
62
+ timeUnixNano: now,
63
+ asDouble: value,
64
+ });
65
+ };
66
+ // gauge 没有理由只收 int:评分值是浮点,统一走 asDouble
67
+ const gauge = (name, value, help, attrs) => {
68
+ const m = takeMetric(name, help);
69
+ m.gauge ??= { dataPoints: [] };
70
+ m.gauge.dataPoints.push({
71
+ attributes: attrs,
72
+ startTimeUnixNano: start,
73
+ timeUnixNano: now,
74
+ asDouble: value,
75
+ });
76
+ };
77
+ const hist = (name, stat, help, attrs) => {
78
+ const m = takeMetric(name, help);
79
+ m.histogram ??= { aggregationTemporality: 2, dataPoints: [] };
80
+ m.histogram.dataPoints.push({
81
+ attributes: attrs,
82
+ startTimeUnixNano: start,
83
+ timeUnixNano: now,
84
+ count: stat.count,
85
+ sum: stat.sumMs,
86
+ // OTLP 的 bucketCounts 是每桶**非累积**计数(Prometheus 文本才是累积语义)
87
+ bucketCounts: stat.perBucket(),
88
+ explicitBounds: [...stat.boundsList],
89
+ });
90
+ };
91
+ sum(`${p}runs_total`, s.runs, 'run 总数', []);
92
+ sum(`${p}runs_failed_total`, s.failed, '失败的 run 数', []);
93
+ // tokens_total:四类 kind 分项收进**同一个** Metric;description 与 Prometheus 侧
94
+ // render() 的 family() 统一成同一句总述(同名 Metric 各带一份描述是 semantic error)
95
+ const tokensHelp = 'token 累计(kind 分项:input / output / cache_read / cache_creation)';
96
+ sum(`${p}tokens_total`, state.tokens.input, tokensHelp, [strAttr('kind', 'input')]);
97
+ sum(`${p}tokens_total`, state.tokens.output, tokensHelp, [strAttr('kind', 'output')]);
98
+ sum(`${p}tokens_total`, state.tokens.cacheRead, tokensHelp, [strAttr('kind', 'cache_read')]);
99
+ sum(`${p}tokens_total`, state.tokens.cacheCreation, tokensHelp, [
100
+ strAttr('kind', 'cache_creation'),
101
+ ]);
102
+ sumDouble(`${p}cost_usd_total`, s.costUsd, '累计成本估算(美元)', []);
103
+ hist(`${p}run_duration_ms`, state.runStat, 'run 时长(毫秒)', []);
104
+ for (const label of [...state.capabilities.keys()].sort()) {
105
+ const acc = state.capabilities.get(label);
106
+ const attrs = [strAttr('capability', label)];
107
+ sum(`${p}capability_calls_total`, acc.calls, '能力调用次数', attrs);
108
+ sum(`${p}capability_errors_total`, acc.errors, '能力失败次数', attrs);
109
+ hist(`${p}capability_duration_ms`, acc.stat, '能力调用耗时(毫秒)', attrs);
110
+ if (acc.tokens !== null)
111
+ sum(`${p}capability_tokens_total`, acc.tokens, '子孙 token 合计', attrs);
112
+ if (acc.costUsd !== null)
113
+ sumDouble(`${p}capability_cost_usd_total`, acc.costUsd, '估算成本(美元)', attrs);
114
+ }
115
+ for (const model of [...state.models.keys()].sort()) {
116
+ const acc = state.models.get(model);
117
+ const attrs = [strAttr('model', model)];
118
+ sum(`${p}model_turns_total`, acc.turns, '模型往返次数', attrs);
119
+ sum(`${p}model_tokens_total`, acc.tokens, '模型 token 合计', attrs);
120
+ sumDouble(`${p}model_cost_usd_total`, acc.costUsd, '模型估算成本(美元)', attrs);
121
+ if (acc.unpricedTurns > 0) {
122
+ sum(`${p}model_unpriced_turns_total`, acc.unpricedTurns, '未定价 turn 数', attrs);
123
+ }
124
+ hist(`${p}model_duration_ms`, acc.stat, '模型往返耗时(毫秒)', attrs);
125
+ }
126
+ for (const key of [...state.scores.keys()].sort()) {
127
+ const acc = state.scores.get(key);
128
+ const { name, source } = scoreLabels(key);
129
+ const attrs = [strAttr('name', name), strAttr('source', source)];
130
+ gauge(`${p}score`, acc.value, '最近一次评分', attrs);
131
+ sum(`${p}score_total`, acc.count, '评分条数', attrs);
132
+ }
133
+ // 基数上限可见性(与 Prometheus 侧 `*_dropped_keys{kind=…}` 同名同义):
134
+ // 三个 kind 收进同一个 gauge Metric
135
+ const droppedHelp = '因基数上限被折叠的不同键数';
136
+ gauge(`${p}dropped_keys`, state.capBudget.dropped, droppedHelp, [strAttr('kind', 'capability')]);
137
+ gauge(`${p}dropped_keys`, state.modelBudget.dropped, droppedHelp, [strAttr('kind', 'model')]);
138
+ gauge(`${p}dropped_keys`, state.scoreBudget.dropped, droppedHelp, [strAttr('kind', 'score')]);
139
+ const metrics = [...metricTable.values()];
140
+ const resourceAttrs = [
141
+ strAttr('service.name', opts.serviceName),
142
+ ...Object.entries(opts.resourceAttributes ?? {}).map(([k, v]) => strAttr(k, v)),
143
+ ];
144
+ return {
145
+ resourceMetrics: [
146
+ {
147
+ resource: { attributes: resourceAttrs },
148
+ scopeMetrics: [{ scope: { name: 'agentia' }, metrics }],
149
+ },
150
+ ],
151
+ };
152
+ }
153
+ export async function flushOtlpMetrics(state, opts) {
154
+ let payload;
155
+ try {
156
+ payload = buildOtlpPayload(state, opts);
157
+ }
158
+ catch (e) {
159
+ opts.onExportError?.(e);
160
+ return;
161
+ }
162
+ try {
163
+ const res = await fetch(`${opts.endpoint}/v1/metrics`, {
164
+ method: 'POST',
165
+ headers: { 'content-type': 'application/json' },
166
+ body: JSON.stringify(payload),
167
+ ...(opts.timeoutMs > 0 ? { signal: AbortSignal.timeout(opts.timeoutMs) } : {}),
168
+ });
169
+ if (!res.ok) {
170
+ const text = (await res.text()).slice(0, 200);
171
+ throw new MetricsExportError(res.status, `OTLP metrics 导出失败: HTTP ${res.status} ${text}`);
172
+ }
173
+ }
174
+ catch (e) {
175
+ opts.onExportError?.(e); // 观测失败不得击穿业务
176
+ }
177
+ }
178
+ //# sourceMappingURL=metrics-otlp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metrics-otlp.js","sourceRoot":"","sources":["../../src/integrations/metrics-otlp.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { MetricsState } from './metrics-state.js';
2
+ export declare function renderPrometheus(state: MetricsState, p: string): string;
@@ -0,0 +1,145 @@
1
+ import { scoreLabels } from './metrics-state.js';
2
+ /**
3
+ * metricsSink 的 **Prometheus 文本渲染**(`text/plain; version=0.0.4`,零依赖手写)。
4
+ * 只读 `MetricsState`,不改账。module 级 export,不进公共面(`src/index.ts`)。
5
+ */
6
+ /**
7
+ * 一个指标家族的完整块:HELP/TYPE 各**恰好一行**,后接全部样本行。
8
+ * expfmt 对同名指标的第二条 HELP/TYPE 是**硬错误**(整次 scrape 失败),
9
+ * 所以家族头必须集中在这里发一次,绝不能让每条样本自带;同名指标也只能有一种 TYPE
10
+ * (histogram 与分位 gauge 因此拆成 `*_duration_ms` 与 `*_duration_ms_last` 两个名字)。
11
+ */
12
+ const family = (name, type, help, samples) => `# HELP ${name} ${help}\n# TYPE ${name} ${type}\n${samples.join('\n')}\n`;
13
+ /** Prometheus label 值转义:\、"、换行必须转义,否则一个含引号的能力名/模型名就损坏整页 exposition */
14
+ const escLabel = (v) => v.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n');
15
+ /** 一个 stat 的 histogram 样本(bucket 累积 + sum + count),labels 形如 `{k="v"}` */
16
+ const histogramSamples = (name, stat, labels = '') => {
17
+ const inner = labels ? labels.slice(1, -1) + ',' : ''; // 去掉外层 {} 再补逗号
18
+ const at = (extra) => `{${inner}${extra}}`;
19
+ const out = [];
20
+ const cum = stat.cumulative();
21
+ for (let i = 0; i < stat.boundsList.length; i++) {
22
+ out.push(`${name}_bucket${at(`le="${stat.boundsList[i]}"`)} ${cum[i]}`);
23
+ }
24
+ out.push(`${name}_bucket${at(`le="+Inf"`)} ${stat.count}`);
25
+ out.push(`${name}_sum${labels} ${stat.sumMs}`);
26
+ out.push(`${name}_count${labels} ${stat.count}`);
27
+ return out;
28
+ };
29
+ export function renderPrometheus(state, p) {
30
+ const out = [];
31
+ out.push(family(`${p}runs_total`, 'counter', 'run 总数(成功 + 失败)', [
32
+ `${p}runs_total ${state.runs}`,
33
+ ]));
34
+ out.push(family(`${p}runs_failed_total`, 'counter', '失败的 run 数(trace.status=error)', [
35
+ `${p}runs_failed_total ${state.failed}`,
36
+ ]));
37
+ out.push(family(`${p}tokens_total`, 'counter', 'token 累计(kind 分项:input / output / cache_read / cache_creation)', [
38
+ `${p}tokens_total{kind="input"} ${state.tokens.input}`,
39
+ `${p}tokens_total{kind="output"} ${state.tokens.output}`,
40
+ `${p}tokens_total{kind="cache_read"} ${state.tokens.cacheRead}`,
41
+ `${p}tokens_total{kind="cache_creation"} ${state.tokens.cacheCreation}`,
42
+ ]));
43
+ out.push(family(`${p}cost_usd_total`, 'counter', '累计成本估算(美元)', [
44
+ `${p}cost_usd_total ${state.costUsd}`,
45
+ ]));
46
+ // 基数上限的**可见性**:被折叠掉的不同键数。与 `snapshot()` 的同名字段一一对应 ——
47
+ // 此前只有 snapshot() 有、render() 没有,于是「按文档把 metricsSink 接到 /metrics」的部署
48
+ // **完全看不见折叠发生**(静默丢失);而同一份文件对「算不出成本的 turn」专门发了
49
+ // `model_unpriced_turns_total`,口径不一致。
50
+ // 与 unpriced 不同:**恒定发三行**(不是 >0 才发)—— 「0 → N」这个变化本身就是要告警的信号。
51
+ out.push(family(`${p}dropped_keys`, 'gauge', '因基数上限被折叠的不同键数(kind 分项)', [
52
+ `${p}dropped_keys{kind="capability"} ${state.capBudget.dropped}`,
53
+ `${p}dropped_keys{kind="model"} ${state.modelBudget.dropped}`,
54
+ `${p}dropped_keys{kind="score"} ${state.scoreBudget.dropped}`,
55
+ ]));
56
+ // 时长:histogram(可跨实例聚合)+ 窗口内精确分位(单实例好读),两种口径并存。
57
+ // 分位 gauge 必须用另一个名字 `*_last` —— 同名指标只允许一种 TYPE,
58
+ // 先发 histogram 再发 gauge 会被 expfmt 判硬错误,整次 scrape 失败。
59
+ out.push(family(`${p}run_duration_ms`, 'histogram', 'run 时长(毫秒)', [
60
+ ...histogramSamples(`${p}run_duration_ms`, state.runStat),
61
+ ]));
62
+ out.push(family(`${p}run_duration_ms_last`, 'gauge', 'run 时长分位(毫秒,滑动窗口内精确值)', [
63
+ `${p}run_duration_ms_last{quantile="0.5"} ${state.runStat.percentile(0.5)}`,
64
+ `${p}run_duration_ms_last{quantile="0.95"} ${state.runStat.percentile(0.95)}`,
65
+ ]));
66
+ // —— 能力维度(E2):同一家族的样本跨 label 聚合,家族头只发一次 ——
67
+ const capLabels = [...state.capabilities.keys()].sort();
68
+ if (capLabels.length > 0) {
69
+ const calls = [];
70
+ const errors = [];
71
+ const durations = [];
72
+ const durationQuantiles = [];
73
+ const capabilityTokens = [];
74
+ const capabilityCosts = [];
75
+ for (const label of capLabels) {
76
+ const acc = state.capabilities.get(label);
77
+ const lv = escLabel(label);
78
+ const l = `{capability="${lv}"}`;
79
+ calls.push(`${p}capability_calls_total${l} ${acc.calls}`);
80
+ errors.push(`${p}capability_errors_total${l} ${acc.errors}`);
81
+ durations.push(...histogramSamples(`${p}capability_duration_ms`, acc.stat, l));
82
+ durationQuantiles.push(`${p}capability_duration_ms_last{capability="${lv}",quantile="0.5"} ${acc.stat.percentile(0.5)}`, `${p}capability_duration_ms_last{capability="${lv}",quantile="0.95"} ${acc.stat.percentile(0.95)}`);
83
+ if (acc.tokens !== null)
84
+ capabilityTokens.push(`${p}capability_tokens_total${l} ${acc.tokens}`);
85
+ if (acc.costUsd !== null)
86
+ capabilityCosts.push(`${p}capability_cost_usd_total${l} ${acc.costUsd}`);
87
+ }
88
+ out.push(family(`${p}capability_calls_total`, 'counter', '能力调用次数', calls));
89
+ out.push(family(`${p}capability_errors_total`, 'counter', '能力失败次数', errors));
90
+ out.push(family(`${p}capability_duration_ms`, 'histogram', '能力调用耗时(毫秒)', durations));
91
+ out.push(family(`${p}capability_duration_ms_last`, 'gauge', '能力调用耗时分位(窗口内精确值)', durationQuantiles));
92
+ if (capabilityTokens.length > 0) {
93
+ out.push(family(`${p}capability_tokens_total`, 'counter', 'skill/subagent 的子孙 token 合计', capabilityTokens));
94
+ }
95
+ if (capabilityCosts.length > 0) {
96
+ out.push(family(`${p}capability_cost_usd_total`, 'counter', 'skill/subagent 的估算成本(美元)', capabilityCosts));
97
+ }
98
+ }
99
+ // —— 模型维度(E3)——
100
+ const modelNames = [...state.models.keys()].sort();
101
+ if (modelNames.length > 0) {
102
+ const turns = [];
103
+ const modelTokens = [];
104
+ const modelCosts = [];
105
+ const unpriced = [];
106
+ const durations = [];
107
+ const durationQuantiles = [];
108
+ for (const model of modelNames) {
109
+ const acc = state.models.get(model);
110
+ const mv = escLabel(model);
111
+ const l = `{model="${mv}"}`;
112
+ turns.push(`${p}model_turns_total${l} ${acc.turns}`);
113
+ modelTokens.push(`${p}model_tokens_total${l} ${acc.tokens}`);
114
+ modelCosts.push(`${p}model_cost_usd_total${l} ${acc.costUsd}`);
115
+ if (acc.unpricedTurns > 0)
116
+ unpriced.push(`${p}model_unpriced_turns_total${l} ${acc.unpricedTurns}`);
117
+ durations.push(...histogramSamples(`${p}model_duration_ms`, acc.stat, l));
118
+ durationQuantiles.push(`${p}model_duration_ms_last{model="${mv}",quantile="0.5"} ${acc.stat.percentile(0.5)}`, `${p}model_duration_ms_last{model="${mv}",quantile="0.95"} ${acc.stat.percentile(0.95)}`);
119
+ }
120
+ out.push(family(`${p}model_turns_total`, 'counter', '模型往返次数', turns));
121
+ out.push(family(`${p}model_tokens_total`, 'counter', '模型 token 合计(四类之和)', modelTokens));
122
+ out.push(family(`${p}model_cost_usd_total`, 'counter', '模型估算成本(美元,仅已定价部分)', modelCosts));
123
+ if (unpriced.length > 0) {
124
+ out.push(family(`${p}model_unpriced_turns_total`, 'counter', '算不出成本的 turn 数(模型不在价格表内)', unpriced));
125
+ }
126
+ out.push(family(`${p}model_duration_ms`, 'histogram', '模型往返耗时(毫秒)', durations));
127
+ out.push(family(`${p}model_duration_ms_last`, 'gauge', '模型往返耗时分位(窗口内精确值)', durationQuantiles));
128
+ }
129
+ // —— 评分维度(R7):gauge 记最近一次值、counter 记条数,label 为 name × source ——
130
+ if (state.scores.size > 0) {
131
+ const gaugeSamples = [];
132
+ const counterSamples = [];
133
+ for (const key of [...state.scores.keys()].sort()) {
134
+ const acc = state.scores.get(key);
135
+ const { name, source } = scoreLabels(key);
136
+ const l = `{name="${escLabel(name)}",source="${escLabel(source)}"}`;
137
+ gaugeSamples.push(`${p}score${l} ${acc.value}`);
138
+ counterSamples.push(`${p}score_total${l} ${acc.count}`);
139
+ }
140
+ out.push(family(`${p}score`, 'gauge', '最近一次评分(label 为评分维度与来源)', gaugeSamples));
141
+ out.push(family(`${p}score_total`, 'counter', '评分条数', counterSamples));
142
+ }
143
+ return out.join('');
144
+ }
145
+ //# sourceMappingURL=metrics-render.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metrics-render.js","sourceRoot":"","sources":["../../src/integrations/metrics-render.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,191 @@
1
+ import type { Trace } from '../core/trace.js';
2
+ /**
3
+ * metricsSink 的**状态层**:进程内累加器 + 快照(本文件)与渲染/导出(`metrics-render.ts` /
4
+ * `metrics-otlp.ts`)分离。本文件只管「账」,不管「怎么给人看 / 怎么发出去」。
5
+ * module 级 export,不进公共面(`src/index.ts`)。
6
+ */
7
+ /** 能力维度指标(`snapshot().capabilities[label]`) */
8
+ export interface CapabilityMetrics {
9
+ /** 该能力的调用次数 */
10
+ calls: number;
11
+ /** 其中失败次数(工具 `ok:false`;skill/subagent 的 capability span `status:'error'`) */
12
+ errors: number;
13
+ /** 单次调用耗时(毫秒)的窗口内精确分位;无样本时 0 */
14
+ latencyP50: number;
15
+ latencyP95: number;
16
+ /** 仅 skill/subagent:子孙 llm.turn 的 token 聚合(四类之和);工具为 null */
17
+ tokens: number | null;
18
+ /** 仅 skill/subagent:估算成本(美元);未定价或工具为 null */
19
+ costUsd: number | null;
20
+ }
21
+ /** 模型维度指标(`snapshot().models[model]`) */
22
+ export interface ModelMetrics {
23
+ /** 该模型的 llm.turn 次数 */
24
+ turns: number;
25
+ /** 四类 token 之和 */
26
+ tokens: number;
27
+ /** 已定价部分的成本(美元);全未定价时为 0 */
28
+ costUsd: number;
29
+ /** 成本估不出来的 turn 数(模型不在价格表内,且未用 priceOverrides 覆盖)*/
30
+ unpricedTurns: number;
31
+ latencyP50: number;
32
+ latencyP95: number;
33
+ }
34
+ /** 评分维度指标(`snapshot().scores[key]`,key 为 `name@source`,source 缺省时裸 name) */
35
+ export interface ScoreMetrics {
36
+ /** 最近一次评分值(gauge 语义 —— 分数不是累加量) */
37
+ value: number;
38
+ /** 评分条数 */
39
+ count: number;
40
+ /** 评分值合计(平均 = sum / count) */
41
+ sum: number;
42
+ }
43
+ /** 进程内累计快照(`snapshot()` 返回) */
44
+ export interface MetricsSnapshot {
45
+ /** 投递过 trace 的 run 总数(= `export` 被调用次数) */
46
+ runs: number;
47
+ /** 其中 `trace.status === 'error'` 的条数(含 budget_exceeded / aborted / error) */
48
+ failed: number;
49
+ /** run 时长(根 span `endedAt - startedAt`)分位,毫秒;窗口内无样本时 0 */
50
+ latencyP50: number;
51
+ latencyP95: number;
52
+ /**
53
+ * 累计 token —— 口径 = **四类之和**(input + output + cacheRead + cacheCreation),
54
+ * 与 `BudgetGuard` 的 token 口径一致。分项在 `render()` 里以 label 给出,不会丢。
55
+ */
56
+ tokens: number;
57
+ /** 累计成本估算(美元);模型不在价格表内时该 run 不计入(见 usage.ts) */
58
+ costUsd: number;
59
+ /**
60
+ * 能力维度(`labelMode:'none'` 时为空对象)。超 `maxCapabilities` 的键折叠在
61
+ * `'__other__'` —— 该桶照常累加,所以这里的**总量**不受上限影响。
62
+ */
63
+ capabilities: Record<string, CapabilityMetrics>;
64
+ /** 模型维度;超 `maxModels` 的键折叠在 `'__other__'`(同上:总量不丢) */
65
+ models: Record<string, ModelMetrics>;
66
+ /**
67
+ * 评分维度(key 为 `name@source`,source 缺省时裸 name;无评分时为空对象);
68
+ * 超 `maxScores` 的键折叠在 `'__other__'`。
69
+ */
70
+ scores: Record<string, ScoreMetrics>;
71
+ /** 因 `maxCapabilities` 上限被归入 `__other__` 的不同能力数 */
72
+ droppedCapabilities: number;
73
+ /** 因 `maxModels` 上限被归入 `__other__` 的不同模型数 */
74
+ droppedModels: number;
75
+ /** 因 `maxScores` 上限被归入 `__other__` 的不同评分维度数 */
76
+ droppedScores: number;
77
+ }
78
+ /**
79
+ * 标签基数配额:三个维度共用的「有界键空间」。
80
+ *
81
+ * 键第一次出现且在额度内 → 分到自己的标签(**永远认自己**,不会时而被折叠时而不折叠);
82
+ * 超限 → 归入 `OTHER_LABEL`。
83
+ *
84
+ * 被折叠的键记进 `over`,只为回答「多少个**不同**的键被折叠了」;这本账自身也有界
85
+ * (`MAX_DROPPED_TRACKING`),满了以后 `dropped` 是下界。
86
+ */
87
+ export declare class KeyBudget {
88
+ private readonly max;
89
+ private readonly assigned;
90
+ private readonly over;
91
+ constructor(max: number);
92
+ /** 取键的标签:额度内原样返回,超限返回 `OTHER_LABEL` */
93
+ take(key: string): string;
94
+ /** 被折叠的**不同**键数(记满 `MAX_DROPPED_TRACKING` 后为下界) */
95
+ get dropped(): number;
96
+ /** 清空配额(`reset()` 用)—— 与 `capabilities` / `models` / `scores` 三张表同步清 */
97
+ reset(): void;
98
+ }
99
+ /**
100
+ * 时长统计:环形窗口(算窗口内精确分位)+ 累积直方图(算可聚合的 bucket)。
101
+ * run / 能力 / 模型共用同一个实现,保证三种粒度的口径与取整完全一致。
102
+ */
103
+ export declare class DurationStat {
104
+ private readonly windowSize;
105
+ private readonly bounds;
106
+ private readonly ring;
107
+ private cursor;
108
+ /** 每桶非累积计数:counts[i] 对应 le = bounds[i];末位是 +Inf 桶 */
109
+ private readonly counts;
110
+ private total;
111
+ private sum;
112
+ constructor(windowSize: number, bounds: readonly number[]);
113
+ add(ms: number): void;
114
+ get count(): number;
115
+ get sumMs(): number;
116
+ get windowSamples(): number;
117
+ /**
118
+ * 窗口内精确分位(Prometheus 的 quantile 语义:取第 ceil(q·n) 个样本)。
119
+ * 算法单源在 `core/stats.ts` —— 与 `report.ts` 的时长 p50/p95 必须同口径,
120
+ * 否则同一条 trace 在看板与报告里会有两个 p95。
121
+ */
122
+ percentile(q: number): number;
123
+ /** 累积 bucket 计数(长度 = bounds+1,末位为 +Inf = count)—— Prometheus 文本语义 */
124
+ cumulative(): number[];
125
+ /** 每桶**非累积**计数 —— OTLP histogram 的 bucketCounts 语义(与 cumulative() 千万别混用) */
126
+ perBucket(): number[];
127
+ get boundsList(): readonly number[];
128
+ reset(): void;
129
+ }
130
+ /** 能力累加器:调用数 / 失败数 / 时长;skill·subagent 还带 token 与成本 */
131
+ export interface CapabilityAcc {
132
+ calls: number;
133
+ errors: number;
134
+ stat: DurationStat;
135
+ tokens: number | null;
136
+ costUsd: number | null;
137
+ }
138
+ export interface ModelAcc {
139
+ turns: number;
140
+ tokens: number;
141
+ costUsd: number;
142
+ unpricedTurns: number;
143
+ stat: DurationStat;
144
+ }
145
+ /**
146
+ * 评分键 → 指标标签。键有两种形态:**折叠桶**(`__other__`,整个键就是标签,不含 `\t`)
147
+ * 与正常的 `name\tsource`(source 缺省为 '')。两个出口(Prometheus 文本 / OTLP)共用一处
148
+ * 解码 —— 各切各的必然有一处把折叠桶切成乱码。
149
+ */
150
+ export declare function scoreLabels(key: string): {
151
+ name: string;
152
+ source: string;
153
+ };
154
+ /**
155
+ * 进程内累加器:`metricsSink` 的全部可变状态(run / 能力 / 模型 / 评分四个维度 +
156
+ * 三个基数配额)。渲染(`metrics-render.ts`)与 OTLP 导出(`metrics-otlp.ts`)只读它。
157
+ */
158
+ export declare class MetricsState {
159
+ private readonly opts;
160
+ runs: number;
161
+ failed: number;
162
+ costUsd: number;
163
+ readonly tokens: {
164
+ input: number;
165
+ output: number;
166
+ cacheRead: number;
167
+ cacheCreation: number;
168
+ };
169
+ readonly runStat: DurationStat;
170
+ readonly capabilities: Map<string, CapabilityAcc>;
171
+ readonly models: Map<string, ModelAcc>;
172
+ /** 评分累加器:key = `${name}\t${source}`(source 缺省 '';\t 不会出现在正常评分名里,做天然分隔符) */
173
+ readonly scores: Map<string, ScoreMetrics>;
174
+ readonly capBudget: KeyBudget;
175
+ readonly modelBudget: KeyBudget;
176
+ readonly scoreBudget: KeyBudget;
177
+ constructor(opts: {
178
+ windowSize: number;
179
+ maxCapabilities: number;
180
+ maxModels: number;
181
+ maxScores: number;
182
+ labelMode: 'capability' | 'kind' | 'none';
183
+ buckets: readonly number[];
184
+ });
185
+ private newCapability;
186
+ /** 能力标签分配:labelMode 决定粒度,capBudget 决定基数上限 */
187
+ private labelFor;
188
+ accumulate(trace: Trace): void;
189
+ snapshot(): MetricsSnapshot;
190
+ reset(): void;
191
+ }