@telemetry-dev/sdk 0.1.8 → 0.1.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/README.md CHANGED
@@ -3,8 +3,11 @@
3
3
  Pure TypeScript SDK for [telemetry.dev](https://telemetry.dev): add LLM traces, logs, and GenAI
4
4
  metrics to any app. Built on OpenTelemetry — spans use the `gen_ai.*` semantic conventions and
5
5
  ship as OTLP protobuf to `https://ingest.telemetry.dev`. Works on Node >= 20.19 and edge runtimes
6
- (Vercel Edge, Cloudflare Workers with `nodejs_compat`). ESM-only; CJS consumers can `require()` it
7
- on Node >= 20.19.
6
+ (Vercel Edge, and Cloudflare Workers with Node.js compatibility, which is on by default for
7
+ compatibility dates of 2026-08-04 or later; earlier dates need the `nodejs_compat` flag). ESM-only;
8
+ CJS consumers can `require()` it on Node >= 20.19.
9
+
10
+ Docs: https://telemetry.dev/docs/sdk/typescript
8
11
 
9
12
  ```sh
10
13
  npm install @telemetry-dev/sdk
@@ -12,10 +15,20 @@ npm install @telemetry-dev/sdk
12
15
 
13
16
  ## Quickstart
14
17
 
18
+ Set `TELEMETRY_DEV_API_KEY` to your project API key. `init()` reads it from the environment.
19
+
15
20
  ```ts
16
21
  import { init, observe, startSpan, propagateAttributes, log, flush } from "@telemetry-dev/sdk";
17
22
 
18
- init({ apiKey: "td_live_..." }); // or TELEMETRY_DEV_API_KEY
23
+ init();
24
+
25
+ // Stand-in for your model client, so the example runs on its own. Replace it with a real call.
26
+ async function callModel(question: string) {
27
+ return {
28
+ message: { role: "assistant", content: `You asked: ${question}` },
29
+ usage: { input: 12, output: 9 },
30
+ };
31
+ }
19
32
 
20
33
  const answer = observe(
21
34
  async function answer(question: string) {
@@ -25,13 +38,13 @@ const answer = observe(
25
38
  provider: "openai",
26
39
  input: [{ role: "user", content: question }],
27
40
  });
28
- const res = await llm.chat(question);
41
+ const res = await callModel(question);
29
42
  generation.end({
30
- output: res.message,
43
+ output: [res.message],
31
44
  usage: { inputTokens: res.usage.input, outputTokens: res.usage.output },
32
45
  });
33
46
  log("generation finished", { attributes: { cached: false } });
34
- return res.message;
47
+ return res.message.content;
35
48
  },
36
49
  { type: "agent" },
37
50
  );
@@ -70,6 +83,12 @@ still record non-content metadata such as response IDs and models, finish reason
70
83
  and errors. Capture flags do not gate stop sequences, tool descriptions and definitions (`gen_ai.tool.description`, `gen_ai.tool.definitions`), caller-supplied metadata or raw attributes,
71
84
  or exception messages and stack traces; redact those separately when needed.
72
85
 
86
+ `mask` receives input and output content, system instructions, log messages, log attributes
87
+ other than numbers and booleans, and content that integrations pass to
88
+ `handle.serializeContent`. `ctx.key` names the attribute being written. It does not receive
89
+ span or propagated `metadata`, raw `attributes`, or exception messages and stack traces; redact
90
+ those separately.
91
+
73
92
  Root spans remain independent by default. `init({ sessionMode: "process" })` groups otherwise
74
93
  uncorrelated spans and logs using a fresh opaque UUID for each enabled SDK initialization.
75
94
  Explicit and propagated session IDs take precedence, and valid parent trace context is preserved.
@@ -79,7 +98,7 @@ new ID. It is not persisted across processes.
79
98
  `sampler` accepts an OpenTelemetry `Sampler` and overrides `OTEL_TRACES_SAMPLER` / `OTEL_TRACES_SAMPLER_ARG`.
80
99
  The default is parent-based always-on. Session roots keep the configured root policy.
81
100
  Nested spans and explicitly parented spans keep the real parent's policy.
82
- The SDK installs the [session sampling wrapper](../otel/README.md#correlation-attributes) on its provider. This includes the global provider that Eve uses.
101
+ The SDK installs the [session sampling wrapper](https://github.com/telemetry-dev/telemetry/blob/main/packages/otel/README.md#correlation-attributes) on its provider. This includes the global provider that Eve uses.
83
102
 
84
103
  ## API
85
104
 
@@ -89,6 +108,13 @@ The SDK installs the [session sampling wrapper](../otel/README.md#correlation-at
89
108
  - `startSpan(name, options?)` — manual handle; does **not** activate context. `handle.update()`,
90
109
  `handle.end()`, `handle.recordOutputChunk(timestampMs?)`, `handle.traceparent`, `handle.span`
91
110
  (raw OTel span).
111
+ - `handle.serializeContent(value, key)` — prepare content that an integration writes outside
112
+ `input` and `output`, such as an error message or a raw attribute, the way `input` and `output`
113
+ are prepared: the configured `mask` with `key`, JSON stringify (strings pass through), then
114
+ truncation to `maxAttributeLength`. Returns `undefined` when the mask throws (reported through
115
+ `onError`) or drops the value, or the value cannot be serialized; write nothing then. It does not
116
+ check `captureInput` / `captureOutput`. Integrations that support older cores should treat a
117
+ missing method as a configured mask.
92
118
  - `startActiveSpan(name, options?, fn)` — callback-scoped; activates context, auto-ends.
93
119
  - `updateActiveSpan(fields)` — update the innermost active span.
94
120
  - `propagateAttributes({ userId, sessionId, metadata }, fn)` — stamps `user.id`,
@@ -165,10 +191,21 @@ that mode only this SDK's spans are exported by default.
165
191
  Any vanilla OTel app can also skip the SDK entirely: point a stock OTLP/HTTP protobuf exporter at
166
192
  `https://ingest.telemetry.dev/v1/traces` with an `Authorization: Bearer td_live_...` header.
167
193
 
194
+ ### Native Claude SDK spans
195
+
196
+ `@anthropic-ai/sdk` 0.134.0 and later emits OpenTelemetry spans natively through the global tracer provider.
197
+ To send these spans to telemetry.dev, refer to [Claude SDK](https://telemetry.dev/docs/integrations/claude-sdk).
198
+ If you use `init({ registerGlobal: true })`, also set `spanFilter: () => true`. The default filter of that mode exports only the spans of this SDK.
199
+ Do not use the native spans and `@telemetry-dev/anthropic` together. If you use the two, each request has two spans.
200
+
168
201
  ## Notes
169
202
 
170
- - AsyncLocalStorage powers context propagation. On runtimes without it (Workers without
171
- `nodejs_compat`) automatic nesting is unavailable entirely unless the host app registered its
203
+ - AsyncLocalStorage powers context propagation. On runtimes without it (Cloudflare Workers without
204
+ Node.js compatibility) automatic nesting is unavailable entirely unless the host app registered its
172
205
  own global OTel context manager — pass `parent` explicitly there.
173
206
  - `@opentelemetry/api` is a peer dependency (installed automatically by npm/pnpm) so the SDK
174
207
  shares the host app's OTel API singleton.
208
+
209
+ ## License
210
+
211
+ MIT. See [LICENSE](https://github.com/telemetry-dev/telemetry/blob/main/packages/sdk/LICENSE).
package/dist/index.d.mts CHANGED
@@ -5,7 +5,11 @@ import { BatchOptions, BatchOptions as BatchOptions$1, ExportMode, ExportMode as
5
5
  import { PushMetricExporter } from "@opentelemetry/sdk-metrics";
6
6
 
7
7
  //#region src/config.d.ts
8
- /** Redaction hook applied to captured content (input/output/log message) before JSON stringification. */
8
+ /**
9
+ * Redaction hook applied to captured content (input/output, log messages and attributes, and
10
+ * content passed to `SpanHandle.serializeContent`) before JSON stringification. Not applied to
11
+ * metadata or raw attributes.
12
+ */
9
13
  type MaskFn<T = unknown> = (value: T, ctx: {
10
14
  key: string;
11
15
  }) => T;
@@ -211,6 +215,15 @@ interface SpanHandle {
211
215
  /** Records an output chunk arrival. Pull-based streams include consumer delay between pulls. */
212
216
  recordOutputChunk(timestampMs?: number): void;
213
217
  reportError?(cause: unknown): void;
218
+ /**
219
+ * Prepares content that the span writes outside `input` and `output` the way those are
220
+ * prepared: the configured mask with `key` (the attribute the result is written to), JSON
221
+ * stringify (strings pass through), then truncation to `maxAttributeLength`. Returns undefined
222
+ * when the mask throws (reported to `onError`) or drops the value, or the value cannot be
223
+ * serialized. Capture flags are not checked. Absent on cores that predate it; treat a missing
224
+ * method as masked.
225
+ */
226
+ serializeContent?(value: unknown, key: string): string | undefined;
214
227
  end(fields?: SpanFields & {
215
228
  endTime?: Date | number;
216
229
  }): void;
package/dist/index.mjs CHANGED
@@ -357,7 +357,7 @@ function prepareCaptureValue(key, value, cfg) {
357
357
  if (cfg.mask) try {
358
358
  masked = cfg.mask(value, { key });
359
359
  } catch (error) {
360
- reportError(cfg.onError, error instanceof Error ? error : new Error(String(error)));
360
+ reportError(cfg.onError, error);
361
361
  return;
362
362
  }
363
363
  const serialized = jsonAttr(masked);
@@ -540,6 +540,7 @@ const NOOP_SPAN_HANDLE = {
540
540
  update: () => NOOP_SPAN_HANDLE,
541
541
  recordOutputChunk: () => {},
542
542
  reportError: () => {},
543
+ serializeContent: () => void 0,
543
544
  end: () => {}
544
545
  };
545
546
  //#endregion
@@ -682,6 +683,9 @@ function createSpanHandle(core, name, options = {}) {
682
683
  reportError(cause) {
683
684
  reportError(meta.onError, cause instanceof Error ? cause : new Error(String(cause)));
684
685
  },
686
+ serializeContent(value, key) {
687
+ return prepareCaptureValue(key, value, meta.capture);
688
+ },
685
689
  end(fields) {
686
690
  try {
687
691
  if (fields) applyFields(span, meta, fields);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@telemetry-dev/sdk",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "description": "Pure TypeScript SDK for telemetry.dev: LLM traces, logs, and GenAI metrics on OpenTelemetry, for Node and edge runtimes.",
5
5
  "keywords": [
6
6
  "genai",
@@ -42,7 +42,7 @@
42
42
  "@opentelemetry/sdk-logs": "^0.218.0",
43
43
  "@opentelemetry/sdk-metrics": "^2.7.1",
44
44
  "@opentelemetry/sdk-trace-base": "^2.7.1",
45
- "@telemetry-dev/otel": "^0.1.5"
45
+ "@telemetry-dev/otel": "^0.1.6"
46
46
  },
47
47
  "devDependencies": {
48
48
  "@opentelemetry/api": "^1.9.1",
package/src/capture.ts CHANGED
@@ -271,7 +271,7 @@ export function prepareCaptureValue(
271
271
  try {
272
272
  masked = cfg.mask(value, { key });
273
273
  } catch (error) {
274
- reportError(cfg.onError, error instanceof Error ? error : new Error(String(error)));
274
+ reportError(cfg.onError, error);
275
275
 
276
276
  return undefined;
277
277
  }
package/src/config.ts CHANGED
@@ -12,7 +12,11 @@ import {
12
12
 
13
13
  export type { BatchOptions, ExportMode, LogLevel, SdkLogLevel } from "@telemetry-dev/otel";
14
14
 
15
- /** Redaction hook applied to captured content (input/output/log message) before JSON stringification. */
15
+ /**
16
+ * Redaction hook applied to captured content (input/output, log messages and attributes, and
17
+ * content passed to `SpanHandle.serializeContent`) before JSON stringification. Not applied to
18
+ * metadata or raw attributes.
19
+ */
16
20
  export type MaskFn<T = unknown> = (value: T, ctx: { key: string }) => T;
17
21
 
18
22
  export interface TelemetryOptions {
package/src/noop.ts CHANGED
@@ -16,5 +16,6 @@ export const NOOP_SPAN_HANDLE: SpanHandle = {
16
16
  update: () => NOOP_SPAN_HANDLE,
17
17
  recordOutputChunk: () => {},
18
18
  reportError: () => {},
19
+ serializeContent: () => undefined,
19
20
  end: () => {},
20
21
  };
package/src/span.ts CHANGED
@@ -38,6 +38,7 @@ import {
38
38
  type SpanFields,
39
39
  type SpanType,
40
40
  } from "./attrs.ts";
41
+ import { prepareCaptureValue } from "./capture.ts";
41
42
  import { type ClientCore, currentClient } from "./client.ts";
42
43
  import { NOOP_SPAN_HANDLE } from "./noop.ts";
43
44
 
@@ -80,6 +81,15 @@ export interface SpanHandle {
80
81
  /** Records an output chunk arrival. Pull-based streams include consumer delay between pulls. */
81
82
  recordOutputChunk(timestampMs?: number): void;
82
83
  reportError?(cause: unknown): void;
84
+ /**
85
+ * Prepares content that the span writes outside `input` and `output` the way those are
86
+ * prepared: the configured mask with `key` (the attribute the result is written to), JSON
87
+ * stringify (strings pass through), then truncation to `maxAttributeLength`. Returns undefined
88
+ * when the mask throws (reported to `onError`) or drops the value, or the value cannot be
89
+ * serialized. Capture flags are not checked. Absent on cores that predate it; treat a missing
90
+ * method as masked.
91
+ */
92
+ serializeContent?(value: unknown, key: string): string | undefined;
83
93
  end(fields?: SpanFields & { endTime?: Date | number }): void;
84
94
  }
85
95
 
@@ -303,6 +313,9 @@ export function createSpanHandle(
303
313
  reportError(cause) {
304
314
  reportError(meta.onError, cause instanceof Error ? cause : new Error(String(cause)));
305
315
  },
316
+ serializeContent(value, key) {
317
+ return prepareCaptureValue(key, value, meta.capture);
318
+ },
306
319
  end(fields) {
307
320
  try {
308
321
  if (fields) applyFields(span, meta, fields);