autotel 3.7.0 → 4.0.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.
Files changed (57) hide show
  1. package/dist/{attributes-ksn4HVbd.js → attributes-CmYpdqCN.js} +2 -11
  2. package/dist/attributes-CmYpdqCN.js.map +1 -0
  3. package/dist/{attributes-D3etyRVc.cjs → attributes-PZ5doLgw.cjs} +2 -11
  4. package/dist/attributes-PZ5doLgw.cjs.map +1 -0
  5. package/dist/attributes.cjs +1 -1
  6. package/dist/attributes.d.cts +2 -2
  7. package/dist/attributes.d.ts +2 -2
  8. package/dist/attributes.js +1 -1
  9. package/dist/{index-CX0aG1Uh.d.ts → index-Ck06vlW2.d.ts} +2 -32
  10. package/dist/index-Ck06vlW2.d.ts.map +1 -0
  11. package/dist/{index-DIWZFKUS.d.cts → index-eKuioqT1.d.cts} +2 -32
  12. package/dist/index-eKuioqT1.d.cts.map +1 -0
  13. package/dist/index.cjs +2 -346
  14. package/dist/index.cjs.map +1 -1
  15. package/dist/index.d.cts +3 -171
  16. package/dist/index.d.cts.map +1 -1
  17. package/dist/index.d.ts +3 -171
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +4 -333
  20. package/dist/index.js.map +1 -1
  21. package/dist/{registry-DfXA3R1L.js → registry-DVSmWg6Y.js} +2 -11
  22. package/dist/registry-DVSmWg6Y.js.map +1 -0
  23. package/dist/{registry-JZg2J3RZ.cjs → registry-DYgvb62e.cjs} +1 -16
  24. package/dist/registry-DYgvb62e.cjs.map +1 -0
  25. package/dist/semantic-conventions.cjs +1 -1
  26. package/dist/semantic-conventions.js +1 -1
  27. package/dist/semantic-helpers.cjs +0 -113
  28. package/dist/semantic-helpers.cjs.map +1 -1
  29. package/dist/semantic-helpers.d.cts +1 -114
  30. package/dist/semantic-helpers.d.cts.map +1 -1
  31. package/dist/semantic-helpers.d.ts +1 -114
  32. package/dist/semantic-helpers.d.ts.map +1 -1
  33. package/dist/semantic-helpers.js +1 -113
  34. package/dist/semantic-helpers.js.map +1 -1
  35. package/package.json +1 -1
  36. package/skills/analyze-traces/SKILL.md +14 -12
  37. package/skills/review-otel-patterns/SKILL.md +4 -2
  38. package/skills/tune-sampling/SKILL.md +8 -3
  39. package/src/attributes/builders.ts +2 -20
  40. package/src/attributes/index.ts +0 -1
  41. package/src/attributes/registry.ts +2 -9
  42. package/src/attributes/types.ts +0 -8
  43. package/src/index.ts +4 -41
  44. package/src/semantic-helpers.test.ts +2 -87
  45. package/src/semantic-helpers.ts +0 -146
  46. package/dist/attributes-D3etyRVc.cjs.map +0 -1
  47. package/dist/attributes-ksn4HVbd.js.map +0 -1
  48. package/dist/index-CX0aG1Uh.d.ts.map +0 -1
  49. package/dist/index-DIWZFKUS.d.cts.map +0 -1
  50. package/dist/registry-DfXA3R1L.js.map +0 -1
  51. package/dist/registry-JZg2J3RZ.cjs.map +0 -1
  52. package/src/gen-ai-cost.test.ts +0 -81
  53. package/src/gen-ai-cost.ts +0 -145
  54. package/src/gen-ai-events.test.ts +0 -135
  55. package/src/gen-ai-events.ts +0 -208
  56. package/src/gen-ai-metrics.test.ts +0 -96
  57. package/src/gen-ai-metrics.ts +0 -128
@@ -90,7 +90,7 @@ Key conventions to recognise:
90
90
  - **Sequential awaits that should be parallel** — sibling spans run end-to-end instead of overlapping.
91
91
  - **N+1 queries** — many short same-named spans (`SELECT * FROM …`) under one parent.
92
92
  - **Cold starts** — `faas.coldstart=true` in Workers or Lambda.
93
- - **Tool retries** — gen-ai spans with `gen_ai.response.finish_reason = error` followed by another call.
93
+ - **Tool retries** — gen-ai spans with `gen_ai.response.finish_reasons` containing `error` followed by another call.
94
94
 
95
95
  ### "Follow this user across services"
96
96
 
@@ -137,21 +137,23 @@ init({ service: 'my-app', debug: 'pretty', spanDumpPath: '.autotel/spans' });
137
137
 
138
138
  LLM calls produce a parent span (kind `CLIENT`) with children for each tool call:
139
139
 
140
- | Attribute | Meaning |
141
- | ---------------------------------------------------------- | ---------------------------------------- |
142
- | `gen_ai.system` | Provider (`openai`, `anthropic`, …) |
143
- | `gen_ai.request.model` | Model id |
144
- | `gen_ai.usage.input_tokens` / `output_tokens` | Token count |
145
- | `gen_ai.usage.cache_read_tokens` / `cache_creation_tokens` | Cache hits |
146
- | `gen_ai.response.finish_reason` | `stop`, `tool_calls`, `length`, `error` |
147
- | `gen_ai.tool.name` | Tool invoked (on tool-call child spans) |
148
- | `gen_ai.cost.usd` | Estimated cost (if pricing map provided) |
140
+ | Attribute | Meaning |
141
+ | ---------------------------------------------------------------------- | ---------------------------------------- |
142
+ | `gen_ai.provider.name` (legacy: `gen_ai.system`) | Provider (`openai`, `anthropic`, …) |
143
+ | `gen_ai.request.model` | Model id |
144
+ | `gen_ai.usage.input_tokens` / `output_tokens` | Token count |
145
+ | `gen_ai.usage.cache_read.input_tokens` / `cache_creation.input_tokens` | Cache hits |
146
+ | `gen_ai.response.finish_reasons` | `stop`, `tool_calls`, `length`, `error` |
147
+ | `gen_ai.tool.name` | Tool invoked (on tool-call child spans) |
148
+ | `gen_ai.usage.cost.usd` | Estimated cost (if pricing map provided) |
149
+
150
+ These canonical `gen_ai.*` attributes are emitted by the `autotel-genai` package (third-party instrumentations may still emit the deprecated `gen_ai.system`).
149
151
 
150
152
  Common findings:
151
153
 
152
- - High `gen_ai.usage.input_tokens` with low `cache_read_tokens` → enable prompt caching.
154
+ - High `gen_ai.usage.input_tokens` with low `gen_ai.usage.cache_read.input_tokens` → enable prompt caching.
153
155
  - Many sequential tool-call spans → consider parallel tool calls if the model supports it.
154
- - `gen_ai.response.finish_reason = length` → bump `max_tokens`.
156
+ - `gen_ai.response.finish_reasons` contains `length` → bump `max_tokens`.
155
157
 
156
158
  ## When the trace is missing
157
159
 
@@ -385,7 +385,9 @@ Compose them at build time with `composeSpanProcessors([...])` — no boilerplat
385
385
 
386
386
  ## AI SDK integration (gen-ai semantic conventions)
387
387
 
388
- autotel implements the **OTel gen-ai semantic conventions** out of the box. Token usage, tool calls, model info, latency, cost — captured as standard attributes (`gen_ai.usage.input_tokens`, `gen_ai.tool.name`, `gen_ai.response.finish_reason`, …) so any backend that understands OTel can render LLM telemetry without custom mapping.
388
+ autotel implements the **OTel gen-ai semantic conventions** out of the box. Token usage, tool calls, model info, latency, cost — captured as standard attributes (`gen_ai.usage.input_tokens`, `gen_ai.tool.name`, `gen_ai.response.finish_reasons`, …) so any backend that understands OTel can render LLM telemetry without custom mapping.
389
+
390
+ > Node.js apps get the same canonical `gen_ai.*` conventions (plus cost, metric views, and agent governance) from the `autotel-genai` package — `traceGenAI` / `recordGenAiUsage` from `autotel-genai/trace` and `genAiMetricViews` from `autotel-genai/metrics`. `withAiTelemetry` below is the edge-runtime entry point.
389
391
 
390
392
  ```typescript
391
393
  import { trace } from 'autotel';
@@ -402,7 +404,7 @@ const handler = trace(async (req) => {
402
404
  });
403
405
  ```
404
406
 
405
- Captured attributes per call: `gen_ai.system`, `gen_ai.request.model`, `gen_ai.usage.{input,output,reasoning,cache_read}_tokens`, `gen_ai.response.finish_reason`, `gen_ai.response.id`, plus per-tool spans with `gen_ai.tool.name`, `gen_ai.tool.duration`. Cost estimation comes for free if you pass a pricing map to `withAiTelemetry`.
407
+ Captured attributes per call: `gen_ai.provider.name`, `gen_ai.request.model`, `gen_ai.usage.input_tokens` / `output_tokens` / `reasoning.output_tokens` / `cache_read.input_tokens`, `gen_ai.response.finish_reasons`, `gen_ai.response.id`, plus per-tool spans with `gen_ai.tool.name`. Cost estimation (`gen_ai.usage.cost.usd`) comes for free if you pass a pricing map to `withAiTelemetry`.
406
408
 
407
409
  Anti-patterns to detect:
408
410
 
@@ -108,7 +108,12 @@ const tail = new TailSamplingProcessor({
108
108
 
109
109
  // 4. Always keep AI traces (rare + expensive — full visibility helps)
110
110
  if (
111
- trace.spans.some((s) => typeof s.attributes['gen_ai.system'] === 'string')
111
+ trace.spans.some(
112
+ (s) =>
113
+ typeof s.attributes['gen_ai.provider.name'] === 'string' ||
114
+ // legacy: third-party instrumentations may still emit gen_ai.system
115
+ typeof s.attributes['gen_ai.system'] === 'string',
116
+ )
112
117
  )
113
118
  return true;
114
119
 
@@ -142,8 +147,8 @@ keep: (trace) => {
142
147
  const cost = trace.spans.reduce(
143
148
  (acc, s) =>
144
149
  acc +
145
- (typeof s.attributes['gen_ai.cost.usd'] === 'number'
146
- ? (s.attributes['gen_ai.cost.usd'] as number)
150
+ (typeof s.attributes['gen_ai.usage.cost.usd'] === 'number'
151
+ ? (s.attributes['gen_ai.usage.cost.usd'] as number)
147
152
  : 0),
148
153
  0,
149
154
  );
@@ -38,7 +38,6 @@ import {
38
38
  FaaSAttributes,
39
39
  FeatureFlagAttributes,
40
40
  MessagingAttributes,
41
- GenAIAttributes,
42
41
  RPCAttributes,
43
42
  GraphQLAttributes,
44
43
  OTelAttributes,
@@ -477,25 +476,8 @@ export const attrs = {
477
476
  },
478
477
  },
479
478
 
480
- genAI: {
481
- system: (value: string) => ({ [GenAIAttributes.system]: value }),
482
- requestModel: (value: string) => ({
483
- [GenAIAttributes.requestModel]: value,
484
- }),
485
- responseModel: (value: string) => ({
486
- [GenAIAttributes.responseModel]: value,
487
- }),
488
- operationName: (value: 'chat' | 'completion' | 'embedding') => ({
489
- [GenAIAttributes.operationName]: value,
490
- }),
491
- usagePromptTokens: (value: number) => ({
492
- [GenAIAttributes.usagePromptTokens]: value,
493
- }),
494
- usageCompletionTokens: (value: number) => ({
495
- [GenAIAttributes.usageCompletionTokens]: value,
496
- }),
497
- provider: (value: string) => ({ [GenAIAttributes.provider]: value }),
498
- },
479
+ // GenAI/LLM attribute builders moved to the `autotel-genai` package
480
+ // (canonical `gen_ai.*` semantic conventions).
499
481
 
500
482
  rpc: {
501
483
  system: (value: string) => ({ [RPCAttributes.system]: value }),
@@ -70,7 +70,6 @@ export type {
70
70
  K8sAttrs,
71
71
  FaaSAttrs,
72
72
  ThreadAttrs,
73
- GenAIAttrs,
74
73
  RPCAttrs,
75
74
  GraphQLAttrs,
76
75
  ClientAttrs,
@@ -155,15 +155,8 @@ export const MessagingAttributes = {
155
155
  consumerGroup: 'messaging.consumer.group' as const,
156
156
  } as const;
157
157
 
158
- export const GenAIAttributes = {
159
- system: 'gen.ai.system' as const,
160
- requestModel: 'gen.ai.request.model' as const,
161
- responseModel: 'gen.ai.response.model' as const,
162
- operationName: 'gen.ai.operation.name' as const,
163
- usagePromptTokens: 'gen.ai.usage.prompt_tokens' as const,
164
- usageCompletionTokens: 'gen.ai.usage.completion_tokens' as const,
165
- provider: 'gen.ai.provider' as const,
166
- } as const;
158
+ // GenAI attribute registry moved to the `autotel-genai` package, which uses the
159
+ // canonical `gen_ai.*` namespace (these legacy `gen.ai.*` keys were non-spec).
167
160
 
168
161
  export const RPCAttributes = {
169
162
  system: 'rpc.system' as const,
@@ -155,14 +155,6 @@ export interface ThreadAttrs {
155
155
  name?: string;
156
156
  }
157
157
 
158
- export interface GenAIAttrs {
159
- system?: string;
160
- requestModel?: string;
161
- responseModel?: string;
162
- operationName?: 'chat' | 'completion' | 'embedding';
163
- provider?: string;
164
- }
165
-
166
158
  export interface RPCAttrs {
167
159
  system?: string;
168
160
  service?: string;
package/src/index.ts CHANGED
@@ -241,43 +241,9 @@ export {
241
241
  createObservableGauge,
242
242
  } from './metric-helpers';
243
243
 
244
- // LLM-tuned histogram buckets pass genAiMetricViews() to your
245
- // MeterProvider so gen_ai.* histograms have useful resolution.
246
- export {
247
- GEN_AI_DURATION_BUCKETS_SECONDS,
248
- GEN_AI_TOKEN_USAGE_BUCKETS,
249
- GEN_AI_COST_USD_BUCKETS,
250
- genAiMetricViews,
251
- llmHistogramAdvice,
252
- } from './gen-ai-metrics';
253
-
254
- // OTel GenAI span event helpers — record prompt-sent / response-received
255
- // / retry / tool-call / stream-first-token as timestamped events aligned
256
- // with the published GenAI semantic conventions.
257
- export {
258
- recordPromptSent,
259
- recordResponseReceived,
260
- recordRetry,
261
- recordToolCall,
262
- recordStreamFirstToken,
263
- type PromptSentEvent,
264
- type ResponseReceivedEvent,
265
- type RetryEvent,
266
- type ToolCallEvent,
267
- type StreamFirstTokenEvent,
268
- } from './gen-ai-events';
269
-
270
- // Per-model LLM cost estimation — estimate USD cost from token usage and
271
- // record it as the gen_ai.usage.cost.usd span attribute.
272
- export {
273
- estimateLLMCost,
274
- recordLLMCost,
275
- MODEL_PRICING,
276
- GEN_AI_COST_ATTRIBUTE,
277
- type ModelPricing,
278
- type TokenUsage,
279
- type EstimateCostOptions,
280
- } from './gen-ai-cost';
244
+ // GenAI / LLM instrumentation (cost, token usage, metric buckets, span event
245
+ // helpers, traceLLM) lives in the dedicated `autotel-genai` package — canonical
246
+ // `gen_ai.*` semantic conventions. Core stays generic and AI-free.
281
247
 
282
248
  // Tracer helpers for custom spans
283
249
  export {
@@ -303,13 +269,11 @@ export {
303
269
  getAutotelTracer,
304
270
  } from './tracer-provider';
305
271
 
306
- // Semantic convention helpers
272
+ // Semantic convention helpers (GenAI/LLM helpers moved to `autotel-genai`).
307
273
  export {
308
- traceLLM,
309
274
  traceDB,
310
275
  traceHTTP,
311
276
  traceMessaging,
312
- type LLMConfig,
313
277
  type DBConfig,
314
278
  type HTTPConfig,
315
279
  type MessagingConfig,
@@ -389,7 +353,6 @@ export {
389
353
  type K8sAttrs,
390
354
  type FaaSAttrs,
391
355
  type ThreadAttrs,
392
- type GenAIAttrs,
393
356
  type RPCAttrs,
394
357
  type GraphQLAttrs,
395
358
  type ClientAttrs,
@@ -3,12 +3,7 @@
3
3
  */
4
4
 
5
5
  import { describe, it, expect, beforeEach } from 'vitest';
6
- import {
7
- traceLLM,
8
- traceDB,
9
- traceHTTP,
10
- traceMessaging,
11
- } from './semantic-helpers';
6
+ import { traceDB, traceHTTP, traceMessaging } from './semantic-helpers';
12
7
  import { createTraceCollector } from './testing';
13
8
 
14
9
  describe('Semantic Helpers', () => {
@@ -18,69 +13,7 @@ describe('Semantic Helpers', () => {
18
13
  collector = createTraceCollector();
19
14
  });
20
15
 
21
- describe('traceLLM', () => {
22
- it('should add Gen AI semantic convention attributes', async () => {
23
- const generateText = traceLLM({
24
- model: 'gpt-4',
25
- operation: 'chat',
26
- provider: 'openai',
27
- })((_ctx) => async (prompt: string) => {
28
- return `Response to: ${prompt}`;
29
- });
30
-
31
- await generateText('Hello');
32
-
33
- const spans = collector.getSpans();
34
- expect(spans).toHaveLength(1);
35
-
36
- const span = spans[0];
37
- expect(span.attributes['gen.ai.request.model']).toBe('gpt-4');
38
- expect(span.attributes['gen.ai.operation.name']).toBe('chat');
39
- expect(span.attributes['gen.ai.system']).toBe('openai');
40
- });
41
-
42
- it('should use default operation when not specified', async () => {
43
- const generateText = traceLLM({
44
- model: 'claude-3',
45
- })((_ctx) => async () => 'result');
46
-
47
- await generateText();
48
-
49
- const spans = collector.getSpans();
50
- expect(spans[0].attributes['gen.ai.operation.name']).toBe('chat');
51
- });
52
-
53
- it('should support embedding operation', async () => {
54
- const embed = traceLLM({
55
- model: 'text-embedding-3-small',
56
- operation: 'embedding',
57
- provider: 'openai',
58
- })((_ctx) => async (_text: string) => [0.1, 0.2, 0.3]);
59
-
60
- await embed('test text');
61
-
62
- const spans = collector.getSpans();
63
- expect(spans[0].attributes['gen.ai.operation.name']).toBe('embedding');
64
- });
65
-
66
- it('should support additional custom attributes', async () => {
67
- const generateText = traceLLM({
68
- model: 'gpt-4',
69
- attributes: {
70
- 'custom.attribute': 'custom-value',
71
- 'custom.number': 123,
72
- },
73
- })((_ctx) => async () => 'result');
74
-
75
- await generateText();
76
-
77
- const spans = collector.getSpans();
78
- const span = spans[0];
79
- expect(span.attributes['gen.ai.request.model']).toBe('gpt-4');
80
- expect(span.attributes['custom.attribute']).toBe('custom-value');
81
- expect(span.attributes['custom.number']).toBe(123);
82
- });
83
- });
16
+ // GenAI/LLM helpers (`traceLLM`) moved to the `autotel-genai` package.
84
17
 
85
18
  describe('traceDB', () => {
86
19
  it('should add DB semantic convention attributes', async () => {
@@ -273,24 +206,6 @@ describe('Semantic Helpers', () => {
273
206
  });
274
207
 
275
208
  describe('Attribute merging', () => {
276
- it('should merge custom attributes with semantic attributes in traceLLM', async () => {
277
- const fn = traceLLM({
278
- model: 'gpt-4',
279
- attributes: {
280
- 'gen.ai.request.temperature': 0.7,
281
- 'custom.attr': 'value',
282
- },
283
- })((_ctx) => async () => 'result');
284
-
285
- await fn();
286
-
287
- const spans = collector.getSpans();
288
- const span = spans[0];
289
- expect(span.attributes['gen.ai.request.model']).toBe('gpt-4');
290
- expect(span.attributes['gen.ai.request.temperature']).toBe(0.7);
291
- expect(span.attributes['custom.attr']).toBe('value');
292
- });
293
-
294
209
  it('should allow custom attributes to override semantic defaults', async () => {
295
210
  const fn = traceDB({
296
211
  system: 'postgresql',
@@ -11,23 +11,6 @@ import { trace } from './functional';
11
11
  import type { TraceContext } from './trace-context';
12
12
  import type { Attributes } from '@opentelemetry/api';
13
13
 
14
- /**
15
- * Configuration for LLM (Large Language Model) operations
16
- *
17
- * Follows Gen AI semantic conventions:
18
- * https://opentelemetry.io/docs/specs/semconv/gen-ai/
19
- */
20
- export interface LLMConfig {
21
- /** Model name (e.g., 'gpt-4', 'claude-3-opus') */
22
- model: string;
23
- /** Operation type */
24
- operation?: 'chat' | 'completion' | 'embedding';
25
- /** Model provider (e.g., 'openai', 'anthropic', 'cohere') - maps to gen.ai.system */
26
- provider?: string;
27
- /** Additional attributes to add to the span */
28
- attributes?: Attributes;
29
- }
30
-
31
14
  /**
32
15
  * Configuration for database operations
33
16
  *
@@ -79,135 +62,6 @@ export interface MessagingConfig {
79
62
  attributes?: Attributes;
80
63
  }
81
64
 
82
- /**
83
- * Trace LLM operations with Gen AI semantic conventions
84
- *
85
- * Automatically adds standard attributes for LLM operations:
86
- * - gen.ai.request.model
87
- * - gen.ai.operation.name
88
- * - gen.ai.system
89
- *
90
- * **Use Cases:**
91
- * - Chat completions
92
- * - Text generation
93
- * - Embeddings
94
- * - Multi-step LLM workflows
95
- *
96
- * @param config - LLM operation configuration
97
- * @returns Traced function factory with Gen AI attributes
98
- *
99
- * @example Chat completion with OpenAI
100
- * ```typescript
101
- * import { traceLLM } from 'autotel/semantic-helpers'
102
- * import OpenAI from 'openai'
103
- *
104
- * const openai = new OpenAI()
105
- *
106
- * export const generateResponse = traceLLM({
107
- * model: 'gpt-4-turbo',
108
- * operation: 'chat',
109
- * provider: 'openai'
110
- * })(ctx => async (prompt: string) => {
111
- * const response = await openai.chat.completions.create({
112
- * model: 'gpt-4-turbo',
113
- * messages: [{ role: 'user', content: prompt }]
114
- * })
115
- *
116
- * // Add usage metrics to span
117
- * ctx.setAttribute('gen.ai.usage.completion_tokens', response.usage?.completion_tokens)
118
- * ctx.setAttribute('gen.ai.usage.prompt_tokens', response.usage?.prompt_tokens)
119
- *
120
- * return response.choices[0].message.content
121
- * })
122
- * ```
123
- *
124
- * @example Anthropic Claude with streaming
125
- * ```typescript
126
- * import { traceLLM } from 'autotel/semantic-helpers'
127
- * import Anthropic from '@anthropic-ai/sdk'
128
- *
129
- * const anthropic = new Anthropic()
130
- *
131
- * export const streamResponse = traceLLM({
132
- * model: 'claude-3-opus-20240229',
133
- * operation: 'chat',
134
- * provider: 'anthropic'
135
- * })(ctx => async function* (prompt: string) {
136
- * const stream = await anthropic.messages.create({
137
- * model: 'claude-3-opus-20240229',
138
- * messages: [{ role: 'user', content: prompt }],
139
- * stream: true,
140
- * max_tokens: 1024
141
- * })
142
- *
143
- * let totalTokens = 0
144
- * for await (const event of stream) {
145
- * if (event.type === 'content_block_delta') {
146
- * yield event.delta.text
147
- * }
148
- * if (event.type === 'message_stop') {
149
- * ctx.setAttribute('gen.ai.usage.completion_tokens', event.message.usage.output_tokens)
150
- * totalTokens = event.message.usage.output_tokens
151
- * }
152
- * }
153
- *
154
- * return totalTokens
155
- * })
156
- * ```
157
- *
158
- * @example Embeddings
159
- * ```typescript
160
- * import { traceLLM } from 'autotel/semantic-helpers'
161
- * import { OpenAIEmbeddings } from '@langchain/openai'
162
- *
163
- * const embeddings = new OpenAIEmbeddings()
164
- *
165
- * export const embed = traceLLM({
166
- * model: 'text-embedding-3-small',
167
- * operation: 'embedding',
168
- * provider: 'openai'
169
- * })(ctx => async (text: string) => {
170
- * const result = await embeddings.embedQuery(text)
171
- * ctx.setAttribute('gen.ai.response.embedding_length', result.length)
172
- * return result
173
- * })
174
- * ```
175
- *
176
- * @public
177
- */
178
- export function traceLLM<TArgs extends unknown[], TReturn>(config: LLMConfig) {
179
- return (
180
- fnFactory: (ctx: TraceContext) => (...args: TArgs) => Promise<TReturn>,
181
- ): ((...args: TArgs) => Promise<TReturn>) => {
182
- return trace<TArgs, TReturn>((ctx) => {
183
- // Set semantic convention attributes
184
- ctx.setAttribute('gen.ai.request.model', config.model);
185
- ctx.setAttribute('gen.ai.operation.name', config.operation || 'chat');
186
- if (config.provider) {
187
- ctx.setAttribute('gen.ai.system', config.provider);
188
- }
189
- if (config.attributes) {
190
- for (const [key, value] of Object.entries(config.attributes)) {
191
- if (value !== undefined && value !== null) {
192
- // setAttribute only accepts primitives (string | number | boolean)
193
- // Arrays and objects should be serialized
194
- const attrValue =
195
- typeof value === 'string' ||
196
- typeof value === 'number' ||
197
- typeof value === 'boolean'
198
- ? value
199
- : JSON.stringify(value);
200
- ctx.setAttribute(key, attrValue);
201
- }
202
- }
203
- }
204
-
205
- // Call the user's factory to get their function and return it
206
- return fnFactory(ctx);
207
- });
208
- };
209
- }
210
-
211
65
  /**
212
66
  * Trace database operations with DB semantic conventions
213
67
  *
@@ -1 +0,0 @@
1
- {"version":3,"file":"attributes-D3etyRVc.cjs","names":["UserAttributes","SessionAttributes","DeviceAttributes","HTTPAttributes","DBAttributes","ServiceAttributes","NetworkAttributes","ServerAddressAttributes","URLAttributes","ErrorAttributes","ExceptionAttributes","ProcessAttributes","ThreadAttributes","ContainerAttributes","K8sAttributes","CloudAttributes","FaaSAttributes","FeatureFlagAttributes","MessagingAttributes","GenAIAttributes","RPCAttributes","GraphQLAttributes","OTelAttributes","CodeAttributes","TLSAttributes","REDACTOR_PATTERNS"],"sources":["../src/attributes/builders.ts","../src/attributes/validators.ts","../src/attributes/utils.ts","../src/attributes/attachers.ts","../src/attributes/domains.ts"],"sourcesContent":["/**\n * Attribute builders for constructing OpenTelemetry attributes\n * Provides both key builders (Pattern A) and object builders (Pattern B)\n *\n * @example Pattern A: Key builders\n * ```typescript\n * attrs.user.id('123') // { 'user.id': '123' }\n * attrs.user.email('user@example.com') // { 'user.email': 'user@example.com' }\n * attrs.http.request.method('GET') // { 'http.request.method': 'GET' }\n * attrs.http.response.statusCode(200) // { 'http.response.status_code': 200 }\n * ```\n *\n * @example Pattern B: Object builders\n * ```typescript\n * attrs.user({ id: '123', email: 'user@example.com' })\n * attrs.http.server({ method: 'GET', route: '/users/:id', statusCode: 200 })\n * attrs.db.client({ system: 'postgresql', operation: 'SELECT', collectionName: 'users' })\n * ```\n */\n\nimport {\n UserAttributes,\n SessionAttributes,\n DeviceAttributes,\n HTTPAttributes,\n DBAttributes,\n ServiceAttributes,\n NetworkAttributes,\n ServerAddressAttributes,\n URLAttributes,\n ErrorAttributes,\n ExceptionAttributes,\n ProcessAttributes,\n ThreadAttributes,\n ContainerAttributes,\n K8sAttributes,\n CloudAttributes,\n FaaSAttributes,\n FeatureFlagAttributes,\n MessagingAttributes,\n GenAIAttributes,\n RPCAttributes,\n GraphQLAttributes,\n OTelAttributes,\n CodeAttributes,\n TLSAttributes,\n} from './registry';\n\nimport type {\n UserAttrs,\n SessionAttrs,\n DeviceAttrs,\n HTTPServerAttrs,\n HTTPClientAttrs,\n DBAttrs,\n ServiceAttrs,\n NetworkAttrs,\n ErrorAttrs,\n MessagingAttrs,\n CloudAttrs,\n ServerAddressAttrs,\n URLAttrs,\n ProcessAttrs,\n ContainerAttrs,\n ExceptionAttrs,\n} from './types';\n\nexport const attrs = {\n user: {\n id: (value: string) => ({ [UserAttributes.id]: value }),\n email: (value: string) => ({ [UserAttributes.email]: value }),\n name: (value: string) => ({ [UserAttributes.name]: value }),\n fullName: (value: string) => ({ [UserAttributes.fullName]: value }),\n hash: (value: string) => ({ [UserAttributes.hash]: value }),\n roles: (value: string[]) => ({ [UserAttributes.roles]: value }),\n\n data: (data: UserAttrs) => {\n const result: Record<string, unknown> = {};\n if (data.id !== undefined) result[UserAttributes.id] = data.id;\n if (data.email !== undefined) result[UserAttributes.email] = data.email;\n if (data.name !== undefined) result[UserAttributes.name] = data.name;\n if (data.fullName !== undefined)\n result[UserAttributes.fullName] = data.fullName;\n if (data.hash !== undefined) result[UserAttributes.hash] = data.hash;\n if (data.roles !== undefined) result[UserAttributes.roles] = data.roles;\n return result;\n },\n },\n\n session: {\n id: (value: string) => ({ [SessionAttributes.id]: value }),\n previousId: (value: string) => ({ [SessionAttributes.previousId]: value }),\n\n data: (data: SessionAttrs) => {\n const result: Record<string, unknown> = {};\n if (data.id !== undefined) result[SessionAttributes.id] = data.id;\n if (data.previousId !== undefined)\n result[SessionAttributes.previousId] = data.previousId;\n return result;\n },\n },\n\n device: {\n id: (value: string) => ({ [DeviceAttributes.id]: value }),\n manufacturer: (value: string) => ({\n [DeviceAttributes.manufacturer]: value,\n }),\n modelIdentifier: (value: string) => ({\n [DeviceAttributes.modelIdentifier]: value,\n }),\n modelName: (value: string) => ({ [DeviceAttributes.modelName]: value }),\n\n data: (data: DeviceAttrs) => {\n const result: Record<string, unknown> = {};\n if (data.id !== undefined) result[DeviceAttributes.id] = data.id;\n if (data.manufacturer !== undefined)\n result[DeviceAttributes.manufacturer] = data.manufacturer;\n if (data.modelIdentifier !== undefined)\n result[DeviceAttributes.modelIdentifier] = data.modelIdentifier;\n if (data.modelName !== undefined)\n result[DeviceAttributes.modelName] = data.modelName;\n return result;\n },\n },\n\n http: {\n request: {\n method: (value: string) => ({ [HTTPAttributes.requestMethod]: value }),\n methodOriginal: (value: string) => ({\n [HTTPAttributes.requestMethodOriginal]: value,\n }),\n resendCount: (value: number) => ({\n [HTTPAttributes.requestResendCount]: value,\n }),\n size: (value: number) => ({ [HTTPAttributes.requestSize]: value }),\n bodySize: (value: number) => ({\n [HTTPAttributes.requestBodySize]: value,\n }),\n },\n\n response: {\n statusCode: (value: number) => ({\n [HTTPAttributes.responseStatusCode]: value,\n }),\n size: (value: number) => ({ [HTTPAttributes.responseSize]: value }),\n bodySize: (value: number) => ({\n [HTTPAttributes.responseBodySize]: value,\n }),\n },\n\n route: (value: string) => ({ [HTTPAttributes.route]: value }),\n connectionState: (value: string) => ({\n [HTTPAttributes.connectionState]: value,\n }),\n\n server: (data: HTTPServerAttrs) => {\n const result: Record<string, unknown> = {};\n if (data.method !== undefined)\n result[HTTPAttributes.requestMethod] = data.method;\n if (data.route !== undefined) result[HTTPAttributes.route] = data.route;\n if (data.statusCode !== undefined)\n result[HTTPAttributes.responseStatusCode] = data.statusCode;\n if (data.bodySize !== undefined)\n result[HTTPAttributes.requestBodySize] = data.bodySize;\n if (data.requestSize !== undefined)\n result[HTTPAttributes.requestSize] = data.requestSize;\n if (data.responseSize !== undefined)\n result[HTTPAttributes.responseSize] = data.responseSize;\n if (data.resendCount !== undefined)\n result[HTTPAttributes.requestResendCount] = data.resendCount;\n return result;\n },\n\n client: (data: HTTPClientAttrs) => {\n const result: Record<string, unknown> = {};\n if (data.method !== undefined)\n result[HTTPAttributes.requestMethod] = data.method;\n if (data.url !== undefined) result[HTTPAttributes.route] = data.url;\n if (data.statusCode !== undefined)\n result[HTTPAttributes.responseStatusCode] = data.statusCode;\n return result;\n },\n },\n\n db: {\n client: {\n system: (value: string) => ({ [DBAttributes.systemName]: value }),\n operation: (value: string) => ({ [DBAttributes.operationName]: value }),\n collectionName: (value: string) => ({\n [DBAttributes.collectionName]: value,\n }),\n namespace: (value: string) => ({ [DBAttributes.namespace]: value }),\n statement: (value: string) => ({ [DBAttributes.statement]: value }),\n querySummary: (value: string) => ({ [DBAttributes.querySummary]: value }),\n queryText: (value: string) => ({ [DBAttributes.queryText]: value }),\n responseStatus: (value: string | number) => ({\n [DBAttributes.responseStatusCode]: value,\n }),\n rowsReturned: (value: number) => ({\n [DBAttributes.responseReturnedRows]: value,\n }),\n\n data: (data: DBAttrs) => {\n const result: Record<string, unknown> = {};\n if (data.system !== undefined)\n result[DBAttributes.systemName] = data.system;\n if (data.operation !== undefined)\n result[DBAttributes.operationName] = data.operation;\n if (data.collectionName !== undefined)\n result[DBAttributes.collectionName] = data.collectionName;\n // 'name' maps to db.namespace (db.name is deprecated per OTel semantic conventions)\n if (data.name !== undefined) result[DBAttributes.namespace] = data.name;\n // 'namespace' takes precedence over 'name' if both are provided\n if (data.namespace !== undefined)\n result[DBAttributes.namespace] = data.namespace;\n if (data.statement !== undefined)\n result[DBAttributes.statement] = data.statement;\n if (data.querySummary !== undefined)\n result[DBAttributes.querySummary] = data.querySummary;\n if (data.queryText !== undefined)\n result[DBAttributes.queryText] = data.queryText;\n if (data.responseStatus !== undefined)\n result[DBAttributes.responseStatusCode] = data.responseStatus;\n if (data.rowsReturned !== undefined)\n result[DBAttributes.responseReturnedRows] = data.rowsReturned;\n return result;\n },\n },\n },\n\n service: {\n name: (value: string) => ({ [ServiceAttributes.name]: value }),\n instance: (value: string) => ({ [ServiceAttributes.instance]: value }),\n version: (value: string) => ({ [ServiceAttributes.version]: value }),\n\n data: (data: ServiceAttrs) => {\n const result: Record<string, unknown> = {};\n if (data.name !== undefined) result[ServiceAttributes.name] = data.name;\n if (data.instance !== undefined)\n result[ServiceAttributes.instance] = data.instance;\n if (data.version !== undefined)\n result[ServiceAttributes.version] = data.version;\n return result;\n },\n },\n\n network: {\n peerAddress: (value: string) => ({\n [NetworkAttributes.peerAddress]: value,\n }),\n peerPort: (value: number) => ({ [NetworkAttributes.peerPort]: value }),\n transport: (value: string) => ({ [NetworkAttributes.transport]: value }),\n protocolName: (value: string) => ({\n [NetworkAttributes.protocolName]: value,\n }),\n protocolVersion: (value: string) => ({\n [NetworkAttributes.protocolVersion]: value,\n }),\n\n data: (data: NetworkAttrs) => {\n const result: Record<string, unknown> = {};\n if (data.peerAddress !== undefined)\n result[NetworkAttributes.peerAddress] = data.peerAddress;\n if (data.peerPort !== undefined)\n result[NetworkAttributes.peerPort] = data.peerPort;\n if (data.transport !== undefined)\n result[NetworkAttributes.transport] = data.transport;\n if (data.protocolName !== undefined)\n result[NetworkAttributes.protocolName] = data.protocolName;\n if (data.protocolVersion !== undefined)\n result[NetworkAttributes.protocolVersion] = data.protocolVersion;\n return result;\n },\n },\n\n server: {\n address: (value: string) => ({ [ServerAddressAttributes.address]: value }),\n port: (value: number) => ({ [ServerAddressAttributes.port]: value }),\n socketAddress: (value: string) => ({\n [ServerAddressAttributes.socketAddress]: value,\n }),\n\n data: (data: ServerAddressAttrs) => {\n const result: Record<string, unknown> = {};\n if (data.address !== undefined)\n result[ServerAddressAttributes.address] = data.address;\n if (data.port !== undefined)\n result[ServerAddressAttributes.port] = data.port;\n if (data.socketAddress !== undefined)\n result[ServerAddressAttributes.socketAddress] = data.socketAddress;\n return result;\n },\n },\n\n url: {\n scheme: (value: string) => ({ [URLAttributes.scheme]: value }),\n full: (value: string) => ({ [URLAttributes.full]: value }),\n path: (value: string) => ({ [URLAttributes.path]: value }),\n query: (value: string) => ({ [URLAttributes.query]: value }),\n fragment: (value: string) => ({ [URLAttributes.fragment]: value }),\n\n data: (data: URLAttrs) => {\n const result: Record<string, unknown> = {};\n if (data.scheme !== undefined) result[URLAttributes.scheme] = data.scheme;\n if (data.full !== undefined) result[URLAttributes.full] = data.full;\n if (data.path !== undefined) result[URLAttributes.path] = data.path;\n if (data.query !== undefined) result[URLAttributes.query] = data.query;\n if (data.fragment !== undefined)\n result[URLAttributes.fragment] = data.fragment;\n return result;\n },\n },\n\n error: {\n type: (value: string) => ({ [ErrorAttributes.type]: value }),\n message: (value: string) => ({ [ErrorAttributes.message]: value }),\n stackTrace: (value: string) => ({ [ErrorAttributes.stackTrace]: value }),\n code: (value: string | number) => ({ [ErrorAttributes.code]: value }),\n\n data: (data: ErrorAttrs) => {\n const result: Record<string, unknown> = {};\n if (data.type !== undefined) result[ErrorAttributes.type] = data.type;\n if (data.message !== undefined)\n result[ErrorAttributes.message] = data.message;\n if (data.stackTrace !== undefined)\n result[ErrorAttributes.stackTrace] = data.stackTrace;\n if (data.code !== undefined) result[ErrorAttributes.code] = data.code;\n return result;\n },\n },\n\n exception: {\n escaped: (value: boolean) => ({ [ExceptionAttributes.escaped]: value }),\n message: (value: string) => ({ [ExceptionAttributes.message]: value }),\n stackTrace: (value: string) => ({\n [ExceptionAttributes.stackTrace]: value,\n }),\n type: (value: string) => ({ [ExceptionAttributes.type]: value }),\n moduleName: (value: string) => ({\n [ExceptionAttributes.moduleName]: value,\n }),\n\n data: (data: ExceptionAttrs) => {\n const result: Record<string, unknown> = {};\n if (data.escaped !== undefined)\n result[ExceptionAttributes.escaped] = data.escaped;\n if (data.message !== undefined)\n result[ExceptionAttributes.message] = data.message;\n if (data.stackTrace !== undefined)\n result[ExceptionAttributes.stackTrace] = data.stackTrace;\n if (data.type !== undefined) result[ExceptionAttributes.type] = data.type;\n if (data.moduleName !== undefined)\n result[ExceptionAttributes.moduleName] = data.moduleName;\n return result;\n },\n },\n\n process: {\n pid: (value: number) => ({ [ProcessAttributes.pid]: value }),\n executablePath: (value: string) => ({\n [ProcessAttributes.executablePath]: value,\n }),\n command: (value: string) => ({ [ProcessAttributes.command]: value }),\n owner: (value: string) => ({ [ProcessAttributes.owner]: value }),\n\n data: (data: ProcessAttrs) => {\n const result: Record<string, unknown> = {};\n if (data.pid !== undefined) result[ProcessAttributes.pid] = data.pid;\n if (data.executablePath !== undefined)\n result[ProcessAttributes.executablePath] = data.executablePath;\n if (data.command !== undefined)\n result[ProcessAttributes.command] = data.command;\n if (data.owner !== undefined)\n result[ProcessAttributes.owner] = data.owner;\n return result;\n },\n },\n\n thread: {\n id: (value: number) => ({ [ThreadAttributes.id]: value }),\n name: (value: string) => ({ [ThreadAttributes.name]: value }),\n },\n\n container: {\n id: (value: string) => ({ [ContainerAttributes.id]: value }),\n name: (value: string) => ({ [ContainerAttributes.name]: value }),\n image: (value: string) => ({ [ContainerAttributes.image]: value }),\n tag: (value: string) => ({ [ContainerAttributes.tag]: value }),\n\n data: (data: ContainerAttrs) => {\n const result: Record<string, unknown> = {};\n if (data.id !== undefined) result[ContainerAttributes.id] = data.id;\n if (data.name !== undefined) result[ContainerAttributes.name] = data.name;\n if (data.image !== undefined)\n result[ContainerAttributes.image] = data.image;\n if (data.tag !== undefined) result[ContainerAttributes.tag] = data.tag;\n return result;\n },\n },\n\n k8s: {\n podName: (value: string) => ({ [K8sAttributes.podName]: value }),\n namespaceName: (value: string) => ({\n [K8sAttributes.namespaceName]: value,\n }),\n deploymentName: (value: string) => ({\n [K8sAttributes.deploymentName]: value,\n }),\n state: (value: string) => ({ [K8sAttributes.state]: value }),\n },\n\n cloud: {\n provider: (value: string) => ({ [CloudAttributes.provider]: value }),\n accountId: (value: string) => ({ [CloudAttributes.accountId]: value }),\n region: (value: string) => ({ [CloudAttributes.region]: value }),\n availabilityZone: (value: string) => ({\n [CloudAttributes.availabilityZone]: value,\n }),\n platform: (value: string) => ({ [CloudAttributes.platform]: value }),\n\n data: (data: CloudAttrs) => {\n const result: Record<string, unknown> = {};\n if (data.provider !== undefined)\n result[CloudAttributes.provider] = data.provider;\n if (data.accountId !== undefined)\n result[CloudAttributes.accountId] = data.accountId;\n if (data.region !== undefined)\n result[CloudAttributes.region] = data.region;\n if (data.availabilityZone !== undefined)\n result[CloudAttributes.availabilityZone] = data.availabilityZone;\n if (data.platform !== undefined)\n result[CloudAttributes.platform] = data.platform;\n return result;\n },\n },\n\n faas: {\n name: (value: string) => ({ [FaaSAttributes.name]: value }),\n version: (value: string) => ({ [FaaSAttributes.version]: value }),\n instance: (value: string) => ({ [FaaSAttributes.instance]: value }),\n execution: (value: string) => ({ [FaaSAttributes.execution]: value }),\n coldstart: (value: boolean) => ({ [FaaSAttributes.coldstart]: value }),\n },\n\n featureFlag: {\n key: (value: string) => ({ [FeatureFlagAttributes.key]: value }),\n provider: (value: string) => ({ [FeatureFlagAttributes.provider]: value }),\n variant: (value: string) => ({ [FeatureFlagAttributes.variant]: value }),\n },\n\n messaging: {\n system: (value: string) => ({ [MessagingAttributes.system]: value }),\n destination: (value: string) => ({\n [MessagingAttributes.destination]: value,\n }),\n operation: (value: 'publish' | 'receive' | 'process') => ({\n [MessagingAttributes.operation]: value,\n }),\n messageId: (value: string) => ({ [MessagingAttributes.messageId]: value }),\n conversationId: (value: string) => ({\n [MessagingAttributes.conversationId]: value,\n }),\n\n data: (data: MessagingAttrs) => {\n const result: Record<string, unknown> = {};\n if (data.system !== undefined)\n result[MessagingAttributes.system] = data.system;\n if (data.destination !== undefined)\n result[MessagingAttributes.destination] = data.destination;\n if (data.operation !== undefined)\n result[MessagingAttributes.operation] = data.operation;\n if (data.messageId !== undefined)\n result[MessagingAttributes.messageId] = data.messageId;\n if (data.conversationId !== undefined)\n result[MessagingAttributes.conversationId] = data.conversationId;\n return result;\n },\n },\n\n genAI: {\n system: (value: string) => ({ [GenAIAttributes.system]: value }),\n requestModel: (value: string) => ({\n [GenAIAttributes.requestModel]: value,\n }),\n responseModel: (value: string) => ({\n [GenAIAttributes.responseModel]: value,\n }),\n operationName: (value: 'chat' | 'completion' | 'embedding') => ({\n [GenAIAttributes.operationName]: value,\n }),\n usagePromptTokens: (value: number) => ({\n [GenAIAttributes.usagePromptTokens]: value,\n }),\n usageCompletionTokens: (value: number) => ({\n [GenAIAttributes.usageCompletionTokens]: value,\n }),\n provider: (value: string) => ({ [GenAIAttributes.provider]: value }),\n },\n\n rpc: {\n system: (value: string) => ({ [RPCAttributes.system]: value }),\n service: (value: string) => ({ [RPCAttributes.service]: value }),\n method: (value: string) => ({ [RPCAttributes.method]: value }),\n grpcStatusCode: (value: number) => ({\n [RPCAttributes.grpcStatusCode]: value,\n }),\n },\n\n graphql: {\n document: (value: string) => ({ [GraphQLAttributes.document]: value }),\n operationName: (value: string) => ({\n [GraphQLAttributes.operationName]: value,\n }),\n operationType: (value: 'query' | 'mutation' | 'subscription') => ({\n [GraphQLAttributes.operationType]: value,\n }),\n },\n\n otel: {\n libraryName: (value: string) => ({ [OTelAttributes.libraryName]: value }),\n libraryVersion: (value: string) => ({\n [OTelAttributes.libraryVersion]: value,\n }),\n statusCode: (value: string) => ({ [OTelAttributes.statusCode]: value }),\n },\n\n code: {\n namespace: (value: string) => ({ [CodeAttributes.namespace]: value }),\n filepath: (value: string) => ({ [CodeAttributes.filepath]: value }),\n function: (value: string) => ({ [CodeAttributes.function]: value }),\n class: (value: string) => ({ [CodeAttributes.class]: value }),\n method: (value: string) => ({ [CodeAttributes.method]: value }),\n column: (value: string) => ({ [CodeAttributes.column]: value }),\n lineNumber: (value: number) => ({ [CodeAttributes.lineNumber]: value }),\n repository: (value: string) => ({ [CodeAttributes.repository]: value }),\n revision: (value: string) => ({ [CodeAttributes.revision]: value }),\n },\n\n tls: {\n protocolVersion: (value: string) => ({\n [TLSAttributes.protocolVersion]: value,\n }),\n cipher: (value: string) => ({ [TLSAttributes.cipher]: value }),\n curveName: (value: string) => ({ [TLSAttributes.curveName]: value }),\n resumed: (value: boolean) => ({ [TLSAttributes.resumed]: value }),\n },\n} as const;\n","/**\n * Attribute validation, PII detection, and guardrails\n * Provides safe-by-default attribute handling with configurable policies\n */\n\nimport { REDACTOR_PATTERNS } from '../attribute-redacting-processor';\n\nexport interface AttributeGuardrails {\n /** How to handle PII in attributes */\n pii?: 'allow' | 'redact' | 'hash' | 'block';\n\n /** Maximum length for attribute values */\n maxLength?: number;\n\n /** Validate enum values against known values */\n validateEnum?: boolean;\n\n /** Log warnings for deprecated attributes instead of throwing */\n warnDeprecated?: boolean;\n\n /** Custom deprecation warnings */\n deprecatedWarnings?: Record<string, string>;\n}\n\nexport interface AttributePolicy {\n guardrails?: AttributeGuardrails;\n /** Custom deprecation warnings for specific attributes */\n deprecatedWarnings?: Record<string, string>;\n}\n\nconst DEPRECATED_ATTRIBUTES = {\n 'enduser.id': 'user.id',\n 'enduser.role': 'user.roles',\n 'enduser.scope': undefined,\n 'http.method': 'http.request.method',\n 'http.host': 'server.address',\n 'http.status_code': 'http.response.status_code',\n 'http.target': 'url.path',\n 'http.url': 'url.full',\n 'http.user_agent': 'user_agent.original',\n 'http.flavor': 'network.protocol.name',\n 'http.scheme': 'url.scheme',\n 'http.server_name': 'server.address',\n 'db.name': 'db.namespace',\n 'db.operation': 'db.operation.name',\n 'db.statement': 'db.query.text',\n 'db.system': 'db.system.name',\n 'db.collection': 'db.collection.name',\n 'db.instance.id': undefined,\n 'db.jdbc.driver_classname': undefined,\n 'db.mssql.instance_name': 'mssql.instance.name',\n 'db.sql.table': 'db.collection.name',\n 'http.client_ip': 'client.address',\n 'user_agent.original': 'user_agent.original',\n} as const;\n\nconst HTTP_METHODS = new Set([\n 'GET',\n 'POST',\n 'PUT',\n 'DELETE',\n 'PATCH',\n 'HEAD',\n 'OPTIONS',\n 'TRACE',\n 'QUERY',\n '_OTHER',\n]);\n\nexport function validateAttribute(\n key: string,\n value: unknown,\n policy: AttributePolicy = {},\n): unknown {\n const { guardrails = {} } = policy;\n\n if (value === undefined || value === null) {\n return undefined;\n }\n\n // For non-string values that don't need transformation, preserve the original type\n if (typeof value !== 'string') {\n // PII checks only apply to strings\n // maxLength only applies to strings\n // validateEnum only applies to strings\n return value;\n }\n\n const stringValue = value;\n\n if (guardrails.pii) {\n const piiResult = applyPIIPolicy(key, stringValue, guardrails.pii);\n if (piiResult !== stringValue) {\n return piiResult;\n }\n }\n\n if (guardrails.maxLength && stringValue.length > guardrails.maxLength) {\n return truncateValue(key, stringValue, guardrails.maxLength);\n }\n\n if (guardrails.validateEnum && HTTP_METHODS.has(stringValue)) {\n const normalizedMethod = normalizeHTTPMethod(stringValue);\n if (normalizedMethod !== stringValue) {\n return normalizedMethod;\n }\n }\n\n return stringValue;\n}\n\nfunction applyPIIPolicy(\n key: string,\n value: string,\n pii: AttributeGuardrails['pii'],\n): string {\n if (pii === 'allow') {\n return value;\n }\n\n if (pii === 'redact') {\n return redactIfPII(key, value);\n }\n\n if (pii === 'hash') {\n return hashIfPII(key, value);\n }\n\n if (pii === 'block' && isPIIKey(key)) {\n throw new Error(\n `PII attribute \"${key}\" is blocked by guardrails. Use pii: \"allow\" to enable it.`,\n );\n }\n\n return value;\n}\n\nfunction isPIIKey(key: string): boolean {\n const piiKeyPatterns = [\n 'email',\n 'phone',\n 'ssn',\n 'credit_card',\n 'password',\n 'secret',\n 'token',\n 'api_key',\n 'authorization',\n ];\n const lowerKey = key.toLowerCase();\n return piiKeyPatterns.some((pattern) => lowerKey.includes(pattern));\n}\n\nfunction redactIfPII(key: string, value: string): string {\n if (isPIIKey(key)) {\n // REDACTOR_PATTERNS values are RegExp patterns\n for (const [, pattern] of Object.entries(REDACTOR_PATTERNS)) {\n if (pattern instanceof RegExp && pattern.test(value)) {\n return '[REDACTED]';\n }\n }\n // If no pattern matched but key is PII, still redact\n return '[REDACTED]';\n }\n return value;\n}\n\nfunction hashIfPII(key: string, value: string): string {\n if (!isPIIKey(key)) {\n return value;\n }\n\n // Use a simple but consistent hash that produces 32-char hex\n // FNV-1a hash variant producing 128-bit output (32 hex chars)\n const FNV_PRIME = 0x01_00_01_93;\n const FNV_OFFSET = 0x81_1c_9d_c5;\n\n // Generate 4 32-bit hashes to produce 32 hex chars\n const hashes: number[] = [];\n for (let round = 0; round < 4; round++) {\n let hash = FNV_OFFSET;\n for (let i = 0; i < value.length; i++) {\n hash ^= (value.codePointAt(i) ?? 0) + round;\n hash = Math.imul(hash, FNV_PRIME);\n }\n hashes.push(hash >>> 0); // Convert to unsigned\n }\n\n return `hash_${hashes.map((h) => h.toString(16).padStart(8, '0')).join('')}`;\n}\n\nfunction truncateValue(key: string, value: string, maxLength: number): string {\n if (value.length <= maxLength) {\n return value;\n }\n return value.slice(0, maxLength - 3) + '...';\n}\n\nfunction normalizeHTTPMethod(method: string): string {\n const upper = method.toUpperCase();\n if (HTTP_METHODS.has(upper)) {\n return upper;\n }\n return upper;\n}\n\nexport function checkDeprecatedAttribute(\n key: string,\n policy: AttributePolicy = {},\n): string | null {\n const { guardrails = {}, deprecatedWarnings = {} } = policy;\n const { warnDeprecated = true } = guardrails;\n\n if (!warnDeprecated) {\n return null;\n }\n\n // Check if the key exists in the deprecated attributes map\n const isDeprecated = key in DEPRECATED_ATTRIBUTES;\n if (isDeprecated) {\n const replacement =\n DEPRECATED_ATTRIBUTES[key as keyof typeof DEPRECATED_ATTRIBUTES];\n if (replacement === undefined) {\n // Deprecated with no replacement (e.g., enduser.scope)\n console.warn(\n `[autotel/attributes] Attribute \"${key}\" is deprecated and has no replacement. ` +\n `Remove or find a replacement in OpenTelemetry semantic conventions.`,\n );\n } else {\n // Deprecated with a known replacement\n console.warn(\n `[autotel/attributes] Attribute \"${key}\" is deprecated. Use \"${replacement}\" instead.`,\n );\n }\n }\n\n if (deprecatedWarnings[key]) {\n console.warn(`[autotel/attributes] ${deprecatedWarnings[key]}`);\n }\n\n const replacement =\n DEPRECATED_ATTRIBUTES[key as keyof typeof DEPRECATED_ATTRIBUTES];\n return replacement ?? null;\n}\n\nexport function autoRedactPII(\n attributes: Record<string, unknown>,\n policy: AttributePolicy = {},\n): Record<string, unknown> {\n const { guardrails = { pii: 'redact' } } = policy;\n\n const redacted: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(attributes)) {\n redacted[key] = validateAttribute(key, value, { guardrails });\n }\n return redacted;\n}\n\nexport function defaultGuardrails(): AttributeGuardrails {\n return {\n pii: 'redact',\n maxLength: 255,\n validateEnum: true,\n warnDeprecated: true,\n };\n}\n","/**\n * Attribute utility functions\n */\n\nimport type { AttributeValue } from '../trace-context';\nimport {\n validateAttribute,\n autoRedactPII,\n defaultGuardrails,\n checkDeprecatedAttribute,\n type AttributePolicy,\n} from './validators';\n\n// Type for objects that have setAttributes method (spans or contexts)\n// Using a generic parameter to accommodate different AttributeValue types\ntype AttributeSetter = {\n setAttributes: (attrs: Record<string, AttributeValue>) => void;\n};\n\nexport function mergeAttrs(\n ...attrSets: Array<Record<string, unknown> | undefined>\n): Record<string, unknown> {\n const result: Record<string, unknown> = {};\n for (const attrSet of attrSets) {\n if (attrSet) {\n Object.assign(result, attrSet);\n }\n }\n return result;\n}\n\nexport function safeSetAttributes(\n span: AttributeSetter,\n attrs: Record<string, unknown>,\n policy?: AttributePolicy,\n): void {\n // Merge user-supplied guardrails with defaults so callers can tweak\n // a single option without opting out of the rest\n const mergedGuardrails = {\n ...defaultGuardrails(),\n ...policy?.guardrails,\n };\n const effectivePolicy: AttributePolicy = {\n ...policy,\n guardrails: mergedGuardrails,\n };\n\n const validated = autoRedactPII(attrs, effectivePolicy);\n\n const sanitizedAttrs: Record<string, AttributeValue> = {};\n for (const [key, value] of Object.entries(validated)) {\n if (value !== undefined) {\n // Check for deprecated attributes and log warnings\n checkDeprecatedAttribute(key, effectivePolicy);\n const validatedValue = validateAttribute(key, value, effectivePolicy);\n if (validatedValue !== undefined) {\n // Cast to AttributeValue since validateAttribute ensures valid types\n sanitizedAttrs[key] = validatedValue as AttributeValue;\n }\n }\n }\n\n span.setAttributes(sanitizedAttrs);\n}\n","/**\n * Signal attachment helpers\n * These functions know WHERE to attach attributes automatically\n * They handle span, resource, and log signals correctly\n */\n\nimport type { Span, Attributes } from '@opentelemetry/api';\nimport {\n resourceFromAttributes,\n type Resource,\n} from '@opentelemetry/resources';\nimport type { TraceContext } from '../trace-context';\nimport { attrs } from './builders';\nimport { safeSetAttributes } from './utils';\n\nexport function setUser(\n spanOrContext: Span | TraceContext,\n data: import('./types').UserAttrs,\n guardrails?: import('./validators').AttributePolicy,\n): void {\n const attributes = attrs.user.data(data);\n safeSetAttributes(spanOrContext, attributes, guardrails);\n}\n\nexport function setSession(\n spanOrContext: Span | TraceContext,\n data: import('./types').SessionAttrs,\n guardrails?: import('./validators').AttributePolicy,\n): void {\n const attributes = attrs.session.data(data);\n safeSetAttributes(spanOrContext, attributes, guardrails);\n}\n\nexport function setDevice(\n spanOrContext: Span | TraceContext,\n data: import('./types').DeviceAttrs,\n guardrails?: import('./validators').AttributePolicy,\n): void {\n const attributes = attrs.device.data(data);\n safeSetAttributes(spanOrContext, attributes, guardrails);\n}\n\nexport function httpServer(\n spanOrContext: Span | TraceContext,\n data: import('./types').HTTPServerAttrs,\n guardrails?: import('./validators').AttributePolicy,\n): void {\n const attributes = attrs.http.server(data);\n\n if ('updateName' in spanOrContext && data.method && data.route) {\n const span = spanOrContext as Span;\n span.updateName(`HTTP ${data.method} ${data.route}`);\n }\n\n safeSetAttributes(spanOrContext, attributes, guardrails);\n}\n\nexport function httpClient(\n spanOrContext: Span | TraceContext,\n data: import('./types').HTTPClientAttrs,\n guardrails?: import('./validators').AttributePolicy,\n): void {\n const attributes = attrs.http.client(data);\n safeSetAttributes(spanOrContext, attributes, guardrails);\n}\n\nexport function dbClient(\n spanOrContext: Span | TraceContext,\n data: import('./types').DBAttrs,\n guardrails?: import('./validators').AttributePolicy,\n): void {\n const attributes = attrs.db.client.data(data);\n safeSetAttributes(spanOrContext, attributes, guardrails);\n}\n\n/**\n * Merge service attributes into a Resource and return a new Resource.\n *\n * Resource.attributes is readonly, so this function returns a new merged\n * Resource rather than mutating the input.\n *\n * @param resource - The existing resource to merge with\n * @param data - Service attributes to add\n * @returns A new Resource with the merged attributes\n *\n * @example\n * ```typescript\n * const baseResource = Resource.default();\n * const enrichedResource = mergeServiceResource(baseResource, {\n * name: 'my-service',\n * version: '1.0.0',\n * });\n * ```\n */\nexport function mergeServiceResource(\n resource: Resource,\n data: import('./types').ServiceAttrs,\n): Resource {\n const attributes = attrs.service.data(data);\n return resource.merge(resourceFromAttributes(attributes as Attributes));\n}\n\nexport function identify(\n spanOrContext: Span | TraceContext,\n data: {\n user?: import('./types').UserAttrs;\n session?: import('./types').SessionAttrs;\n device?: import('./types').DeviceAttrs;\n },\n guardrails?: import('./validators').AttributePolicy,\n): void {\n const allAttrs = [];\n\n if (data.user) {\n allAttrs.push(attrs.user.data(data.user));\n }\n if (data.session) {\n allAttrs.push(attrs.session.data(data.session));\n }\n if (data.device) {\n allAttrs.push(attrs.device.data(data.device));\n }\n\n const merged: Record<string, unknown> = {};\n for (const attrSet of allAttrs) {\n Object.assign(merged, attrSet);\n }\n\n safeSetAttributes(spanOrContext, merged, guardrails);\n}\n\nexport function request(\n spanOrContext: Span | TraceContext,\n data: import('./types').HTTPServerAttrs & {\n clientIp?: string;\n },\n guardrails?: import('./validators').AttributePolicy,\n): void {\n const httpAttrs = attrs.http.server(data);\n const networkAttrs = attrs.network.peerAddress(data.clientIp || '');\n const merged = { ...httpAttrs, ...networkAttrs };\n safeSetAttributes(spanOrContext, merged, guardrails);\n}\n\nexport function setError(\n spanOrContext: Span | TraceContext,\n data: import('./types').ErrorAttrs,\n guardrails?: import('./validators').AttributePolicy,\n): void {\n const attributes = attrs.error.data(data);\n safeSetAttributes(spanOrContext, attributes, guardrails);\n}\n\nexport function setException(\n spanOrContext: Span | TraceContext,\n data: import('./types').ExceptionAttrs,\n guardrails?: import('./validators').AttributePolicy,\n): void {\n const attributes = attrs.exception.data(data);\n safeSetAttributes(spanOrContext, attributes, guardrails);\n}\n","/**\n * Domain helpers for common attribute patterns\n * These bundle multiple attribute groups into semantic helpers\n */\n\nimport { attrs } from './builders';\nimport { safeSetAttributes } from './utils';\n\nexport function transaction(\n spanOrContext: import('../trace-context').TraceContext,\n config: {\n user?: import('./types').UserAttrs;\n session?: import('./types').SessionAttrs;\n method?: string;\n route?: string;\n statusCode?: number;\n clientIp?: string;\n },\n guardrails?: import('./validators').AttributePolicy,\n): void {\n const userAttrs = attrs.user.data(config.user || {});\n const sessionAttrs = attrs.session.data(config.session || {});\n const httpAttrs = attrs.http.server({\n method: config.method,\n route: config.route,\n statusCode: config.statusCode,\n });\n const networkAttrs = attrs.network.peerAddress(config.clientIp || '');\n\n const merged = {\n ...userAttrs,\n ...sessionAttrs,\n ...httpAttrs,\n ...networkAttrs,\n };\n\n if (config.method && config.route && 'updateName' in spanOrContext) {\n spanOrContext.updateName(`HTTP ${config.method} ${config.route}`);\n }\n\n safeSetAttributes(spanOrContext, merged, guardrails);\n}\n"],"mappings":"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"}