@zanii/blackbox 0.12.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/otlp/index.d.ts +17 -0
- package/dist/otlp/index.js +119 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -83,6 +83,10 @@ verifyAnswerCredential(credential, lines, bodies, packs); // { ok, problems }: t
|
|
|
83
83
|
|
|
84
84
|
The same checks run offline as pure functions: `toolHallucinations`, `groundingFindings`, `factsIn`, `loadReferencePacks`, `answerRisk`, `detectorAccuracy`.
|
|
85
85
|
|
|
86
|
+
## New in 0.13.0
|
|
87
|
+
|
|
88
|
+
- `fromOtlp(body)`: OpenTelemetry spans (GenAI semconv or OpenInference) as session events, the mapping behind gateway v0.15's `POST /v1/traces`. Point any framework's OTLP exporter at the gateway with the session's token, and its spans join the record.
|
|
89
|
+
|
|
86
90
|
## New in 0.12.0
|
|
87
91
|
|
|
88
92
|
- Tax credit notes: `taxCreditNote` / `renderTaxCreditNote`, the document and printable copy behind gateway v0.14's `POST /v1/invoices/:number/credit-notes`.
|
package/dist/index.d.ts
CHANGED
|
@@ -42,7 +42,7 @@ export { commitContent, memoryChain, memoryEntry, verifyMemoryContent, verifyMem
|
|
|
42
42
|
export { type AmountUnit, anthropicStatement, type Baseline, baselines, type Connector, checkConnector, checkStatement, csvStatement, customStatement, decimalToMicroUsd, fuel, modelSpend, openaiStatement, pageParams, parseXml, reconcileBilling, requestFor, resolvePath, type Statement, } from "./money/index.ts";
|
|
43
43
|
export { deadlinesDue, filingPack, type OccurrenceFacts, type OccurrenceFramework, type OccurrenceReport, occurrenceFacts, occurrenceReport, renderOccurrence, } from "./occurrence/index.ts";
|
|
44
44
|
export { ingestOcsf, type OcsfEvent, type OcsfVersion, ocsfLine, toOcsf } from "./ocsf/index.ts";
|
|
45
|
-
export { toOtlp } from "./otlp/index.ts";
|
|
45
|
+
export { fromOtlp, type OtlpEvent, toOtlp } from "./otlp/index.ts";
|
|
46
46
|
export { checkIdentifier, checkPack, corePack, type PackEntry, packData, packFrameworks, } from "./packs/index.ts";
|
|
47
47
|
export { type PolicyChange, type PolicyDelta, policyDelta } from "./policy/delta.ts";
|
|
48
48
|
export { type Draft, type DraftGroup, policyDrafts } from "./policy/drafts.ts";
|
package/dist/index.js
CHANGED
|
@@ -44,7 +44,7 @@ export { commitContent, memoryChain, memoryEntry, verifyMemoryContent, verifyMem
|
|
|
44
44
|
export { anthropicStatement, baselines, checkConnector, checkStatement, csvStatement, customStatement, decimalToMicroUsd, fuel, modelSpend, openaiStatement, pageParams, parseXml, reconcileBilling, requestFor, resolvePath, } from "./money/index.js";
|
|
45
45
|
export { deadlinesDue, filingPack, occurrenceFacts, occurrenceReport, renderOccurrence, } from "./occurrence/index.js";
|
|
46
46
|
export { ingestOcsf, ocsfLine, toOcsf } from "./ocsf/index.js";
|
|
47
|
-
export { toOtlp } from "./otlp/index.js";
|
|
47
|
+
export { fromOtlp, toOtlp } from "./otlp/index.js";
|
|
48
48
|
export { checkIdentifier, checkPack, corePack, packData, packFrameworks, } from "./packs/index.js";
|
|
49
49
|
export { policyDelta } from "./policy/delta.js";
|
|
50
50
|
export { policyDrafts } from "./policy/drafts.js";
|
package/dist/otlp/index.d.ts
CHANGED
|
@@ -28,4 +28,21 @@ export declare function toOtlp(lines: readonly string[], options?: {
|
|
|
28
28
|
}[];
|
|
29
29
|
}[];
|
|
30
30
|
};
|
|
31
|
+
/** One event for `POST /v1/sessions/:id/sdk-events`, numbered by the server (spec/sdk.md §1). */
|
|
32
|
+
export interface OtlpEvent {
|
|
33
|
+
event_id: string;
|
|
34
|
+
type: string;
|
|
35
|
+
name: string;
|
|
36
|
+
ts: string;
|
|
37
|
+
data: Record<string, unknown>;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* spec/otlp.md §4: OTLP/JSON traces as session events: one per span, typed from its GenAI or
|
|
41
|
+
* OpenInference attributes, its event_id `otel:<trace>:<span>` so a resent span adds nothing.
|
|
42
|
+
*/
|
|
43
|
+
export declare function fromOtlp(body: unknown): {
|
|
44
|
+
events: OtlpEvent[];
|
|
45
|
+
} | {
|
|
46
|
+
error: string;
|
|
47
|
+
};
|
|
31
48
|
export {};
|
package/dist/otlp/index.js
CHANGED
|
@@ -398,3 +398,122 @@ function toOtlpV2(lines) {
|
|
|
398
398
|
],
|
|
399
399
|
};
|
|
400
400
|
}
|
|
401
|
+
const LLM_OPS = new Set(["chat", "text_completion", "generate_content", "embeddings"]);
|
|
402
|
+
const MAX_SPANS = 5000;
|
|
403
|
+
const MAX_DATA = 60_000;
|
|
404
|
+
function anyValue(v) {
|
|
405
|
+
if (!v || typeof v !== "object")
|
|
406
|
+
return null;
|
|
407
|
+
if (typeof v.stringValue === "string")
|
|
408
|
+
return v.stringValue;
|
|
409
|
+
if (typeof v.boolValue === "boolean")
|
|
410
|
+
return v.boolValue;
|
|
411
|
+
if (v.intValue !== undefined) {
|
|
412
|
+
const n = Number(v.intValue);
|
|
413
|
+
return Number.isSafeInteger(n) ? n : String(v.intValue);
|
|
414
|
+
}
|
|
415
|
+
if (typeof v.doubleValue === "number")
|
|
416
|
+
return v.doubleValue;
|
|
417
|
+
if (v.arrayValue)
|
|
418
|
+
return (v.arrayValue.values ?? []).map(anyValue);
|
|
419
|
+
if (v.kvlistValue)
|
|
420
|
+
return attributes(v.kvlistValue.values);
|
|
421
|
+
return null;
|
|
422
|
+
}
|
|
423
|
+
function attributes(list) {
|
|
424
|
+
const out = {};
|
|
425
|
+
if (!Array.isArray(list))
|
|
426
|
+
return out;
|
|
427
|
+
for (const kv of list)
|
|
428
|
+
if (typeof kv?.key === "string")
|
|
429
|
+
out[kv.key] = anyValue(kv.value);
|
|
430
|
+
return out;
|
|
431
|
+
}
|
|
432
|
+
/** The event type for a span, from GenAI semconv and OpenInference attributes. */
|
|
433
|
+
function spanType(a) {
|
|
434
|
+
const op = a["gen_ai.operation.name"];
|
|
435
|
+
const kind = a["openinference.span.kind"];
|
|
436
|
+
if ((typeof op === "string" && LLM_OPS.has(op)) || kind === "LLM" || kind === "EMBEDDING")
|
|
437
|
+
return "llm.call";
|
|
438
|
+
if (op === "execute_tool" || kind === "TOOL")
|
|
439
|
+
return "tool.call";
|
|
440
|
+
if (kind === "RETRIEVER")
|
|
441
|
+
return "retrieval";
|
|
442
|
+
if (op === "invoke_agent" || op === "create_agent" || kind === "AGENT" || kind === "CHAIN")
|
|
443
|
+
return "step";
|
|
444
|
+
return "otel.span";
|
|
445
|
+
}
|
|
446
|
+
const isoOfNanos = (n) => {
|
|
447
|
+
const s = typeof n === "string" || typeof n === "number" ? String(n) : "";
|
|
448
|
+
if (!/^[0-9]{1,20}$/.test(s))
|
|
449
|
+
return "";
|
|
450
|
+
return new Date(Number(BigInt(s) / 1000000n)).toISOString();
|
|
451
|
+
};
|
|
452
|
+
const size = (o) => new TextEncoder().encode(JSON.stringify(o)).length;
|
|
453
|
+
/**
|
|
454
|
+
* spec/otlp.md §4: OTLP/JSON traces as session events: one per span, typed from its GenAI or
|
|
455
|
+
* OpenInference attributes, its event_id `otel:<trace>:<span>` so a resent span adds nothing.
|
|
456
|
+
*/
|
|
457
|
+
export function fromOtlp(body) {
|
|
458
|
+
const rs = body?.resourceSpans;
|
|
459
|
+
if (!body || typeof body !== "object" || !Array.isArray(rs))
|
|
460
|
+
return { error: "not OTLP/JSON traces (no resourceSpans)" };
|
|
461
|
+
const events = [];
|
|
462
|
+
for (const r of rs) {
|
|
463
|
+
const service = attributes(r?.resource?.attributes)["service.name"];
|
|
464
|
+
const scopes = Array.isArray(r?.scopeSpans) ? r.scopeSpans : [];
|
|
465
|
+
for (const ss of scopes) {
|
|
466
|
+
const spans = Array.isArray(ss?.spans) ? ss.spans : [];
|
|
467
|
+
for (const sp of spans) {
|
|
468
|
+
const trace = sp?.traceId;
|
|
469
|
+
const span = sp?.spanId;
|
|
470
|
+
if (typeof trace !== "string" || !/^[0-9a-f]{32}$/i.test(trace))
|
|
471
|
+
return { error: "a span without a 32-hex traceId" };
|
|
472
|
+
if (typeof span !== "string" || !/^[0-9a-f]{16}$/i.test(span))
|
|
473
|
+
return { error: "a span without a 16-hex spanId" };
|
|
474
|
+
if (events.length === MAX_SPANS)
|
|
475
|
+
return { error: `more than ${MAX_SPANS} spans` };
|
|
476
|
+
const attrs = attributes(sp.attributes);
|
|
477
|
+
const status = sp.status;
|
|
478
|
+
const data = {
|
|
479
|
+
source: "otlp",
|
|
480
|
+
...(typeof service === "string" ? { service } : {}),
|
|
481
|
+
scope: {
|
|
482
|
+
name: typeof ss?.scope?.name === "string" ? ss.scope.name : "",
|
|
483
|
+
...(typeof ss?.scope?.version === "string" ? { version: ss.scope.version } : {}),
|
|
484
|
+
},
|
|
485
|
+
trace_id: trace.toLowerCase(),
|
|
486
|
+
span_id: span.toLowerCase(),
|
|
487
|
+
...(typeof sp.parentSpanId === "string" && sp.parentSpanId
|
|
488
|
+
? { parent_span_id: sp.parentSpanId.toLowerCase() }
|
|
489
|
+
: {}),
|
|
490
|
+
kind: typeof sp.kind === "number" ? sp.kind : 0,
|
|
491
|
+
start_unix_nano: String(sp.startTimeUnixNano ?? ""),
|
|
492
|
+
end_unix_nano: String(sp.endTimeUnixNano ?? ""),
|
|
493
|
+
...(status && typeof status.code === "number" && status.code !== 0
|
|
494
|
+
? {
|
|
495
|
+
status: {
|
|
496
|
+
code: status.code,
|
|
497
|
+
...(typeof status.message === "string" ? { message: status.message } : {}),
|
|
498
|
+
},
|
|
499
|
+
}
|
|
500
|
+
: {}),
|
|
501
|
+
attributes: attrs,
|
|
502
|
+
};
|
|
503
|
+
if (size(data) > MAX_DATA) {
|
|
504
|
+
// keep the small attributes (ids, models, counts), drop the long ones (prompts, outputs)
|
|
505
|
+
data.attributes = Object.fromEntries(Object.entries(attrs).filter(([, v]) => size(v) <= 1000));
|
|
506
|
+
data.truncated = true;
|
|
507
|
+
}
|
|
508
|
+
events.push({
|
|
509
|
+
event_id: `otel:${trace.toLowerCase()}:${span.toLowerCase()}`,
|
|
510
|
+
type: spanType(attrs),
|
|
511
|
+
name: typeof sp.name === "string" ? sp.name.slice(0, 256) : "span",
|
|
512
|
+
ts: isoOfNanos(sp.startTimeUnixNano),
|
|
513
|
+
data,
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
return { events };
|
|
519
|
+
}
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const VERSION = "0.
|
|
1
|
+
export declare const VERSION = "0.13.0";
|
package/dist/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// The SDK version, on its own so the CLI can print it without loading the whole SDK.
|
|
2
|
-
export const VERSION = "0.
|
|
2
|
+
export const VERSION = "0.13.0";
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zanii/blackbox",
|
|
3
3
|
"license": "Apache-2.0",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.13.0",
|
|
5
5
|
"description": "The flight recorder for AI agents: sessions, a zero-loss spool, framework hooks, approvals, and offline verification of the gateway's hash-chained record.",
|
|
6
6
|
"keywords": [
|
|
7
7
|
"ai-agents",
|