darwin-langgraph 0.2.0-alpha.1 → 0.4.0-alpha.1
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/CHANGELOG.md +348 -0
- package/README.md +108 -3
- package/dist/create-darwin-node.d.ts +55 -0
- package/dist/create-darwin-node.d.ts.map +1 -1
- package/dist/create-darwin-node.js +56 -1
- package/dist/create-darwin-node.js.map +1 -1
- package/dist/darwin-accumulating-annotation.d.ts +81 -0
- package/dist/darwin-accumulating-annotation.d.ts.map +1 -0
- package/dist/darwin-accumulating-annotation.js +144 -0
- package/dist/darwin-accumulating-annotation.js.map +1 -0
- package/dist/darwin-callback-handler.d.ts +161 -2
- package/dist/darwin-callback-handler.d.ts.map +1 -1
- package/dist/darwin-callback-handler.js +417 -25
- package/dist/darwin-callback-handler.js.map +1 -1
- package/dist/errors.d.ts +16 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +19 -0
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +7 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -3
- package/dist/index.js.map +1 -1
- package/dist/to-otel-attributes.d.ts +49 -0
- package/dist/to-otel-attributes.d.ts.map +1 -1
- package/dist/to-otel-attributes.js +38 -2
- package/dist/to-otel-attributes.js.map +1 -1
- package/dist/to-w3c-trace-context.d.ts +81 -0
- package/dist/to-w3c-trace-context.d.ts.map +1 -0
- package/dist/to-w3c-trace-context.js +134 -0
- package/dist/to-w3c-trace-context.js.map +1 -0
- package/dist/token-budget.d.ts +141 -0
- package/dist/token-budget.d.ts.map +1 -0
- package/dist/token-budget.js +225 -0
- package/dist/token-budget.js.map +1 -0
- package/dist/with-darwin-evolution.d.ts +40 -0
- package/dist/with-darwin-evolution.d.ts.map +1 -1
- package/dist/with-darwin-evolution.js +55 -1
- package/dist/with-darwin-evolution.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -41,12 +41,20 @@ export { createDarwinNode, } from "./create-darwin-node.js";
|
|
|
41
41
|
export { darwinAnnotation, getDarwinChannelSpec, lastWriteWinsTrajectoryReducer, } from "./darwin-annotation.js";
|
|
42
42
|
export { withDarwinEvolution, } from "./with-darwin-evolution.js";
|
|
43
43
|
// V0.2 — LangChain-native callback handler (preferred over `withDarwinEvolution`)
|
|
44
|
-
|
|
44
|
+
// V0.4 — extended with onToolEvent + maxTrajectoryBytes + MAX_KNOWN_TRACE_VERSION
|
|
45
|
+
export { DarwinCallbackHandler, MAX_KNOWN_TRACE_VERSION, } from "./darwin-callback-handler.js";
|
|
45
46
|
// V0.2 — OTEL GenAI Semantic Conventions mapping
|
|
47
|
+
// V0.4 — extended with langGraphNode / langGraphStep / userId / requestId
|
|
46
48
|
export { toOtelAttributes, toolCallToOtelAttributes, } from "./to-otel-attributes.js";
|
|
47
49
|
// V0.2 — MessagesAnnotation interop for graphs mixing createReactAgent + createDarwinNode
|
|
48
50
|
export { darwinMessagesAnnotation, getMessagesChannelSpec, } from "./darwin-messages-annotation.js";
|
|
49
|
-
|
|
51
|
+
// V0.4 — accumulating annotation for graphs that fan-out multiple Darwin nodes
|
|
52
|
+
export { darwinAccumulatingAnnotation, getDarwinAccumulatingChannelSpec, darwinTrajectoryAccumulatorReducer, } from "./darwin-accumulating-annotation.js";
|
|
53
|
+
// V0.4 — token-budget callbacks (production guard against runaway agents)
|
|
54
|
+
export { TokenBudgetCallbackHandler, createTokenBudgetCallbacks, } from "./token-budget.js";
|
|
55
|
+
// V0.4 — W3C Trace Context mapper for cross-process span correlation
|
|
56
|
+
export { toW3CTraceContext, } from "./to-w3c-trace-context.js";
|
|
57
|
+
export { DarwinNodeError, DarwinEvolutionHookError, DarwinTokenBudgetExceededError, } from "./errors.js";
|
|
50
58
|
/** Adapter version — sync with `package.json` on every release. */
|
|
51
|
-
export const VERSION = "0.
|
|
59
|
+
export const VERSION = "0.4.0-alpha.1";
|
|
52
60
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAEH,OAAO,EACL,gBAAgB,GAIjB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,8BAA8B,GAC/B,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EACL,mBAAmB,GAIpB,MAAM,4BAA4B,CAAC;AAEpC,kFAAkF;AAClF,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAEH,OAAO,EACL,gBAAgB,GAIjB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACL,gBAAgB,EAChB,oBAAoB,EACpB,8BAA8B,GAC/B,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EACL,mBAAmB,GAIpB,MAAM,4BAA4B,CAAC;AAEpC,kFAAkF;AAClF,kFAAkF;AAClF,OAAO,EACL,qBAAqB,EACrB,uBAAuB,GAGxB,MAAM,8BAA8B,CAAC;AAEtC,iDAAiD;AACjD,0EAA0E;AAC1E,OAAO,EACL,gBAAgB,EAChB,wBAAwB,GAIzB,MAAM,yBAAyB,CAAC;AAEjC,0FAA0F;AAC1F,OAAO,EACL,wBAAwB,EACxB,sBAAsB,GACvB,MAAM,iCAAiC,CAAC;AAEzC,+EAA+E;AAC/E,OAAO,EACL,4BAA4B,EAC5B,gCAAgC,EAChC,kCAAkC,GACnC,MAAM,qCAAqC,CAAC;AAE7C,0EAA0E;AAC1E,OAAO,EACL,0BAA0B,EAC1B,0BAA0B,GAI3B,MAAM,mBAAmB,CAAC;AAE3B,qEAAqE;AACrE,OAAO,EACL,iBAAiB,GAGlB,MAAM,2BAA2B,CAAC;AAKnC,OAAO,EACL,eAAe,EACf,wBAAwB,EACxB,8BAA8B,GAC/B,MAAM,aAAa,CAAC;AAarB,mEAAmE;AACnE,MAAM,CAAC,MAAM,OAAO,GAAG,eAAe,CAAC"}
|
|
@@ -51,6 +51,55 @@ export interface ToOtelAttributesOptions {
|
|
|
51
51
|
* Override for organisation-wide naming conventions (e.g. `"company.darwin"`).
|
|
52
52
|
*/
|
|
53
53
|
customNamespace?: string;
|
|
54
|
+
/**
|
|
55
|
+
* V0.4 — LangGraph node name. Emits `gen_ai.langgraph.node` per the
|
|
56
|
+
* Coralogix / Last9 / Honeycomb convention for LangGraph instrumentation.
|
|
57
|
+
*
|
|
58
|
+
* Pass `event.nodeName` from {@link DarwinTrajectoryEvent} to populate.
|
|
59
|
+
*
|
|
60
|
+
* NEW V0.4 (S1235).
|
|
61
|
+
*/
|
|
62
|
+
langGraphNode?: string;
|
|
63
|
+
/**
|
|
64
|
+
* V0.4 — LangGraph step counter for this node execution. Emits
|
|
65
|
+
* `gen_ai.langgraph.step`. Optional — LangGraph does not always surface
|
|
66
|
+
* the step counter through callbacks; consumers who track it can
|
|
67
|
+
* forward it here.
|
|
68
|
+
*
|
|
69
|
+
* NEW V0.4 (S1235).
|
|
70
|
+
*/
|
|
71
|
+
langGraphStep?: number;
|
|
72
|
+
/**
|
|
73
|
+
* V0.4 — End-user id (the authenticated user the request was made on
|
|
74
|
+
* behalf of). Emits the OTel cross-cutting `enduser.id` attribute
|
|
75
|
+
* (the canonical OTel general-semconv attribute for end-user identity)
|
|
76
|
+
* AND the historical alias `gen_ai.request.user` that older Coralogix /
|
|
77
|
+
* Last9 dashboards key off.
|
|
78
|
+
*
|
|
79
|
+
* R1 Research Finding 1 (S1235): the GenAI semconv registry does NOT
|
|
80
|
+
* define `gen_ai.request.user` or `gen_ai.user.id` — `enduser.id` from
|
|
81
|
+
* the cross-cutting attributes is the spec-correct key. We emit both
|
|
82
|
+
* so users on either dashboard convention keep working.
|
|
83
|
+
*
|
|
84
|
+
* NEW V0.4 (S1235).
|
|
85
|
+
*/
|
|
86
|
+
userId?: string;
|
|
87
|
+
/**
|
|
88
|
+
* V0.4 — Conversation / thread id (typically the LangGraph
|
|
89
|
+
* `thread_id` from `config.configurable`). Emits the OTel
|
|
90
|
+
* `gen_ai.conversation.id` attribute (canonical spec key for grouping
|
|
91
|
+
* spans into one logical conversation in Langfuse / Honeycomb).
|
|
92
|
+
*
|
|
93
|
+
* NEW V0.4 (S1235).
|
|
94
|
+
*/
|
|
95
|
+
conversationId?: string;
|
|
96
|
+
/**
|
|
97
|
+
* V0.4 — Custom request id (correlates with upstream HTTP request
|
|
98
|
+
* tracing). Emits `gen_ai.request.id`. Optional.
|
|
99
|
+
*
|
|
100
|
+
* NEW V0.4 (S1235).
|
|
101
|
+
*/
|
|
102
|
+
requestId?: string;
|
|
54
103
|
}
|
|
55
104
|
/** Flat attribute bag matching OTEL's primitive-only attribute types. */
|
|
56
105
|
export type OtelAttributes = Record<string, string | number | boolean>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"to-otel-attributes.d.ts","sourceRoot":"","sources":["../src/to-otel-attributes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpD,oDAAoD;AACpD,MAAM,WAAW,uBAAuB;IACtC,uDAAuD;IACvD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iFAAiF;IACjF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"to-otel-attributes.d.ts","sourceRoot":"","sources":["../src/to-otel-attributes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpD,oDAAoD;AACpD,MAAM,WAAW,uBAAuB;IACtC,uDAAuD;IACvD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iFAAiF;IACjF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;;;;;;;;;;;OAaG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;;;OAOG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,yEAAyE;AACzE,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;AAEvE;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,cAAc,EAC1B,IAAI,GAAE,uBAA4B,GACjC,cAAc,CA8GhB;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,WAAW,mBAAmB;IAClC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mEAAmE;IACnE,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,gEAAgE;IAChE,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,cAAc,CAAC,WAAW,CAAC,CAAC,MAAM,CAAC,EACzC,IAAI,GAAE,mBAAwB,GAC7B,cAAc,CAoDhB"}
|
|
@@ -68,8 +68,17 @@
|
|
|
68
68
|
* ```
|
|
69
69
|
*/
|
|
70
70
|
export function toOtelAttributes(trajectory, opts = {}) {
|
|
71
|
-
|
|
72
|
-
|
|
71
|
+
// R2 MUST-FIX (S1235): forward-compat — accept `version >= 1` to
|
|
72
|
+
// match the R1 P0-2 widening in `isExecutionTrace` and the
|
|
73
|
+
// accumulator reducer. A v=2 trajectory that passed both upstream
|
|
74
|
+
// gates would otherwise crash here. Same structural guard as
|
|
75
|
+
// `isExecutionTrace`.
|
|
76
|
+
if (!trajectory ||
|
|
77
|
+
typeof trajectory !== "object" ||
|
|
78
|
+
typeof trajectory.version !== "number" ||
|
|
79
|
+
!Number.isFinite(trajectory.version) ||
|
|
80
|
+
trajectory.version < 1) {
|
|
81
|
+
throw new TypeError(`toOtelAttributes: trajectory must be an ExecutionTrace with version>=1, got ${trajectory === null ? "null" : typeof trajectory}`);
|
|
73
82
|
}
|
|
74
83
|
const ns = opts.customNamespace ?? "darwin";
|
|
75
84
|
const attrs = {
|
|
@@ -80,6 +89,33 @@ export function toOtelAttributes(trajectory, opts = {}) {
|
|
|
80
89
|
attrs["gen_ai.agent.name"] = opts.agentName;
|
|
81
90
|
if (opts.agentId)
|
|
82
91
|
attrs["gen_ai.agent.id"] = opts.agentId;
|
|
92
|
+
// V0.4 (S1235): LangGraph-specific attributes per Coralogix / Last9 /
|
|
93
|
+
// Honeycomb 2026 convention for LangGraph instrumentation. Emitted
|
|
94
|
+
// alongside the canonical gen_ai attrs so dashboards that filter by
|
|
95
|
+
// `gen_ai.langgraph.node` (one bar per node) work out-of-box.
|
|
96
|
+
if (typeof opts.langGraphNode === "string" && opts.langGraphNode.length > 0) {
|
|
97
|
+
attrs["gen_ai.langgraph.node"] = opts.langGraphNode;
|
|
98
|
+
}
|
|
99
|
+
if (typeof opts.langGraphStep === "number" &&
|
|
100
|
+
Number.isFinite(opts.langGraphStep) &&
|
|
101
|
+
opts.langGraphStep >= 0) {
|
|
102
|
+
attrs["gen_ai.langgraph.step"] = opts.langGraphStep;
|
|
103
|
+
}
|
|
104
|
+
if (typeof opts.userId === "string" && opts.userId.length > 0) {
|
|
105
|
+
// R1 Research 1 fix (S1235): emit the OTel-spec-canonical
|
|
106
|
+
// `enduser.id` AND the historical alias `gen_ai.request.user` that
|
|
107
|
+
// older Coralogix / Last9 dashboards key off, so both consumer
|
|
108
|
+
// conventions keep working.
|
|
109
|
+
attrs["enduser.id"] = opts.userId;
|
|
110
|
+
attrs["gen_ai.request.user"] = opts.userId;
|
|
111
|
+
}
|
|
112
|
+
if (typeof opts.conversationId === "string" &&
|
|
113
|
+
opts.conversationId.length > 0) {
|
|
114
|
+
attrs["gen_ai.conversation.id"] = opts.conversationId;
|
|
115
|
+
}
|
|
116
|
+
if (typeof opts.requestId === "string" && opts.requestId.length > 0) {
|
|
117
|
+
attrs["gen_ai.request.id"] = opts.requestId;
|
|
118
|
+
}
|
|
83
119
|
// Token usage — only emit when provider reported FINITE numbers. NaN
|
|
84
120
|
// and Infinity pass `typeof === "number"` but OTEL exporters drop
|
|
85
121
|
// spans with non-finite numeric attrs silently (R1 V0.2 Critic
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"to-otel-attributes.js","sourceRoot":"","sources":["../src/to-otel-attributes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;
|
|
1
|
+
{"version":3,"file":"to-otel-attributes.js","sourceRoot":"","sources":["../src/to-otel-attributes.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Surface 11 (V0.4) — `toW3CTraceContext(event, opts?)`.
|
|
3
|
+
*
|
|
4
|
+
* Pure mapper from a {@link DarwinTrajectoryEvent} to the
|
|
5
|
+
* [W3C Trace Context](https://www.w3.org/TR/trace-context/) `traceparent`
|
|
6
|
+
* header format so consumers can propagate Darwin trajectories into
|
|
7
|
+
* external systems (Langfuse, Honeycomb, Datadog APM, Tempo) without
|
|
8
|
+
* pulling in `@opentelemetry/api` as a dependency.
|
|
9
|
+
*
|
|
10
|
+
* Format reference (spec §3.2.2):
|
|
11
|
+
*
|
|
12
|
+
* traceparent = version "-" trace-id "-" parent-id "-" trace-flags
|
|
13
|
+
*
|
|
14
|
+
* - `version` = `"00"` (we emit only the current spec version).
|
|
15
|
+
* - `trace-id` = 32 hex chars. We map LangChain's UUID `parentRunId` (if
|
|
16
|
+
* present) by stripping dashes — UUID is 32 hex chars + 4 dashes — so
|
|
17
|
+
* the strip yields exactly 32 chars. If `parentRunId` is undefined we
|
|
18
|
+
* fall back to `runId` (top-level invokes form their own trace).
|
|
19
|
+
* - `parent-id` = 16 hex chars. We map LangChain's UUID `runId` by
|
|
20
|
+
* stripping dashes and TAKING THE FIRST 16 chars (parent-id is shorter
|
|
21
|
+
* than trace-id by spec design — first 16 chars of a UUID are random
|
|
22
|
+
* enough that collision probability per-trace is negligible).
|
|
23
|
+
* - `trace-flags` = `"01"` (sampled). We always emit sampled — consumers
|
|
24
|
+
* that want head-based sampling should drop the entire event upstream
|
|
25
|
+
* rather than fiddle with the flag.
|
|
26
|
+
*
|
|
27
|
+
* The W3C spec forbids `trace-id == "00000000000000000000000000000000"`
|
|
28
|
+
* and `parent-id == "0000000000000000"`. If either UUID strips to those
|
|
29
|
+
* all-zero strings we return `undefined` rather than emit an invalid
|
|
30
|
+
* header that downstream parsers reject.
|
|
31
|
+
*
|
|
32
|
+
* **Pure, no I/O, no allocation surprises.** The function builds a single
|
|
33
|
+
* string. Safe to call inside `onTrajectory` hot paths.
|
|
34
|
+
*
|
|
35
|
+
* NEW V0.4 (S1235).
|
|
36
|
+
*/
|
|
37
|
+
import type { DarwinTrajectoryEvent } from "./with-darwin-evolution.js";
|
|
38
|
+
/** Options accepted by {@link toW3CTraceContext}. */
|
|
39
|
+
export interface ToW3CTraceContextOptions {
|
|
40
|
+
/**
|
|
41
|
+
* Override the trace-flags byte. Default `"01"` (sampled). Set `"00"`
|
|
42
|
+
* for not-sampled — consumers can use this to mark trajectories that
|
|
43
|
+
* would otherwise be sampled out before they reach the W3C-aware
|
|
44
|
+
* exporter.
|
|
45
|
+
*/
|
|
46
|
+
traceFlags?: "00" | "01";
|
|
47
|
+
}
|
|
48
|
+
/** Return shape: a single W3C-compliant `traceparent` header value. */
|
|
49
|
+
export interface W3CTraceContext {
|
|
50
|
+
/** Full `traceparent` header value (spec §3.2). */
|
|
51
|
+
traceparent: string;
|
|
52
|
+
/** 32-hex trace ID — convenience accessor. */
|
|
53
|
+
traceId: string;
|
|
54
|
+
/** 16-hex span ID (the W3C `parent-id` slot) — convenience accessor. */
|
|
55
|
+
spanId: string;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Convert a {@link DarwinTrajectoryEvent} into a W3C `traceparent` header
|
|
59
|
+
* value. Returns `undefined` when no usable runId is present or when the
|
|
60
|
+
* stripped UUIDs collapse to the all-zero strings the spec forbids.
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```ts
|
|
64
|
+
* import { toW3CTraceContext, DarwinCallbackHandler } from "darwin-langgraph";
|
|
65
|
+
*
|
|
66
|
+
* const handler = new DarwinCallbackHandler({
|
|
67
|
+
* nodeMap: { research: "researcher" },
|
|
68
|
+
* onTrajectory: async (event) => {
|
|
69
|
+
* const ctx = toW3CTraceContext(event);
|
|
70
|
+
* if (!ctx) return;
|
|
71
|
+
* await fetch("https://langfuse.example/api/traces", {
|
|
72
|
+
* method: "POST",
|
|
73
|
+
* headers: { traceparent: ctx.traceparent },
|
|
74
|
+
* body: JSON.stringify({ trace: event.trajectory }),
|
|
75
|
+
* });
|
|
76
|
+
* },
|
|
77
|
+
* });
|
|
78
|
+
* ```
|
|
79
|
+
*/
|
|
80
|
+
export declare function toW3CTraceContext(event: DarwinTrajectoryEvent, opts?: ToW3CTraceContextOptions): W3CTraceContext | undefined;
|
|
81
|
+
//# sourceMappingURL=to-w3c-trace-context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"to-w3c-trace-context.d.ts","sourceRoot":"","sources":["../src/to-w3c-trace-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAExE,qDAAqD;AACrD,MAAM,WAAW,wBAAwB;IACvC;;;;;OAKG;IACH,UAAU,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;CAC1B;AAED,uEAAuE;AACvE,MAAM,WAAW,eAAe;IAC9B,mDAAmD;IACnD,WAAW,EAAE,MAAM,CAAC;IACpB,8CAA8C;IAC9C,OAAO,EAAE,MAAM,CAAC;IAChB,wEAAwE;IACxE,MAAM,EAAE,MAAM,CAAC;CAChB;AAsBD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,qBAAqB,EAC5B,IAAI,GAAE,wBAA6B,GAClC,eAAe,GAAG,SAAS,CAwD7B"}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Surface 11 (V0.4) — `toW3CTraceContext(event, opts?)`.
|
|
3
|
+
*
|
|
4
|
+
* Pure mapper from a {@link DarwinTrajectoryEvent} to the
|
|
5
|
+
* [W3C Trace Context](https://www.w3.org/TR/trace-context/) `traceparent`
|
|
6
|
+
* header format so consumers can propagate Darwin trajectories into
|
|
7
|
+
* external systems (Langfuse, Honeycomb, Datadog APM, Tempo) without
|
|
8
|
+
* pulling in `@opentelemetry/api` as a dependency.
|
|
9
|
+
*
|
|
10
|
+
* Format reference (spec §3.2.2):
|
|
11
|
+
*
|
|
12
|
+
* traceparent = version "-" trace-id "-" parent-id "-" trace-flags
|
|
13
|
+
*
|
|
14
|
+
* - `version` = `"00"` (we emit only the current spec version).
|
|
15
|
+
* - `trace-id` = 32 hex chars. We map LangChain's UUID `parentRunId` (if
|
|
16
|
+
* present) by stripping dashes — UUID is 32 hex chars + 4 dashes — so
|
|
17
|
+
* the strip yields exactly 32 chars. If `parentRunId` is undefined we
|
|
18
|
+
* fall back to `runId` (top-level invokes form their own trace).
|
|
19
|
+
* - `parent-id` = 16 hex chars. We map LangChain's UUID `runId` by
|
|
20
|
+
* stripping dashes and TAKING THE FIRST 16 chars (parent-id is shorter
|
|
21
|
+
* than trace-id by spec design — first 16 chars of a UUID are random
|
|
22
|
+
* enough that collision probability per-trace is negligible).
|
|
23
|
+
* - `trace-flags` = `"01"` (sampled). We always emit sampled — consumers
|
|
24
|
+
* that want head-based sampling should drop the entire event upstream
|
|
25
|
+
* rather than fiddle with the flag.
|
|
26
|
+
*
|
|
27
|
+
* The W3C spec forbids `trace-id == "00000000000000000000000000000000"`
|
|
28
|
+
* and `parent-id == "0000000000000000"`. If either UUID strips to those
|
|
29
|
+
* all-zero strings we return `undefined` rather than emit an invalid
|
|
30
|
+
* header that downstream parsers reject.
|
|
31
|
+
*
|
|
32
|
+
* **Pure, no I/O, no allocation surprises.** The function builds a single
|
|
33
|
+
* string. Safe to call inside `onTrajectory` hot paths.
|
|
34
|
+
*
|
|
35
|
+
* NEW V0.4 (S1235).
|
|
36
|
+
*/
|
|
37
|
+
const ZERO_TRACE_ID = "00000000000000000000000000000000";
|
|
38
|
+
const ZERO_SPAN_ID = "0000000000000000";
|
|
39
|
+
function uuidToHex(uuid) {
|
|
40
|
+
// UUIDs are case-insensitive hex; lowercase to match the W3C spec.
|
|
41
|
+
return uuid.replace(/-/g, "").toLowerCase();
|
|
42
|
+
}
|
|
43
|
+
function isPureHex(s) {
|
|
44
|
+
// Cheap test — characters 0-9, a-f. ASCII range check is faster than
|
|
45
|
+
// a regex on hot paths.
|
|
46
|
+
for (let i = 0; i < s.length; i++) {
|
|
47
|
+
const c = s.charCodeAt(i);
|
|
48
|
+
const isDigit = c >= 48 && c <= 57; // 0-9
|
|
49
|
+
const isLowerHex = c >= 97 && c <= 102; // a-f
|
|
50
|
+
if (!isDigit && !isLowerHex)
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Convert a {@link DarwinTrajectoryEvent} into a W3C `traceparent` header
|
|
57
|
+
* value. Returns `undefined` when no usable runId is present or when the
|
|
58
|
+
* stripped UUIDs collapse to the all-zero strings the spec forbids.
|
|
59
|
+
*
|
|
60
|
+
* @example
|
|
61
|
+
* ```ts
|
|
62
|
+
* import { toW3CTraceContext, DarwinCallbackHandler } from "darwin-langgraph";
|
|
63
|
+
*
|
|
64
|
+
* const handler = new DarwinCallbackHandler({
|
|
65
|
+
* nodeMap: { research: "researcher" },
|
|
66
|
+
* onTrajectory: async (event) => {
|
|
67
|
+
* const ctx = toW3CTraceContext(event);
|
|
68
|
+
* if (!ctx) return;
|
|
69
|
+
* await fetch("https://langfuse.example/api/traces", {
|
|
70
|
+
* method: "POST",
|
|
71
|
+
* headers: { traceparent: ctx.traceparent },
|
|
72
|
+
* body: JSON.stringify({ trace: event.trajectory }),
|
|
73
|
+
* });
|
|
74
|
+
* },
|
|
75
|
+
* });
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
export function toW3CTraceContext(event, opts = {}) {
|
|
79
|
+
if (!event.runId)
|
|
80
|
+
return undefined;
|
|
81
|
+
const runHex = uuidToHex(event.runId);
|
|
82
|
+
// The W3C spec is strict about character set and length. If callers
|
|
83
|
+
// pass a non-UUID runId (e.g. from a custom run identifier scheme)
|
|
84
|
+
// bail out rather than emit a malformed header.
|
|
85
|
+
if (runHex.length < 32 || !isPureHex(runHex))
|
|
86
|
+
return undefined;
|
|
87
|
+
let traceIdSource;
|
|
88
|
+
let spanFromFirstHalf;
|
|
89
|
+
if (event.parentRunId) {
|
|
90
|
+
const parentHex = uuidToHex(event.parentRunId);
|
|
91
|
+
if (parentHex.length < 32 || !isPureHex(parentHex)) {
|
|
92
|
+
// Parent is malformed — fall through to runId-only trace. In that
|
|
93
|
+
// case we MUST domain-separate the spanId from the traceId; see
|
|
94
|
+
// the no-parent branch comment below.
|
|
95
|
+
traceIdSource = runHex;
|
|
96
|
+
spanFromFirstHalf = false;
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
// Hierarchical: trace-id from parent, span-id from runId. The
|
|
100
|
+
// first 16 hex of the runId are random in v4 / v7 UUIDs (variant
|
|
101
|
+
// bits live at char 16+), so collision probability per trace is
|
|
102
|
+
// negligible.
|
|
103
|
+
traceIdSource = parentHex;
|
|
104
|
+
spanFromFirstHalf = true;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
// Top-level invoke: when there is no separate parentRunId, deriving
|
|
109
|
+
// BOTH traceId AND spanId from the FIRST 16 chars of the same UUID
|
|
110
|
+
// produces a `traceparent` where the spanId is a prefix of the
|
|
111
|
+
// traceId. Several OTEL backends (Datadog APM, Honeycomb classic)
|
|
112
|
+
// reject or misroute such spans because they look like self-parents.
|
|
113
|
+
// R1 P0-1 fix (S1235): take the SECOND half of the run hex for the
|
|
114
|
+
// spanId so the two slots are domain-separated even when sourced
|
|
115
|
+
// from the same UUID.
|
|
116
|
+
traceIdSource = runHex;
|
|
117
|
+
spanFromFirstHalf = false;
|
|
118
|
+
}
|
|
119
|
+
const traceId = traceIdSource.slice(0, 32);
|
|
120
|
+
const spanId = spanFromFirstHalf
|
|
121
|
+
? runHex.slice(0, 16)
|
|
122
|
+
: runHex.slice(16, 32);
|
|
123
|
+
// Spec §3.2.2.2: trace-id == 0...0 is invalid. Same for parent-id.
|
|
124
|
+
if (traceId === ZERO_TRACE_ID || spanId === ZERO_SPAN_ID) {
|
|
125
|
+
return undefined;
|
|
126
|
+
}
|
|
127
|
+
const traceFlags = opts.traceFlags ?? "01";
|
|
128
|
+
return {
|
|
129
|
+
traceparent: `00-${traceId}-${spanId}-${traceFlags}`,
|
|
130
|
+
traceId,
|
|
131
|
+
spanId,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=to-w3c-trace-context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"to-w3c-trace-context.js","sourceRoot":"","sources":["../src/to-w3c-trace-context.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Surface 10 (V0.4) — `createTokenBudgetCallbacks(budget, onExceed)`.
|
|
3
|
+
*
|
|
4
|
+
* Production-pattern from the LangGraph TypeScript Production Guide
|
|
5
|
+
* (langgraphjs.guide/production): a token budget enforced per-invocation
|
|
6
|
+
* across ALL LLM calls inside a graph, throwing when the budget is
|
|
7
|
+
* exceeded. Mirrors the canonical example with two differences:
|
|
8
|
+
*
|
|
9
|
+
* 1. Returned as a `BaseCallbackHandler` subclass so it composes with
|
|
10
|
+
* {@link DarwinCallbackHandler} via the standard `{ callbacks: [...] }`
|
|
11
|
+
* option — no model-side patching required, works against any
|
|
12
|
+
* LangChain provider that emits `handleLLMEnd`.
|
|
13
|
+
* 2. Throws a typed {@link DarwinTokenBudgetExceededError} so consumers
|
|
14
|
+
* can `instanceof`-check without parsing message text.
|
|
15
|
+
*
|
|
16
|
+
* The handler tracks BOTH the LangChain-canonical `output.llmOutput.tokenUsage`
|
|
17
|
+
* shape (used by `ChatOpenAI`, `ChatAnthropic`, etc.) AND the per-message
|
|
18
|
+
* usage shape (`generations[0][0].message.usage_metadata`) some providers
|
|
19
|
+
* emit. The total is monotonically non-decreasing across `handleLLMEnd`
|
|
20
|
+
* events; once the budget is crossed the throw happens INSIDE
|
|
21
|
+
* `handleLLMEnd`, which LangChain surfaces back to the caller of
|
|
22
|
+
* `graph.invoke` / `graph.stream`.
|
|
23
|
+
*
|
|
24
|
+
* IMPORTANT: the throw is the only reliable abort signal — LangGraph's
|
|
25
|
+
* node-level retry policy may still re-fire the budget-exceeded path.
|
|
26
|
+
* The `onExceed` callback fires once per handler instance regardless of
|
|
27
|
+
* how many times the throw is observed (use it for logging / alerting,
|
|
28
|
+
* not retry).
|
|
29
|
+
*
|
|
30
|
+
* NEW V0.4 (S1235).
|
|
31
|
+
*/
|
|
32
|
+
import { BaseCallbackHandler } from "@langchain/core/callbacks/base";
|
|
33
|
+
import type { LLMResult } from "@langchain/core/outputs";
|
|
34
|
+
/**
|
|
35
|
+
* Options accepted by {@link createTokenBudgetCallbacks}.
|
|
36
|
+
*/
|
|
37
|
+
export interface TokenBudgetOptions {
|
|
38
|
+
/**
|
|
39
|
+
* Per-handler-instance token ceiling. Counts the SUM of input + output
|
|
40
|
+
* tokens across every `handleLLMEnd` event. Must be a finite positive
|
|
41
|
+
* integer; invalid values throw `DarwinTokenBudgetExceededError`
|
|
42
|
+
* eagerly on construction so misconfigured deployments fail loud.
|
|
43
|
+
*/
|
|
44
|
+
budget: number;
|
|
45
|
+
/**
|
|
46
|
+
* Fire-and-forget side-effect when the budget is exceeded. Receives the
|
|
47
|
+
* cumulative total at the moment of breach, the configured budget, and
|
|
48
|
+
* an optional `providerHint` extracted from the LLMResult (e.g. the
|
|
49
|
+
* model name when emitted). Errors thrown inside this callback are
|
|
50
|
+
* swallowed with a single warn so they cannot mask the budget throw.
|
|
51
|
+
*
|
|
52
|
+
* Use this for logging / metrics / alerting — not for retry.
|
|
53
|
+
*/
|
|
54
|
+
onExceed?: (info: TokenBudgetExceedInfo) => void;
|
|
55
|
+
/**
|
|
56
|
+
* Fire on every `handleLLMEnd` event with the running total. Useful for
|
|
57
|
+
* dashboards or step-by-step budget consumption traces. Optional.
|
|
58
|
+
*/
|
|
59
|
+
onTick?: (info: TokenBudgetTickInfo) => void;
|
|
60
|
+
}
|
|
61
|
+
export interface TokenBudgetExceedInfo {
|
|
62
|
+
budget: number;
|
|
63
|
+
totalTokens: number;
|
|
64
|
+
providerHint: string | undefined;
|
|
65
|
+
}
|
|
66
|
+
export interface TokenBudgetTickInfo {
|
|
67
|
+
budget: number;
|
|
68
|
+
totalTokens: number;
|
|
69
|
+
deltaTokens: number;
|
|
70
|
+
remainingTokens: number;
|
|
71
|
+
providerHint: string | undefined;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* LangChain `BaseCallbackHandler` that enforces a hard token budget
|
|
75
|
+
* across all LLM calls observed during its lifetime.
|
|
76
|
+
*
|
|
77
|
+
* Construct once per `graph.invoke` call (do NOT reuse across requests —
|
|
78
|
+
* the running total is per-handler-instance). The
|
|
79
|
+
* {@link createTokenBudgetCallbacks} factory makes the per-invocation
|
|
80
|
+
* shape ergonomic.
|
|
81
|
+
*/
|
|
82
|
+
export declare class TokenBudgetCallbackHandler extends BaseCallbackHandler {
|
|
83
|
+
readonly name = "DarwinTokenBudgetHandler";
|
|
84
|
+
/**
|
|
85
|
+
* V0.4 R1 P2-3 fix (S1235): `awaitHandlers = true` is required so
|
|
86
|
+
* LangChain's callback manager AWAITS this handler before continuing
|
|
87
|
+
* — the synchronous `throw` from `handleLLMEnd` MUST reach the
|
|
88
|
+
* `graph.invoke` caller. With `awaitHandlers = false` the throw may
|
|
89
|
+
* surface as an unhandled rejection in some LangChain versions
|
|
90
|
+
* rather than aborting the graph, which is the entire point of a
|
|
91
|
+
* token budget.
|
|
92
|
+
*/
|
|
93
|
+
readonly awaitHandlers = true;
|
|
94
|
+
private readonly budget;
|
|
95
|
+
private readonly onExceed;
|
|
96
|
+
private readonly onTick;
|
|
97
|
+
private totalTokens;
|
|
98
|
+
private exceedWarned;
|
|
99
|
+
private exceedFired;
|
|
100
|
+
private tickErrorWarned;
|
|
101
|
+
constructor(opts: TokenBudgetOptions);
|
|
102
|
+
handleLLMEnd(output: LLMResult): void;
|
|
103
|
+
private warnTick;
|
|
104
|
+
/** Helper for tests + debug introspection. */
|
|
105
|
+
getTotalTokens(): number;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Factory that returns a single-element `BaseCallbackHandler[]` ready to
|
|
109
|
+
* be passed via the standard LangGraph `{ callbacks: [...] }` option.
|
|
110
|
+
* Returning an array is the ergonomic shape — most consumers spread it
|
|
111
|
+
* into a larger callbacks array next to {@link DarwinCallbackHandler}.
|
|
112
|
+
*
|
|
113
|
+
* @example
|
|
114
|
+
* ```ts
|
|
115
|
+
* import {
|
|
116
|
+
* DarwinCallbackHandler,
|
|
117
|
+
* createTokenBudgetCallbacks,
|
|
118
|
+
* DarwinTokenBudgetExceededError,
|
|
119
|
+
* } from "darwin-langgraph";
|
|
120
|
+
*
|
|
121
|
+
* try {
|
|
122
|
+
* const result = await graph.invoke({ task: "..." }, {
|
|
123
|
+
* callbacks: [
|
|
124
|
+
* new DarwinCallbackHandler({ nodeMap, onTrajectory }),
|
|
125
|
+
* ...createTokenBudgetCallbacks({
|
|
126
|
+
* budget: 20_000,
|
|
127
|
+
* onExceed: ({ totalTokens, providerHint }) => {
|
|
128
|
+
* console.error(`Budget breach: ${totalTokens} tok (${providerHint})`);
|
|
129
|
+
* },
|
|
130
|
+
* }),
|
|
131
|
+
* ],
|
|
132
|
+
* });
|
|
133
|
+
* } catch (err) {
|
|
134
|
+
* if (err instanceof DarwinTokenBudgetExceededError) {
|
|
135
|
+
* // ... graceful degradation, fallback model, etc.
|
|
136
|
+
* }
|
|
137
|
+
* }
|
|
138
|
+
* ```
|
|
139
|
+
*/
|
|
140
|
+
export declare function createTokenBudgetCallbacks(opts: TokenBudgetOptions): [TokenBudgetCallbackHandler];
|
|
141
|
+
//# sourceMappingURL=token-budget.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-budget.d.ts","sourceRoot":"","sources":["../src/token-budget.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAIzD;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC;;;;;OAKG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;;;;;;;OAQG;IACH,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,qBAAqB,KAAK,IAAI,CAAC;IACjD;;;OAGG;IACH,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,mBAAmB,KAAK,IAAI,CAAC;CAC9C;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,GAAG,SAAS,CAAC;CAClC;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,GAAG,SAAS,CAAC;CAClC;AA8DD;;;;;;;;GAQG;AACH,qBAAa,0BAA2B,SAAQ,mBAAmB;IACjE,SAAyB,IAAI,8BAA8B;IAC3D;;;;;;;;OAQG;IACH,SAAyB,aAAa,QAAQ;IAE9C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAiC;IAC1D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA+B;IACtD,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,eAAe,CAAS;gBAEpB,IAAI,EAAE,kBAAkB;IAyB3B,YAAY,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI;IAoD9C,OAAO,CAAC,QAAQ;IAUhB,8CAA8C;IACvC,cAAc,IAAI,MAAM;CAGhC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,kBAAkB,GACvB,CAAC,0BAA0B,CAAC,CAE9B"}
|