@aipack-ai/observability 0.0.1 → 1.0.2
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/index.d.ts +209 -8
- package/dist/index.js +446 -18
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.d.ts
CHANGED
|
@@ -25,6 +25,12 @@ interface RunRecord {
|
|
|
25
25
|
outputTokens: number;
|
|
26
26
|
cacheRead?: number;
|
|
27
27
|
cacheWrite?: number;
|
|
28
|
+
/** Phase 6 — Cost:本次 run 总成本(分),由 worker 端 CostCalculator 累加后写入 */
|
|
29
|
+
costCents?: number;
|
|
30
|
+
/** Phase 9 — W3C Trace Context:外部系统传入的父 trace-id(跨系统调用链) */
|
|
31
|
+
parentTraceId?: string;
|
|
32
|
+
/** Phase 9 — W3C Trace Context:标准 32 hex 的 w3c trace-id,用于与 OpenTelemetry 链路打通 */
|
|
33
|
+
w3cTraceId?: string;
|
|
28
34
|
}
|
|
29
35
|
interface SpanRecord {
|
|
30
36
|
traceId: string;
|
|
@@ -42,6 +48,8 @@ interface SpanRecord {
|
|
|
42
48
|
cacheWrite?: number;
|
|
43
49
|
/** 会话标识(支撑 session 维度 token 统计) */
|
|
44
50
|
sessionKey?: string;
|
|
51
|
+
/** Phase 6 — Cost:本次模型调用成本(分),由 worker 端 CostCalculator 计算后写入 */
|
|
52
|
+
costCents?: number;
|
|
45
53
|
}
|
|
46
54
|
interface ToolCallRecord {
|
|
47
55
|
traceId: string;
|
|
@@ -99,6 +107,142 @@ interface EventBatch {
|
|
|
99
107
|
events: EventRecord[];
|
|
100
108
|
}
|
|
101
109
|
|
|
110
|
+
/**
|
|
111
|
+
* Phase 9 — 采样策略模块。
|
|
112
|
+
*
|
|
113
|
+
* 三种采样策略:
|
|
114
|
+
* - none: 不采样(全量,或兼容旧 sampleRate 行为)
|
|
115
|
+
* - traceid-ratio: 一致性 head 采样。同 traceId 全量子采样(runs/spans/toolCalls 一致)
|
|
116
|
+
* - error-priority: 错误 run 必采,成功 run 按 rate 采样
|
|
117
|
+
* - slow-priority: 慢请求(> P95 阈值)必采,其余按 rate 采样
|
|
118
|
+
*/
|
|
119
|
+
type SampleStrategy = {
|
|
120
|
+
type: 'none';
|
|
121
|
+
} | {
|
|
122
|
+
type: 'traceid-ratio';
|
|
123
|
+
rate: number;
|
|
124
|
+
} | {
|
|
125
|
+
type: 'error-priority';
|
|
126
|
+
successRate: number;
|
|
127
|
+
} | {
|
|
128
|
+
type: 'slow-priority';
|
|
129
|
+
rate: number;
|
|
130
|
+
slowThresholdMs?: number;
|
|
131
|
+
}
|
|
132
|
+
/** 【旧兼容】sampleRate 的语义:整 run 全量保留,仅过滤 model/tool spans 与 toolCalls(明细级) */
|
|
133
|
+
| {
|
|
134
|
+
type: 'legacy-detail-ratio';
|
|
135
|
+
rate: number;
|
|
136
|
+
};
|
|
137
|
+
interface SampleStrategyOptions {
|
|
138
|
+
/** 采样策略:'traceid-ratio' | 'error-priority' | 'slow-priority' | 'none'(默认 none) */
|
|
139
|
+
strategy?: 'traceid-ratio' | 'error-priority' | 'slow-priority' | 'none';
|
|
140
|
+
/** traceid-ratio / slow-priority 的采样率(0-1),默认 0.1 */
|
|
141
|
+
rate?: number;
|
|
142
|
+
/** error-priority 下成功 run 的采样率(0-1),默认 0.1 */
|
|
143
|
+
successRate?: number;
|
|
144
|
+
/** slow-priority 下慢请求阈值(ms),未配置时走 SDK 内部滑动 P95(若无法统计则默认 30000ms) */
|
|
145
|
+
slowThresholdMs?: number;
|
|
146
|
+
}
|
|
147
|
+
/** 将用户配置解析为结构化 SampleStrategy */
|
|
148
|
+
declare function resolveSampleStrategy(opts: SampleStrategyOptions): SampleStrategy;
|
|
149
|
+
/** 旧 sampleRate(0–1 简单概率)转换为等价策略:
|
|
150
|
+
* 【重要】语义保留:整 run(+ run span)全量保留,仅 model/tool spans 与 toolCalls 按 rate 过滤。
|
|
151
|
+
*/
|
|
152
|
+
declare function legacySampleRateToStrategy(rate: number | undefined): SampleStrategy;
|
|
153
|
+
interface SamplingJudge {
|
|
154
|
+
/** run 是否采样:只对整 run 的"是否保留"决策;spans/toolCalls 随 run */
|
|
155
|
+
shouldKeepRun(info: {
|
|
156
|
+
traceId: string;
|
|
157
|
+
status: 'success' | 'error' | 'validation';
|
|
158
|
+
durationMs: number;
|
|
159
|
+
}): boolean;
|
|
160
|
+
/** 明细(span/toolCall)采样:当 run 采样通过时才进一步过滤;未通过直接 false */
|
|
161
|
+
shouldKeepDetail(traceId: string): boolean;
|
|
162
|
+
}
|
|
163
|
+
declare function createSamplingJudge(strategy: SampleStrategy): SamplingJudge;
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Phase 9 — W3C Trace Context 支持。
|
|
167
|
+
*
|
|
168
|
+
* 标准:https://www.w3.org/TR/trace-context/
|
|
169
|
+
*
|
|
170
|
+
* traceparent 格式:
|
|
171
|
+
* version-traceid-parentid-traceflags
|
|
172
|
+
* 例:00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01
|
|
173
|
+
*
|
|
174
|
+
* - version: 2 hex (00)
|
|
175
|
+
* - trace-id: 32 hex(16 bytes,必须非全 0)
|
|
176
|
+
* - parent-id: 16 hex(8 bytes,必须非全 0)
|
|
177
|
+
* - trace-flags: 2 hex(bit 0 = sampled,其余保留为 0)
|
|
178
|
+
*/
|
|
179
|
+
interface W3cTraceContext {
|
|
180
|
+
/** 标准 W3C trace-id(32 hex 小写) */
|
|
181
|
+
traceId: string;
|
|
182
|
+
/** 标准 W3C parent-id / span-id(16 hex 小写) */
|
|
183
|
+
parentId: string;
|
|
184
|
+
/** 采样标记(trace-flags 最低位) */
|
|
185
|
+
sampled: boolean;
|
|
186
|
+
/** 原始 traceparent 字符串(用于回传) */
|
|
187
|
+
raw: string;
|
|
188
|
+
}
|
|
189
|
+
type ParseResult = {
|
|
190
|
+
ok: true;
|
|
191
|
+
ctx: W3cTraceContext;
|
|
192
|
+
} | {
|
|
193
|
+
ok: false;
|
|
194
|
+
reason: string;
|
|
195
|
+
};
|
|
196
|
+
/** 解析 traceparent 头。版本 ff 或格式不合法时返回 { ok:false },不抛错。 */
|
|
197
|
+
declare function parseTraceparent(raw: string | null | undefined): ParseResult;
|
|
198
|
+
/** 组装 traceparent 头字符串 */
|
|
199
|
+
declare function formatTraceparent(ctx: {
|
|
200
|
+
traceId: string;
|
|
201
|
+
parentId: string;
|
|
202
|
+
sampled: boolean;
|
|
203
|
+
}): string;
|
|
204
|
+
/**
|
|
205
|
+
* 把外部 W3C trace-id 映射到 aipack traceId(双向绑定)。
|
|
206
|
+
*
|
|
207
|
+
* 策略:
|
|
208
|
+
* - 外部 w3cTraceId 与 aipack traceId 不同(aipack traceId 为随机字符串)。
|
|
209
|
+
* 在 RunRecord 上同时记 w3cTraceId(外部传入)与 aipack 自身 traceId,
|
|
210
|
+
* 面板通过 w3cTraceId 建立跨系统跳转。
|
|
211
|
+
*/
|
|
212
|
+
/** 生成一个新的 W3C trace-id(32 hex,非全 0) */
|
|
213
|
+
declare function generateW3cTraceId(): string;
|
|
214
|
+
/** 生成一个新的 W3C parent-id / span-id(16 hex,非全 0) */
|
|
215
|
+
declare function generateW3cParentId(): string;
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Phase 9 — PII 内置脱敏规则库。
|
|
219
|
+
*
|
|
220
|
+
* 默认启用:手机号 / 邮箱 / 身份证号 / 银行卡号 / IPv4 地址。
|
|
221
|
+
* 脱敏策略:
|
|
222
|
+
* - mask:保留前后若干位,中间用 * 替换(默认策略)
|
|
223
|
+
* - hash:SHA-256 截断 16 hex(不可逆,只用于等值对比)
|
|
224
|
+
* - drop:完全删除该字段(极端敏感场景)
|
|
225
|
+
*
|
|
226
|
+
* 本文件仅提供「规则」与「脱敏函数」,不做字段级策略配置。
|
|
227
|
+
* 字段级策略(项目级)由服务端 redact_rules 表管理,SDK 侧通过 API 拉取或默认启用内置。
|
|
228
|
+
*/
|
|
229
|
+
type RedactAction = 'mask' | 'hash' | 'drop';
|
|
230
|
+
interface RedactRule {
|
|
231
|
+
/** 规则名,用于面板配置匹配 */
|
|
232
|
+
name: string;
|
|
233
|
+
/** 匹配正则(global,允许多次命中) */
|
|
234
|
+
pattern: RegExp;
|
|
235
|
+
/** 默认脱敏动作 */
|
|
236
|
+
defaultAction: RedactAction;
|
|
237
|
+
/** 掩码函数(仅 action=mask 时调用),默认保留前后 N 位 */
|
|
238
|
+
maskFn?: (match: string) => string;
|
|
239
|
+
}
|
|
240
|
+
declare const BUILTIN_RULES: RedactRule[];
|
|
241
|
+
/** 对单个字符串做所有内置规则的脱敏(默认 mask 动作) */
|
|
242
|
+
declare function redactString(raw: string, actionOverrides?: Record<string, RedactAction>): string;
|
|
243
|
+
/** 对任意 JSON 值(对象/数组/字符串)做深度脱敏 */
|
|
244
|
+
declare function redactValue<T = unknown>(value: T, actionOverrides?: Record<string, RedactAction>): T;
|
|
245
|
+
|
|
102
246
|
/**
|
|
103
247
|
* Telemetry 接口实现(客户端 SDK):事件 → 原始记录 → 上报队列 → HttpReporter。
|
|
104
248
|
*
|
|
@@ -110,6 +254,11 @@ interface EventBatch {
|
|
|
110
254
|
*
|
|
111
255
|
* 注意:runtime 的 emitTelemetry 会解构方法后调用(fn(info)),故所有回调必须为
|
|
112
256
|
* 箭头函数属性以绑定 this,否则 this.queued / this.startedAt 为 undefined。
|
|
257
|
+
*
|
|
258
|
+
* Phase 9 新增:
|
|
259
|
+
* - 采样策略:error-priority / slow-priority / traceid-ratio(整 run 维度 + 明细联动)
|
|
260
|
+
* - W3C Trace Context:traceparent 头解析 → parentTraceId/w3cTraceId 写入 RunRecord
|
|
261
|
+
* - PII 脱敏:内置规则库深度遍历 events.data 做脱敏(默认启用,可关闭)
|
|
113
262
|
*/
|
|
114
263
|
|
|
115
264
|
interface FlushQueueOptions {
|
|
@@ -117,12 +266,25 @@ interface FlushQueueOptions {
|
|
|
117
266
|
intervalMs?: number;
|
|
118
267
|
/** 积攒条数触发(含 runs/spans/toolCalls/permissions 总数),默认 50 */
|
|
119
268
|
batchSize?: number;
|
|
120
|
-
/** P2-1 明细采样率(0–1):只作用于 model/tool spans 与 toolCalls;runs/permissions/events
|
|
269
|
+
/** 【旧兼容】P2-1 明细采样率(0–1):只作用于 model/tool spans 与 toolCalls;runs/permissions/events 全量。
|
|
270
|
+
* 新版 sampleStrategy 优先,sampleRate 仅在未配置 sampleStrategy 时生效(等价 traceid-ratio)。 */
|
|
121
271
|
sampleRate?: number;
|
|
122
|
-
/**
|
|
272
|
+
/** Phase 9 — 采样策略(error-priority / slow-priority / traceid-ratio)。优先级高于 sampleRate。 */
|
|
273
|
+
sampleStrategy?: SampleStrategyOptions;
|
|
274
|
+
/** P2-1 脱敏钩子:send 前对整批改写(防 PII 明文上报)。
|
|
275
|
+
* 当内置脱敏启用时,自定义钩子在内置之后执行(用户可进一步定制)。 */
|
|
123
276
|
redact?: (batch: EventBatch) => EventBatch;
|
|
277
|
+
/** Phase 9 — 是否启用内置 PII 脱敏规则(手机号/邮箱/身份证/银行卡/IP)。默认 true。 */
|
|
278
|
+
redactEnabled?: boolean;
|
|
279
|
+
/** Phase 9 — 字段级脱敏动作覆盖:{ phone: 'hash', email: 'drop' }。未配置规则走默认 mask。 */
|
|
280
|
+
redactOverrides?: Record<string, RedactAction>;
|
|
124
281
|
/** 发布版本:注入每条 run 记录(RunRecord.appVersion),供收集端按版本聚合。缺省不携带 */
|
|
125
282
|
appVersion?: string;
|
|
283
|
+
/**
|
|
284
|
+
* Phase 9 — 外部 traceparent 上下文(来自 HTTP 请求头 / 消息队列属性)。
|
|
285
|
+
* 传入字符串时自动解析;解析失败则忽略(不影响正常上报)。
|
|
286
|
+
*/
|
|
287
|
+
traceparent?: string | W3cTraceContext;
|
|
126
288
|
}
|
|
127
289
|
declare class ObservabilityTelemetry implements Telemetry {
|
|
128
290
|
private reporter;
|
|
@@ -132,9 +294,16 @@ declare class ObservabilityTelemetry implements Telemetry {
|
|
|
132
294
|
/** in-flight run 顺序(最近开始的排最后),供 logger 关联当前 traceId */
|
|
133
295
|
private inFlightOrder;
|
|
134
296
|
private batchSize;
|
|
135
|
-
private sampleRate;
|
|
136
297
|
private redact?;
|
|
137
298
|
private appVersion?;
|
|
299
|
+
private redactEnabled;
|
|
300
|
+
private redactOverrides?;
|
|
301
|
+
private sampleStrategy;
|
|
302
|
+
private judge;
|
|
303
|
+
/** 外部传入的 traceparent 上下文(共享:所有 run 视作其子链路) */
|
|
304
|
+
private w3cCtx;
|
|
305
|
+
/** traceId → 为该 run 生成的 w3c trace 数据(上报时注入 RunRecord) */
|
|
306
|
+
private w3cByRun;
|
|
138
307
|
constructor(reporter: {
|
|
139
308
|
send(batch: EventBatch): Promise<boolean>;
|
|
140
309
|
}, opts?: FlushQueueOptions);
|
|
@@ -154,17 +323,18 @@ declare class ObservabilityTelemetry implements Telemetry {
|
|
|
154
323
|
/**
|
|
155
324
|
* P2-1 自定义业务事件(通用埋点)。
|
|
156
325
|
* run 内调用自动注入当前 traceId;run 外可通过 opts 显式传 traceId/sessionKey。
|
|
326
|
+
* Phase 9:内置 PII 脱敏在 flush 阶段对 data 深度遍历。
|
|
157
327
|
*/
|
|
158
328
|
emit(name: string, data?: unknown, opts?: {
|
|
159
329
|
traceId?: string;
|
|
160
330
|
sessionKey?: string;
|
|
161
331
|
}): void;
|
|
332
|
+
/** Phase 9 — 主动读取/注入当前 W3C traceparent(可传给下游 HTTP 调用)。 */
|
|
333
|
+
currentTraceparent(): string | null;
|
|
162
334
|
/** 立即上报队列中残留的数据(fire-and-forget,失败由 reporter 缓存补报) */
|
|
163
335
|
flush(): void;
|
|
164
336
|
/** 停止定时器并等残留上报完成(进程退出前调用) */
|
|
165
337
|
close(): Promise<void>;
|
|
166
|
-
/** P2-1 明细采样判定:rate>=1 恒过、<=0 恒弃、否则按概率 */
|
|
167
|
-
private sample;
|
|
168
338
|
private runRecord;
|
|
169
339
|
private runSpan;
|
|
170
340
|
private isEmpty;
|
|
@@ -314,12 +484,38 @@ interface CreateObservabilityOptions {
|
|
|
314
484
|
serviceName?: string;
|
|
315
485
|
headers?: Record<string, string>;
|
|
316
486
|
};
|
|
317
|
-
/**
|
|
487
|
+
/**
|
|
488
|
+
* 【旧兼容】P2-1 明细采样率(0–1):只对 model/tool spans 与 toolCalls 采样,
|
|
489
|
+
* runs/permissions/events 全量。缺省 1。
|
|
490
|
+
* 新版 sampleStrategy 优先;sampleRate 未设置 sampleStrategy 时等价 traceid-ratio。
|
|
491
|
+
*/
|
|
318
492
|
sampleRate?: number;
|
|
319
|
-
/**
|
|
493
|
+
/**
|
|
494
|
+
* Phase 9 — 采样策略:错误/慢请求优先或一致性 traceId 采样。
|
|
495
|
+
* 优先级高于 sampleRate。
|
|
496
|
+
*
|
|
497
|
+
* 示例:
|
|
498
|
+
* // 错误 100% 保留,成功按 10% 采样
|
|
499
|
+
* sampleStrategy: { strategy: 'error-priority', successRate: 0.1 }
|
|
500
|
+
* // 同 trace 稳定采/弃(跨明细一致)
|
|
501
|
+
* sampleStrategy: { strategy: 'traceid-ratio', rate: 0.2 }
|
|
502
|
+
*/
|
|
503
|
+
sampleStrategy?: SampleStrategyOptions;
|
|
504
|
+
/** P2-1 脱敏钩子:send 前对整批改写(防 PII 明文上报),在内置脱敏之后执行 */
|
|
320
505
|
redact?: (batch: EventBatch) => EventBatch;
|
|
506
|
+
/** Phase 9 — 是否启用内置 PII 脱敏(手机号/邮箱/身份证/银行卡/IP)。默认 true。 */
|
|
507
|
+
redactEnabled?: boolean;
|
|
508
|
+
/** Phase 9 — 字段级脱敏动作覆盖:{ phone: 'hash', email: 'drop', idCard: 'drop' } */
|
|
509
|
+
redactOverrides?: Record<string, RedactAction>;
|
|
321
510
|
/** 发布版本(如 '1.3.0'),随每条 run 上报,供收集端按版本聚合对比。缺省不携带 */
|
|
322
511
|
version?: string;
|
|
512
|
+
/**
|
|
513
|
+
* Phase 9 — W3C Trace Context 父链路。
|
|
514
|
+
* - 字符串:traceparent 请求头原值(如 `00-xxxxxx-yyyyyy-01`)
|
|
515
|
+
* - 对象:已解析的 W3cTraceContext
|
|
516
|
+
* 配置后,所有 run 视作其子链路,面板可跨系统跳转。
|
|
517
|
+
*/
|
|
518
|
+
traceparent?: string | W3cTraceContext;
|
|
323
519
|
}
|
|
324
520
|
interface Observability {
|
|
325
521
|
/** 注入 RuntimeOptions.telemetry */
|
|
@@ -335,7 +531,12 @@ interface Observability {
|
|
|
335
531
|
flush(): void;
|
|
336
532
|
/** 停止定时器并等残留上报完成(进程退出前调用) */
|
|
337
533
|
close(): Promise<void>;
|
|
534
|
+
/**
|
|
535
|
+
* Phase 9 — 当前链路的 traceparent 字符串(可传给下游 HTTP 调用实现跨系统追踪)。
|
|
536
|
+
* 无 in-flight run 且未配置外部 traceparent 时返回 null。
|
|
537
|
+
*/
|
|
538
|
+
currentTraceparent(): string | null;
|
|
338
539
|
}
|
|
339
540
|
declare function createObservability(opts: CreateObservabilityOptions): Observability;
|
|
340
541
|
|
|
341
|
-
export { type CreateObservabilityOptions, type EventBatch, type EventRecord, type FlushQueueOptions, HttpReporter, type LogFormat, type LogLevel, type Logger, type LoggerOptions, type Observability, ObservabilityTelemetry, type OtlpExporterOptions, type OtlpTraceExporter, type PermissionRecord, type ReporterOptions, type RetryRecord, type RunRecord, type SpanRecord, type ToolCallRecord, createLogger, createObservability, createOtlpTraceExporter, toOtlpJsonTraces };
|
|
542
|
+
export { BUILTIN_RULES, type CreateObservabilityOptions, type EventBatch, type EventRecord, type FlushQueueOptions, HttpReporter, type LogFormat, type LogLevel, type Logger, type LoggerOptions, type Observability, ObservabilityTelemetry, type OtlpExporterOptions, type OtlpTraceExporter, type PermissionRecord, type RedactAction, type RedactRule, type ReporterOptions, type RetryRecord, type RunRecord, type SampleStrategy, type SamplingJudge, type SampleStrategyOptions as SamplingOptions, type SpanRecord, type ToolCallRecord, type W3cTraceContext, createLogger, createObservability, createOtlpTraceExporter, createSamplingJudge, formatTraceparent, generateW3cParentId, generateW3cTraceId, legacySampleRateToStrategy, parseTraceparent, redactString, redactValue, resolveSampleStrategy, toOtlpJsonTraces };
|
package/dist/index.js
CHANGED
|
@@ -165,6 +165,322 @@ function trimBatch(batch, max) {
|
|
|
165
165
|
return out;
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
+
// src/sampling.ts
|
|
169
|
+
function resolveSampleStrategy(opts) {
|
|
170
|
+
const type = opts.strategy ?? "none";
|
|
171
|
+
switch (type) {
|
|
172
|
+
case "traceid-ratio": {
|
|
173
|
+
const rate = clamp01(opts.rate ?? 0.1);
|
|
174
|
+
return { type: "traceid-ratio", rate };
|
|
175
|
+
}
|
|
176
|
+
case "error-priority": {
|
|
177
|
+
const successRate = clamp01(opts.successRate ?? 0.1);
|
|
178
|
+
return { type: "error-priority", successRate };
|
|
179
|
+
}
|
|
180
|
+
case "slow-priority": {
|
|
181
|
+
const rate = clamp01(opts.rate ?? 0.1);
|
|
182
|
+
return { type: "slow-priority", rate, slowThresholdMs: opts.slowThresholdMs };
|
|
183
|
+
}
|
|
184
|
+
case "none":
|
|
185
|
+
default:
|
|
186
|
+
return { type: "none" };
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
function legacySampleRateToStrategy(rate) {
|
|
190
|
+
if (rate === void 0 || rate >= 1) return { type: "none" };
|
|
191
|
+
const r = clamp01(rate);
|
|
192
|
+
return { type: "legacy-detail-ratio", rate: r };
|
|
193
|
+
}
|
|
194
|
+
function createSamplingJudge(strategy) {
|
|
195
|
+
if (strategy.type === "traceid-ratio") {
|
|
196
|
+
const rate = strategy.rate;
|
|
197
|
+
if (rate <= 0) {
|
|
198
|
+
return {
|
|
199
|
+
shouldKeepRun: () => false,
|
|
200
|
+
shouldKeepDetail: () => false
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
if (rate >= 1) {
|
|
204
|
+
return {
|
|
205
|
+
shouldKeepRun: () => true,
|
|
206
|
+
shouldKeepDetail: () => true
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
return {
|
|
210
|
+
shouldKeepRun: ({ traceId }) => traceIdConsistentHash(traceId) < rate,
|
|
211
|
+
shouldKeepDetail: (traceId) => traceIdConsistentHash(traceId) < rate
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
if (strategy.type === "error-priority") {
|
|
215
|
+
const { successRate } = strategy;
|
|
216
|
+
if (successRate <= 0) {
|
|
217
|
+
return {
|
|
218
|
+
shouldKeepRun: ({ status }) => status === "error",
|
|
219
|
+
shouldKeepDetail: (traceId) => sampledRunSet.has(traceId)
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
if (successRate >= 1) {
|
|
223
|
+
return {
|
|
224
|
+
shouldKeepRun: () => true,
|
|
225
|
+
shouldKeepDetail: () => true
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
return {
|
|
229
|
+
shouldKeepRun: ({ traceId, status }) => {
|
|
230
|
+
const keep = status === "error" ? true : Math.random() < successRate;
|
|
231
|
+
if (keep) sampledRunSet.add(traceId);
|
|
232
|
+
else sampledRunSet.delete(traceId);
|
|
233
|
+
return keep;
|
|
234
|
+
},
|
|
235
|
+
shouldKeepDetail: (traceId) => sampledRunSet.has(traceId)
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
if (strategy.type === "slow-priority") {
|
|
239
|
+
const { rate, slowThresholdMs } = strategy;
|
|
240
|
+
const window = new SlidingP95();
|
|
241
|
+
const thresholdFallback = slowThresholdMs ?? 3e4;
|
|
242
|
+
if (rate <= 0) {
|
|
243
|
+
return {
|
|
244
|
+
shouldKeepRun: ({ traceId, durationMs }) => {
|
|
245
|
+
const p95 = window.get() ?? thresholdFallback;
|
|
246
|
+
window.push(durationMs);
|
|
247
|
+
const keep = durationMs >= p95;
|
|
248
|
+
if (keep) sampledRunSet.add(traceId);
|
|
249
|
+
else sampledRunSet.delete(traceId);
|
|
250
|
+
return keep;
|
|
251
|
+
},
|
|
252
|
+
shouldKeepDetail: (traceId) => sampledRunSet.has(traceId)
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
if (rate >= 1) {
|
|
256
|
+
return {
|
|
257
|
+
shouldKeepRun: () => true,
|
|
258
|
+
shouldKeepDetail: () => true
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
return {
|
|
262
|
+
shouldKeepRun: ({ traceId, durationMs }) => {
|
|
263
|
+
const p95 = window.get() ?? thresholdFallback;
|
|
264
|
+
window.push(durationMs);
|
|
265
|
+
const keep = durationMs >= p95 ? true : Math.random() < rate;
|
|
266
|
+
if (keep) sampledRunSet.add(traceId);
|
|
267
|
+
else sampledRunSet.delete(traceId);
|
|
268
|
+
return keep;
|
|
269
|
+
},
|
|
270
|
+
shouldKeepDetail: (traceId) => sampledRunSet.has(traceId)
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
if (strategy.type === "legacy-detail-ratio") {
|
|
274
|
+
const rate = strategy.rate;
|
|
275
|
+
if (rate <= 0) {
|
|
276
|
+
return {
|
|
277
|
+
shouldKeepRun: ({ traceId }) => {
|
|
278
|
+
sampledRunSet.add(traceId);
|
|
279
|
+
return true;
|
|
280
|
+
},
|
|
281
|
+
shouldKeepDetail: () => false
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
if (rate >= 1) {
|
|
285
|
+
return {
|
|
286
|
+
shouldKeepRun: ({ traceId }) => {
|
|
287
|
+
sampledRunSet.add(traceId);
|
|
288
|
+
return true;
|
|
289
|
+
},
|
|
290
|
+
shouldKeepDetail: () => true
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
return {
|
|
294
|
+
shouldKeepRun: ({ traceId }) => {
|
|
295
|
+
sampledRunSet.add(traceId);
|
|
296
|
+
return true;
|
|
297
|
+
},
|
|
298
|
+
shouldKeepDetail: (traceId) => traceIdConsistentHash(traceId) < rate
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
return {
|
|
302
|
+
shouldKeepRun: ({ traceId }) => {
|
|
303
|
+
sampledRunSet.add(traceId);
|
|
304
|
+
return true;
|
|
305
|
+
},
|
|
306
|
+
shouldKeepDetail: () => true
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
var sampledRunSet = /* @__PURE__ */ new Set();
|
|
310
|
+
function clamp01(v) {
|
|
311
|
+
if (!Number.isFinite(v)) return 0;
|
|
312
|
+
if (v < 0) return 0;
|
|
313
|
+
if (v > 1) return 1;
|
|
314
|
+
return v;
|
|
315
|
+
}
|
|
316
|
+
function traceIdConsistentHash(traceId) {
|
|
317
|
+
let h = 2166136261;
|
|
318
|
+
for (let i = 0; i < traceId.length; i++) {
|
|
319
|
+
h ^= traceId.charCodeAt(i);
|
|
320
|
+
h = Math.imul(h, 16777619);
|
|
321
|
+
}
|
|
322
|
+
return (h >>> 0) / 4294967296;
|
|
323
|
+
}
|
|
324
|
+
var SlidingP95 = class _SlidingP95 {
|
|
325
|
+
buf = [];
|
|
326
|
+
static CAP = 1e3;
|
|
327
|
+
push(d) {
|
|
328
|
+
this.buf.push(d);
|
|
329
|
+
if (this.buf.length > _SlidingP95.CAP) this.buf.shift();
|
|
330
|
+
}
|
|
331
|
+
/** 返回 p95,样本不足返回 undefined(调用方 fallback 到默认阈值) */
|
|
332
|
+
get() {
|
|
333
|
+
if (this.buf.length < 30) return void 0;
|
|
334
|
+
const sorted = [...this.buf].sort((a, b) => a - b);
|
|
335
|
+
const idx = Math.floor(sorted.length * 0.95);
|
|
336
|
+
return sorted[Math.min(idx, sorted.length - 1)];
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
|
|
340
|
+
// src/w3c-trace-context.ts
|
|
341
|
+
function parseTraceparent(raw) {
|
|
342
|
+
if (!raw) return { ok: false, reason: "missing" };
|
|
343
|
+
const s = raw.trim().toLowerCase();
|
|
344
|
+
const parts = s.split("-");
|
|
345
|
+
if (parts.length !== 4) return { ok: false, reason: "format: parts != 4" };
|
|
346
|
+
const [versionHex, traceIdHex, parentIdHex, flagsHex] = parts;
|
|
347
|
+
if (!/^[0-9a-f]{2}$/.test(versionHex)) return { ok: false, reason: "invalid version" };
|
|
348
|
+
if (versionHex === "ff") return { ok: false, reason: "version ff is invalid" };
|
|
349
|
+
if (!/^[0-9a-f]{32}$/.test(traceIdHex)) return { ok: false, reason: "invalid trace-id" };
|
|
350
|
+
if (/^0{32}$/.test(traceIdHex)) return { ok: false, reason: "trace-id all zeros" };
|
|
351
|
+
if (!/^[0-9a-f]{16}$/.test(parentIdHex)) return { ok: false, reason: "invalid parent-id" };
|
|
352
|
+
if (/^0{16}$/.test(parentIdHex)) return { ok: false, reason: "parent-id all zeros" };
|
|
353
|
+
if (!/^[0-9a-f]{2}$/.test(flagsHex)) return { ok: false, reason: "invalid trace-flags" };
|
|
354
|
+
const flags = parseInt(flagsHex, 16);
|
|
355
|
+
return {
|
|
356
|
+
ok: true,
|
|
357
|
+
ctx: {
|
|
358
|
+
traceId: traceIdHex,
|
|
359
|
+
parentId: parentIdHex,
|
|
360
|
+
sampled: (flags & 1) === 1,
|
|
361
|
+
raw: s
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
function formatTraceparent(ctx) {
|
|
366
|
+
const flags = ctx.sampled ? "01" : "00";
|
|
367
|
+
return `00-${ctx.traceId}-${ctx.parentId}-${flags}`;
|
|
368
|
+
}
|
|
369
|
+
function generateW3cTraceId() {
|
|
370
|
+
const bytes = cryptoRandomBytes(16);
|
|
371
|
+
if (bytes.every((b) => b === 0)) bytes[0] = 1;
|
|
372
|
+
return toHex(bytes);
|
|
373
|
+
}
|
|
374
|
+
function generateW3cParentId() {
|
|
375
|
+
const bytes = cryptoRandomBytes(8);
|
|
376
|
+
if (bytes.every((b) => b === 0)) bytes[0] = 1;
|
|
377
|
+
return toHex(bytes);
|
|
378
|
+
}
|
|
379
|
+
function cryptoRandomBytes(n) {
|
|
380
|
+
const cryptoObj = globalThis.crypto;
|
|
381
|
+
if (typeof globalThis !== "undefined" && cryptoObj?.getRandomValues) {
|
|
382
|
+
const buf = new Uint8Array(n);
|
|
383
|
+
cryptoObj.getRandomValues(buf);
|
|
384
|
+
return buf;
|
|
385
|
+
}
|
|
386
|
+
const out = new Uint8Array(n);
|
|
387
|
+
for (let i = 0; i < n; i++) out[i] = Math.floor(Math.random() * 256);
|
|
388
|
+
return out;
|
|
389
|
+
}
|
|
390
|
+
function toHex(bytes) {
|
|
391
|
+
let s = "";
|
|
392
|
+
for (const b of bytes) s += b.toString(16).padStart(2, "0");
|
|
393
|
+
return s;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// src/redact/rules.ts
|
|
397
|
+
import { createHash } from "crypto";
|
|
398
|
+
var PHONE_RE = /(?<!\d)1[3-9]\d{9}(?!\d)/g;
|
|
399
|
+
var EMAIL_RE = /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/g;
|
|
400
|
+
var ID_CARD_RE = /(?<!\d)\d{17}[\dXx](?!\d)/g;
|
|
401
|
+
var BANK_CARD_RE = /(?<!\d)\d{16,19}(?!\d)/g;
|
|
402
|
+
var IPV4_RE = /\b(?:\d{1,3}\.){3}\d{1,3}\b/g;
|
|
403
|
+
var BUILTIN_RULES = [
|
|
404
|
+
{
|
|
405
|
+
name: "phone",
|
|
406
|
+
pattern: PHONE_RE,
|
|
407
|
+
defaultAction: "mask",
|
|
408
|
+
maskFn: (m) => m.slice(0, 3) + "****" + m.slice(-4)
|
|
409
|
+
// 139****1234
|
|
410
|
+
},
|
|
411
|
+
{
|
|
412
|
+
name: "email",
|
|
413
|
+
pattern: EMAIL_RE,
|
|
414
|
+
defaultAction: "mask",
|
|
415
|
+
maskFn: (m) => {
|
|
416
|
+
const at = m.indexOf("@");
|
|
417
|
+
if (at <= 1) return "*".repeat(at) + m.slice(at);
|
|
418
|
+
const user = m.slice(0, at);
|
|
419
|
+
return user[0] + "*".repeat(Math.max(at - 2, 1)) + user[at - 1] + m.slice(at);
|
|
420
|
+
}
|
|
421
|
+
},
|
|
422
|
+
{
|
|
423
|
+
name: "idCard",
|
|
424
|
+
pattern: ID_CARD_RE,
|
|
425
|
+
defaultAction: "mask",
|
|
426
|
+
maskFn: (m) => m.slice(0, 6) + "********" + m.slice(-4)
|
|
427
|
+
// 6 + 8* + 4 = 18
|
|
428
|
+
},
|
|
429
|
+
{
|
|
430
|
+
name: "bankCard",
|
|
431
|
+
pattern: BANK_CARD_RE,
|
|
432
|
+
defaultAction: "mask",
|
|
433
|
+
maskFn: (m) => m.slice(0, 4) + " **** **** " + m.slice(-4)
|
|
434
|
+
// 6222 **** **** 1234
|
|
435
|
+
},
|
|
436
|
+
{
|
|
437
|
+
name: "ipv4",
|
|
438
|
+
pattern: IPV4_RE,
|
|
439
|
+
defaultAction: "mask",
|
|
440
|
+
maskFn: (m) => m.replace(/\.\d+\.\d+$/, ".*.*")
|
|
441
|
+
// 192.168.*.*
|
|
442
|
+
}
|
|
443
|
+
];
|
|
444
|
+
function redactString(raw, actionOverrides) {
|
|
445
|
+
if (!raw) return raw;
|
|
446
|
+
let out = raw;
|
|
447
|
+
for (const rule of BUILTIN_RULES) {
|
|
448
|
+
const action = actionOverrides?.[rule.name] ?? rule.defaultAction;
|
|
449
|
+
if (action === "drop") {
|
|
450
|
+
out = out.replace(rule.pattern, "");
|
|
451
|
+
} else if (action === "hash") {
|
|
452
|
+
out = out.replace(rule.pattern, (m) => sha256Trunc(m));
|
|
453
|
+
} else {
|
|
454
|
+
const fn = rule.maskFn ?? defaultMask;
|
|
455
|
+
out = out.replace(rule.pattern, fn);
|
|
456
|
+
}
|
|
457
|
+
rule.pattern.lastIndex = 0;
|
|
458
|
+
}
|
|
459
|
+
return out;
|
|
460
|
+
}
|
|
461
|
+
function redactValue(value, actionOverrides) {
|
|
462
|
+
if (value === null || value === void 0) return value;
|
|
463
|
+
if (typeof value === "string") return redactString(value, actionOverrides);
|
|
464
|
+
if (typeof value !== "object") return value;
|
|
465
|
+
if (Array.isArray(value)) {
|
|
466
|
+
return value.map((v) => redactValue(v, actionOverrides));
|
|
467
|
+
}
|
|
468
|
+
const out = {};
|
|
469
|
+
for (const [k, v] of Object.entries(value)) {
|
|
470
|
+
out[k] = redactValue(v, actionOverrides);
|
|
471
|
+
}
|
|
472
|
+
return out;
|
|
473
|
+
}
|
|
474
|
+
function defaultMask(m) {
|
|
475
|
+
if (m.length <= 2) return "*".repeat(m.length);
|
|
476
|
+
const keepHead = Math.max(1, Math.floor(m.length * 0.25));
|
|
477
|
+
const keepTail = Math.max(1, Math.floor(m.length * 0.25));
|
|
478
|
+
return m.slice(0, keepHead) + "*".repeat(m.length - keepHead - keepTail) + m.slice(-keepTail);
|
|
479
|
+
}
|
|
480
|
+
function sha256Trunc(s) {
|
|
481
|
+
return createHash("sha256").update(s).digest("hex").slice(0, 16);
|
|
482
|
+
}
|
|
483
|
+
|
|
168
484
|
// src/telemetry.ts
|
|
169
485
|
var emptyBatch = () => ({
|
|
170
486
|
runs: [],
|
|
@@ -178,9 +494,17 @@ var ObservabilityTelemetry = class {
|
|
|
178
494
|
constructor(reporter, opts = {}) {
|
|
179
495
|
this.reporter = reporter;
|
|
180
496
|
this.batchSize = opts.batchSize ?? 50;
|
|
181
|
-
this.sampleRate = opts.sampleRate ?? 1;
|
|
182
497
|
this.redact = opts.redact;
|
|
183
498
|
this.appVersion = opts.appVersion;
|
|
499
|
+
this.redactEnabled = opts.redactEnabled ?? true;
|
|
500
|
+
this.redactOverrides = opts.redactOverrides;
|
|
501
|
+
if (opts.sampleStrategy && opts.sampleStrategy.strategy) {
|
|
502
|
+
this.sampleStrategy = resolveSampleStrategy(opts.sampleStrategy);
|
|
503
|
+
} else {
|
|
504
|
+
this.sampleStrategy = legacySampleRateToStrategy(opts.sampleRate);
|
|
505
|
+
}
|
|
506
|
+
this.judge = createSamplingJudge(this.sampleStrategy);
|
|
507
|
+
this.w3cCtx = parseTraceparentInput(opts.traceparent);
|
|
184
508
|
const intervalMs = opts.intervalMs ?? 5e3;
|
|
185
509
|
this.timer = setInterval(() => void this.flush(), intervalMs);
|
|
186
510
|
this.timer.unref?.();
|
|
@@ -193,24 +517,47 @@ var ObservabilityTelemetry = class {
|
|
|
193
517
|
/** in-flight run 顺序(最近开始的排最后),供 logger 关联当前 traceId */
|
|
194
518
|
inFlightOrder = [];
|
|
195
519
|
batchSize;
|
|
196
|
-
sampleRate;
|
|
197
520
|
redact;
|
|
198
521
|
appVersion;
|
|
522
|
+
redactEnabled;
|
|
523
|
+
redactOverrides;
|
|
524
|
+
// ─── Phase 9:采样 ───────────────────────────────────────────────
|
|
525
|
+
sampleStrategy;
|
|
526
|
+
judge;
|
|
527
|
+
// ─── Phase 9:W3C Trace Context ──────────────────────────────────
|
|
528
|
+
/** 外部传入的 traceparent 上下文(共享:所有 run 视作其子链路) */
|
|
529
|
+
w3cCtx;
|
|
530
|
+
/** traceId → 为该 run 生成的 w3c trace 数据(上报时注入 RunRecord) */
|
|
531
|
+
w3cByRun = /* @__PURE__ */ new Map();
|
|
199
532
|
onRunStart = (info) => {
|
|
200
533
|
this.startedAt.set(info.traceId, info.queuedAt);
|
|
201
534
|
this.inFlightOrder = this.inFlightOrder.filter((id) => id !== info.traceId);
|
|
202
535
|
this.inFlightOrder.push(info.traceId);
|
|
536
|
+
const parent = this.w3cCtx;
|
|
537
|
+
const w3cTraceId = parent ? parent.traceId : generateW3cTraceId();
|
|
538
|
+
const parentTraceId = parent ? parent.traceId : void 0;
|
|
539
|
+
this.w3cByRun.set(info.traceId, { w3cTraceId, parentTraceId });
|
|
203
540
|
};
|
|
204
541
|
onRunEnd = (info) => {
|
|
205
542
|
const queuedAt = this.startedAt.get(info.traceId) ?? Date.now() - info.durationMs;
|
|
206
543
|
this.startedAt.delete(info.traceId);
|
|
207
544
|
this.inFlightOrder = this.inFlightOrder.filter((id) => id !== info.traceId);
|
|
208
|
-
this.
|
|
545
|
+
const keep = this.judge.shouldKeepRun({
|
|
546
|
+
traceId: info.traceId,
|
|
547
|
+
status: info.success ? "success" : info.errorClass === "validation" ? "validation" : "error",
|
|
548
|
+
durationMs: info.durationMs
|
|
549
|
+
});
|
|
550
|
+
if (!keep) {
|
|
551
|
+
this.w3cByRun.delete(info.traceId);
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
554
|
+
const run = this.runRecord(info, queuedAt);
|
|
555
|
+
this.queued.runs.push(run);
|
|
209
556
|
this.queued.spans.push(this.runSpan(info, queuedAt));
|
|
210
557
|
this.maybeFlush();
|
|
211
558
|
};
|
|
212
559
|
onToolCall = (info) => {
|
|
213
|
-
if (!this.
|
|
560
|
+
if (!this.judge.shouldKeepDetail(info.traceId)) return;
|
|
214
561
|
const startedAt = Date.now() - info.durationMs;
|
|
215
562
|
this.queued.spans.push({
|
|
216
563
|
traceId: info.traceId,
|
|
@@ -234,7 +581,7 @@ var ObservabilityTelemetry = class {
|
|
|
234
581
|
this.maybeFlush();
|
|
235
582
|
};
|
|
236
583
|
onModelCall = (info) => {
|
|
237
|
-
if (!this.
|
|
584
|
+
if (!this.judge.shouldKeepDetail(info.traceId)) return;
|
|
238
585
|
this.queued.spans.push({
|
|
239
586
|
traceId: info.traceId,
|
|
240
587
|
spanId: info.spanId,
|
|
@@ -254,6 +601,7 @@ var ObservabilityTelemetry = class {
|
|
|
254
601
|
this.maybeFlush();
|
|
255
602
|
};
|
|
256
603
|
onRetry = (info) => {
|
|
604
|
+
if (!this.judge.shouldKeepDetail(info.traceId)) return;
|
|
257
605
|
this.queued.retries.push({
|
|
258
606
|
traceId: info.traceId,
|
|
259
607
|
spanId: info.spanId,
|
|
@@ -288,6 +636,7 @@ var ObservabilityTelemetry = class {
|
|
|
288
636
|
/**
|
|
289
637
|
* P2-1 自定义业务事件(通用埋点)。
|
|
290
638
|
* run 内调用自动注入当前 traceId;run 外可通过 opts 显式传 traceId/sessionKey。
|
|
639
|
+
* Phase 9:内置 PII 脱敏在 flush 阶段对 data 深度遍历。
|
|
291
640
|
*/
|
|
292
641
|
emit(name, data, opts = {}) {
|
|
293
642
|
this.queued.events.push({
|
|
@@ -299,12 +648,29 @@ var ObservabilityTelemetry = class {
|
|
|
299
648
|
});
|
|
300
649
|
this.maybeFlush();
|
|
301
650
|
}
|
|
651
|
+
/** Phase 9 — 主动读取/注入当前 W3C traceparent(可传给下游 HTTP 调用)。 */
|
|
652
|
+
currentTraceparent() {
|
|
653
|
+
const traceId = this.currentContext().traceId;
|
|
654
|
+
if (!traceId) {
|
|
655
|
+
if (this.w3cCtx) return this.w3cCtx.raw;
|
|
656
|
+
return null;
|
|
657
|
+
}
|
|
658
|
+
const w3c = this.w3cByRun.get(traceId);
|
|
659
|
+
const w3cTraceId = w3c?.w3cTraceId ?? (this.w3cCtx?.traceId ?? generateW3cTraceId());
|
|
660
|
+
const parentId = aipackSpanIdToW3c(traceId);
|
|
661
|
+
return formatTraceparent({ traceId: w3cTraceId, parentId, sampled: true });
|
|
662
|
+
}
|
|
302
663
|
/** 立即上报队列中残留的数据(fire-and-forget,失败由 reporter 缓存补报) */
|
|
303
664
|
flush() {
|
|
304
665
|
if (this.isEmpty()) return;
|
|
305
666
|
const batch = this.queued;
|
|
306
667
|
this.queued = emptyBatch();
|
|
307
|
-
|
|
668
|
+
let final = batch;
|
|
669
|
+
if (this.redactEnabled) {
|
|
670
|
+
final = applyBuiltinRedact(final, this.redactOverrides);
|
|
671
|
+
}
|
|
672
|
+
if (this.redact) final = this.redact(final);
|
|
673
|
+
void this.reporter.send(final);
|
|
308
674
|
}
|
|
309
675
|
/** 停止定时器并等残留上报完成(进程退出前调用) */
|
|
310
676
|
async close() {
|
|
@@ -315,15 +681,14 @@ var ObservabilityTelemetry = class {
|
|
|
315
681
|
if (this.isEmpty()) return;
|
|
316
682
|
const batch = this.queued;
|
|
317
683
|
this.queued = emptyBatch();
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
if (this.sampleRate >= 1) return true;
|
|
323
|
-
if (this.sampleRate <= 0) return false;
|
|
324
|
-
return Math.random() < this.sampleRate;
|
|
684
|
+
let final = batch;
|
|
685
|
+
if (this.redactEnabled) final = applyBuiltinRedact(final, this.redactOverrides);
|
|
686
|
+
if (this.redact) final = this.redact(final);
|
|
687
|
+
await this.reporter.send(final);
|
|
325
688
|
}
|
|
326
689
|
runRecord(info, queuedAt) {
|
|
690
|
+
const w3c = this.w3cByRun.get(info.traceId);
|
|
691
|
+
this.w3cByRun.delete(info.traceId);
|
|
327
692
|
return {
|
|
328
693
|
traceId: info.traceId,
|
|
329
694
|
startedAt: queuedAt,
|
|
@@ -342,7 +707,9 @@ var ObservabilityTelemetry = class {
|
|
|
342
707
|
outputTokens: info.tokens.output,
|
|
343
708
|
cacheRead: info.tokens.cacheRead,
|
|
344
709
|
cacheWrite: info.tokens.cacheWrite,
|
|
345
|
-
...this.appVersion !== void 0 ? { appVersion: this.appVersion } : {}
|
|
710
|
+
...this.appVersion !== void 0 ? { appVersion: this.appVersion } : {},
|
|
711
|
+
...w3c?.parentTraceId !== void 0 ? { parentTraceId: w3c.parentTraceId } : {},
|
|
712
|
+
...w3c?.w3cTraceId !== void 0 ? { w3cTraceId: w3c.w3cTraceId } : {}
|
|
346
713
|
};
|
|
347
714
|
}
|
|
348
715
|
runSpan(info, queuedAt) {
|
|
@@ -368,6 +735,52 @@ var ObservabilityTelemetry = class {
|
|
|
368
735
|
if (total >= this.batchSize) this.flush();
|
|
369
736
|
}
|
|
370
737
|
};
|
|
738
|
+
function applyBuiltinRedact(batch, overrides) {
|
|
739
|
+
const events = batch.events.map(
|
|
740
|
+
(e) => e.data === void 0 ? e : { ...e, data: redactValue(e.data, overrides) }
|
|
741
|
+
);
|
|
742
|
+
const permissions = batch.permissions.map((p) => ({
|
|
743
|
+
...p,
|
|
744
|
+
reason: redactString2(p.reason, overrides)
|
|
745
|
+
}));
|
|
746
|
+
return {
|
|
747
|
+
runs: batch.runs,
|
|
748
|
+
// runs 字段结构化,不做默认脱敏(避免破坏聚合)
|
|
749
|
+
spans: batch.spans,
|
|
750
|
+
toolCalls: batch.toolCalls,
|
|
751
|
+
retries: batch.retries,
|
|
752
|
+
events,
|
|
753
|
+
permissions
|
|
754
|
+
};
|
|
755
|
+
}
|
|
756
|
+
function parseTraceparentInput(input) {
|
|
757
|
+
if (!input) return null;
|
|
758
|
+
if (typeof input === "string") {
|
|
759
|
+
const r = parseTraceparent(input);
|
|
760
|
+
return r.ok ? r.ctx : null;
|
|
761
|
+
}
|
|
762
|
+
return input && typeof input.traceId === "string" ? input : null;
|
|
763
|
+
}
|
|
764
|
+
function aipackSpanIdToW3c(aipackTraceId) {
|
|
765
|
+
let h = 2166136261;
|
|
766
|
+
for (let i = 0; i < aipackTraceId.length; i++) {
|
|
767
|
+
h ^= aipackTraceId.charCodeAt(i);
|
|
768
|
+
h = Math.imul(h, 16777619);
|
|
769
|
+
}
|
|
770
|
+
const hex = (h >>> 0).toString(16).padStart(8, "0");
|
|
771
|
+
let h2 = 2216829733;
|
|
772
|
+
for (let i = aipackTraceId.length - 1; i >= 0; i--) {
|
|
773
|
+
h2 ^= aipackTraceId.charCodeAt(i);
|
|
774
|
+
h2 = Math.imul(h2, 16777619);
|
|
775
|
+
}
|
|
776
|
+
const hex2 = (h2 >>> 0).toString(16).padStart(8, "0");
|
|
777
|
+
const out = hex + hex2;
|
|
778
|
+
if (/^0{16}$/.test(out)) return "0000000000000001";
|
|
779
|
+
return out;
|
|
780
|
+
}
|
|
781
|
+
function redactString2(raw, overrides) {
|
|
782
|
+
return redactValue(raw, overrides);
|
|
783
|
+
}
|
|
371
784
|
|
|
372
785
|
// src/logger.ts
|
|
373
786
|
var LEVEL_ORDER = { debug: 10, info: 20, warn: 30, error: 40 };
|
|
@@ -432,9 +845,9 @@ function defaultDest(line) {
|
|
|
432
845
|
}
|
|
433
846
|
|
|
434
847
|
// src/otlp.ts
|
|
435
|
-
import { createHash } from "crypto";
|
|
848
|
+
import { createHash as createHash2 } from "crypto";
|
|
436
849
|
function bytesOf(input, n) {
|
|
437
|
-
return
|
|
850
|
+
return createHash2("md5").update(input).digest().subarray(0, n);
|
|
438
851
|
}
|
|
439
852
|
function str(value) {
|
|
440
853
|
return { stringValue: value == null ? "" : String(value) };
|
|
@@ -593,8 +1006,12 @@ function createObservability(opts) {
|
|
|
593
1006
|
intervalMs: opts.flushIntervalMs,
|
|
594
1007
|
batchSize: opts.flushBatchSize,
|
|
595
1008
|
sampleRate: opts.sampleRate,
|
|
1009
|
+
sampleStrategy: opts.sampleStrategy,
|
|
596
1010
|
redact: opts.redact,
|
|
597
|
-
|
|
1011
|
+
redactEnabled: opts.redactEnabled,
|
|
1012
|
+
redactOverrides: opts.redactOverrides,
|
|
1013
|
+
appVersion: opts.version,
|
|
1014
|
+
traceparent: opts.traceparent
|
|
598
1015
|
});
|
|
599
1016
|
const logger = createLogger({
|
|
600
1017
|
tags: { app: opts.appId },
|
|
@@ -607,15 +1024,26 @@ function createObservability(opts) {
|
|
|
607
1024
|
flush: () => telemetry.flush(),
|
|
608
1025
|
close: async () => {
|
|
609
1026
|
await telemetry.close();
|
|
610
|
-
}
|
|
1027
|
+
},
|
|
1028
|
+
currentTraceparent: () => telemetry.currentTraceparent()
|
|
611
1029
|
};
|
|
612
1030
|
}
|
|
613
1031
|
export {
|
|
1032
|
+
BUILTIN_RULES,
|
|
614
1033
|
HttpReporter,
|
|
615
1034
|
ObservabilityTelemetry,
|
|
616
1035
|
createLogger,
|
|
617
1036
|
createObservability,
|
|
618
1037
|
createOtlpTraceExporter,
|
|
1038
|
+
createSamplingJudge,
|
|
1039
|
+
formatTraceparent,
|
|
1040
|
+
generateW3cParentId,
|
|
1041
|
+
generateW3cTraceId,
|
|
1042
|
+
legacySampleRateToStrategy,
|
|
1043
|
+
parseTraceparent,
|
|
1044
|
+
redactString,
|
|
1045
|
+
redactValue,
|
|
1046
|
+
resolveSampleStrategy,
|
|
619
1047
|
toOtlpJsonTraces
|
|
620
1048
|
};
|
|
621
1049
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/reporter.ts","../src/telemetry.ts","../src/logger.ts","../src/otlp.ts","../src/index.ts"],"sourcesContent":["/**\n * HttpReporter — 把批量事件上报到收集服务(埋点模式)。\n *\n * - POST {endpoint}/api/v1/ingest,携带 x-app-id / x-app-secret 鉴权头\n * - 上报失败(网络错误 / 5xx / 429)→ 写入本地缓存文件({cacheDir}/{appId}.json),\n * 下次 send 前先补报缓存,全部成功才删除\n * - 4xx(鉴权失败、参数错误)判定为不可重试,直接丢弃并 warn,避免缓存无限重试\n * - 缓存条数上限 maxCacheSize,超出后丢弃最旧记录\n * - 并发保护:send 串行执行,重复调用合并为一次\n */\n\nimport { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport type { EventBatch } from './types';\n\nexport interface ReporterOptions {\n /** 收集服务地址,如 http://localhost:8787(必填) */\n endpoint: string;\n appId: string;\n appSecret: string;\n /** 缓存目录,默认 ./.aipack/observability */\n cacheDir?: string;\n /** 缓存条数上限(含 runs/spans/toolCalls/permissions),默认 2000 */\n maxCacheSize?: number;\n /** 上报超时(ms),默认 5000 */\n timeoutMs?: number;\n /** 便于测试注入;默认全局 fetch */\n fetchImpl?: typeof fetch;\n}\n\nconst EMPTY_BATCH = (): EventBatch => ({\n runs: [],\n spans: [],\n toolCalls: [],\n permissions: [],\n retries: [],\n events: [],\n});\n\nexport class HttpReporter {\n private endpoint: string;\n private appId: string;\n private appSecret: string;\n private cachePath: string;\n private maxCacheSize: number;\n private timeoutMs: number;\n private fetchImpl: typeof fetch;\n /** 串行发送锁:true 表示有发送在进行中 */\n private sending = false;\n /** 发送期间新到的批次,发送完成后继续补发 */\n private pending: EventBatch[] = [];\n\n constructor(opts: ReporterOptions) {\n this.endpoint = opts.endpoint.replace(/\\/+$/, '');\n this.appId = opts.appId;\n this.appSecret = opts.appSecret;\n this.cachePath = path.join(\n opts.cacheDir ?? path.join(process.cwd(), '.aipack', 'observability'),\n `${opts.appId}.json`,\n );\n this.maxCacheSize = opts.maxCacheSize ?? 2000;\n this.timeoutMs = opts.timeoutMs ?? 5000;\n this.fetchImpl = opts.fetchImpl ?? globalThis.fetch;\n }\n\n /** 上报批次;失败自动落缓存,返回是否成功(测试断言用) */\n async send(batch: EventBatch): Promise<boolean> {\n this.pending.push(batch);\n if (this.sending) return false; // 已有一轮发送在跑,数据会被顺带带走\n this.sending = true;\n try {\n return await this.drain();\n } finally {\n this.sending = false;\n }\n }\n\n /** 排空所有批次(含缓存补报)。返回最后一轮是否成功 */\n private async drain(): Promise<boolean> {\n let lastOk = false;\n for (;;) {\n const cache = await this.readCache();\n const batch = mergeBatches(cache, ...this.pending);\n this.pending = [];\n if (isEmpty(batch)) return lastOk;\n\n lastOk = await this.tryUpload(batch);\n if (!lastOk) {\n // 失败 → 写回缓存,等待下次 flush\n await this.writeCache(batch);\n return false;\n }\n // 成功 → 清缓存\n await this.removeCache();\n }\n }\n\n /** 尝试上报;返回是否成功(4xx 视为最终失败,不再重试) */\n private async tryUpload(batch: EventBatch): Promise<boolean> {\n try {\n const ac = new AbortController();\n const timer = setTimeout(() => ac.abort(), this.timeoutMs);\n try {\n const res = await this.fetchImpl(`${this.endpoint}/api/v1/ingest`, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n 'x-app-id': this.appId,\n 'x-app-secret': this.appSecret,\n },\n body: JSON.stringify({ appId: this.appId, ...batch }),\n signal: ac.signal,\n });\n if (res.ok) return true;\n if (res.status >= 400 && res.status < 500 && res.status !== 429) {\n // 鉴权/参数错误:重试无意义,丢弃\n console.warn(\n `[aipack/observability] 上报被拒绝(${res.status}),丢弃 ${count(batch)} 条。检查 appId/appSecret 与 endpoint。`,\n );\n return true; // 视为已处理(丢弃),不再缓存\n }\n // 5xx / 429:可重试\n console.warn(`[aipack/observability] 上报失败(${res.status}),将缓存补报。`);\n return false;\n } finally {\n clearTimeout(timer);\n }\n } catch (err) {\n // 网络错误 / 超时:可重试\n console.warn('[aipack/observability] 上报网络错误,将缓存补报:', (err as Error).message);\n return false;\n }\n }\n\n // ─── 本地缓存 ──────────────────────────────────────────────────\n\n private async readCache(): Promise<EventBatch> {\n try {\n const raw = await fs.readFile(this.cachePath, 'utf8');\n const parsed = JSON.parse(raw) as EventBatch;\n return {\n runs: Array.isArray(parsed.runs) ? parsed.runs : [],\n spans: Array.isArray(parsed.spans) ? parsed.spans : [],\n toolCalls: Array.isArray(parsed.toolCalls) ? parsed.toolCalls : [],\n permissions: Array.isArray(parsed.permissions) ? parsed.permissions : [],\n retries: Array.isArray(parsed.retries) ? parsed.retries : [],\n events: Array.isArray(parsed.events) ? parsed.events : [],\n };\n } catch {\n return EMPTY_BATCH();\n }\n }\n\n private async writeCache(batch: EventBatch): Promise<void> {\n try {\n await fs.mkdir(path.dirname(this.cachePath), { recursive: true });\n await fs.writeFile(this.cachePath, JSON.stringify(trimBatch(batch, this.maxCacheSize)), 'utf8');\n } catch (err) {\n console.warn('[aipack/observability] 缓存写入失败(数据丢弃):', (err as Error).message);\n }\n }\n\n private async removeCache(): Promise<void> {\n try {\n await fs.unlink(this.cachePath);\n } catch {\n // 无缓存文件属正常\n }\n }\n}\n\n// ─── 工具 ─────────────────────────────────────────────────────────\n\nfunction isEmpty(batch: EventBatch): boolean {\n return (\n !batch.runs.length &&\n !batch.spans.length &&\n !batch.toolCalls.length &&\n !batch.permissions.length &&\n !batch.retries.length &&\n !batch.events.length\n );\n}\n\nfunction count(batch: EventBatch): number {\n return (\n batch.runs.length +\n batch.spans.length +\n batch.toolCalls.length +\n batch.permissions.length +\n batch.retries.length +\n batch.events.length\n );\n}\n\nfunction mergeBatches(...batches: EventBatch[]): EventBatch {\n const out = EMPTY_BATCH();\n for (const b of batches) {\n out.runs.push(...b.runs);\n out.spans.push(...b.spans);\n out.toolCalls.push(...b.toolCalls);\n out.permissions.push(...b.permissions);\n out.retries.push(...b.retries);\n out.events.push(...b.events);\n }\n return out;\n}\n\n/** 超过上限时按 runs → spans → toolCalls → permissions → retries → events 顺序从头部裁剪(保留最新) */\nfunction trimBatch(batch: EventBatch, max: number): EventBatch {\n const total = count(batch);\n if (total <= max) return batch;\n let excess = total - max;\n const out: EventBatch = {\n ...batch,\n runs: [...batch.runs],\n spans: [...batch.spans],\n toolCalls: [...batch.toolCalls],\n permissions: [...batch.permissions],\n retries: [...batch.retries],\n events: [...batch.events],\n };\n for (const key of ['runs', 'spans', 'toolCalls', 'permissions', 'retries', 'events'] as const) {\n if (excess <= 0) break;\n const removed = Math.min(out[key].length, excess);\n out[key].splice(0, removed);\n excess -= removed;\n }\n return out;\n}\n","/**\n * Telemetry 接口实现(客户端 SDK):事件 → 原始记录 → 上报队列 → HttpReporter。\n *\n * 埋点模式:不在本地聚合/落盘,事件同步转成 RunRecord / SpanRecord / ToolCallRecord /\n * PermissionRecord 后入队,由定时器批量 POST 到收集服务。上报失败由 reporter 缓存补报,\n * 事件路径零阻塞、失败不阻断 run()。\n *\n * 载荷无 startedAt 字段:用「到达时刻 - durationMs」推导;run 级用 onRunStart.queuedAt。\n *\n * 注意:runtime 的 emitTelemetry 会解构方法后调用(fn(info)),故所有回调必须为\n * 箭头函数属性以绑定 this,否则 this.queued / this.startedAt 为 undefined。\n */\n\nimport type {\n Telemetry,\n RunStartTelemetryInfo,\n RunTelemetryInfo,\n ToolTelemetryInfo,\n ModelTelemetryInfo,\n RetryTelemetryInfo,\n PermissionDeniedTelemetryInfo,\n} from '@aipack-ai/agent';\nimport type { RunRecord, SpanRecord, ToolCallRecord, PermissionRecord, EventBatch } from './types';\n\nexport interface FlushQueueOptions {\n /** 上报周期(ms),默认 5000 */\n intervalMs?: number;\n /** 积攒条数触发(含 runs/spans/toolCalls/permissions 总数),默认 50 */\n batchSize?: number;\n /** P2-1 明细采样率(0–1):只作用于 model/tool spans 与 toolCalls;runs/permissions/events 全量。缺省 1(全量) */\n sampleRate?: number;\n /** P2-1 脱敏钩子:send 前对整批改写(防 PII 明文上报) */\n redact?: (batch: EventBatch) => EventBatch;\n /** 发布版本:注入每条 run 记录(RunRecord.appVersion),供收集端按版本聚合。缺省不携带 */\n appVersion?: string;\n}\n\nconst emptyBatch = (): EventBatch => ({\n runs: [],\n spans: [],\n toolCalls: [],\n permissions: [],\n retries: [],\n events: [],\n});\n\nexport class ObservabilityTelemetry implements Telemetry {\n private queued: EventBatch = emptyBatch();\n private timer: NodeJS.Timeout | undefined;\n private startedAt = new Map<string, number>(); // traceId -> queuedAt\n /** in-flight run 顺序(最近开始的排最后),供 logger 关联当前 traceId */\n private inFlightOrder: string[] = [];\n private batchSize: number;\n private sampleRate: number;\n private redact?: (batch: EventBatch) => EventBatch;\n private appVersion?: string;\n\n constructor(private reporter: { send(batch: EventBatch): Promise<boolean> }, opts: FlushQueueOptions = {}) {\n this.batchSize = opts.batchSize ?? 50;\n this.sampleRate = opts.sampleRate ?? 1;\n this.redact = opts.redact;\n this.appVersion = opts.appVersion;\n const intervalMs = opts.intervalMs ?? 5000;\n this.timer = setInterval(() => void this.flush(), intervalMs);\n this.timer.unref?.();\n }\n\n onRunStart = (info: RunStartTelemetryInfo): void => {\n this.startedAt.set(info.traceId, info.queuedAt);\n this.inFlightOrder = this.inFlightOrder.filter((id) => id !== info.traceId);\n this.inFlightOrder.push(info.traceId);\n };\n\n onRunEnd = (info: RunTelemetryInfo): void => {\n const queuedAt = this.startedAt.get(info.traceId) ?? Date.now() - info.durationMs;\n this.startedAt.delete(info.traceId);\n this.inFlightOrder = this.inFlightOrder.filter((id) => id !== info.traceId);\n\n this.queued.runs.push(this.runRecord(info, queuedAt));\n this.queued.spans.push(this.runSpan(info, queuedAt));\n this.maybeFlush();\n };\n\n onToolCall = (info: ToolTelemetryInfo): void => {\n if (!this.sample()) return; // P2-1 明细采样\n const startedAt = Date.now() - info.durationMs;\n this.queued.spans.push({\n traceId: info.traceId,\n spanId: info.spanId,\n kind: 'tool',\n name: `tool:${info.toolName}`,\n startedAt,\n durationMs: info.durationMs,\n status: info.status === 'ok' ? 'ok' : 'error',\n errorClass: info.errorClass,\n sessionKey: info.sessionKey,\n });\n this.queued.toolCalls.push({\n traceId: info.traceId,\n spanId: info.spanId,\n toolName: info.toolName,\n status: info.status,\n durationMs: info.durationMs,\n errorClass: info.errorClass,\n });\n this.maybeFlush();\n };\n\n onModelCall = (info: ModelTelemetryInfo): void => {\n if (!this.sample()) return; // P2-1 明细采样\n this.queued.spans.push({\n traceId: info.traceId,\n spanId: info.spanId,\n kind: 'model',\n name: `model:${info.modelId}`,\n startedAt: Date.now() - info.durationMs,\n durationMs: info.durationMs,\n status: info.errorClass ? 'error' : 'ok',\n errorClass: info.errorClass,\n attempts: info.attempts,\n inputTokens: info.inputTokens,\n outputTokens: info.outputTokens,\n cacheRead: info.cacheRead,\n cacheWrite: info.cacheWrite,\n sessionKey: info.sessionKey,\n });\n this.maybeFlush();\n };\n\n onRetry = (info: RetryTelemetryInfo): void => {\n // P2-2:per-attempt 重试明细(关联 model span),server 落 retry_attempts 表\n this.queued.retries.push({\n traceId: info.traceId,\n spanId: info.spanId,\n provider: info.provider,\n modelId: info.modelId,\n attempt: info.attempt,\n errorClass: info.errorClass,\n status: info.status,\n delayMs: info.delayMs,\n timestamp: Date.now(),\n });\n this.maybeFlush();\n };\n\n onPermissionDenied = (info: PermissionDeniedTelemetryInfo): void => {\n this.queued.permissions.push({\n traceId: info.traceId,\n sessionKey: info.sessionKey,\n toolName: info.toolName,\n reason: info.reason,\n timestamp: Date.now(),\n });\n this.maybeFlush();\n };\n\n /**\n * 当前 run 上下文(供结构化 logger 注入 traceId)。\n * 单并发时返回唯一 in-flight run;并发时返回最近开始的(约定语义,文档说明)。\n */\n currentContext(): { traceId?: string } {\n const traceId = this.inFlightOrder[this.inFlightOrder.length - 1];\n return traceId ? { traceId } : {};\n }\n\n /**\n * P2-1 自定义业务事件(通用埋点)。\n * run 内调用自动注入当前 traceId;run 外可通过 opts 显式传 traceId/sessionKey。\n */\n emit(name: string, data?: unknown, opts: { traceId?: string; sessionKey?: string } = {}): void {\n this.queued.events.push({\n traceId: opts.traceId ?? this.currentContext().traceId,\n sessionKey: opts.sessionKey,\n name,\n data,\n timestamp: Date.now(),\n });\n this.maybeFlush();\n }\n\n /** 立即上报队列中残留的数据(fire-and-forget,失败由 reporter 缓存补报) */\n flush(): void {\n if (this.isEmpty()) return;\n const batch = this.queued;\n this.queued = emptyBatch();\n void this.reporter.send(this.redact ? this.redact(batch) : batch);\n }\n\n /** 停止定时器并等残留上报完成(进程退出前调用) */\n async close(): Promise<void> {\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = undefined;\n }\n if (this.isEmpty()) return;\n const batch = this.queued;\n this.queued = emptyBatch();\n await this.reporter.send(this.redact ? this.redact(batch) : batch);\n }\n\n /** P2-1 明细采样判定:rate>=1 恒过、<=0 恒弃、否则按概率 */\n private sample(): boolean {\n if (this.sampleRate >= 1) return true;\n if (this.sampleRate <= 0) return false;\n return Math.random() < this.sampleRate;\n }\n\n private runRecord(info: RunTelemetryInfo, queuedAt: number): RunRecord {\n return {\n traceId: info.traceId,\n startedAt: queuedAt,\n endedAt: queuedAt + info.durationMs,\n sessionKey: info.sessionKey,\n channel: info.request.channel,\n model: info.request.model,\n status: info.success\n ? 'success'\n : info.errorClass === 'validation'\n ? 'validation'\n : 'error',\n errorClass: info.errorClass,\n turns: info.turnCount,\n durationMs: info.durationMs,\n activeMs: info.activeMs,\n queuedMs: info.queuedMs,\n ttftMs: info.ttftMs,\n inputTokens: info.tokens.input,\n outputTokens: info.tokens.output,\n cacheRead: info.tokens.cacheRead,\n cacheWrite: info.tokens.cacheWrite,\n ...(this.appVersion !== undefined ? { appVersion: this.appVersion } : {}),\n };\n }\n\n private runSpan(info: RunTelemetryInfo, queuedAt: number): SpanRecord {\n return {\n traceId: info.traceId,\n spanId: info.traceId,\n kind: 'run',\n name: 'run',\n startedAt: queuedAt,\n durationMs: info.durationMs,\n status: info.success ? 'ok' : 'error',\n errorClass: info.errorClass,\n inputTokens: info.tokens.input,\n outputTokens: info.tokens.output,\n sessionKey: info.sessionKey,\n };\n }\n\n private isEmpty(): boolean {\n return (\n !this.queued.runs.length &&\n !this.queued.spans.length &&\n !this.queued.toolCalls.length &&\n !this.queued.permissions.length &&\n !this.queued.retries.length &&\n !this.queued.events.length\n );\n }\n\n private maybeFlush(): void {\n const total =\n this.queued.runs.length +\n this.queued.spans.length +\n this.queued.toolCalls.length +\n this.queued.permissions.length +\n this.queued.retries.length +\n this.queued.events.length;\n if (total >= this.batchSize) this.flush();\n }\n}\n","/**\n * 结构化 logger(P1-1 日志关联)。\n *\n * 约定:logfmt 或 JSON 输出,可通过 context() 动态注入当前上下文(如 traceId),\n * 使应用日志与 Trace 关联(排障时按 traceId 检索日志流)。\n *\n * const logger = createLogger({ format: 'logfmt', context: () => telemetry.currentContext() });\n * logger.info('tool called', { tool: 'search_flights', durationMs: 120 });\n * // => time=2026-08-11T10:00:00.000Z level=info msg=\"tool called\" tool=search_flights durationMs=120\n *\n * 与收集服务 webhook/告警日志不同:本 logger 只负责\"应用日志\"输出约定,\n * 不参与上报(上报走 telemetry)。\n */\n\nexport type LogLevel = 'debug' | 'info' | 'warn' | 'error';\nexport type LogFormat = 'logfmt' | 'json';\n\nexport interface LoggerOptions {\n /** 输出格式,默认 logfmt */\n format?: LogFormat;\n /** 最低输出级别,默认 info */\n level?: LogLevel;\n /** 每行日志的固定字段(如 service/app),会被动态 context / 单行 fields 覆盖 */\n tags?: Record<string, unknown>;\n /** 动态上下文(每行调用),例如 () => telemetry.currentContext() 注入 traceId */\n context?: () => Record<string, unknown> | undefined;\n /** 输出目标,默认 console(line 已含换行) */\n dest?: (line: string) => void;\n /** 是否注入 ISO 时间戳字段 time,默认 true */\n timestamps?: boolean;\n}\n\nexport interface Logger {\n debug(msg: string, fields?: Record<string, unknown>): void;\n info(msg: string, fields?: Record<string, unknown>): void;\n warn(msg: string, fields?: Record<string, unknown>): void;\n error(msg: string, fields?: Record<string, unknown>): void;\n /** 派生子 logger:合并部分配置(如固定业务标签) */\n child(overrides: Partial<LoggerOptions>): Logger;\n}\n\nconst LEVEL_ORDER: Record<LogLevel, number> = { debug: 10, info: 20, warn: 30, error: 40 };\n\n/** 隐藏敏感字段名(值以 *** 代替),防止 token/key 明文进日志 */\nconst REDACTED_KEYS = /(secret|token|password|key|authorization|apikey)/i;\n\nexport function createLogger(opts: LoggerOptions = {}): Logger {\n const { format = 'logfmt', dest = defaultDest } = opts;\n const minLevel = LEVEL_ORDER[opts.level ?? 'info'];\n const timestamps = opts.timestamps ?? true;\n\n const emit = (level: LogLevel, msg: string, fields?: Record<string, unknown>): void => {\n if (LEVEL_ORDER[level] < minLevel) return;\n const context = opts.context?.() ?? {};\n dest(formatLine({ format, level, msg, fields, context, tags: opts.tags, timestamps }));\n };\n\n return {\n debug: (msg, fields) => emit('debug', msg, fields),\n info: (msg, fields) => emit('info', msg, fields),\n warn: (msg, fields) => emit('warn', msg, fields),\n error: (msg, fields) => emit('error', msg, fields),\n child: (overrides) =>\n createLogger({\n format: overrides.format ?? opts.format,\n level: overrides.level ?? opts.level,\n tags: { ...opts.tags, ...overrides.tags },\n context: overrides.context ?? opts.context,\n dest: overrides.dest ?? opts.dest,\n timestamps: overrides.timestamps ?? opts.timestamps,\n }),\n };\n}\n\ninterface FormatInput {\n format: LogFormat;\n level: LogLevel;\n msg: string;\n fields?: Record<string, unknown>;\n context?: Record<string, unknown>;\n tags?: Record<string, unknown>;\n timestamps: boolean;\n}\n\nfunction formatLine(input: FormatInput): string {\n const data: Record<string, unknown> = {};\n if (input.timestamps) data.time = new Date().toISOString();\n data.level = input.level;\n data.msg = input.msg;\n // 顺序:tags → context → fields(后者覆盖前者)\n for (const src of [input.tags, input.context, input.fields]) {\n if (!src) continue;\n for (const [k, v] of Object.entries(src)) {\n data[k] = REDACTED_KEYS.test(k) ? '***' : v;\n }\n }\n return input.format === 'json' ? JSON.stringify(data) : toLogfmt(data);\n}\n\n/** logfmt 渲染:值含空白/引号/等号/反斜杠时按 RFC 引号包裹并转义 */\nfunction toLogfmt(data: Record<string, unknown>): string {\n const parts: string[] = [];\n for (const [k, v] of Object.entries(data)) {\n if (v === undefined || v === null) continue;\n parts.push(`${k}=${logfmtValue(v)}`);\n }\n return parts.join(' ');\n}\n\nfunction logfmtValue(v: unknown): string {\n if (typeof v === 'string') return quoteLogfmt(v);\n if (typeof v === 'number' || typeof v === 'boolean') return String(v);\n if (typeof v === 'bigint') return v.toString();\n // 对象/数组 → JSON 序列化后引号包裹\n return quoteLogfmt(JSON.stringify(v));\n}\n\nfunction quoteLogfmt(s: string): string {\n if (!/[ \\t\"=\\\\]/u.test(s)) return s;\n return '\"' + s.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"').replace(/\\n/g, '\\\\n') + '\"';\n}\n\nfunction defaultDest(line: string): void {\n // 走 console.log 而非 process.stdout.write,便于宿主拦截/重定向\n console.log(line);\n}\n","/**\n * OTLP/HTTP JSON trace exporter(P1-2 标准协议,档位 B 起步)。\n *\n * 无外部依赖:直接把 EventBatch 映射为 OTLP JSON ExportTraceServiceRequest\n * (protobuf JSON 编码),POST 到 OpenTelemetry Collector 的 /v1/traces。\n *\n * - traceId/spanId:OTLP 要求 16/8 字节二进制(base64),本 SDK 的 traceId 是任意\n * 字符串,故用 md5 做确定性映射(同一字符串恒得同一字节序列);\n * - 导出为 best-effort:失败仅 warn,不影响主上报链路(主链路仍走 HttpReporter);\n * - 未配置 otlp 时零开销(不构造 exporter)。\n */\n\nimport { createHash } from 'node:crypto';\nimport type { EventBatch, RunRecord, SpanRecord } from './types';\n\nexport interface OtlpExporterOptions {\n /** OTLP/HTTP JSON 端点,如 http://otel-collector:4318(POST {endpoint}/v1/traces) */\n endpoint: string;\n /** resource.service.name,默认 = appId */\n serviceName?: string;\n /** 应用标识(写入 span attribute aipack.app,便于按应用过滤) */\n appId?: string;\n /** 额外请求头(如 x-otlp-token) */\n headers?: Record<string, string>;\n /** 超时(ms),默认 3000 */\n timeoutMs?: number;\n /** 便于测试注入;默认全局 fetch */\n fetchImpl?: typeof fetch;\n}\n\nexport interface OtlpTraceExporter {\n /** 导出批次;失败内部消化,恒不抛错,返回是否成功 */\n export(batch: EventBatch): Promise<boolean>;\n}\n\n/** md5 确定性映射为 N 字节(traceId=16B / spanId=8B) */\nfunction bytesOf(input: string, n: number): Buffer {\n return createHash('md5').update(input).digest().subarray(0, n);\n}\n\nfunction str(value: unknown): { stringValue: string } {\n return { stringValue: value == null ? '' : String(value) };\n}\nfunction int64(value: number): { intValue: string } {\n return { intValue: String(Math.round(value || 0)) };\n}\n\nfunction attr(key: string, value: { stringValue?: string; intValue?: string }): object {\n return { key, value };\n}\n\n/** token 总消耗(input + output + cacheRead + cacheWrite);无 token 数据时返回 undefined */\nfunction tokenTotal(r: { inputTokens?: number; outputTokens?: number; cacheRead?: number; cacheWrite?: number }): number | undefined {\n if (r.inputTokens === undefined && r.outputTokens === undefined) return undefined;\n return (r.inputTokens ?? 0) + (r.outputTokens ?? 0) + (r.cacheRead ?? 0) + (r.cacheWrite ?? 0);\n}\n\n/** RunRecord → OTLP span(根 span,kind=INTERNAL) */\nfunction runToSpan(r: RunRecord, appId?: string): object {\n const startNs = BigInt(r.startedAt) * 1_000_000n;\n const endNs = BigInt(r.startedAt + r.durationMs) * 1_000_000n;\n const ok = r.status === 'success';\n const attrs: object[] = [\n attr('session.key', str(r.sessionKey)),\n attr('aipack.status', str(r.status)),\n attr('aipack.turns', int64(r.turns)),\n attr('aipack.duration_ms', int64(r.durationMs)),\n attr('tokens.input', int64(r.inputTokens)),\n attr('tokens.output', int64(r.outputTokens)),\n ];\n if (tokenTotal(r) !== undefined) attrs.push(attr('tokens.total', int64(tokenTotal(r)!)));\n if (appId) attrs.push(attr('aipack.app', str(appId)));\n if (r.model) attrs.push(attr('model', str(r.model)));\n if (r.errorClass) attrs.push(attr('aipack.error_class', str(r.errorClass)));\n return {\n traceId: bytesOf(r.traceId, 16).toString('base64'),\n spanId: bytesOf(`${r.traceId}:run`, 8).toString('base64'),\n name: 'agent.run',\n kind: 1, // INTERNAL\n startTimeUnixNano: startNs.toString(),\n endTimeUnixNano: endNs.toString(),\n attributes: attrs,\n status: ok ? { code: 1 } : { code: 2, message: r.errorClass || r.status },\n };\n}\n\n/** SpanRecord → OTLP span */\nfunction spanToSpan(s: SpanRecord, appId?: string): object {\n const startNs = BigInt(s.startedAt) * 1_000_000n;\n const endNs = BigInt(s.startedAt + s.durationMs) * 1_000_000n;\n const ok = s.status === 'ok';\n const attrs: object[] = [\n attr('span.kind', str(s.kind)),\n attr('session.key', str(s.sessionKey)),\n attr('aipack.status', str(s.status)),\n attr('aipack.duration_ms', int64(s.durationMs)),\n ];\n if (appId) attrs.push(attr('aipack.app', str(appId)));\n if (s.inputTokens !== undefined) attrs.push(attr('tokens.input', int64(s.inputTokens)));\n if (s.outputTokens !== undefined) attrs.push(attr('tokens.output', int64(s.outputTokens)));\n if (tokenTotal(s) !== undefined) attrs.push(attr('tokens.total', int64(tokenTotal(s)!)));\n if (s.attempts !== undefined) attrs.push(attr('aipack.attempts', int64(s.attempts)));\n if (s.errorClass) attrs.push(attr('aipack.error_class', str(s.errorClass)));\n return {\n traceId: bytesOf(s.traceId, 16).toString('base64'),\n spanId: bytesOf(s.spanId, 8).toString('base64'),\n name: s.name,\n kind: 1, // INTERNAL\n startTimeUnixNano: startNs.toString(),\n endTimeUnixNano: endNs.toString(),\n attributes: attrs,\n status: ok ? { code: 1 } : { code: 2, message: s.errorClass || s.status },\n };\n}\n\n/** EventBatch → OTLP ExportTraceServiceRequest(JSON 对象,可直接 JSON.stringify) */\nexport function toOtlpJsonTraces(batch: EventBatch, serviceName: string, appId?: string): object {\n const spans: object[] = [];\n for (const r of batch.runs) spans.push(runToSpan(r, appId));\n for (const s of batch.spans) spans.push(spanToSpan(s, appId));\n return {\n resourceSpans: [\n {\n resource: {\n attributes: [\n { key: 'service.name', value: str(serviceName) },\n { key: 'telemetry.sdk.name', value: str('aipack') },\n { key: 'telemetry.sdk.language', value: str('nodejs') },\n ],\n },\n scopeSpans: [\n {\n scope: { name: 'aipack', version: '0.1.0' },\n spans,\n },\n ],\n },\n ],\n };\n}\n\nexport function createOtlpTraceExporter(opts: OtlpExporterOptions): OtlpTraceExporter {\n const endpoint = opts.endpoint.replace(/\\/+$/, '');\n const serviceName = opts.serviceName ?? 'aipack-app';\n const timeoutMs = opts.timeoutMs ?? 3000;\n const headers = { 'content-type': 'application/json', ...opts.headers };\n const fetchImpl = opts.fetchImpl ?? globalThis.fetch;\n\n return {\n async export(batch) {\n const runs = batch.runs.length;\n const spans = batch.spans.length;\n if (!runs && !spans) return true; // 无 trace 数据(纯 tool/permission)→ 无操作\n try {\n const body = JSON.stringify(toOtlpJsonTraces(batch, serviceName, opts.appId));\n const ac = new AbortController();\n const timer = setTimeout(() => ac.abort(), timeoutMs);\n let res: Response;\n try {\n res = await fetchImpl(`${endpoint}/v1/traces`, {\n method: 'POST',\n headers,\n body,\n signal: ac.signal,\n });\n } finally {\n clearTimeout(timer);\n }\n if (res.ok) return true;\n console.warn(`[aipack/observability] OTLP 导出被拒绝(${res.status}),已跳过(不影响主上报)`);\n return false;\n } catch (err) {\n console.warn('[aipack/observability] OTLP 导出失败(不影响主上报):', (err as Error).message);\n return false;\n }\n },\n };\n}\n","/**\n * @aipack-ai/observability — aipack 可观测性上报 SDK。\n *\n * 埋点上报模式:客户端只需 appId + appSecret,6 类 Telemetry 事件自动批量\n * POST 到收集服务(默认 http://localhost:8787),失败本地缓存补报。\n *\n * const obs = createObservability({ appId: 'my-app', appSecret: 'xxx' });\n * createRuntime({ ..., telemetry: obs.telemetry });\n *\n * 收集服务见 @aipack-ai/observability-server(独立包,SQLite 落盘 + 内存聚合 + REST API)。\n */\n\nimport { HttpReporter } from './reporter';\nimport { ObservabilityTelemetry } from './telemetry';\nimport { createLogger, type Logger } from './logger';\nimport { createOtlpTraceExporter } from './otlp';\n\nexport interface CreateObservabilityOptions {\n /** 应用标识(必填),收集端用它校验身份并隔离数据 */\n appId: string;\n /** 应用密钥(必填),与收集端 OBS_APPS 白名单匹配 */\n appSecret: string;\n /** 收集服务地址,默认 http://localhost:8787 */\n endpoint?: string;\n /** 上报失败本地缓存目录,默认 ./.aipack/observability */\n cacheDir?: string;\n /** 缓存条数上限,默认 2000 */\n maxCacheSize?: number;\n /** 上报周期(ms),默认 5000 */\n flushIntervalMs?: number;\n /** 积攒条数触发上报,默认 50 */\n flushBatchSize?: number;\n /** 可选:OTLP/HTTP JSON trace 导出(推 OpenTelemetry Collector),失败不影响主上报 */\n otlp?: {\n endpoint: string;\n serviceName?: string;\n headers?: Record<string, string>;\n };\n /** P2-1 明细采样率(0–1):只对 model/tool spans 与 toolCalls 采样,runs/permissions/events 全量。缺省 1 */\n sampleRate?: number;\n /** P2-1 脱敏钩子:send 前对整批改写(防 PII 明文上报),示例见 docs/observability-roadmap.md §P2-1 */\n redact?: (batch: import('./types').EventBatch) => import('./types').EventBatch;\n /** 发布版本(如 '1.3.0'),随每条 run 上报,供收集端按版本聚合对比。缺省不携带 */\n version?: string;\n}\n\nexport interface Observability {\n /** 注入 RuntimeOptions.telemetry */\n telemetry: ObservabilityTelemetry;\n /** 结构化 logger:自动注入当前 run 的 traceId(P1-1 日志关联) */\n logger: Logger;\n /** P2-1 自定义业务事件:run 内自动带 traceId,入 Trace 时间轴 */\n emit(name: string, data?: unknown, opts?: { traceId?: string; sessionKey?: string }): void;\n /** 立即上报队列中的残留数据 */\n flush(): void;\n /** 停止定时器并等残留上报完成(进程退出前调用) */\n close(): Promise<void>;\n}\n\nexport function createObservability(opts: CreateObservabilityOptions): Observability {\n const reporter = new HttpReporter({\n endpoint: opts.endpoint ?? 'http://localhost:8787',\n appId: opts.appId,\n appSecret: opts.appSecret,\n cacheDir: opts.cacheDir,\n maxCacheSize: opts.maxCacheSize,\n });\n\n // OTLP 导出作为旁路:await 但不影响主上报结果;exporter 内部恒不抛错\n const sink: { send(batch: import('./types').EventBatch): Promise<boolean> } = reporter;\n if (opts.otlp) {\n const exporter = createOtlpTraceExporter({\n endpoint: opts.otlp.endpoint,\n serviceName: opts.otlp.serviceName ?? opts.appId,\n appId: opts.appId,\n headers: opts.otlp.headers,\n });\n sink.send = async (batch) => {\n const ok = await reporter.send(batch);\n await exporter.export(batch);\n return ok;\n };\n }\n\n const telemetry = new ObservabilityTelemetry(sink, {\n intervalMs: opts.flushIntervalMs,\n batchSize: opts.flushBatchSize,\n sampleRate: opts.sampleRate,\n redact: opts.redact,\n appVersion: opts.version,\n });\n const logger = createLogger({\n tags: { app: opts.appId },\n context: () => telemetry.currentContext(),\n });\n\n return {\n telemetry,\n logger,\n emit: (name, data, emitOpts) => telemetry.emit(name, data, emitOpts),\n flush: () => telemetry.flush(),\n close: async () => {\n await telemetry.close();\n },\n };\n}\n\n// ─── 类型导出 ─────────────────────────────────────────────────────\n\nexport { ObservabilityTelemetry } from './telemetry';\nexport type { FlushQueueOptions } from './telemetry';\nexport { HttpReporter } from './reporter';\nexport type { ReporterOptions } from './reporter';\nexport { createLogger } from './logger';\nexport type { Logger, LoggerOptions, LogLevel, LogFormat } from './logger';\nexport { createOtlpTraceExporter, toOtlpJsonTraces } from './otlp';\nexport type { OtlpTraceExporter, OtlpExporterOptions } from './otlp';\nexport type {\n RunRecord,\n SpanRecord,\n ToolCallRecord,\n PermissionRecord,\n EventRecord,\n RetryRecord,\n EventBatch,\n} from './types';\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/reporter.ts","../src/sampling.ts","../src/w3c-trace-context.ts","../src/redact/rules.ts","../src/telemetry.ts","../src/logger.ts","../src/otlp.ts","../src/index.ts"],"sourcesContent":["/**\n * HttpReporter — 把批量事件上报到收集服务(埋点模式)。\n *\n * - POST {endpoint}/api/v1/ingest,携带 x-app-id / x-app-secret 鉴权头\n * - 上报失败(网络错误 / 5xx / 429)→ 写入本地缓存文件({cacheDir}/{appId}.json),\n * 下次 send 前先补报缓存,全部成功才删除\n * - 4xx(鉴权失败、参数错误)判定为不可重试,直接丢弃并 warn,避免缓存无限重试\n * - 缓存条数上限 maxCacheSize,超出后丢弃最旧记录\n * - 并发保护:send 串行执行,重复调用合并为一次\n */\n\nimport { promises as fs } from 'node:fs';\nimport path from 'node:path';\nimport type { EventBatch } from './types';\n\nexport interface ReporterOptions {\n /** 收集服务地址,如 http://localhost:8787(必填) */\n endpoint: string;\n appId: string;\n appSecret: string;\n /** 缓存目录,默认 ./.aipack/observability */\n cacheDir?: string;\n /** 缓存条数上限(含 runs/spans/toolCalls/permissions),默认 2000 */\n maxCacheSize?: number;\n /** 上报超时(ms),默认 5000 */\n timeoutMs?: number;\n /** 便于测试注入;默认全局 fetch */\n fetchImpl?: typeof fetch;\n}\n\nconst EMPTY_BATCH = (): EventBatch => ({\n runs: [],\n spans: [],\n toolCalls: [],\n permissions: [],\n retries: [],\n events: [],\n});\n\nexport class HttpReporter {\n private endpoint: string;\n private appId: string;\n private appSecret: string;\n private cachePath: string;\n private maxCacheSize: number;\n private timeoutMs: number;\n private fetchImpl: typeof fetch;\n /** 串行发送锁:true 表示有发送在进行中 */\n private sending = false;\n /** 发送期间新到的批次,发送完成后继续补发 */\n private pending: EventBatch[] = [];\n\n constructor(opts: ReporterOptions) {\n this.endpoint = opts.endpoint.replace(/\\/+$/, '');\n this.appId = opts.appId;\n this.appSecret = opts.appSecret;\n this.cachePath = path.join(\n opts.cacheDir ?? path.join(process.cwd(), '.aipack', 'observability'),\n `${opts.appId}.json`,\n );\n this.maxCacheSize = opts.maxCacheSize ?? 2000;\n this.timeoutMs = opts.timeoutMs ?? 5000;\n this.fetchImpl = opts.fetchImpl ?? globalThis.fetch;\n }\n\n /** 上报批次;失败自动落缓存,返回是否成功(测试断言用) */\n async send(batch: EventBatch): Promise<boolean> {\n this.pending.push(batch);\n if (this.sending) return false; // 已有一轮发送在跑,数据会被顺带带走\n this.sending = true;\n try {\n return await this.drain();\n } finally {\n this.sending = false;\n }\n }\n\n /** 排空所有批次(含缓存补报)。返回最后一轮是否成功 */\n private async drain(): Promise<boolean> {\n let lastOk = false;\n for (;;) {\n const cache = await this.readCache();\n const batch = mergeBatches(cache, ...this.pending);\n this.pending = [];\n if (isEmpty(batch)) return lastOk;\n\n lastOk = await this.tryUpload(batch);\n if (!lastOk) {\n // 失败 → 写回缓存,等待下次 flush\n await this.writeCache(batch);\n return false;\n }\n // 成功 → 清缓存\n await this.removeCache();\n }\n }\n\n /** 尝试上报;返回是否成功(4xx 视为最终失败,不再重试) */\n private async tryUpload(batch: EventBatch): Promise<boolean> {\n try {\n const ac = new AbortController();\n const timer = setTimeout(() => ac.abort(), this.timeoutMs);\n try {\n const res = await this.fetchImpl(`${this.endpoint}/api/v1/ingest`, {\n method: 'POST',\n headers: {\n 'content-type': 'application/json',\n 'x-app-id': this.appId,\n 'x-app-secret': this.appSecret,\n },\n body: JSON.stringify({ appId: this.appId, ...batch }),\n signal: ac.signal,\n });\n if (res.ok) return true;\n if (res.status >= 400 && res.status < 500 && res.status !== 429) {\n // 鉴权/参数错误:重试无意义,丢弃\n console.warn(\n `[aipack/observability] 上报被拒绝(${res.status}),丢弃 ${count(batch)} 条。检查 appId/appSecret 与 endpoint。`,\n );\n return true; // 视为已处理(丢弃),不再缓存\n }\n // 5xx / 429:可重试\n console.warn(`[aipack/observability] 上报失败(${res.status}),将缓存补报。`);\n return false;\n } finally {\n clearTimeout(timer);\n }\n } catch (err) {\n // 网络错误 / 超时:可重试\n console.warn('[aipack/observability] 上报网络错误,将缓存补报:', (err as Error).message);\n return false;\n }\n }\n\n // ─── 本地缓存 ──────────────────────────────────────────────────\n\n private async readCache(): Promise<EventBatch> {\n try {\n const raw = await fs.readFile(this.cachePath, 'utf8');\n const parsed = JSON.parse(raw) as EventBatch;\n return {\n runs: Array.isArray(parsed.runs) ? parsed.runs : [],\n spans: Array.isArray(parsed.spans) ? parsed.spans : [],\n toolCalls: Array.isArray(parsed.toolCalls) ? parsed.toolCalls : [],\n permissions: Array.isArray(parsed.permissions) ? parsed.permissions : [],\n retries: Array.isArray(parsed.retries) ? parsed.retries : [],\n events: Array.isArray(parsed.events) ? parsed.events : [],\n };\n } catch {\n return EMPTY_BATCH();\n }\n }\n\n private async writeCache(batch: EventBatch): Promise<void> {\n try {\n await fs.mkdir(path.dirname(this.cachePath), { recursive: true });\n await fs.writeFile(this.cachePath, JSON.stringify(trimBatch(batch, this.maxCacheSize)), 'utf8');\n } catch (err) {\n console.warn('[aipack/observability] 缓存写入失败(数据丢弃):', (err as Error).message);\n }\n }\n\n private async removeCache(): Promise<void> {\n try {\n await fs.unlink(this.cachePath);\n } catch {\n // 无缓存文件属正常\n }\n }\n}\n\n// ─── 工具 ─────────────────────────────────────────────────────────\n\nfunction isEmpty(batch: EventBatch): boolean {\n return (\n !batch.runs.length &&\n !batch.spans.length &&\n !batch.toolCalls.length &&\n !batch.permissions.length &&\n !batch.retries.length &&\n !batch.events.length\n );\n}\n\nfunction count(batch: EventBatch): number {\n return (\n batch.runs.length +\n batch.spans.length +\n batch.toolCalls.length +\n batch.permissions.length +\n batch.retries.length +\n batch.events.length\n );\n}\n\nfunction mergeBatches(...batches: EventBatch[]): EventBatch {\n const out = EMPTY_BATCH();\n for (const b of batches) {\n out.runs.push(...b.runs);\n out.spans.push(...b.spans);\n out.toolCalls.push(...b.toolCalls);\n out.permissions.push(...b.permissions);\n out.retries.push(...b.retries);\n out.events.push(...b.events);\n }\n return out;\n}\n\n/** 超过上限时按 runs → spans → toolCalls → permissions → retries → events 顺序从头部裁剪(保留最新) */\nfunction trimBatch(batch: EventBatch, max: number): EventBatch {\n const total = count(batch);\n if (total <= max) return batch;\n let excess = total - max;\n const out: EventBatch = {\n ...batch,\n runs: [...batch.runs],\n spans: [...batch.spans],\n toolCalls: [...batch.toolCalls],\n permissions: [...batch.permissions],\n retries: [...batch.retries],\n events: [...batch.events],\n };\n for (const key of ['runs', 'spans', 'toolCalls', 'permissions', 'retries', 'events'] as const) {\n if (excess <= 0) break;\n const removed = Math.min(out[key].length, excess);\n out[key].splice(0, removed);\n excess -= removed;\n }\n return out;\n}\n","/**\n * Phase 9 — 采样策略模块。\n *\n * 三种采样策略:\n * - none: 不采样(全量,或兼容旧 sampleRate 行为)\n * - traceid-ratio: 一致性 head 采样。同 traceId 全量子采样(runs/spans/toolCalls 一致)\n * - error-priority: 错误 run 必采,成功 run 按 rate 采样\n * - slow-priority: 慢请求(> P95 阈值)必采,其余按 rate 采样\n */\n\nexport type SampleStrategy =\n | { type: 'none' }\n | { type: 'traceid-ratio'; rate: number }\n | { type: 'error-priority'; successRate: number }\n | { type: 'slow-priority'; rate: number; slowThresholdMs?: number }\n /** 【旧兼容】sampleRate 的语义:整 run 全量保留,仅过滤 model/tool spans 与 toolCalls(明细级) */\n | { type: 'legacy-detail-ratio'; rate: number };\n\nexport interface SampleStrategyOptions {\n /** 采样策略:'traceid-ratio' | 'error-priority' | 'slow-priority' | 'none'(默认 none) */\n strategy?: 'traceid-ratio' | 'error-priority' | 'slow-priority' | 'none';\n /** traceid-ratio / slow-priority 的采样率(0-1),默认 0.1 */\n rate?: number;\n /** error-priority 下成功 run 的采样率(0-1),默认 0.1 */\n successRate?: number;\n /** slow-priority 下慢请求阈值(ms),未配置时走 SDK 内部滑动 P95(若无法统计则默认 30000ms) */\n slowThresholdMs?: number;\n}\n\n/** 将用户配置解析为结构化 SampleStrategy */\nexport function resolveSampleStrategy(opts: SampleStrategyOptions): SampleStrategy {\n const type = opts.strategy ?? 'none';\n switch (type) {\n case 'traceid-ratio': {\n const rate = clamp01(opts.rate ?? 0.1);\n return { type: 'traceid-ratio', rate };\n }\n case 'error-priority': {\n const successRate = clamp01(opts.successRate ?? 0.1);\n return { type: 'error-priority', successRate };\n }\n case 'slow-priority': {\n const rate = clamp01(opts.rate ?? 0.1);\n return { type: 'slow-priority', rate, slowThresholdMs: opts.slowThresholdMs };\n }\n case 'none':\n default:\n return { type: 'none' };\n }\n}\n\n/** 旧 sampleRate(0–1 简单概率)转换为等价策略:\n * 【重要】语义保留:整 run(+ run span)全量保留,仅 model/tool spans 与 toolCalls 按 rate 过滤。\n */\nexport function legacySampleRateToStrategy(rate: number | undefined): SampleStrategy {\n if (rate === undefined || rate >= 1) return { type: 'none' };\n const r = clamp01(rate);\n return { type: 'legacy-detail-ratio', rate: r };\n}\n\n// ─── 采样判定器(SDK 单例,内部维护状态)────────────────────────\n\nexport interface SamplingJudge {\n /** run 是否采样:只对整 run 的\"是否保留\"决策;spans/toolCalls 随 run */\n shouldKeepRun(info: {\n traceId: string;\n status: 'success' | 'error' | 'validation';\n durationMs: number;\n }): boolean;\n /** 明细(span/toolCall)采样:当 run 采样通过时才进一步过滤;未通过直接 false */\n shouldKeepDetail(traceId: string): boolean;\n}\n\nexport function createSamplingJudge(strategy: SampleStrategy): SamplingJudge {\n // ─── traceid-ratio:一致性哈希,同 trace 结果稳定 ────────────────\n if (strategy.type === 'traceid-ratio') {\n const rate = strategy.rate;\n if (rate <= 0) {\n return {\n shouldKeepRun: () => false,\n shouldKeepDetail: () => false,\n };\n }\n if (rate >= 1) {\n return {\n shouldKeepRun: () => true,\n shouldKeepDetail: () => true,\n };\n }\n return {\n shouldKeepRun: ({ traceId }) => traceIdConsistentHash(traceId) < rate,\n shouldKeepDetail: (traceId) => traceIdConsistentHash(traceId) < rate,\n };\n }\n\n // ─── error-priority:错误必采,成功按 successRate 概率采样 ───────\n if (strategy.type === 'error-priority') {\n const { successRate } = strategy;\n if (successRate <= 0) {\n // 仅保留错误\n return {\n shouldKeepRun: ({ status }) => status === 'error',\n shouldKeepDetail: (traceId) => sampledRunSet.has(traceId),\n };\n }\n if (successRate >= 1) {\n return {\n shouldKeepRun: () => true,\n shouldKeepDetail: () => true,\n };\n }\n return {\n shouldKeepRun: ({ traceId, status }) => {\n const keep = status === 'error' ? true : Math.random() < successRate;\n if (keep) sampledRunSet.add(traceId);\n else sampledRunSet.delete(traceId);\n return keep;\n },\n shouldKeepDetail: (traceId) => sampledRunSet.has(traceId),\n };\n }\n\n // ─── slow-priority:慢请求必采,其余按 rate 采样 ───────────────\n if (strategy.type === 'slow-priority') {\n const { rate, slowThresholdMs } = strategy;\n // 慢阈值:未配置时滑动 P95 近似(前 1000 条 duration 升序数组 + p95)\n const window = new SlidingP95();\n const thresholdFallback = slowThresholdMs ?? 30_000;\n if (rate <= 0) {\n return {\n shouldKeepRun: ({ traceId, durationMs }) => {\n const p95 = window.get() ?? thresholdFallback;\n window.push(durationMs);\n const keep = durationMs >= p95;\n if (keep) sampledRunSet.add(traceId);\n else sampledRunSet.delete(traceId);\n return keep;\n },\n shouldKeepDetail: (traceId) => sampledRunSet.has(traceId),\n };\n }\n if (rate >= 1) {\n return {\n shouldKeepRun: () => true,\n shouldKeepDetail: () => true,\n };\n }\n return {\n shouldKeepRun: ({ traceId, durationMs }) => {\n const p95 = window.get() ?? thresholdFallback;\n window.push(durationMs);\n const keep = durationMs >= p95 ? true : Math.random() < rate;\n if (keep) sampledRunSet.add(traceId);\n else sampledRunSet.delete(traceId);\n return keep;\n },\n shouldKeepDetail: (traceId) => sampledRunSet.has(traceId),\n };\n }\n\n // ─── legacy-detail-ratio:旧 sampleRate 语义(run 全量,仅明细过滤)─────\n if (strategy.type === 'legacy-detail-ratio') {\n const rate = strategy.rate;\n if (rate <= 0) {\n // rate=0:run 保留,所有明细丢弃\n return {\n shouldKeepRun: ({ traceId }) => {\n sampledRunSet.add(traceId);\n return true;\n },\n shouldKeepDetail: () => false,\n };\n }\n if (rate >= 1) {\n return {\n shouldKeepRun: ({ traceId }) => {\n sampledRunSet.add(traceId);\n return true;\n },\n shouldKeepDetail: () => true,\n };\n }\n return {\n shouldKeepRun: ({ traceId }) => {\n sampledRunSet.add(traceId);\n return true;\n },\n shouldKeepDetail: (traceId) => traceIdConsistentHash(traceId) < rate,\n };\n }\n\n // ─── none:全量 ───────────────────────────────────────────────\n return {\n shouldKeepRun: ({ traceId }) => {\n sampledRunSet.add(traceId);\n return true;\n },\n shouldKeepDetail: () => true,\n };\n}\n\n// ─── 内部工具 ────────────────────────────────────────────────────\n\n/** 已采样通过的 traceId 集合:用于 shouldKeepDetail 联动判定 */\nconst sampledRunSet = new Set<string>();\n\nfunction clamp01(v: number): number {\n if (!Number.isFinite(v)) return 0;\n if (v < 0) return 0;\n if (v > 1) return 1;\n return v;\n}\n\n/** traceId → [0,1) 一致性哈希(简单但足够的 MD5 截断替代:FNV-1a 32bit / 2^32) */\nfunction traceIdConsistentHash(traceId: string): number {\n let h = 0x811c9dc5; // FNV offset basis\n for (let i = 0; i < traceId.length; i++) {\n h ^= traceId.charCodeAt(i);\n h = Math.imul(h, 0x01000193); // FNV prime\n }\n return (h >>> 0) / 0x100000000;\n}\n\n/** 滑动 P95 近似:维护最近 N 条 duration,取排序后第 95% 位置 */\nclass SlidingP95 {\n private buf: number[] = [];\n private static readonly CAP = 1000;\n\n push(d: number): void {\n this.buf.push(d);\n if (this.buf.length > SlidingP95.CAP) this.buf.shift();\n }\n\n /** 返回 p95,样本不足返回 undefined(调用方 fallback 到默认阈值) */\n get(): number | undefined {\n if (this.buf.length < 30) return undefined;\n const sorted = [...this.buf].sort((a, b) => a - b);\n const idx = Math.floor(sorted.length * 0.95);\n return sorted[Math.min(idx, sorted.length - 1)];\n }\n}\n","/**\n * Phase 9 — W3C Trace Context 支持。\n *\n * 标准:https://www.w3.org/TR/trace-context/\n *\n * traceparent 格式:\n * version-traceid-parentid-traceflags\n * 例:00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-01\n *\n * - version: 2 hex (00)\n * - trace-id: 32 hex(16 bytes,必须非全 0)\n * - parent-id: 16 hex(8 bytes,必须非全 0)\n * - trace-flags: 2 hex(bit 0 = sampled,其余保留为 0)\n */\n\nexport interface W3cTraceContext {\n /** 标准 W3C trace-id(32 hex 小写) */\n traceId: string;\n /** 标准 W3C parent-id / span-id(16 hex 小写) */\n parentId: string;\n /** 采样标记(trace-flags 最低位) */\n sampled: boolean;\n /** 原始 traceparent 字符串(用于回传) */\n raw: string;\n}\n\nexport type ParseResult =\n | { ok: true; ctx: W3cTraceContext }\n | { ok: false; reason: string };\n\n/** 解析 traceparent 头。版本 ff 或格式不合法时返回 { ok:false },不抛错。 */\nexport function parseTraceparent(raw: string | null | undefined): ParseResult {\n if (!raw) return { ok: false, reason: 'missing' };\n // 允许前后空白 + 大小写不敏感\n const s = raw.trim().toLowerCase();\n const parts = s.split('-');\n if (parts.length !== 4) return { ok: false, reason: 'format: parts != 4' };\n const [versionHex, traceIdHex, parentIdHex, flagsHex] = parts;\n if (!/^[0-9a-f]{2}$/.test(versionHex)) return { ok: false, reason: 'invalid version' };\n if (versionHex === 'ff') return { ok: false, reason: 'version ff is invalid' };\n if (!/^[0-9a-f]{32}$/.test(traceIdHex)) return { ok: false, reason: 'invalid trace-id' };\n if (/^0{32}$/.test(traceIdHex)) return { ok: false, reason: 'trace-id all zeros' };\n if (!/^[0-9a-f]{16}$/.test(parentIdHex)) return { ok: false, reason: 'invalid parent-id' };\n if (/^0{16}$/.test(parentIdHex)) return { ok: false, reason: 'parent-id all zeros' };\n if (!/^[0-9a-f]{2}$/.test(flagsHex)) return { ok: false, reason: 'invalid trace-flags' };\n const flags = parseInt(flagsHex, 16);\n return {\n ok: true,\n ctx: {\n traceId: traceIdHex,\n parentId: parentIdHex,\n sampled: (flags & 0x01) === 0x01,\n raw: s,\n },\n };\n}\n\n/** 组装 traceparent 头字符串 */\nexport function formatTraceparent(ctx: { traceId: string; parentId: string; sampled: boolean }): string {\n const flags = ctx.sampled ? '01' : '00';\n return `00-${ctx.traceId}-${ctx.parentId}-${flags}`;\n}\n\n/**\n * 把外部 W3C trace-id 映射到 aipack traceId(双向绑定)。\n *\n * 策略:\n * - 外部 w3cTraceId 与 aipack traceId 不同(aipack traceId 为随机字符串)。\n * 在 RunRecord 上同时记 w3cTraceId(外部传入)与 aipack 自身 traceId,\n * 面板通过 w3cTraceId 建立跨系统跳转。\n */\n\n/** 生成一个新的 W3C trace-id(32 hex,非全 0) */\nexport function generateW3cTraceId(): string {\n const bytes = cryptoRandomBytes(16);\n if (bytes.every((b) => b === 0)) bytes[0] = 1; // 避免全 0\n return toHex(bytes);\n}\n\n/** 生成一个新的 W3C parent-id / span-id(16 hex,非全 0) */\nexport function generateW3cParentId(): string {\n const bytes = cryptoRandomBytes(8);\n if (bytes.every((b) => b === 0)) bytes[0] = 1;\n return toHex(bytes);\n}\n\n// ─── 工具 ────────────────────────────────────────────────────────\n\n/** 最小化 crypto 接口(避免引用 dom lib 的 Crypto 类型) */\ninterface WebCryptoLike {\n getRandomValues<T extends ArrayBufferView>(array: T): T;\n}\n\nfunction cryptoRandomBytes(n: number): Uint8Array {\n // Node 18+ 全局有 crypto;回退到 Math.random 保证兼容测试/浏览器\n const cryptoObj = (globalThis as unknown as { crypto?: WebCryptoLike }).crypto;\n if (typeof globalThis !== 'undefined' && cryptoObj?.getRandomValues) {\n const buf = new Uint8Array(n);\n cryptoObj.getRandomValues(buf);\n return buf;\n }\n const out = new Uint8Array(n);\n for (let i = 0; i < n; i++) out[i] = Math.floor(Math.random() * 256);\n return out;\n}\n\nfunction toHex(bytes: Uint8Array): string {\n let s = '';\n for (const b of bytes) s += b.toString(16).padStart(2, '0');\n return s;\n}\n","/**\n * Phase 9 — PII 内置脱敏规则库。\n *\n * 默认启用:手机号 / 邮箱 / 身份证号 / 银行卡号 / IPv4 地址。\n * 脱敏策略:\n * - mask:保留前后若干位,中间用 * 替换(默认策略)\n * - hash:SHA-256 截断 16 hex(不可逆,只用于等值对比)\n * - drop:完全删除该字段(极端敏感场景)\n *\n * 本文件仅提供「规则」与「脱敏函数」,不做字段级策略配置。\n * 字段级策略(项目级)由服务端 redact_rules 表管理,SDK 侧通过 API 拉取或默认启用内置。\n */\n\nimport { createHash } from 'node:crypto';\n\nexport type RedactAction = 'mask' | 'hash' | 'drop';\n\nexport interface RedactRule {\n /** 规则名,用于面板配置匹配 */\n name: string;\n /** 匹配正则(global,允许多次命中) */\n pattern: RegExp;\n /** 默认脱敏动作 */\n defaultAction: RedactAction;\n /** 掩码函数(仅 action=mask 时调用),默认保留前后 N 位 */\n maskFn?: (match: string) => string;\n}\n\n/** 中国大陆手机号:11 位,1 开头,第二位 3-9 */\nconst PHONE_RE = /(?<!\\d)1[3-9]\\d{9}(?!\\d)/g;\n/** 邮箱:简单且够用的正则(user@domain.tld) */\nconst EMAIL_RE = /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}/g;\n/** 18 位身份证号(17 位数字 + 校验位 X/x/数字),不做校验位数学校验 */\nconst ID_CARD_RE = /(?<!\\d)\\d{17}[\\dXx](?!\\d)/g;\n/** 16-19 位银行卡号(Luhn 校验此处略过,仅按形态) */\nconst BANK_CARD_RE = /(?<!\\d)\\d{16,19}(?!\\d)/g;\n/** IPv4 地址(宽松匹配;不做 0-255 范围校验) */\nconst IPV4_RE = /\\b(?:\\d{1,3}\\.){3}\\d{1,3}\\b/g;\n\nexport const BUILTIN_RULES: RedactRule[] = [\n {\n name: 'phone',\n pattern: PHONE_RE,\n defaultAction: 'mask',\n maskFn: (m) => m.slice(0, 3) + '****' + m.slice(-4), // 139****1234\n },\n {\n name: 'email',\n pattern: EMAIL_RE,\n defaultAction: 'mask',\n maskFn: (m) => {\n const at = m.indexOf('@');\n if (at <= 1) return '*'.repeat(at) + m.slice(at);\n const user = m.slice(0, at);\n return user[0] + '*'.repeat(Math.max(at - 2, 1)) + user[at - 1] + m.slice(at);\n },\n },\n {\n name: 'idCard',\n pattern: ID_CARD_RE,\n defaultAction: 'mask',\n maskFn: (m) => m.slice(0, 6) + '********' + m.slice(-4), // 6 + 8* + 4 = 18\n },\n {\n name: 'bankCard',\n pattern: BANK_CARD_RE,\n defaultAction: 'mask',\n maskFn: (m) => m.slice(0, 4) + ' **** **** ' + m.slice(-4), // 6222 **** **** 1234\n },\n {\n name: 'ipv4',\n pattern: IPV4_RE,\n defaultAction: 'mask',\n maskFn: (m) => m.replace(/\\.\\d+\\.\\d+$/, '.*.*'), // 192.168.*.*\n },\n];\n\n/** 对单个字符串做所有内置规则的脱敏(默认 mask 动作) */\nexport function redactString(raw: string, actionOverrides?: Record<string, RedactAction>): string {\n if (!raw) return raw;\n let out = raw;\n for (const rule of BUILTIN_RULES) {\n const action = actionOverrides?.[rule.name] ?? rule.defaultAction;\n if (action === 'drop') {\n out = out.replace(rule.pattern, '');\n } else if (action === 'hash') {\n out = out.replace(rule.pattern, (m) => sha256Trunc(m));\n } else {\n // mask\n const fn = rule.maskFn ?? defaultMask;\n out = out.replace(rule.pattern, fn);\n }\n // reset regex lastIndex(global regex 会维护状态)\n rule.pattern.lastIndex = 0;\n }\n return out;\n}\n\n/** 对任意 JSON 值(对象/数组/字符串)做深度脱敏 */\nexport function redactValue<T = unknown>(value: T, actionOverrides?: Record<string, RedactAction>): T {\n if (value === null || value === undefined) return value;\n if (typeof value === 'string') return redactString(value, actionOverrides) as unknown as T;\n if (typeof value !== 'object') return value;\n if (Array.isArray(value)) {\n return value.map((v) => redactValue(v, actionOverrides)) as unknown as T;\n }\n // plain object\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) {\n out[k] = redactValue(v, actionOverrides);\n }\n return out as unknown as T;\n}\n\nfunction defaultMask(m: string): string {\n if (m.length <= 2) return '*'.repeat(m.length);\n const keepHead = Math.max(1, Math.floor(m.length * 0.25));\n const keepTail = Math.max(1, Math.floor(m.length * 0.25));\n return m.slice(0, keepHead) + '*'.repeat(m.length - keepHead - keepTail) + m.slice(-keepTail);\n}\n\nfunction sha256Trunc(s: string): string {\n return createHash('sha256').update(s).digest('hex').slice(0, 16);\n}\n","/**\n * Telemetry 接口实现(客户端 SDK):事件 → 原始记录 → 上报队列 → HttpReporter。\n *\n * 埋点模式:不在本地聚合/落盘,事件同步转成 RunRecord / SpanRecord / ToolCallRecord /\n * PermissionRecord 后入队,由定时器批量 POST 到收集服务。上报失败由 reporter 缓存补报,\n * 事件路径零阻塞、失败不阻断 run()。\n *\n * 载荷无 startedAt 字段:用「到达时刻 - durationMs」推导;run 级用 onRunStart.queuedAt。\n *\n * 注意:runtime 的 emitTelemetry 会解构方法后调用(fn(info)),故所有回调必须为\n * 箭头函数属性以绑定 this,否则 this.queued / this.startedAt 为 undefined。\n *\n * Phase 9 新增:\n * - 采样策略:error-priority / slow-priority / traceid-ratio(整 run 维度 + 明细联动)\n * - W3C Trace Context:traceparent 头解析 → parentTraceId/w3cTraceId 写入 RunRecord\n * - PII 脱敏:内置规则库深度遍历 events.data 做脱敏(默认启用,可关闭)\n */\n\nimport type {\n Telemetry,\n RunStartTelemetryInfo,\n RunTelemetryInfo,\n ToolTelemetryInfo,\n ModelTelemetryInfo,\n RetryTelemetryInfo,\n PermissionDeniedTelemetryInfo,\n} from '@aipack-ai/agent';\nimport type { RunRecord, SpanRecord, ToolCallRecord, PermissionRecord, EventBatch } from './types';\nimport {\n createSamplingJudge,\n legacySampleRateToStrategy,\n resolveSampleStrategy,\n type SampleStrategy,\n type SampleStrategyOptions,\n type SamplingJudge,\n} from './sampling';\nimport {\n parseTraceparent,\n generateW3cTraceId,\n generateW3cParentId,\n formatTraceparent,\n type W3cTraceContext,\n} from './w3c-trace-context';\nimport { redactValue, type RedactAction } from './redact/rules';\n\nexport interface FlushQueueOptions {\n /** 上报周期(ms),默认 5000 */\n intervalMs?: number;\n /** 积攒条数触发(含 runs/spans/toolCalls/permissions 总数),默认 50 */\n batchSize?: number;\n /** 【旧兼容】P2-1 明细采样率(0–1):只作用于 model/tool spans 与 toolCalls;runs/permissions/events 全量。\n * 新版 sampleStrategy 优先,sampleRate 仅在未配置 sampleStrategy 时生效(等价 traceid-ratio)。 */\n sampleRate?: number;\n /** Phase 9 — 采样策略(error-priority / slow-priority / traceid-ratio)。优先级高于 sampleRate。 */\n sampleStrategy?: SampleStrategyOptions;\n /** P2-1 脱敏钩子:send 前对整批改写(防 PII 明文上报)。\n * 当内置脱敏启用时,自定义钩子在内置之后执行(用户可进一步定制)。 */\n redact?: (batch: EventBatch) => EventBatch;\n /** Phase 9 — 是否启用内置 PII 脱敏规则(手机号/邮箱/身份证/银行卡/IP)。默认 true。 */\n redactEnabled?: boolean;\n /** Phase 9 — 字段级脱敏动作覆盖:{ phone: 'hash', email: 'drop' }。未配置规则走默认 mask。 */\n redactOverrides?: Record<string, RedactAction>;\n /** 发布版本:注入每条 run 记录(RunRecord.appVersion),供收集端按版本聚合。缺省不携带 */\n appVersion?: string;\n /**\n * Phase 9 — 外部 traceparent 上下文(来自 HTTP 请求头 / 消息队列属性)。\n * 传入字符串时自动解析;解析失败则忽略(不影响正常上报)。\n */\n traceparent?: string | W3cTraceContext;\n}\n\nconst emptyBatch = (): EventBatch => ({\n runs: [],\n spans: [],\n toolCalls: [],\n permissions: [],\n retries: [],\n events: [],\n});\n\nexport class ObservabilityTelemetry implements Telemetry {\n private queued: EventBatch = emptyBatch();\n private timer: NodeJS.Timeout | undefined;\n private startedAt = new Map<string, number>(); // traceId -> queuedAt\n /** in-flight run 顺序(最近开始的排最后),供 logger 关联当前 traceId */\n private inFlightOrder: string[] = [];\n private batchSize: number;\n private redact?: (batch: EventBatch) => EventBatch;\n private appVersion?: string;\n private redactEnabled: boolean;\n private redactOverrides?: Record<string, RedactAction>;\n\n // ─── Phase 9:采样 ───────────────────────────────────────────────\n private sampleStrategy: SampleStrategy;\n private judge: SamplingJudge;\n\n // ─── Phase 9:W3C Trace Context ──────────────────────────────────\n /** 外部传入的 traceparent 上下文(共享:所有 run 视作其子链路) */\n private w3cCtx: W3cTraceContext | null;\n /** traceId → 为该 run 生成的 w3c trace 数据(上报时注入 RunRecord) */\n private w3cByRun = new Map<string, { w3cTraceId: string; parentTraceId?: string }>();\n\n constructor(private reporter: { send(batch: EventBatch): Promise<boolean> }, opts: FlushQueueOptions = {}) {\n this.batchSize = opts.batchSize ?? 50;\n this.redact = opts.redact;\n this.appVersion = opts.appVersion;\n this.redactEnabled = opts.redactEnabled ?? true;\n this.redactOverrides = opts.redactOverrides;\n\n // ─── 采样策略:sampleStrategy 优先;缺省退回 sampleRate(兼容旧语义);都未配置 = none ───\n if (opts.sampleStrategy && opts.sampleStrategy.strategy) {\n this.sampleStrategy = resolveSampleStrategy(opts.sampleStrategy);\n } else {\n this.sampleStrategy = legacySampleRateToStrategy(opts.sampleRate);\n }\n this.judge = createSamplingJudge(this.sampleStrategy);\n\n // ─── W3C traceparent ─────────────────────────────────────────\n this.w3cCtx = parseTraceparentInput(opts.traceparent);\n\n const intervalMs = opts.intervalMs ?? 5000;\n this.timer = setInterval(() => void this.flush(), intervalMs);\n this.timer.unref?.();\n }\n\n onRunStart = (info: RunStartTelemetryInfo): void => {\n this.startedAt.set(info.traceId, info.queuedAt);\n this.inFlightOrder = this.inFlightOrder.filter((id) => id !== info.traceId);\n this.inFlightOrder.push(info.traceId);\n\n // Phase 9:为该 run 生成 W3C 上下文(即使外部未传入也生成自身 w3cTraceId,便于面板统一展示)\n const parent = this.w3cCtx;\n const w3cTraceId = parent ? parent.traceId : generateW3cTraceId();\n const parentTraceId = parent ? parent.traceId : undefined;\n this.w3cByRun.set(info.traceId, { w3cTraceId, parentTraceId });\n };\n\n onRunEnd = (info: RunTelemetryInfo): void => {\n const queuedAt = this.startedAt.get(info.traceId) ?? Date.now() - info.durationMs;\n this.startedAt.delete(info.traceId);\n this.inFlightOrder = this.inFlightOrder.filter((id) => id !== info.traceId);\n\n // Phase 9:整 run 采样判定(必须先判定,才能联动明细 shouldKeepDetail)\n const keep = this.judge.shouldKeepRun({\n traceId: info.traceId,\n status: info.success\n ? 'success'\n : info.errorClass === 'validation'\n ? 'validation'\n : 'error',\n durationMs: info.durationMs,\n });\n if (!keep) {\n this.w3cByRun.delete(info.traceId);\n return;\n }\n\n const run = this.runRecord(info, queuedAt);\n this.queued.runs.push(run);\n this.queued.spans.push(this.runSpan(info, queuedAt));\n this.maybeFlush();\n };\n\n onToolCall = (info: ToolTelemetryInfo): void => {\n if (!this.judge.shouldKeepDetail(info.traceId)) return; // Phase 9:采样联动\n const startedAt = Date.now() - info.durationMs;\n this.queued.spans.push({\n traceId: info.traceId,\n spanId: info.spanId,\n kind: 'tool',\n name: `tool:${info.toolName}`,\n startedAt,\n durationMs: info.durationMs,\n status: info.status === 'ok' ? 'ok' : 'error',\n errorClass: info.errorClass,\n sessionKey: info.sessionKey,\n });\n this.queued.toolCalls.push({\n traceId: info.traceId,\n spanId: info.spanId,\n toolName: info.toolName,\n status: info.status,\n durationMs: info.durationMs,\n errorClass: info.errorClass,\n });\n this.maybeFlush();\n };\n\n onModelCall = (info: ModelTelemetryInfo): void => {\n if (!this.judge.shouldKeepDetail(info.traceId)) return; // Phase 9:采样联动\n this.queued.spans.push({\n traceId: info.traceId,\n spanId: info.spanId,\n kind: 'model',\n name: `model:${info.modelId}`,\n startedAt: Date.now() - info.durationMs,\n durationMs: info.durationMs,\n status: info.errorClass ? 'error' : 'ok',\n errorClass: info.errorClass,\n attempts: info.attempts,\n inputTokens: info.inputTokens,\n outputTokens: info.outputTokens,\n cacheRead: info.cacheRead,\n cacheWrite: info.cacheWrite,\n sessionKey: info.sessionKey,\n });\n this.maybeFlush();\n };\n\n onRetry = (info: RetryTelemetryInfo): void => {\n if (!this.judge.shouldKeepDetail(info.traceId)) return; // Phase 9:采样联动(重试只保留已采 runs)\n // P2-2:per-attempt 重试明细(关联 model span),server 落 retry_attempts 表\n this.queued.retries.push({\n traceId: info.traceId,\n spanId: info.spanId,\n provider: info.provider,\n modelId: info.modelId,\n attempt: info.attempt,\n errorClass: info.errorClass,\n status: info.status,\n delayMs: info.delayMs,\n timestamp: Date.now(),\n });\n this.maybeFlush();\n };\n\n onPermissionDenied = (info: PermissionDeniedTelemetryInfo): void => {\n // 权限事件一般数量少,全量保留(不参与采样,便于安全审计)\n this.queued.permissions.push({\n traceId: info.traceId,\n sessionKey: info.sessionKey,\n toolName: info.toolName,\n reason: info.reason,\n timestamp: Date.now(),\n });\n this.maybeFlush();\n };\n\n /**\n * 当前 run 上下文(供结构化 logger 注入 traceId)。\n * 单并发时返回唯一 in-flight run;并发时返回最近开始的(约定语义,文档说明)。\n */\n currentContext(): { traceId?: string } {\n const traceId = this.inFlightOrder[this.inFlightOrder.length - 1];\n return traceId ? { traceId } : {};\n }\n\n /**\n * P2-1 自定义业务事件(通用埋点)。\n * run 内调用自动注入当前 traceId;run 外可通过 opts 显式传 traceId/sessionKey。\n * Phase 9:内置 PII 脱敏在 flush 阶段对 data 深度遍历。\n */\n emit(name: string, data?: unknown, opts: { traceId?: string; sessionKey?: string } = {}): void {\n this.queued.events.push({\n traceId: opts.traceId ?? this.currentContext().traceId,\n sessionKey: opts.sessionKey,\n name,\n data,\n timestamp: Date.now(),\n });\n this.maybeFlush();\n }\n\n /** Phase 9 — 主动读取/注入当前 W3C traceparent(可传给下游 HTTP 调用)。 */\n currentTraceparent(): string | null {\n const traceId = this.currentContext().traceId;\n if (!traceId) {\n // 无 in-flight run:若有共享外部 ctx,返回父 spanId;否则生成一次性\n if (this.w3cCtx) return this.w3cCtx.raw;\n return null;\n }\n const w3c = this.w3cByRun.get(traceId);\n const w3cTraceId = w3c?.w3cTraceId ?? (this.w3cCtx?.traceId ?? generateW3cTraceId());\n // 把 aipack traceId 截断/映射为 16 hex 的 spanId(deterministic)\n const parentId = aipackSpanIdToW3c(traceId);\n return formatTraceparent({ traceId: w3cTraceId, parentId, sampled: true });\n }\n\n /** 立即上报队列中残留的数据(fire-and-forget,失败由 reporter 缓存补报) */\n flush(): void {\n if (this.isEmpty()) return;\n const batch = this.queued;\n this.queued = emptyBatch();\n let final = batch;\n // Phase 9:内置 PII 脱敏(先内置规则,再用户自定义 redact 钩子)\n if (this.redactEnabled) {\n final = applyBuiltinRedact(final, this.redactOverrides);\n }\n if (this.redact) final = this.redact(final);\n void this.reporter.send(final);\n }\n\n /** 停止定时器并等残留上报完成(进程退出前调用) */\n async close(): Promise<void> {\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = undefined;\n }\n if (this.isEmpty()) return;\n const batch = this.queued;\n this.queued = emptyBatch();\n let final = batch;\n if (this.redactEnabled) final = applyBuiltinRedact(final, this.redactOverrides);\n if (this.redact) final = this.redact(final);\n await this.reporter.send(final);\n }\n\n private runRecord(info: RunTelemetryInfo, queuedAt: number): RunRecord {\n const w3c = this.w3cByRun.get(info.traceId);\n this.w3cByRun.delete(info.traceId);\n return {\n traceId: info.traceId,\n startedAt: queuedAt,\n endedAt: queuedAt + info.durationMs,\n sessionKey: info.sessionKey,\n channel: info.request.channel,\n model: info.request.model,\n status: info.success\n ? 'success'\n : info.errorClass === 'validation'\n ? 'validation'\n : 'error',\n errorClass: info.errorClass,\n turns: info.turnCount,\n durationMs: info.durationMs,\n activeMs: info.activeMs,\n queuedMs: info.queuedMs,\n ttftMs: info.ttftMs,\n inputTokens: info.tokens.input,\n outputTokens: info.tokens.output,\n cacheRead: info.tokens.cacheRead,\n cacheWrite: info.tokens.cacheWrite,\n ...(this.appVersion !== undefined ? { appVersion: this.appVersion } : {}),\n ...(w3c?.parentTraceId !== undefined ? { parentTraceId: w3c.parentTraceId } : {}),\n ...(w3c?.w3cTraceId !== undefined ? { w3cTraceId: w3c.w3cTraceId } : {}),\n };\n }\n\n private runSpan(info: RunTelemetryInfo, queuedAt: number): SpanRecord {\n return {\n traceId: info.traceId,\n spanId: info.traceId,\n kind: 'run',\n name: 'run',\n startedAt: queuedAt,\n durationMs: info.durationMs,\n status: info.success ? 'ok' : 'error',\n errorClass: info.errorClass,\n inputTokens: info.tokens.input,\n outputTokens: info.tokens.output,\n sessionKey: info.sessionKey,\n };\n }\n\n private isEmpty(): boolean {\n return (\n !this.queued.runs.length &&\n !this.queued.spans.length &&\n !this.queued.toolCalls.length &&\n !this.queued.permissions.length &&\n !this.queued.retries.length &&\n !this.queued.events.length\n );\n }\n\n private maybeFlush(): void {\n const total =\n this.queued.runs.length +\n this.queued.spans.length +\n this.queued.toolCalls.length +\n this.queued.permissions.length +\n this.queued.retries.length +\n this.queued.events.length;\n if (total >= this.batchSize) this.flush();\n }\n}\n\n// ─── 工具 ─────────────────────────────────────────────────────────\n\n/** 对整批 EventBatch 应用内置 PII 脱敏 */\nfunction applyBuiltinRedact(batch: EventBatch, overrides?: Record<string, RedactAction>): EventBatch {\n // events.data 深度遍历(最可能含 PII 的自由字段)\n const events = batch.events.map((e) =>\n e.data === undefined ? e : { ...e, data: redactValue(e.data, overrides) },\n );\n // permissions.reason 可能带用户输入\n const permissions = batch.permissions.map((p) => ({\n ...p,\n reason: redactString(p.reason, overrides),\n }));\n return {\n runs: batch.runs, // runs 字段结构化,不做默认脱敏(避免破坏聚合)\n spans: batch.spans,\n toolCalls: batch.toolCalls,\n retries: batch.retries,\n events,\n permissions,\n };\n}\n\n/** 兼容用户输入:字符串或结构化对象 */\nfunction parseTraceparentInput(input: string | W3cTraceContext | undefined): W3cTraceContext | null {\n if (!input) return null;\n if (typeof input === 'string') {\n const r = parseTraceparent(input);\n return r.ok ? r.ctx : null;\n }\n // 结构化对象\n return input && typeof input.traceId === 'string' ? input : null;\n}\n\n/** aipack traceId → 16 hex W3C parentId(deterministic:FNV-1a 截断) */\nfunction aipackSpanIdToW3c(aipackTraceId: string): string {\n let h = 0x811c9dc5;\n for (let i = 0; i < aipackTraceId.length; i++) {\n h ^= aipackTraceId.charCodeAt(i);\n h = Math.imul(h, 0x01000193);\n }\n const hex = (h >>> 0).toString(16).padStart(8, '0');\n // 16 hex 需要再补 8:后半段用简单二次哈希\n let h2 = 0x84222325;\n for (let i = aipackTraceId.length - 1; i >= 0; i--) {\n h2 ^= aipackTraceId.charCodeAt(i);\n h2 = Math.imul(h2, 0x01000193);\n }\n const hex2 = (h2 >>> 0).toString(16).padStart(8, '0');\n const out = hex + hex2;\n if (/^0{16}$/.test(out)) return '0000000000000001';\n return out;\n}\n\n/** 复用 rules.ts 的字符串脱敏(避免 circular import:此处内联简单 wrap) */\nfunction redactString(raw: string, overrides?: Record<string, RedactAction>): string {\n return redactValue<string>(raw, overrides);\n}\n","/**\n * 结构化 logger(P1-1 日志关联)。\n *\n * 约定:logfmt 或 JSON 输出,可通过 context() 动态注入当前上下文(如 traceId),\n * 使应用日志与 Trace 关联(排障时按 traceId 检索日志流)。\n *\n * const logger = createLogger({ format: 'logfmt', context: () => telemetry.currentContext() });\n * logger.info('tool called', { tool: 'search_flights', durationMs: 120 });\n * // => time=2026-08-11T10:00:00.000Z level=info msg=\"tool called\" tool=search_flights durationMs=120\n *\n * 与收集服务 webhook/告警日志不同:本 logger 只负责\"应用日志\"输出约定,\n * 不参与上报(上报走 telemetry)。\n */\n\nexport type LogLevel = 'debug' | 'info' | 'warn' | 'error';\nexport type LogFormat = 'logfmt' | 'json';\n\nexport interface LoggerOptions {\n /** 输出格式,默认 logfmt */\n format?: LogFormat;\n /** 最低输出级别,默认 info */\n level?: LogLevel;\n /** 每行日志的固定字段(如 service/app),会被动态 context / 单行 fields 覆盖 */\n tags?: Record<string, unknown>;\n /** 动态上下文(每行调用),例如 () => telemetry.currentContext() 注入 traceId */\n context?: () => Record<string, unknown> | undefined;\n /** 输出目标,默认 console(line 已含换行) */\n dest?: (line: string) => void;\n /** 是否注入 ISO 时间戳字段 time,默认 true */\n timestamps?: boolean;\n}\n\nexport interface Logger {\n debug(msg: string, fields?: Record<string, unknown>): void;\n info(msg: string, fields?: Record<string, unknown>): void;\n warn(msg: string, fields?: Record<string, unknown>): void;\n error(msg: string, fields?: Record<string, unknown>): void;\n /** 派生子 logger:合并部分配置(如固定业务标签) */\n child(overrides: Partial<LoggerOptions>): Logger;\n}\n\nconst LEVEL_ORDER: Record<LogLevel, number> = { debug: 10, info: 20, warn: 30, error: 40 };\n\n/** 隐藏敏感字段名(值以 *** 代替),防止 token/key 明文进日志 */\nconst REDACTED_KEYS = /(secret|token|password|key|authorization|apikey)/i;\n\nexport function createLogger(opts: LoggerOptions = {}): Logger {\n const { format = 'logfmt', dest = defaultDest } = opts;\n const minLevel = LEVEL_ORDER[opts.level ?? 'info'];\n const timestamps = opts.timestamps ?? true;\n\n const emit = (level: LogLevel, msg: string, fields?: Record<string, unknown>): void => {\n if (LEVEL_ORDER[level] < minLevel) return;\n const context = opts.context?.() ?? {};\n dest(formatLine({ format, level, msg, fields, context, tags: opts.tags, timestamps }));\n };\n\n return {\n debug: (msg, fields) => emit('debug', msg, fields),\n info: (msg, fields) => emit('info', msg, fields),\n warn: (msg, fields) => emit('warn', msg, fields),\n error: (msg, fields) => emit('error', msg, fields),\n child: (overrides) =>\n createLogger({\n format: overrides.format ?? opts.format,\n level: overrides.level ?? opts.level,\n tags: { ...opts.tags, ...overrides.tags },\n context: overrides.context ?? opts.context,\n dest: overrides.dest ?? opts.dest,\n timestamps: overrides.timestamps ?? opts.timestamps,\n }),\n };\n}\n\ninterface FormatInput {\n format: LogFormat;\n level: LogLevel;\n msg: string;\n fields?: Record<string, unknown>;\n context?: Record<string, unknown>;\n tags?: Record<string, unknown>;\n timestamps: boolean;\n}\n\nfunction formatLine(input: FormatInput): string {\n const data: Record<string, unknown> = {};\n if (input.timestamps) data.time = new Date().toISOString();\n data.level = input.level;\n data.msg = input.msg;\n // 顺序:tags → context → fields(后者覆盖前者)\n for (const src of [input.tags, input.context, input.fields]) {\n if (!src) continue;\n for (const [k, v] of Object.entries(src)) {\n data[k] = REDACTED_KEYS.test(k) ? '***' : v;\n }\n }\n return input.format === 'json' ? JSON.stringify(data) : toLogfmt(data);\n}\n\n/** logfmt 渲染:值含空白/引号/等号/反斜杠时按 RFC 引号包裹并转义 */\nfunction toLogfmt(data: Record<string, unknown>): string {\n const parts: string[] = [];\n for (const [k, v] of Object.entries(data)) {\n if (v === undefined || v === null) continue;\n parts.push(`${k}=${logfmtValue(v)}`);\n }\n return parts.join(' ');\n}\n\nfunction logfmtValue(v: unknown): string {\n if (typeof v === 'string') return quoteLogfmt(v);\n if (typeof v === 'number' || typeof v === 'boolean') return String(v);\n if (typeof v === 'bigint') return v.toString();\n // 对象/数组 → JSON 序列化后引号包裹\n return quoteLogfmt(JSON.stringify(v));\n}\n\nfunction quoteLogfmt(s: string): string {\n if (!/[ \\t\"=\\\\]/u.test(s)) return s;\n return '\"' + s.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"').replace(/\\n/g, '\\\\n') + '\"';\n}\n\nfunction defaultDest(line: string): void {\n // 走 console.log 而非 process.stdout.write,便于宿主拦截/重定向\n console.log(line);\n}\n","/**\n * OTLP/HTTP JSON trace exporter(P1-2 标准协议,档位 B 起步)。\n *\n * 无外部依赖:直接把 EventBatch 映射为 OTLP JSON ExportTraceServiceRequest\n * (protobuf JSON 编码),POST 到 OpenTelemetry Collector 的 /v1/traces。\n *\n * - traceId/spanId:OTLP 要求 16/8 字节二进制(base64),本 SDK 的 traceId 是任意\n * 字符串,故用 md5 做确定性映射(同一字符串恒得同一字节序列);\n * - 导出为 best-effort:失败仅 warn,不影响主上报链路(主链路仍走 HttpReporter);\n * - 未配置 otlp 时零开销(不构造 exporter)。\n */\n\nimport { createHash } from 'node:crypto';\nimport type { EventBatch, RunRecord, SpanRecord } from './types';\n\nexport interface OtlpExporterOptions {\n /** OTLP/HTTP JSON 端点,如 http://otel-collector:4318(POST {endpoint}/v1/traces) */\n endpoint: string;\n /** resource.service.name,默认 = appId */\n serviceName?: string;\n /** 应用标识(写入 span attribute aipack.app,便于按应用过滤) */\n appId?: string;\n /** 额外请求头(如 x-otlp-token) */\n headers?: Record<string, string>;\n /** 超时(ms),默认 3000 */\n timeoutMs?: number;\n /** 便于测试注入;默认全局 fetch */\n fetchImpl?: typeof fetch;\n}\n\nexport interface OtlpTraceExporter {\n /** 导出批次;失败内部消化,恒不抛错,返回是否成功 */\n export(batch: EventBatch): Promise<boolean>;\n}\n\n/** md5 确定性映射为 N 字节(traceId=16B / spanId=8B) */\nfunction bytesOf(input: string, n: number): Buffer {\n return createHash('md5').update(input).digest().subarray(0, n);\n}\n\nfunction str(value: unknown): { stringValue: string } {\n return { stringValue: value == null ? '' : String(value) };\n}\nfunction int64(value: number): { intValue: string } {\n return { intValue: String(Math.round(value || 0)) };\n}\n\nfunction attr(key: string, value: { stringValue?: string; intValue?: string }): object {\n return { key, value };\n}\n\n/** token 总消耗(input + output + cacheRead + cacheWrite);无 token 数据时返回 undefined */\nfunction tokenTotal(r: { inputTokens?: number; outputTokens?: number; cacheRead?: number; cacheWrite?: number }): number | undefined {\n if (r.inputTokens === undefined && r.outputTokens === undefined) return undefined;\n return (r.inputTokens ?? 0) + (r.outputTokens ?? 0) + (r.cacheRead ?? 0) + (r.cacheWrite ?? 0);\n}\n\n/** RunRecord → OTLP span(根 span,kind=INTERNAL) */\nfunction runToSpan(r: RunRecord, appId?: string): object {\n const startNs = BigInt(r.startedAt) * 1_000_000n;\n const endNs = BigInt(r.startedAt + r.durationMs) * 1_000_000n;\n const ok = r.status === 'success';\n const attrs: object[] = [\n attr('session.key', str(r.sessionKey)),\n attr('aipack.status', str(r.status)),\n attr('aipack.turns', int64(r.turns)),\n attr('aipack.duration_ms', int64(r.durationMs)),\n attr('tokens.input', int64(r.inputTokens)),\n attr('tokens.output', int64(r.outputTokens)),\n ];\n if (tokenTotal(r) !== undefined) attrs.push(attr('tokens.total', int64(tokenTotal(r)!)));\n if (appId) attrs.push(attr('aipack.app', str(appId)));\n if (r.model) attrs.push(attr('model', str(r.model)));\n if (r.errorClass) attrs.push(attr('aipack.error_class', str(r.errorClass)));\n return {\n traceId: bytesOf(r.traceId, 16).toString('base64'),\n spanId: bytesOf(`${r.traceId}:run`, 8).toString('base64'),\n name: 'agent.run',\n kind: 1, // INTERNAL\n startTimeUnixNano: startNs.toString(),\n endTimeUnixNano: endNs.toString(),\n attributes: attrs,\n status: ok ? { code: 1 } : { code: 2, message: r.errorClass || r.status },\n };\n}\n\n/** SpanRecord → OTLP span */\nfunction spanToSpan(s: SpanRecord, appId?: string): object {\n const startNs = BigInt(s.startedAt) * 1_000_000n;\n const endNs = BigInt(s.startedAt + s.durationMs) * 1_000_000n;\n const ok = s.status === 'ok';\n const attrs: object[] = [\n attr('span.kind', str(s.kind)),\n attr('session.key', str(s.sessionKey)),\n attr('aipack.status', str(s.status)),\n attr('aipack.duration_ms', int64(s.durationMs)),\n ];\n if (appId) attrs.push(attr('aipack.app', str(appId)));\n if (s.inputTokens !== undefined) attrs.push(attr('tokens.input', int64(s.inputTokens)));\n if (s.outputTokens !== undefined) attrs.push(attr('tokens.output', int64(s.outputTokens)));\n if (tokenTotal(s) !== undefined) attrs.push(attr('tokens.total', int64(tokenTotal(s)!)));\n if (s.attempts !== undefined) attrs.push(attr('aipack.attempts', int64(s.attempts)));\n if (s.errorClass) attrs.push(attr('aipack.error_class', str(s.errorClass)));\n return {\n traceId: bytesOf(s.traceId, 16).toString('base64'),\n spanId: bytesOf(s.spanId, 8).toString('base64'),\n name: s.name,\n kind: 1, // INTERNAL\n startTimeUnixNano: startNs.toString(),\n endTimeUnixNano: endNs.toString(),\n attributes: attrs,\n status: ok ? { code: 1 } : { code: 2, message: s.errorClass || s.status },\n };\n}\n\n/** EventBatch → OTLP ExportTraceServiceRequest(JSON 对象,可直接 JSON.stringify) */\nexport function toOtlpJsonTraces(batch: EventBatch, serviceName: string, appId?: string): object {\n const spans: object[] = [];\n for (const r of batch.runs) spans.push(runToSpan(r, appId));\n for (const s of batch.spans) spans.push(spanToSpan(s, appId));\n return {\n resourceSpans: [\n {\n resource: {\n attributes: [\n { key: 'service.name', value: str(serviceName) },\n { key: 'telemetry.sdk.name', value: str('aipack') },\n { key: 'telemetry.sdk.language', value: str('nodejs') },\n ],\n },\n scopeSpans: [\n {\n scope: { name: 'aipack', version: '0.1.0' },\n spans,\n },\n ],\n },\n ],\n };\n}\n\nexport function createOtlpTraceExporter(opts: OtlpExporterOptions): OtlpTraceExporter {\n const endpoint = opts.endpoint.replace(/\\/+$/, '');\n const serviceName = opts.serviceName ?? 'aipack-app';\n const timeoutMs = opts.timeoutMs ?? 3000;\n const headers = { 'content-type': 'application/json', ...opts.headers };\n const fetchImpl = opts.fetchImpl ?? globalThis.fetch;\n\n return {\n async export(batch) {\n const runs = batch.runs.length;\n const spans = batch.spans.length;\n if (!runs && !spans) return true; // 无 trace 数据(纯 tool/permission)→ 无操作\n try {\n const body = JSON.stringify(toOtlpJsonTraces(batch, serviceName, opts.appId));\n const ac = new AbortController();\n const timer = setTimeout(() => ac.abort(), timeoutMs);\n let res: Response;\n try {\n res = await fetchImpl(`${endpoint}/v1/traces`, {\n method: 'POST',\n headers,\n body,\n signal: ac.signal,\n });\n } finally {\n clearTimeout(timer);\n }\n if (res.ok) return true;\n console.warn(`[aipack/observability] OTLP 导出被拒绝(${res.status}),已跳过(不影响主上报)`);\n return false;\n } catch (err) {\n console.warn('[aipack/observability] OTLP 导出失败(不影响主上报):', (err as Error).message);\n return false;\n }\n },\n };\n}\n","/**\n * @aipack-ai/observability — aipack 可观测性上报 SDK。\n *\n * 埋点上报模式:客户端只需 appId + appSecret,6 类 Telemetry 事件自动批量\n * POST 到收集服务(默认 http://localhost:8787),失败本地缓存补报。\n *\n * const obs = createObservability({ appId: 'my-app', appSecret: 'xxx' });\n * createRuntime({ ..., telemetry: obs.telemetry });\n *\n * 收集服务见 @aipack-ai/observability-server(独立包,SQLite 落盘 + 内存聚合 + REST API)。\n *\n * Phase 9 新增:\n * - sampleStrategy:error-priority / slow-priority / traceid-ratio(替代简单 sampleRate)\n * - W3C Trace Context:traceparent 输入/输出(跨系统调用链打通)\n * - 内置 PII 脱敏:默认启用,支持规则级动作覆盖(mask/hash/drop)\n */\n\nimport { HttpReporter } from './reporter';\nimport { ObservabilityTelemetry } from './telemetry';\nimport { createLogger, type Logger } from './logger';\nimport { createOtlpTraceExporter } from './otlp';\nimport type { SampleStrategyOptions } from './sampling';\nimport type { W3cTraceContext } from './w3c-trace-context';\nimport type { RedactAction } from './redact/rules';\n\nexport interface CreateObservabilityOptions {\n /** 应用标识(必填),收集端用它校验身份并隔离数据 */\n appId: string;\n /** 应用密钥(必填),与收集端 OBS_APPS 白名单匹配 */\n appSecret: string;\n /** 收集服务地址,默认 http://localhost:8787 */\n endpoint?: string;\n /** 上报失败本地缓存目录,默认 ./.aipack/observability */\n cacheDir?: string;\n /** 缓存条数上限,默认 2000 */\n maxCacheSize?: number;\n /** 上报周期(ms),默认 5000 */\n flushIntervalMs?: number;\n /** 积攒条数触发上报,默认 50 */\n flushBatchSize?: number;\n /** 可选:OTLP/HTTP JSON trace 导出(推 OpenTelemetry Collector),失败不影响主上报 */\n otlp?: {\n endpoint: string;\n serviceName?: string;\n headers?: Record<string, string>;\n };\n /**\n * 【旧兼容】P2-1 明细采样率(0–1):只对 model/tool spans 与 toolCalls 采样,\n * runs/permissions/events 全量。缺省 1。\n * 新版 sampleStrategy 优先;sampleRate 未设置 sampleStrategy 时等价 traceid-ratio。\n */\n sampleRate?: number;\n /**\n * Phase 9 — 采样策略:错误/慢请求优先或一致性 traceId 采样。\n * 优先级高于 sampleRate。\n *\n * 示例:\n * // 错误 100% 保留,成功按 10% 采样\n * sampleStrategy: { strategy: 'error-priority', successRate: 0.1 }\n * // 同 trace 稳定采/弃(跨明细一致)\n * sampleStrategy: { strategy: 'traceid-ratio', rate: 0.2 }\n */\n sampleStrategy?: SampleStrategyOptions;\n /** P2-1 脱敏钩子:send 前对整批改写(防 PII 明文上报),在内置脱敏之后执行 */\n redact?: (batch: import('./types').EventBatch) => import('./types').EventBatch;\n /** Phase 9 — 是否启用内置 PII 脱敏(手机号/邮箱/身份证/银行卡/IP)。默认 true。 */\n redactEnabled?: boolean;\n /** Phase 9 — 字段级脱敏动作覆盖:{ phone: 'hash', email: 'drop', idCard: 'drop' } */\n redactOverrides?: Record<string, RedactAction>;\n /** 发布版本(如 '1.3.0'),随每条 run 上报,供收集端按版本聚合对比。缺省不携带 */\n version?: string;\n /**\n * Phase 9 — W3C Trace Context 父链路。\n * - 字符串:traceparent 请求头原值(如 `00-xxxxxx-yyyyyy-01`)\n * - 对象:已解析的 W3cTraceContext\n * 配置后,所有 run 视作其子链路,面板可跨系统跳转。\n */\n traceparent?: string | W3cTraceContext;\n}\n\nexport interface Observability {\n /** 注入 RuntimeOptions.telemetry */\n telemetry: ObservabilityTelemetry;\n /** 结构化 logger:自动注入当前 run 的 traceId(P1-1 日志关联) */\n logger: Logger;\n /** P2-1 自定义业务事件:run 内自动带 traceId,入 Trace 时间轴 */\n emit(name: string, data?: unknown, opts?: { traceId?: string; sessionKey?: string }): void;\n /** 立即上报队列中的残留数据 */\n flush(): void;\n /** 停止定时器并等残留上报完成(进程退出前调用) */\n close(): Promise<void>;\n /**\n * Phase 9 — 当前链路的 traceparent 字符串(可传给下游 HTTP 调用实现跨系统追踪)。\n * 无 in-flight run 且未配置外部 traceparent 时返回 null。\n */\n currentTraceparent(): string | null;\n}\n\nexport function createObservability(opts: CreateObservabilityOptions): Observability {\n const reporter = new HttpReporter({\n endpoint: opts.endpoint ?? 'http://localhost:8787',\n appId: opts.appId,\n appSecret: opts.appSecret,\n cacheDir: opts.cacheDir,\n maxCacheSize: opts.maxCacheSize,\n });\n\n // OTLP 导出作为旁路:await 但不影响主上报结果;exporter 内部恒不抛错\n const sink: { send(batch: import('./types').EventBatch): Promise<boolean> } = reporter;\n if (opts.otlp) {\n const exporter = createOtlpTraceExporter({\n endpoint: opts.otlp.endpoint,\n serviceName: opts.otlp.serviceName ?? opts.appId,\n appId: opts.appId,\n headers: opts.otlp.headers,\n });\n sink.send = async (batch) => {\n const ok = await reporter.send(batch);\n await exporter.export(batch);\n return ok;\n };\n }\n\n const telemetry = new ObservabilityTelemetry(sink, {\n intervalMs: opts.flushIntervalMs,\n batchSize: opts.flushBatchSize,\n sampleRate: opts.sampleRate,\n sampleStrategy: opts.sampleStrategy,\n redact: opts.redact,\n redactEnabled: opts.redactEnabled,\n redactOverrides: opts.redactOverrides,\n appVersion: opts.version,\n traceparent: opts.traceparent,\n });\n const logger = createLogger({\n tags: { app: opts.appId },\n context: () => telemetry.currentContext(),\n });\n\n return {\n telemetry,\n logger,\n emit: (name, data, emitOpts) => telemetry.emit(name, data, emitOpts),\n flush: () => telemetry.flush(),\n close: async () => {\n await telemetry.close();\n },\n currentTraceparent: () => telemetry.currentTraceparent(),\n };\n}\n\n// ─── 类型导出 ─────────────────────────────────────────────────────\n\nexport { ObservabilityTelemetry } from './telemetry';\nexport type { FlushQueueOptions } from './telemetry';\nexport { HttpReporter } from './reporter';\nexport type { ReporterOptions } from './reporter';\nexport { createLogger } from './logger';\nexport type { Logger, LoggerOptions, LogLevel, LogFormat } from './logger';\nexport { createOtlpTraceExporter, toOtlpJsonTraces } from './otlp';\nexport type { OtlpTraceExporter, OtlpExporterOptions } from './otlp';\n// Phase 9 新增导出:采样 / W3C / 脱敏\nexport {\n createSamplingJudge,\n resolveSampleStrategy,\n legacySampleRateToStrategy,\n} from './sampling';\nexport type {\n SampleStrategy,\n SampleStrategyOptions as SamplingOptions,\n SamplingJudge,\n} from './sampling';\nexport {\n parseTraceparent,\n formatTraceparent,\n generateW3cTraceId,\n generateW3cParentId,\n} from './w3c-trace-context';\nexport type { W3cTraceContext } from './w3c-trace-context';\nexport { BUILTIN_RULES, redactString, redactValue } from './redact/rules';\nexport type { RedactRule, RedactAction } from './redact/rules';\nexport type {\n RunRecord,\n SpanRecord,\n ToolCallRecord,\n PermissionRecord,\n EventRecord,\n RetryRecord,\n EventBatch,\n} from './types';\n"],"mappings":"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","names":["redactString","createHash"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aipack-ai/observability",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "aipack 可观测性上报 SDK(S2):埋点上报模式,appId+appSecret 一行接入,失败本地缓存补报",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -28,18 +28,18 @@
|
|
|
28
28
|
"node": ">=18.0.0"
|
|
29
29
|
},
|
|
30
30
|
"peerDependencies": {
|
|
31
|
-
"@aipack-ai/agent": "
|
|
31
|
+
"@aipack-ai/agent": "1.0.2"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"@types/node": "^20.12.0",
|
|
35
35
|
"tsup": "^8.5.1",
|
|
36
36
|
"tsx": "^4.16.0",
|
|
37
37
|
"typescript": "^5.5.0",
|
|
38
|
-
"@aipack-ai/agent": "
|
|
38
|
+
"@aipack-ai/agent": "1.0.2"
|
|
39
39
|
},
|
|
40
40
|
"scripts": {
|
|
41
41
|
"build": "tsup",
|
|
42
|
-
"prebuild": "pnpm --filter @aipack-ai/agent build || true",
|
|
42
|
+
"prebuild": "[ -f ../agent/dist/index.d.ts ] || pnpm --filter @aipack-ai/agent build || true",
|
|
43
43
|
"typecheck": "tsc --noEmit",
|
|
44
44
|
"test": "node --import tsx --test test/observability.test.ts"
|
|
45
45
|
}
|