@nagi-js/otel 0.1.1-rc.7 → 0.1.1-rc.9
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 +0 -45
- package/dist/index.js +26 -7
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,63 +1,18 @@
|
|
|
1
1
|
import { FlowHooks, StepCtx } from '@nagi-js/core';
|
|
2
2
|
import { Span, Tracer, Attributes } from '@opentelemetry/api';
|
|
3
3
|
|
|
4
|
-
/**
|
|
5
|
-
* Fan out a single `FlowHooks` call site to multiple subscribers, awaiting each in
|
|
6
|
-
* declaration order. A throw from one subscriber is logged via `console.error` and
|
|
7
|
-
* does NOT prevent later subscribers (or the workflow runtime) from running.
|
|
8
|
-
*
|
|
9
|
-
* Nagi's runtime accepts a single `hooks` value — this is how users wire
|
|
10
|
-
* `otelHooks()` alongside their own logger / metrics hooks.
|
|
11
|
-
*/
|
|
12
4
|
declare function composeHooks(...hooks: readonly FlowHooks[]): FlowHooks;
|
|
13
5
|
|
|
14
|
-
/**
|
|
15
|
-
* Returns the OTel `Span` tracking the current step attempt, or `undefined` when
|
|
16
|
-
* no `otelHooks()` is wired or the step is not in-flight (e.g. running under a
|
|
17
|
-
* different `nagi()` instance, or after the step has completed).
|
|
18
|
-
*
|
|
19
|
-
* Useful inside user handlers to add custom attributes or open child spans:
|
|
20
|
-
*
|
|
21
|
-
* ```ts
|
|
22
|
-
* b.task({
|
|
23
|
-
* run: async (ctx) => {
|
|
24
|
-
* const span = getStepSpan(ctx);
|
|
25
|
-
* span?.setAttribute("my.custom.attr", 42);
|
|
26
|
-
* ...
|
|
27
|
-
* },
|
|
28
|
-
* });
|
|
29
|
-
* ```
|
|
30
|
-
*
|
|
31
|
-
* Note: this span is NOT installed as the active context — `trace.getActiveSpan()`
|
|
32
|
-
* inside the handler returns the caller's context, not this span.
|
|
33
|
-
*/
|
|
34
6
|
declare function getStepSpan(ctx: Pick<StepCtx<unknown>, "runId" | "stepId" | "attempt">): Span | undefined;
|
|
35
7
|
|
|
36
8
|
interface OtelHooksOpts {
|
|
37
|
-
/**
|
|
38
|
-
* Custom `Tracer`. Defaults to `trace.getTracer("@nagi-js/otel", "0.0.0")` —
|
|
39
|
-
* a no-op when no SDK is registered (per the OTel API contract).
|
|
40
|
-
*/
|
|
41
9
|
readonly tracer?: Tracer;
|
|
42
|
-
/** Override `"flow"`/`"step"` span-name prefixes (low cardinality; do NOT use IDs). */
|
|
43
10
|
readonly spanNamePrefix?: {
|
|
44
11
|
readonly flow?: string;
|
|
45
12
|
readonly step?: string;
|
|
46
13
|
};
|
|
47
|
-
/** Extra attributes stamped on every span (e.g. `deployment.environment`). */
|
|
48
14
|
readonly defaultAttributes?: Attributes;
|
|
49
15
|
}
|
|
50
|
-
/**
|
|
51
|
-
* `FlowHooks` adapter that maps Nagi lifecycle events to OpenTelemetry spans.
|
|
52
|
-
*
|
|
53
|
-
* Hierarchy: one `flow {flowId}` span per run; per-step-attempt sibling `step
|
|
54
|
-
* {stepId}` spans underneath. All spans are `INTERNAL` kind. Errors call
|
|
55
|
-
* `span.recordException` + `setStatus(ERROR)`; success leaves status `UNSET`
|
|
56
|
-
* per the Tracing API spec recommendation for instrumentation libraries.
|
|
57
|
-
*
|
|
58
|
-
* Adapter errors are swallowed: a misconfigured tracer must never crash a
|
|
59
|
-
* workflow. Throws are logged via `console.error`.
|
|
60
|
-
*/
|
|
61
16
|
declare function otelHooks(opts?: OtelHooksOpts): FlowHooks;
|
|
62
17
|
|
|
63
18
|
export { type OtelHooksOpts, composeHooks, getStepSpan, otelHooks };
|
package/dist/index.js
CHANGED
|
@@ -103,6 +103,16 @@ function otelHooks(opts = {}) {
|
|
|
103
103
|
parentCtx
|
|
104
104
|
);
|
|
105
105
|
}
|
|
106
|
+
function resolveParentContext(event) {
|
|
107
|
+
if (!event.parent) return context.active();
|
|
108
|
+
const stepSpan = stepSpanRegistry.get(
|
|
109
|
+
stepKey(event.parent.runId, event.parent.stepId, event.parent.attempt)
|
|
110
|
+
);
|
|
111
|
+
if (stepSpan) return trace.setSpan(context.active(), stepSpan);
|
|
112
|
+
const parentFlowCtx = flowCtxs.get(event.parent.runId);
|
|
113
|
+
if (parentFlowCtx) return parentFlowCtx;
|
|
114
|
+
return context.active();
|
|
115
|
+
}
|
|
106
116
|
function endStepSpanOk(event, span) {
|
|
107
117
|
const k = stepKey(event.runId, event.stepId, event.attempt);
|
|
108
118
|
const startedAt = stepStartTimes.get(k);
|
|
@@ -125,11 +135,22 @@ function otelHooks(opts = {}) {
|
|
|
125
135
|
}
|
|
126
136
|
return {
|
|
127
137
|
onFlowStart: withGuard("onFlowStart", (event) => {
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
138
|
+
const parentCtx = resolveParentContext(event);
|
|
139
|
+
const attrs = { ...flowAttrs(event) };
|
|
140
|
+
if (event.parent) {
|
|
141
|
+
attrs["nagi.parent.run.id"] = event.parent.runId;
|
|
142
|
+
attrs["nagi.parent.step.id"] = event.parent.stepId;
|
|
143
|
+
attrs["nagi.parent.step.attempt"] = event.parent.attempt;
|
|
144
|
+
}
|
|
145
|
+
const span = tracer.startSpan(
|
|
146
|
+
`${flowPrefix} ${event.flowId}`,
|
|
147
|
+
{
|
|
148
|
+
kind: SpanKind.INTERNAL,
|
|
149
|
+
startTime: event.at,
|
|
150
|
+
attributes: attrs
|
|
151
|
+
},
|
|
152
|
+
parentCtx
|
|
153
|
+
);
|
|
133
154
|
flowCtxs.set(event.runId, trace.setSpan(context.active(), span));
|
|
134
155
|
}),
|
|
135
156
|
onFlowComplete: withGuard("onFlowComplete", (event) => {
|
|
@@ -173,8 +194,6 @@ function otelHooks(opts = {}) {
|
|
|
173
194
|
event.at
|
|
174
195
|
);
|
|
175
196
|
}),
|
|
176
|
-
// onSignalSent is declared in core/types.ts but never fired by the runtime;
|
|
177
|
-
// intentionally omitted until core wires a sender path.
|
|
178
197
|
onSignalReceived: withGuard(
|
|
179
198
|
"onSignalReceived",
|
|
180
199
|
(event) => {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/compose.ts","../src/context.ts","../src/hooks.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type {\n FlowCompleteEvent,\n FlowErrorEvent,\n FlowHooks,\n FlowStartEvent,\n SignalReceivedEvent,\n SignalSentEvent,\n StepCompleteEvent,\n StepErrorEvent,\n StepRetryEvent,\n StepStartEvent,\n} from \"@nagi-js/core\";\n\n/**\n * Fan out a single `FlowHooks` call site to multiple subscribers, awaiting each in\n * declaration order. A throw from one subscriber is logged via `console.error` and\n * does NOT prevent later subscribers (or the workflow runtime) from running.\n *\n * Nagi's runtime accepts a single `hooks` value — this is how users wire\n * `otelHooks()` alongside their own logger / metrics hooks.\n */\nexport function composeHooks(...hooks: readonly FlowHooks[]): FlowHooks {\n // Build a local FlowHooks-shaped object. Optional fields are only set when at\n // least one subscriber provides them — required by `exactOptionalPropertyTypes`.\n const result: {\n onFlowStart?: (event: FlowStartEvent) => Promise<void>;\n onFlowComplete?: (event: FlowCompleteEvent) => Promise<void>;\n onFlowError?: (event: FlowErrorEvent) => Promise<void>;\n onStepStart?: (event: StepStartEvent) => Promise<void>;\n onStepComplete?: (event: StepCompleteEvent) => Promise<void>;\n onStepError?: (event: StepErrorEvent) => Promise<void>;\n onStepRetry?: (event: StepRetryEvent) => Promise<void>;\n onSignalSent?: (event: SignalSentEvent) => Promise<void>;\n onSignalReceived?: (event: SignalReceivedEvent) => Promise<void>;\n } = {};\n\n const fs = pick(hooks, \"onFlowStart\");\n if (fs.length > 0) result.onFlowStart = fanout(\"onFlowStart\", fs);\n\n const fc = pick(hooks, \"onFlowComplete\");\n if (fc.length > 0) result.onFlowComplete = fanout(\"onFlowComplete\", fc);\n\n const fe = pick(hooks, \"onFlowError\");\n if (fe.length > 0) result.onFlowError = fanout(\"onFlowError\", fe);\n\n const ss = pick(hooks, \"onStepStart\");\n if (ss.length > 0) result.onStepStart = fanout(\"onStepStart\", ss);\n\n const sc = pick(hooks, \"onStepComplete\");\n if (sc.length > 0) result.onStepComplete = fanout(\"onStepComplete\", sc);\n\n const se = pick(hooks, \"onStepError\");\n if (se.length > 0) result.onStepError = fanout(\"onStepError\", se);\n\n const sr = pick(hooks, \"onStepRetry\");\n if (sr.length > 0) result.onStepRetry = fanout(\"onStepRetry\", sr);\n\n const sigS = pick(hooks, \"onSignalSent\");\n if (sigS.length > 0) result.onSignalSent = fanout(\"onSignalSent\", sigS);\n\n const sigR = pick(hooks, \"onSignalReceived\");\n if (sigR.length > 0)\n result.onSignalReceived = fanout(\"onSignalReceived\", sigR);\n\n return result;\n}\n\nfunction pick<K extends keyof FlowHooks>(\n hooks: readonly FlowHooks[],\n name: K,\n): Array<NonNullable<FlowHooks[K]>> {\n const out: Array<NonNullable<FlowHooks[K]>> = [];\n for (const h of hooks) {\n const fn = h[name];\n if (fn !== undefined) out.push(fn);\n }\n return out;\n}\n\nfunction fanout<E>(\n name: string,\n subs: ReadonlyArray<(event: E) => void | Promise<void>>,\n): (event: E) => Promise<void> {\n return async (event: E) => {\n for (const fn of subs) {\n try {\n await fn(event);\n } catch (err) {\n console.error(\n `[@nagi-js/otel] composeHooks: ${name} subscriber threw`,\n err,\n );\n }\n }\n };\n}\n","import type { AttemptNumber, RunId, StepCtx, StepId } from \"@nagi-js/core\";\nimport type { Span } from \"@opentelemetry/api\";\n\n/**\n * ASCII unit separator. Composite-key delimiter for `(runId, stepId, attempt)`.\n * Unambiguous because `runId` is `run-<uuid>` and `stepId` is a dotted identifier;\n * neither contains control chars.\n */\nconst SEP = \"\\x1f\";\n\nexport function stepKey(\n runId: RunId,\n stepId: StepId,\n attempt: AttemptNumber,\n): string {\n return `${runId}${SEP}${stepId}${SEP}${attempt}`;\n}\n\n/**\n * Process-global registry of in-flight step spans, keyed by `(runId, stepId, attempt)`.\n * Populated by `otelHooks()` on `onStepStart`; cleared on completion / error / retry.\n *\n * Single-process, single-runtime: Nagi already assumes a runtime per process, so a\n * module-level Map is the right granularity. Bounded by steps-in-flight, which the\n * runtime itself bounds.\n */\nexport const stepSpanRegistry = new Map<string, Span>();\n\n/**\n * Returns the OTel `Span` tracking the current step attempt, or `undefined` when\n * no `otelHooks()` is wired or the step is not in-flight (e.g. running under a\n * different `nagi()` instance, or after the step has completed).\n *\n * Useful inside user handlers to add custom attributes or open child spans:\n *\n * ```ts\n * b.task({\n * run: async (ctx) => {\n * const span = getStepSpan(ctx);\n * span?.setAttribute(\"my.custom.attr\", 42);\n * ...\n * },\n * });\n * ```\n *\n * Note: this span is NOT installed as the active context — `trace.getActiveSpan()`\n * inside the handler returns the caller's context, not this span.\n */\nexport function getStepSpan(\n ctx: Pick<StepCtx<unknown>, \"runId\" | \"stepId\" | \"attempt\">,\n): Span | undefined {\n return stepSpanRegistry.get(stepKey(ctx.runId, ctx.stepId, ctx.attempt));\n}\n","import type {\n AttemptNumber,\n FlowCompleteEvent,\n FlowErrorEvent,\n FlowHooks,\n FlowStartEvent,\n RunId,\n SerializedError,\n SignalReceivedEvent,\n StepCompleteEvent,\n StepErrorEvent,\n StepEvent,\n StepId,\n StepRetryEvent,\n StepStartEvent,\n} from \"@nagi-js/core\";\nimport {\n type Attributes,\n type Context,\n context,\n type Span,\n SpanKind,\n SpanStatusCode,\n type Tracer,\n trace,\n} from \"@opentelemetry/api\";\nimport { stepKey, stepSpanRegistry } from \"./context\";\n\nconst TRACER_NAME = \"@nagi-js/otel\";\nconst TRACER_VERSION = \"0.0.0\";\n\nconst DEFAULT_FLOW_PREFIX = \"flow\";\nconst DEFAULT_STEP_PREFIX = \"step\";\n\nexport interface OtelHooksOpts {\n /**\n * Custom `Tracer`. Defaults to `trace.getTracer(\"@nagi-js/otel\", \"0.0.0\")` —\n * a no-op when no SDK is registered (per the OTel API contract).\n */\n readonly tracer?: Tracer;\n /** Override `\"flow\"`/`\"step\"` span-name prefixes (low cardinality; do NOT use IDs). */\n readonly spanNamePrefix?: { readonly flow?: string; readonly step?: string };\n /** Extra attributes stamped on every span (e.g. `deployment.environment`). */\n readonly defaultAttributes?: Attributes;\n}\n\n/**\n * `FlowHooks` adapter that maps Nagi lifecycle events to OpenTelemetry spans.\n *\n * Hierarchy: one `flow {flowId}` span per run; per-step-attempt sibling `step\n * {stepId}` spans underneath. All spans are `INTERNAL` kind. Errors call\n * `span.recordException` + `setStatus(ERROR)`; success leaves status `UNSET`\n * per the Tracing API spec recommendation for instrumentation libraries.\n *\n * Adapter errors are swallowed: a misconfigured tracer must never crash a\n * workflow. Throws are logged via `console.error`.\n */\nexport function otelHooks(opts: OtelHooksOpts = {}): FlowHooks {\n const tracer: Tracer =\n opts.tracer ?? trace.getTracer(TRACER_NAME, TRACER_VERSION);\n const flowPrefix = opts.spanNamePrefix?.flow ?? DEFAULT_FLOW_PREFIX;\n const stepPrefix = opts.spanNamePrefix?.step ?? DEFAULT_STEP_PREFIX;\n const baseAttrs: Attributes = opts.defaultAttributes ?? {};\n\n // Per-run flow-span context. Used as the parent for step spans.\n const flowCtxs = new Map<RunId, Context>();\n // Per-step match-duration tracking: dispatch.ts hard-codes durationMs=0 on\n // match completion, so we compute it ourselves from the stashed start time.\n const stepStartTimes = new Map<string, Date>();\n\n function withGuard<E>(\n name: string,\n fn: (event: E) => void,\n ): (event: E) => void {\n return (event: E) => {\n try {\n fn(event);\n } catch (err) {\n console.error(`[@nagi-js/otel] ${name} hook failed`, err);\n }\n };\n }\n\n function flowAttrs(event: {\n readonly runId: RunId;\n readonly flowId: string;\n }): Attributes {\n return {\n ...baseAttrs,\n \"nagi.flow.id\": event.flowId,\n \"nagi.run.id\": event.runId,\n };\n }\n\n function stepAttrs(event: StepEvent): Attributes {\n return {\n ...flowAttrs(event),\n \"nagi.step.id\": event.stepId,\n \"nagi.step.attempt\": event.attempt,\n \"nagi.step.kind\": event.kind,\n };\n }\n\n function startStepSpan(event: StepStartEvent): Span {\n const parentCtx = flowCtxs.get(event.runId) ?? context.active();\n return tracer.startSpan(\n `${stepPrefix} ${event.stepId}`,\n {\n kind: SpanKind.INTERNAL,\n startTime: event.at,\n attributes: stepAttrs(event),\n },\n parentCtx,\n );\n }\n\n function endStepSpanOk(event: StepCompleteEvent, span: Span): void {\n const k = stepKey(event.runId, event.stepId, event.attempt);\n const startedAt = stepStartTimes.get(k);\n // Match steps get a hard-coded durationMs=0 from the runtime; recover the\n // real value from the stashed start time when available.\n const durationMs =\n event.kind === \"match\" && startedAt\n ? event.at.getTime() - startedAt.getTime()\n : event.durationMs;\n span.setAttribute(\"nagi.step.duration_ms\", durationMs);\n span.end(event.at);\n }\n\n function endStepSpanErr(\n span: Span,\n error: SerializedError,\n endAt: Date,\n ): void {\n span.recordException(toError(error));\n span.setAttribute(\"error.type\", error.name);\n span.setStatus({ code: SpanStatusCode.ERROR, message: error.message });\n span.end(endAt);\n }\n\n function consumeStepSpan(\n runId: RunId,\n stepId: StepId,\n attempt: AttemptNumber,\n ): Span | undefined {\n const k = stepKey(runId, stepId, attempt);\n const span = stepSpanRegistry.get(k);\n stepSpanRegistry.delete(k);\n stepStartTimes.delete(k);\n return span;\n }\n\n return {\n onFlowStart: withGuard<FlowStartEvent>(\"onFlowStart\", (event) => {\n const span = tracer.startSpan(`${flowPrefix} ${event.flowId}`, {\n kind: SpanKind.INTERNAL,\n startTime: event.at,\n attributes: flowAttrs(event),\n });\n flowCtxs.set(event.runId, trace.setSpan(context.active(), span));\n }),\n\n onFlowComplete: withGuard<FlowCompleteEvent>(\"onFlowComplete\", (event) => {\n const ctx = flowCtxs.get(event.runId);\n flowCtxs.delete(event.runId);\n const span = ctx ? trace.getSpan(ctx) : undefined;\n span?.end(event.at);\n }),\n\n onFlowError: withGuard<FlowErrorEvent>(\"onFlowError\", (event) => {\n const ctx = flowCtxs.get(event.runId);\n flowCtxs.delete(event.runId);\n const span = ctx ? trace.getSpan(ctx) : undefined;\n if (span) endStepSpanErr(span, event.error, event.at);\n }),\n\n onStepStart: withGuard<StepStartEvent>(\"onStepStart\", (event) => {\n const span = startStepSpan(event);\n const k = stepKey(event.runId, event.stepId, event.attempt);\n stepSpanRegistry.set(k, span);\n stepStartTimes.set(k, event.at);\n }),\n\n onStepComplete: withGuard<StepCompleteEvent>(\"onStepComplete\", (event) => {\n const span = consumeStepSpan(event.runId, event.stepId, event.attempt);\n if (span) endStepSpanOk(event, span);\n }),\n\n onStepError: withGuard<StepErrorEvent>(\"onStepError\", (event) => {\n const span = consumeStepSpan(event.runId, event.stepId, event.attempt);\n if (span) endStepSpanErr(span, event.error, event.at);\n }),\n\n onStepRetry: withGuard<StepRetryEvent>(\"onStepRetry\", (event) => {\n // End the failed attempt's span with ERROR status. The next onStepStart\n // for attempt+1 opens a fresh sibling span under the same flow span.\n const span = consumeStepSpan(event.runId, event.stepId, event.attempt);\n if (span) endStepSpanErr(span, event.error, event.at);\n\n // Surface the retry on the flow span so users can see retry latency in\n // the run-level trace without inspecting individual step attempts.\n const flowCtx = flowCtxs.get(event.runId);\n const flowSpan = flowCtx ? trace.getSpan(flowCtx) : undefined;\n flowSpan?.addEvent(\n \"nagi.retry.scheduled\",\n {\n \"nagi.step.id\": event.stepId,\n \"nagi.step.attempt\": event.attempt,\n \"nagi.next_attempt_at\": event.nextAttemptAt.toISOString(),\n },\n event.at,\n );\n }),\n\n // onSignalSent is declared in core/types.ts but never fired by the runtime;\n // intentionally omitted until core wires a sender path.\n onSignalReceived: withGuard<SignalReceivedEvent>(\n \"onSignalReceived\",\n (event) => {\n const span = consumeStepSpan(event.runId, event.stepId, event.attempt);\n if (!span) return;\n span.addEvent(\n \"nagi.signal.received\",\n { \"nagi.signal.payload_present\": event.payload !== null },\n event.at,\n );\n span.setAttribute(\"nagi.step.duration_ms\", 0);\n span.end(event.at);\n },\n ),\n };\n}\n\nfunction toError(err: SerializedError): Error {\n // `recordException` accepts `{ name, message, stack }`. We rebuild a\n // shape-compatible object rather than a real Error to preserve the original\n // name/stack exactly.\n const e = new Error(err.message);\n e.name = err.name;\n if (err.stack !== undefined) e.stack = err.stack;\n return e;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/compose.ts","../src/context.ts","../src/hooks.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type {\n FlowCompleteEvent,\n FlowErrorEvent,\n FlowHooks,\n FlowStartEvent,\n SignalReceivedEvent,\n SignalSentEvent,\n StepCompleteEvent,\n StepErrorEvent,\n StepRetryEvent,\n StepStartEvent,\n} from \"@nagi-js/core\";\n\nexport function composeHooks(...hooks: readonly FlowHooks[]): FlowHooks {\n const result: {\n onFlowStart?: (event: FlowStartEvent) => Promise<void>;\n onFlowComplete?: (event: FlowCompleteEvent) => Promise<void>;\n onFlowError?: (event: FlowErrorEvent) => Promise<void>;\n onStepStart?: (event: StepStartEvent) => Promise<void>;\n onStepComplete?: (event: StepCompleteEvent) => Promise<void>;\n onStepError?: (event: StepErrorEvent) => Promise<void>;\n onStepRetry?: (event: StepRetryEvent) => Promise<void>;\n onSignalSent?: (event: SignalSentEvent) => Promise<void>;\n onSignalReceived?: (event: SignalReceivedEvent) => Promise<void>;\n } = {};\n\n const fs = pick(hooks, \"onFlowStart\");\n if (fs.length > 0) result.onFlowStart = fanout(\"onFlowStart\", fs);\n\n const fc = pick(hooks, \"onFlowComplete\");\n if (fc.length > 0) result.onFlowComplete = fanout(\"onFlowComplete\", fc);\n\n const fe = pick(hooks, \"onFlowError\");\n if (fe.length > 0) result.onFlowError = fanout(\"onFlowError\", fe);\n\n const ss = pick(hooks, \"onStepStart\");\n if (ss.length > 0) result.onStepStart = fanout(\"onStepStart\", ss);\n\n const sc = pick(hooks, \"onStepComplete\");\n if (sc.length > 0) result.onStepComplete = fanout(\"onStepComplete\", sc);\n\n const se = pick(hooks, \"onStepError\");\n if (se.length > 0) result.onStepError = fanout(\"onStepError\", se);\n\n const sr = pick(hooks, \"onStepRetry\");\n if (sr.length > 0) result.onStepRetry = fanout(\"onStepRetry\", sr);\n\n const sigS = pick(hooks, \"onSignalSent\");\n if (sigS.length > 0) result.onSignalSent = fanout(\"onSignalSent\", sigS);\n\n const sigR = pick(hooks, \"onSignalReceived\");\n if (sigR.length > 0)\n result.onSignalReceived = fanout(\"onSignalReceived\", sigR);\n\n return result;\n}\n\nfunction pick<K extends keyof FlowHooks>(\n hooks: readonly FlowHooks[],\n name: K,\n): Array<NonNullable<FlowHooks[K]>> {\n const out: Array<NonNullable<FlowHooks[K]>> = [];\n for (const h of hooks) {\n const fn = h[name];\n if (fn !== undefined) out.push(fn);\n }\n return out;\n}\n\nfunction fanout<E>(\n name: string,\n subs: ReadonlyArray<(event: E) => void | Promise<void>>,\n): (event: E) => Promise<void> {\n return async (event: E) => {\n for (const fn of subs) {\n try {\n await fn(event);\n } catch (err) {\n console.error(\n `[@nagi-js/otel] composeHooks: ${name} subscriber threw`,\n err,\n );\n }\n }\n };\n}\n","import type { AttemptNumber, RunId, StepCtx, StepId } from \"@nagi-js/core\";\nimport type { Span } from \"@opentelemetry/api\";\n\nconst SEP = \"\\x1f\";\n\nexport function stepKey(\n runId: RunId,\n stepId: StepId,\n attempt: AttemptNumber,\n): string {\n return `${runId}${SEP}${stepId}${SEP}${attempt}`;\n}\n\nexport const stepSpanRegistry = new Map<string, Span>();\n\nexport function getStepSpan(\n ctx: Pick<StepCtx<unknown>, \"runId\" | \"stepId\" | \"attempt\">,\n): Span | undefined {\n return stepSpanRegistry.get(stepKey(ctx.runId, ctx.stepId, ctx.attempt));\n}\n","import type {\n AttemptNumber,\n FlowCompleteEvent,\n FlowErrorEvent,\n FlowHooks,\n FlowStartEvent,\n RunId,\n SerializedError,\n SignalReceivedEvent,\n StepCompleteEvent,\n StepErrorEvent,\n StepEvent,\n StepId,\n StepRetryEvent,\n StepStartEvent,\n} from \"@nagi-js/core\";\nimport {\n type Attributes,\n type Context,\n context,\n type Span,\n SpanKind,\n SpanStatusCode,\n type Tracer,\n trace,\n} from \"@opentelemetry/api\";\nimport { stepKey, stepSpanRegistry } from \"./context\";\n\nconst TRACER_NAME = \"@nagi-js/otel\";\nconst TRACER_VERSION = \"0.0.0\";\n\nconst DEFAULT_FLOW_PREFIX = \"flow\";\nconst DEFAULT_STEP_PREFIX = \"step\";\n\nexport interface OtelHooksOpts {\n readonly tracer?: Tracer;\n readonly spanNamePrefix?: { readonly flow?: string; readonly step?: string };\n readonly defaultAttributes?: Attributes;\n}\n\nexport function otelHooks(opts: OtelHooksOpts = {}): FlowHooks {\n const tracer: Tracer =\n opts.tracer ?? trace.getTracer(TRACER_NAME, TRACER_VERSION);\n const flowPrefix = opts.spanNamePrefix?.flow ?? DEFAULT_FLOW_PREFIX;\n const stepPrefix = opts.spanNamePrefix?.step ?? DEFAULT_STEP_PREFIX;\n const baseAttrs: Attributes = opts.defaultAttributes ?? {};\n\n const flowCtxs = new Map<RunId, Context>();\n const stepStartTimes = new Map<string, Date>();\n\n function withGuard<E>(\n name: string,\n fn: (event: E) => void,\n ): (event: E) => void {\n return (event: E) => {\n try {\n fn(event);\n } catch (err) {\n console.error(`[@nagi-js/otel] ${name} hook failed`, err);\n }\n };\n }\n\n function flowAttrs(event: {\n readonly runId: RunId;\n readonly flowId: string;\n }): Attributes {\n return {\n ...baseAttrs,\n \"nagi.flow.id\": event.flowId,\n \"nagi.run.id\": event.runId,\n };\n }\n\n function stepAttrs(event: StepEvent): Attributes {\n return {\n ...flowAttrs(event),\n \"nagi.step.id\": event.stepId,\n \"nagi.step.attempt\": event.attempt,\n \"nagi.step.kind\": event.kind,\n };\n }\n\n function startStepSpan(event: StepStartEvent): Span {\n const parentCtx = flowCtxs.get(event.runId) ?? context.active();\n return tracer.startSpan(\n `${stepPrefix} ${event.stepId}`,\n {\n kind: SpanKind.INTERNAL,\n startTime: event.at,\n attributes: stepAttrs(event),\n },\n parentCtx,\n );\n }\n\n function resolveParentContext(event: FlowStartEvent): Context {\n if (!event.parent) return context.active();\n const stepSpan = stepSpanRegistry.get(\n stepKey(event.parent.runId, event.parent.stepId, event.parent.attempt),\n );\n if (stepSpan) return trace.setSpan(context.active(), stepSpan);\n const parentFlowCtx = flowCtxs.get(event.parent.runId);\n if (parentFlowCtx) return parentFlowCtx;\n return context.active();\n }\n\n function endStepSpanOk(event: StepCompleteEvent, span: Span): void {\n const k = stepKey(event.runId, event.stepId, event.attempt);\n const startedAt = stepStartTimes.get(k);\n const durationMs =\n event.kind === \"match\" && startedAt\n ? event.at.getTime() - startedAt.getTime()\n : event.durationMs;\n span.setAttribute(\"nagi.step.duration_ms\", durationMs);\n span.end(event.at);\n }\n\n function endStepSpanErr(\n span: Span,\n error: SerializedError,\n endAt: Date,\n ): void {\n span.recordException(toError(error));\n span.setAttribute(\"error.type\", error.name);\n span.setStatus({ code: SpanStatusCode.ERROR, message: error.message });\n span.end(endAt);\n }\n\n function consumeStepSpan(\n runId: RunId,\n stepId: StepId,\n attempt: AttemptNumber,\n ): Span | undefined {\n const k = stepKey(runId, stepId, attempt);\n const span = stepSpanRegistry.get(k);\n stepSpanRegistry.delete(k);\n stepStartTimes.delete(k);\n return span;\n }\n\n return {\n onFlowStart: withGuard<FlowStartEvent>(\"onFlowStart\", (event) => {\n const parentCtx = resolveParentContext(event);\n const attrs: Attributes = { ...flowAttrs(event) };\n if (event.parent) {\n attrs[\"nagi.parent.run.id\"] = event.parent.runId;\n attrs[\"nagi.parent.step.id\"] = event.parent.stepId;\n attrs[\"nagi.parent.step.attempt\"] = event.parent.attempt;\n }\n const span = tracer.startSpan(\n `${flowPrefix} ${event.flowId}`,\n {\n kind: SpanKind.INTERNAL,\n startTime: event.at,\n attributes: attrs,\n },\n parentCtx,\n );\n flowCtxs.set(event.runId, trace.setSpan(context.active(), span));\n }),\n\n onFlowComplete: withGuard<FlowCompleteEvent>(\"onFlowComplete\", (event) => {\n const ctx = flowCtxs.get(event.runId);\n flowCtxs.delete(event.runId);\n const span = ctx ? trace.getSpan(ctx) : undefined;\n span?.end(event.at);\n }),\n\n onFlowError: withGuard<FlowErrorEvent>(\"onFlowError\", (event) => {\n const ctx = flowCtxs.get(event.runId);\n flowCtxs.delete(event.runId);\n const span = ctx ? trace.getSpan(ctx) : undefined;\n if (span) endStepSpanErr(span, event.error, event.at);\n }),\n\n onStepStart: withGuard<StepStartEvent>(\"onStepStart\", (event) => {\n const span = startStepSpan(event);\n const k = stepKey(event.runId, event.stepId, event.attempt);\n stepSpanRegistry.set(k, span);\n stepStartTimes.set(k, event.at);\n }),\n\n onStepComplete: withGuard<StepCompleteEvent>(\"onStepComplete\", (event) => {\n const span = consumeStepSpan(event.runId, event.stepId, event.attempt);\n if (span) endStepSpanOk(event, span);\n }),\n\n onStepError: withGuard<StepErrorEvent>(\"onStepError\", (event) => {\n const span = consumeStepSpan(event.runId, event.stepId, event.attempt);\n if (span) endStepSpanErr(span, event.error, event.at);\n }),\n\n onStepRetry: withGuard<StepRetryEvent>(\"onStepRetry\", (event) => {\n const span = consumeStepSpan(event.runId, event.stepId, event.attempt);\n if (span) endStepSpanErr(span, event.error, event.at);\n\n const flowCtx = flowCtxs.get(event.runId);\n const flowSpan = flowCtx ? trace.getSpan(flowCtx) : undefined;\n flowSpan?.addEvent(\n \"nagi.retry.scheduled\",\n {\n \"nagi.step.id\": event.stepId,\n \"nagi.step.attempt\": event.attempt,\n \"nagi.next_attempt_at\": event.nextAttemptAt.toISOString(),\n },\n event.at,\n );\n }),\n\n onSignalReceived: withGuard<SignalReceivedEvent>(\n \"onSignalReceived\",\n (event) => {\n const span = consumeStepSpan(event.runId, event.stepId, event.attempt);\n if (!span) return;\n span.addEvent(\n \"nagi.signal.received\",\n { \"nagi.signal.payload_present\": event.payload !== null },\n event.at,\n );\n span.setAttribute(\"nagi.step.duration_ms\", 0);\n span.end(event.at);\n },\n ),\n };\n}\n\nfunction toError(err: SerializedError): Error {\n const e = new Error(err.message);\n e.name = err.name;\n if (err.stack !== undefined) e.stack = err.stack;\n return e;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nagi-js/otel",
|
|
3
|
-
"version": "0.1.1-rc.
|
|
3
|
+
"version": "0.1.1-rc.9",
|
|
4
4
|
"description": "OpenTelemetry hooks adapter for nagi.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"LICENSE"
|
|
36
36
|
],
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@nagi-js/core": "0.1.1-rc.
|
|
38
|
+
"@nagi-js/core": "0.1.1-rc.9"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
41
|
"@opentelemetry/api": "^1.9.0"
|