{"version":3,"sources":["../../tracing/otel.ts"],"sourcesContent":["/**\n * Morph Tracing — Vercel AI SDK helper.\n *\n * The Vercel AI SDK emits its own OpenTelemetry spans when you pass\n * `experimental_telemetry`. There is nothing to monkey-patch; instead you tag\n * each call with `metadata()` so Morph can attribute the resulting spans to a\n * user/conversation/event.\n *\n * @example\n * ```ts\n * import { generateText } from \"ai\";\n * import { metadata } from \"@morphllm/morphsdk/tracing/otel\";\n *\n * const res = await generateText({\n *   model: openai(\"gpt-4o\"),\n *   prompt: \"Hello!\",\n *   experimental_telemetry: {\n *     isEnabled: true,\n *     metadata: metadata({ userId: \"user-123\", convoId: \"convo-456\" }),\n *   },\n * });\n * ```\n */\nimport type { MetadataOptions } from './types.js';\n\n/**\n * Reserved metadata keys Morph owns. Custom `properties` can't overwrite these,\n * so attribution (user_id / convo_id / event_id) stays intact.\n *\n * The AI SDK stores metadata as `ai.telemetry.metadata.<key>`; Traceloop's span\n * processor copies them to `traceloop.association.properties.<key>`, which is\n * what ClickHouse views read. Use snake_case names (user_id, convo_id, …).\n */\nconst RESERVED_KEYS = new Set(['user_id', 'convo_id', 'event_id', 'event_name']);\n\nfunction uuid(): string {\n  // Node 18+ and modern runtimes expose globalThis.crypto.randomUUID.\n  const c = (globalThis as { crypto?: { randomUUID?: () => string } }).crypto;\n  if (c?.randomUUID) return c.randomUUID();\n  // Fallback: RFC4122-ish without crypto.\n  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (ch) => {\n    const r = (Math.random() * 16) | 0;\n    const v = ch === 'x' ? r : (r & 0x3) | 0x8;\n    return v.toString(16);\n  });\n}\n\n/**\n * Build the metadata object for the Vercel AI SDK's\n * `experimental_telemetry.metadata`. The values are propagated to every span the\n * AI SDK creates for that call. Generate a fresh `eventId` per call for grouping.\n */\nexport function metadata(opts: MetadataOptions): Record<string, string> {\n  const result: Record<string, string> = {\n    user_id: opts.userId,\n    event_id: opts.eventId ?? uuid(),\n  };\n  if (opts.convoId) result.convo_id = opts.convoId;\n  if (opts.eventName) result.event_name = opts.eventName;\n  if (opts.properties) {\n    for (const [key, value] of Object.entries(opts.properties)) {\n      if (!RESERVED_KEYS.has(key)) result[key] = value;\n    }\n  }\n  return result;\n}\n\nexport default { metadata };\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAiCA,IAAM,gBAAgB,oBAAI,IAAI,CAAC,WAAW,YAAY,YAAY,YAAY,CAAC;AAE/E,SAAS,OAAe;AAEtB,QAAM,IAAK,WAA0D;AACrE,MAAI,GAAG,WAAY,QAAO,EAAE,WAAW;AAEvC,SAAO,uCAAuC,QAAQ,SAAS,CAAC,OAAO;AACrE,UAAM,IAAK,KAAK,OAAO,IAAI,KAAM;AACjC,UAAM,IAAI,OAAO,MAAM,IAAK,IAAI,IAAO;AACvC,WAAO,EAAE,SAAS,EAAE;AAAA,EACtB,CAAC;AACH;AAOO,SAAS,SAAS,MAA+C;AACtE,QAAM,SAAiC;AAAA,IACrC,SAAS,KAAK;AAAA,IACd,UAAU,KAAK,WAAW,KAAK;AAAA,EACjC;AACA,MAAI,KAAK,QAAS,QAAO,WAAW,KAAK;AACzC,MAAI,KAAK,UAAW,QAAO,aAAa,KAAK;AAC7C,MAAI,KAAK,YAAY;AACnB,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,UAAU,GAAG;AAC1D,UAAI,CAAC,cAAc,IAAI,GAAG,EAAG,QAAO,GAAG,IAAI;AAAA,IAC7C;AAAA,EACF;AACA,SAAO;AACT;AAEA,IAAO,eAAQ,EAAE,SAAS;","names":[]}