@sentry/core 10.36.0 → 10.38.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 (153) hide show
  1. package/build/cjs/exports.js +10 -0
  2. package/build/cjs/exports.js.map +1 -1
  3. package/build/cjs/index.js +82 -78
  4. package/build/cjs/index.js.map +1 -1
  5. package/build/cjs/integrations/captureconsole.js +2 -2
  6. package/build/cjs/integrations/captureconsole.js.map +1 -1
  7. package/build/cjs/integrations/conversationId.js +37 -0
  8. package/build/cjs/integrations/conversationId.js.map +1 -0
  9. package/build/cjs/integrations/mcp-server/errorCapture.js +2 -2
  10. package/build/cjs/integrations/mcp-server/errorCapture.js.map +1 -1
  11. package/build/cjs/integrations/supabase.js +4 -4
  12. package/build/cjs/integrations/supabase.js.map +1 -1
  13. package/build/cjs/scope.js +20 -0
  14. package/build/cjs/scope.js.map +1 -1
  15. package/build/cjs/semanticAttributes.js +16 -0
  16. package/build/cjs/semanticAttributes.js.map +1 -1
  17. package/build/cjs/tracing/ai/gen-ai-attributes.js +51 -7
  18. package/build/cjs/tracing/ai/gen-ai-attributes.js.map +1 -1
  19. package/build/cjs/tracing/ai/messageTruncation.js +26 -45
  20. package/build/cjs/tracing/ai/messageTruncation.js.map +1 -1
  21. package/build/cjs/tracing/ai/utils.js +50 -3
  22. package/build/cjs/tracing/ai/utils.js.map +1 -1
  23. package/build/cjs/tracing/anthropic-ai/index.js +3 -3
  24. package/build/cjs/tracing/anthropic-ai/index.js.map +1 -1
  25. package/build/cjs/tracing/anthropic-ai/streaming.js +3 -3
  26. package/build/cjs/tracing/anthropic-ai/streaming.js.map +1 -1
  27. package/build/cjs/tracing/anthropic-ai/utils.js +17 -6
  28. package/build/cjs/tracing/anthropic-ai/utils.js.map +1 -1
  29. package/build/cjs/tracing/google-genai/index.js +12 -5
  30. package/build/cjs/tracing/google-genai/index.js.map +1 -1
  31. package/build/cjs/tracing/google-genai/streaming.js +2 -2
  32. package/build/cjs/tracing/google-genai/streaming.js.map +1 -1
  33. package/build/cjs/tracing/langchain/index.js +9 -9
  34. package/build/cjs/tracing/langchain/index.js.map +1 -1
  35. package/build/cjs/tracing/langchain/utils.js +40 -25
  36. package/build/cjs/tracing/langchain/utils.js.map +1 -1
  37. package/build/cjs/tracing/langgraph/index.js +14 -6
  38. package/build/cjs/tracing/langgraph/index.js.map +1 -1
  39. package/build/cjs/tracing/openai/index.js +52 -14
  40. package/build/cjs/tracing/openai/index.js.map +1 -1
  41. package/build/cjs/tracing/openai/streaming.js +2 -2
  42. package/build/cjs/tracing/openai/streaming.js.map +1 -1
  43. package/build/cjs/tracing/openai/utils.js +4 -3
  44. package/build/cjs/tracing/openai/utils.js.map +1 -1
  45. package/build/cjs/tracing/vercel-ai/constants.js +22 -0
  46. package/build/cjs/tracing/vercel-ai/constants.js.map +1 -1
  47. package/build/cjs/tracing/vercel-ai/index.js +41 -11
  48. package/build/cjs/tracing/vercel-ai/index.js.map +1 -1
  49. package/build/cjs/tracing/vercel-ai/utils.js +19 -5
  50. package/build/cjs/tracing/vercel-ai/utils.js.map +1 -1
  51. package/build/cjs/trpc.js +3 -3
  52. package/build/cjs/trpc.js.map +1 -1
  53. package/build/cjs/utils/aggregate-errors.js +13 -5
  54. package/build/cjs/utils/aggregate-errors.js.map +1 -1
  55. package/build/cjs/utils/exports.js +7 -7
  56. package/build/cjs/utils/exports.js.map +1 -1
  57. package/build/cjs/utils/flushIfServerless.js +2 -2
  58. package/build/cjs/utils/flushIfServerless.js.map +1 -1
  59. package/build/cjs/utils/traceData.js +2 -2
  60. package/build/cjs/utils/traceData.js.map +1 -1
  61. package/build/cjs/utils/version.js +1 -1
  62. package/build/esm/exports.js +12 -3
  63. package/build/esm/exports.js.map +1 -1
  64. package/build/esm/feedback.js +1 -1
  65. package/build/esm/index.js +3 -2
  66. package/build/esm/index.js.map +1 -1
  67. package/build/esm/integrations/conversationId.js +35 -0
  68. package/build/esm/integrations/conversationId.js.map +1 -0
  69. package/build/esm/integrations/moduleMetadata.js +1 -1
  70. package/build/esm/integrations/third-party-errors-filter.js +1 -1
  71. package/build/esm/logs/internal.js +1 -1
  72. package/build/esm/package.json +1 -1
  73. package/build/esm/scope.js +20 -0
  74. package/build/esm/scope.js.map +1 -1
  75. package/build/esm/semanticAttributes.js +16 -1
  76. package/build/esm/semanticAttributes.js.map +1 -1
  77. package/build/esm/tracing/ai/gen-ai-attributes.js +43 -6
  78. package/build/esm/tracing/ai/gen-ai-attributes.js.map +1 -1
  79. package/build/esm/tracing/ai/messageTruncation.js +26 -45
  80. package/build/esm/tracing/ai/messageTruncation.js.map +1 -1
  81. package/build/esm/tracing/ai/utils.js +50 -4
  82. package/build/esm/tracing/ai/utils.js.map +1 -1
  83. package/build/esm/tracing/anthropic-ai/utils.js +17 -6
  84. package/build/esm/tracing/anthropic-ai/utils.js.map +1 -1
  85. package/build/esm/tracing/google-genai/index.js +11 -4
  86. package/build/esm/tracing/google-genai/index.js.map +1 -1
  87. package/build/esm/tracing/langchain/index.js +6 -6
  88. package/build/esm/tracing/langchain/index.js.map +1 -1
  89. package/build/esm/tracing/langchain/utils.js +41 -26
  90. package/build/esm/tracing/langchain/utils.js.map +1 -1
  91. package/build/esm/tracing/langgraph/index.js +12 -4
  92. package/build/esm/tracing/langgraph/index.js.map +1 -1
  93. package/build/esm/tracing/measurement.js +1 -1
  94. package/build/esm/tracing/openai/index.js +51 -13
  95. package/build/esm/tracing/openai/index.js.map +1 -1
  96. package/build/esm/tracing/openai/utils.js +4 -3
  97. package/build/esm/tracing/openai/utils.js.map +1 -1
  98. package/build/esm/tracing/trace.js +3 -3
  99. package/build/esm/tracing/vercel-ai/constants.js +20 -1
  100. package/build/esm/tracing/vercel-ai/constants.js.map +1 -1
  101. package/build/esm/tracing/vercel-ai/index.js +44 -14
  102. package/build/esm/tracing/vercel-ai/index.js.map +1 -1
  103. package/build/esm/tracing/vercel-ai/utils.js +21 -7
  104. package/build/esm/tracing/vercel-ai/utils.js.map +1 -1
  105. package/build/esm/utils/aggregate-errors.js +13 -5
  106. package/build/esm/utils/aggregate-errors.js.map +1 -1
  107. package/build/esm/utils/exports.js +7 -7
  108. package/build/esm/utils/exports.js.map +1 -1
  109. package/build/esm/utils/version.js +1 -1
  110. package/build/types/exports.d.ts +6 -0
  111. package/build/types/exports.d.ts.map +1 -1
  112. package/build/types/index.d.ts +2 -1
  113. package/build/types/index.d.ts.map +1 -1
  114. package/build/types/integrations/conversationId.d.ts +9 -0
  115. package/build/types/integrations/conversationId.d.ts.map +1 -0
  116. package/build/types/scope.d.ts +9 -0
  117. package/build/types/scope.d.ts.map +1 -1
  118. package/build/types/semanticAttributes.d.ts +13 -0
  119. package/build/types/semanticAttributes.d.ts.map +1 -1
  120. package/build/types/tracing/ai/gen-ai-attributes.d.ts +35 -5
  121. package/build/types/tracing/ai/gen-ai-attributes.d.ts.map +1 -1
  122. package/build/types/tracing/ai/messageTruncation.d.ts.map +1 -1
  123. package/build/types/tracing/ai/utils.d.ts +13 -1
  124. package/build/types/tracing/ai/utils.d.ts.map +1 -1
  125. package/build/types/tracing/anthropic-ai/utils.d.ts +1 -0
  126. package/build/types/tracing/anthropic-ai/utils.d.ts.map +1 -1
  127. package/build/types/tracing/google-genai/index.d.ts.map +1 -1
  128. package/build/types/tracing/langchain/index.d.ts.map +1 -1
  129. package/build/types/tracing/langchain/utils.d.ts +4 -2
  130. package/build/types/tracing/langchain/utils.d.ts.map +1 -1
  131. package/build/types/tracing/langgraph/index.d.ts.map +1 -1
  132. package/build/types/tracing/openai/index.d.ts.map +1 -1
  133. package/build/types/tracing/openai/utils.d.ts +2 -1
  134. package/build/types/tracing/openai/utils.d.ts.map +1 -1
  135. package/build/types/tracing/vercel-ai/constants.d.ts +3 -0
  136. package/build/types/tracing/vercel-ai/constants.d.ts.map +1 -1
  137. package/build/types/tracing/vercel-ai/index.d.ts.map +1 -1
  138. package/build/types/tracing/vercel-ai/utils.d.ts.map +1 -1
  139. package/build/types/types-hoist/clientreport.d.ts +1 -1
  140. package/build/types/types-hoist/clientreport.d.ts.map +1 -1
  141. package/build/types-ts3.8/exports.d.ts +6 -0
  142. package/build/types-ts3.8/index.d.ts +2 -1
  143. package/build/types-ts3.8/integrations/conversationId.d.ts +9 -0
  144. package/build/types-ts3.8/scope.d.ts +9 -0
  145. package/build/types-ts3.8/semanticAttributes.d.ts +13 -0
  146. package/build/types-ts3.8/tracing/ai/gen-ai-attributes.d.ts +35 -5
  147. package/build/types-ts3.8/tracing/ai/utils.d.ts +13 -1
  148. package/build/types-ts3.8/tracing/anthropic-ai/utils.d.ts +1 -0
  149. package/build/types-ts3.8/tracing/langchain/utils.d.ts +4 -2
  150. package/build/types-ts3.8/tracing/openai/utils.d.ts +2 -1
  151. package/build/types-ts3.8/tracing/vercel-ai/constants.d.ts +3 -0
  152. package/build/types-ts3.8/types-hoist/clientreport.d.ts +1 -1
  153. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sources":["../../../../src/tracing/langchain/utils.ts"],"sourcesContent":["import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '../../semanticAttributes';\nimport type { SpanAttributeValue } from '../../types-hoist/span';\nimport {\n GEN_AI_OPERATION_NAME_ATTRIBUTE,\n GEN_AI_REQUEST_FREQUENCY_PENALTY_ATTRIBUTE,\n GEN_AI_REQUEST_MAX_TOKENS_ATTRIBUTE,\n GEN_AI_REQUEST_MESSAGES_ATTRIBUTE,\n GEN_AI_REQUEST_MESSAGES_ORIGINAL_LENGTH_ATTRIBUTE,\n GEN_AI_REQUEST_MODEL_ATTRIBUTE,\n GEN_AI_REQUEST_PRESENCE_PENALTY_ATTRIBUTE,\n GEN_AI_REQUEST_STREAM_ATTRIBUTE,\n GEN_AI_REQUEST_TEMPERATURE_ATTRIBUTE,\n GEN_AI_REQUEST_TOP_P_ATTRIBUTE,\n GEN_AI_RESPONSE_FINISH_REASONS_ATTRIBUTE,\n GEN_AI_RESPONSE_ID_ATTRIBUTE,\n GEN_AI_RESPONSE_MODEL_ATTRIBUTE,\n GEN_AI_RESPONSE_STOP_REASON_ATTRIBUTE,\n GEN_AI_RESPONSE_TEXT_ATTRIBUTE,\n GEN_AI_RESPONSE_TOOL_CALLS_ATTRIBUTE,\n GEN_AI_SYSTEM_ATTRIBUTE,\n GEN_AI_USAGE_CACHE_CREATION_INPUT_TOKENS_ATTRIBUTE,\n GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS_ATTRIBUTE,\n GEN_AI_USAGE_INPUT_TOKENS_ATTRIBUTE,\n GEN_AI_USAGE_OUTPUT_TOKENS_ATTRIBUTE,\n GEN_AI_USAGE_TOTAL_TOKENS_ATTRIBUTE,\n} from '../ai/gen-ai-attributes';\nimport { truncateGenAiMessages } from '../ai/messageTruncation';\nimport { LANGCHAIN_ORIGIN, ROLE_MAP } from './constants';\nimport type { LangChainLLMResult, LangChainMessage, LangChainSerialized } from './types';\n\n/**\n * Assigns an attribute only when the value is neither `undefined` nor `null`.\n *\n * We keep this tiny helper because call sites are repetitive and easy to miswrite.\n * It also preserves falsy-but-valid values like `0` and `\"\"`.\n */\nconst setIfDefined = (target: Record<string, SpanAttributeValue>, key: string, value: unknown): void => {\n if (value != null) target[key] = value as SpanAttributeValue;\n};\n\n/**\n * Like `setIfDefined`, but converts the value with `Number()` and skips only when the\n * result is `NaN`. This ensures numeric 0 makes it through (unlike truthy checks).\n */\nconst setNumberIfDefined = (target: Record<string, SpanAttributeValue>, key: string, value: unknown): void => {\n const n = Number(value);\n if (!Number.isNaN(n)) target[key] = n;\n};\n\n/**\n * Converts a value to a string. Avoids double-quoted JSON strings where a plain\n * string is desired, but still handles objects/arrays safely.\n */\nfunction asString(v: unknown): string {\n if (typeof v === 'string') return v;\n try {\n return JSON.stringify(v);\n } catch {\n return String(v);\n }\n}\n\n/**\n * Normalizes a single role token to our canonical set.\n *\n * @param role Incoming role value (free-form, any casing)\n * @returns Canonical role: 'user' | 'assistant' | 'system' | 'function' | 'tool' | <passthrough>\n */\nfunction normalizeMessageRole(role: string): string {\n const normalized = role.toLowerCase();\n return ROLE_MAP[normalized] ?? normalized;\n}\n\n/**\n * Infers a role from a LangChain message constructor name.\n *\n * Checks for substrings like \"System\", \"Human\", \"AI\", etc.\n */\nfunction normalizeRoleNameFromCtor(name: string): string {\n if (name.includes('System')) return 'system';\n if (name.includes('Human')) return 'user';\n if (name.includes('AI') || name.includes('Assistant')) return 'assistant';\n if (name.includes('Function')) return 'function';\n if (name.includes('Tool')) return 'tool';\n return 'user';\n}\n\n/**\n * Returns invocation params from a LangChain `tags` object.\n *\n * LangChain often passes runtime parameters (model, temperature, etc.) via the\n * `tags.invocation_params` bag. If `tags` is an array (LangChain sometimes uses\n * string tags), we return `undefined`.\n *\n * @param tags LangChain tags (string[] or record)\n * @returns The `invocation_params` object, if present\n */\nexport function getInvocationParams(tags?: string[] | Record<string, unknown>): Record<string, unknown> | undefined {\n if (!tags || Array.isArray(tags)) return undefined;\n return tags.invocation_params as Record<string, unknown> | undefined;\n}\n\n/**\n * Normalizes a heterogeneous set of LangChain messages to `{ role, content }`.\n *\n * Why so many branches? LangChain messages can arrive in several shapes:\n * - Message classes with `_getType()` (most reliable)\n * - Classes with meaningful constructor names (e.g. `SystemMessage`)\n * - Plain objects with `type`, or `{ role, content }`\n * - Serialized format with `{ lc: 1, id: [...], kwargs: { content } }`\n * We preserve the prioritization to minimize behavioral drift.\n *\n * @param messages Mixed LangChain messages\n * @returns Array of normalized `{ role, content }`\n */\nexport function normalizeLangChainMessages(messages: LangChainMessage[]): Array<{ role: string; content: string }> {\n return messages.map(message => {\n // 1) Prefer _getType() when present\n const maybeGetType = (message as { _getType?: () => string })._getType;\n if (typeof maybeGetType === 'function') {\n const messageType = maybeGetType.call(message);\n return {\n role: normalizeMessageRole(messageType),\n content: asString(message.content),\n };\n }\n\n // 2) Then try constructor name (SystemMessage / HumanMessage / ...)\n const ctor = (message as { constructor?: { name?: string } }).constructor?.name;\n if (ctor) {\n return {\n role: normalizeMessageRole(normalizeRoleNameFromCtor(ctor)),\n content: asString(message.content),\n };\n }\n\n // 3) Then objects with `type`\n if (message.type) {\n const role = String(message.type).toLowerCase();\n return {\n role: normalizeMessageRole(role),\n content: asString(message.content),\n };\n }\n\n // 4) Then objects with `{ role, content }`\n if (message.role) {\n return {\n role: normalizeMessageRole(String(message.role)),\n content: asString(message.content),\n };\n }\n\n // 5) Serialized LangChain format (lc: 1)\n if (message.lc === 1 && message.kwargs) {\n const id = message.id;\n const messageType = Array.isArray(id) && id.length > 0 ? id[id.length - 1] : '';\n const role = typeof messageType === 'string' ? normalizeRoleNameFromCtor(messageType) : 'user';\n\n return {\n role: normalizeMessageRole(role),\n content: asString(message.kwargs?.content),\n };\n }\n\n // 6) Fallback: treat as user text\n return {\n role: 'user',\n content: asString(message.content),\n };\n });\n}\n\n/**\n * Extracts request attributes common to both LLM and ChatModel invocations.\n *\n * Source precedence:\n * 1) `invocationParams` (highest)\n * 2) `langSmithMetadata`\n *\n * Numeric values are set even when 0 (e.g. `temperature: 0`), but skipped if `NaN`.\n */\nfunction extractCommonRequestAttributes(\n serialized: LangChainSerialized,\n invocationParams?: Record<string, unknown>,\n langSmithMetadata?: Record<string, unknown>,\n): Record<string, SpanAttributeValue> {\n const attrs: Record<string, SpanAttributeValue> = {};\n\n // Get kwargs if available (from constructor type)\n const kwargs = 'kwargs' in serialized ? serialized.kwargs : undefined;\n\n const temperature = invocationParams?.temperature ?? langSmithMetadata?.ls_temperature ?? kwargs?.temperature;\n setNumberIfDefined(attrs, GEN_AI_REQUEST_TEMPERATURE_ATTRIBUTE, temperature);\n\n const maxTokens = invocationParams?.max_tokens ?? langSmithMetadata?.ls_max_tokens ?? kwargs?.max_tokens;\n setNumberIfDefined(attrs, GEN_AI_REQUEST_MAX_TOKENS_ATTRIBUTE, maxTokens);\n\n const topP = invocationParams?.top_p ?? kwargs?.top_p;\n setNumberIfDefined(attrs, GEN_AI_REQUEST_TOP_P_ATTRIBUTE, topP);\n\n const frequencyPenalty = invocationParams?.frequency_penalty;\n setNumberIfDefined(attrs, GEN_AI_REQUEST_FREQUENCY_PENALTY_ATTRIBUTE, frequencyPenalty);\n\n const presencePenalty = invocationParams?.presence_penalty;\n setNumberIfDefined(attrs, GEN_AI_REQUEST_PRESENCE_PENALTY_ATTRIBUTE, presencePenalty);\n\n // LangChain uses `stream`. We only set the attribute if the key actually exists\n // (some callbacks report `false` even on streamed requests, this stems from LangChain's callback handler).\n if (invocationParams && 'stream' in invocationParams) {\n setIfDefined(attrs, GEN_AI_REQUEST_STREAM_ATTRIBUTE, Boolean(invocationParams.stream));\n }\n\n return attrs;\n}\n\n/**\n * Small helper to assemble boilerplate attributes shared by both request extractors.\n */\nfunction baseRequestAttributes(\n system: unknown,\n modelName: unknown,\n operation: 'pipeline' | 'chat',\n serialized: LangChainSerialized,\n invocationParams?: Record<string, unknown>,\n langSmithMetadata?: Record<string, unknown>,\n): Record<string, SpanAttributeValue> {\n return {\n [GEN_AI_SYSTEM_ATTRIBUTE]: asString(system ?? 'langchain'),\n [GEN_AI_OPERATION_NAME_ATTRIBUTE]: operation,\n [GEN_AI_REQUEST_MODEL_ATTRIBUTE]: asString(modelName),\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: LANGCHAIN_ORIGIN,\n ...extractCommonRequestAttributes(serialized, invocationParams, langSmithMetadata),\n };\n}\n\n/**\n * Extracts attributes for plain LLM invocations (string prompts).\n *\n * - Operation is tagged as `pipeline` to distinguish from chat-style invocations.\n * - When `recordInputs` is true, string prompts are wrapped into `{role:\"user\"}`\n * messages to align with the chat schema used elsewhere.\n */\nexport function extractLLMRequestAttributes(\n llm: LangChainSerialized,\n prompts: string[],\n recordInputs: boolean,\n invocationParams?: Record<string, unknown>,\n langSmithMetadata?: Record<string, unknown>,\n): Record<string, SpanAttributeValue> {\n const system = langSmithMetadata?.ls_provider;\n const modelName = invocationParams?.model ?? langSmithMetadata?.ls_model_name ?? 'unknown';\n\n const attrs = baseRequestAttributes(system, modelName, 'pipeline', llm, invocationParams, langSmithMetadata);\n\n if (recordInputs && Array.isArray(prompts) && prompts.length > 0) {\n setIfDefined(attrs, GEN_AI_REQUEST_MESSAGES_ORIGINAL_LENGTH_ATTRIBUTE, prompts.length);\n const messages = prompts.map(p => ({ role: 'user', content: p }));\n setIfDefined(attrs, GEN_AI_REQUEST_MESSAGES_ATTRIBUTE, asString(messages));\n }\n\n return attrs;\n}\n\n/**\n * Extracts attributes for ChatModel invocations (array-of-arrays of messages).\n *\n * - Operation is tagged as `chat`.\n * - We flatten LangChain's `LangChainMessage[][]` and normalize shapes into a\n * consistent `{ role, content }` array when `recordInputs` is true.\n * - Provider system value falls back to `serialized.id?.[2]`.\n */\nexport function extractChatModelRequestAttributes(\n llm: LangChainSerialized,\n langChainMessages: LangChainMessage[][],\n recordInputs: boolean,\n invocationParams?: Record<string, unknown>,\n langSmithMetadata?: Record<string, unknown>,\n): Record<string, SpanAttributeValue> {\n const system = langSmithMetadata?.ls_provider ?? llm.id?.[2];\n const modelName = invocationParams?.model ?? langSmithMetadata?.ls_model_name ?? 'unknown';\n\n const attrs = baseRequestAttributes(system, modelName, 'chat', llm, invocationParams, langSmithMetadata);\n\n if (recordInputs && Array.isArray(langChainMessages) && langChainMessages.length > 0) {\n const normalized = normalizeLangChainMessages(langChainMessages.flat());\n setIfDefined(attrs, GEN_AI_REQUEST_MESSAGES_ORIGINAL_LENGTH_ATTRIBUTE, normalized.length);\n const truncated = truncateGenAiMessages(normalized);\n setIfDefined(attrs, GEN_AI_REQUEST_MESSAGES_ATTRIBUTE, asString(truncated));\n }\n\n return attrs;\n}\n\n/**\n * Scans generations for Anthropic-style `tool_use` items and records them.\n *\n * LangChain represents some provider messages (e.g., Anthropic) with a `message.content`\n * array that may include objects `{ type: 'tool_use', ... }`. We collect and attach\n * them as a JSON array on `gen_ai.response.tool_calls` for downstream consumers.\n */\nfunction addToolCallsAttributes(generations: LangChainMessage[][], attrs: Record<string, SpanAttributeValue>): void {\n const toolCalls: unknown[] = [];\n const flatGenerations = generations.flat();\n\n for (const gen of flatGenerations) {\n const content = gen.message?.content;\n if (Array.isArray(content)) {\n for (const item of content) {\n const t = item as { type: string };\n if (t.type === 'tool_use') toolCalls.push(t);\n }\n }\n }\n\n if (toolCalls.length > 0) {\n setIfDefined(attrs, GEN_AI_RESPONSE_TOOL_CALLS_ATTRIBUTE, asString(toolCalls));\n }\n}\n\n/**\n * Adds token usage attributes, supporting both OpenAI (`tokenUsage`) and Anthropic (`usage`) formats.\n * - Preserve zero values (0 tokens) by avoiding truthy checks.\n * - Compute a total for Anthropic when not explicitly provided.\n * - Include cache token metrics when present.\n */\nfunction addTokenUsageAttributes(\n llmOutput: LangChainLLMResult['llmOutput'],\n attrs: Record<string, SpanAttributeValue>,\n): void {\n if (!llmOutput) return;\n\n const tokenUsage = llmOutput.tokenUsage as\n | { promptTokens?: number; completionTokens?: number; totalTokens?: number }\n | undefined;\n const anthropicUsage = llmOutput.usage as\n | {\n input_tokens?: number;\n output_tokens?: number;\n cache_creation_input_tokens?: number;\n cache_read_input_tokens?: number;\n }\n | undefined;\n\n if (tokenUsage) {\n setNumberIfDefined(attrs, GEN_AI_USAGE_INPUT_TOKENS_ATTRIBUTE, tokenUsage.promptTokens);\n setNumberIfDefined(attrs, GEN_AI_USAGE_OUTPUT_TOKENS_ATTRIBUTE, tokenUsage.completionTokens);\n setNumberIfDefined(attrs, GEN_AI_USAGE_TOTAL_TOKENS_ATTRIBUTE, tokenUsage.totalTokens);\n } else if (anthropicUsage) {\n setNumberIfDefined(attrs, GEN_AI_USAGE_INPUT_TOKENS_ATTRIBUTE, anthropicUsage.input_tokens);\n setNumberIfDefined(attrs, GEN_AI_USAGE_OUTPUT_TOKENS_ATTRIBUTE, anthropicUsage.output_tokens);\n\n // Compute total when not provided by the provider.\n const input = Number(anthropicUsage.input_tokens);\n const output = Number(anthropicUsage.output_tokens);\n const total = (Number.isNaN(input) ? 0 : input) + (Number.isNaN(output) ? 0 : output);\n if (total > 0) setNumberIfDefined(attrs, GEN_AI_USAGE_TOTAL_TOKENS_ATTRIBUTE, total);\n\n // Extra Anthropic cache metrics (present only when caching is enabled)\n if (anthropicUsage.cache_creation_input_tokens !== undefined)\n setNumberIfDefined(\n attrs,\n GEN_AI_USAGE_CACHE_CREATION_INPUT_TOKENS_ATTRIBUTE,\n anthropicUsage.cache_creation_input_tokens,\n );\n if (anthropicUsage.cache_read_input_tokens !== undefined)\n setNumberIfDefined(attrs, GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS_ATTRIBUTE, anthropicUsage.cache_read_input_tokens);\n }\n}\n\n/**\n * Extracts response-related attributes based on a `LangChainLLMResult`.\n *\n * - Records finish reasons when present on generations (e.g., OpenAI)\n * - When `recordOutputs` is true, captures textual response content and any\n * tool calls.\n * - Also propagates model name (`model_name` or `model`), response `id`, and\n * `stop_reason` (for providers that use it).\n */\nexport function extractLlmResponseAttributes(\n llmResult: LangChainLLMResult,\n recordOutputs: boolean,\n): Record<string, SpanAttributeValue> | undefined {\n if (!llmResult) return;\n\n const attrs: Record<string, SpanAttributeValue> = {};\n\n if (Array.isArray(llmResult.generations)) {\n const finishReasons = llmResult.generations\n .flat()\n .map(g => {\n // v1 uses generationInfo.finish_reason\n if (g.generationInfo?.finish_reason) {\n return g.generationInfo.finish_reason;\n }\n // v0.3+ uses generation_info.finish_reason\n if (g.generation_info?.finish_reason) {\n return g.generation_info.finish_reason;\n }\n return null;\n })\n .filter((r): r is string => typeof r === 'string');\n\n if (finishReasons.length > 0) {\n setIfDefined(attrs, GEN_AI_RESPONSE_FINISH_REASONS_ATTRIBUTE, asString(finishReasons));\n }\n\n // Tool calls metadata (names, IDs) are not PII, so capture them regardless of recordOutputs\n addToolCallsAttributes(llmResult.generations as LangChainMessage[][], attrs);\n\n if (recordOutputs) {\n const texts = llmResult.generations\n .flat()\n .map(gen => gen.text ?? gen.message?.content)\n .filter(t => typeof t === 'string');\n\n if (texts.length > 0) {\n setIfDefined(attrs, GEN_AI_RESPONSE_TEXT_ATTRIBUTE, asString(texts));\n }\n }\n }\n\n addTokenUsageAttributes(llmResult.llmOutput, attrs);\n\n const llmOutput = llmResult.llmOutput;\n\n // Extract from v1 generations structure if available\n const firstGeneration = llmResult.generations?.[0]?.[0];\n const v1Message = firstGeneration?.message;\n\n // Provider model identifier: `model_name` (OpenAI-style) or `model` (others)\n // v1 stores this in message.response_metadata.model_name\n const modelName = llmOutput?.model_name ?? llmOutput?.model ?? v1Message?.response_metadata?.model_name;\n if (modelName) setIfDefined(attrs, GEN_AI_RESPONSE_MODEL_ATTRIBUTE, modelName);\n\n // Response ID: v1 stores this in message.id\n const responseId = llmOutput?.id ?? v1Message?.id;\n if (responseId) {\n setIfDefined(attrs, GEN_AI_RESPONSE_ID_ATTRIBUTE, responseId);\n }\n\n // Stop reason: v1 stores this in message.response_metadata.finish_reason\n const stopReason = llmOutput?.stop_reason ?? v1Message?.response_metadata?.finish_reason;\n if (stopReason) {\n setIfDefined(attrs, GEN_AI_RESPONSE_STOP_REASON_ATTRIBUTE, asString(stopReason));\n }\n\n return attrs;\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"utils.js","sources":["../../../../src/tracing/langchain/utils.ts"],"sourcesContent":["import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '../../semanticAttributes';\nimport type { SpanAttributeValue } from '../../types-hoist/span';\nimport {\n GEN_AI_INPUT_MESSAGES_ATTRIBUTE,\n GEN_AI_INPUT_MESSAGES_ORIGINAL_LENGTH_ATTRIBUTE,\n GEN_AI_OPERATION_NAME_ATTRIBUTE,\n GEN_AI_REQUEST_FREQUENCY_PENALTY_ATTRIBUTE,\n GEN_AI_REQUEST_MAX_TOKENS_ATTRIBUTE,\n GEN_AI_REQUEST_MODEL_ATTRIBUTE,\n GEN_AI_REQUEST_PRESENCE_PENALTY_ATTRIBUTE,\n GEN_AI_REQUEST_STREAM_ATTRIBUTE,\n GEN_AI_REQUEST_TEMPERATURE_ATTRIBUTE,\n GEN_AI_REQUEST_TOP_P_ATTRIBUTE,\n GEN_AI_RESPONSE_FINISH_REASONS_ATTRIBUTE,\n GEN_AI_RESPONSE_ID_ATTRIBUTE,\n GEN_AI_RESPONSE_MODEL_ATTRIBUTE,\n GEN_AI_RESPONSE_STOP_REASON_ATTRIBUTE,\n GEN_AI_RESPONSE_TEXT_ATTRIBUTE,\n GEN_AI_RESPONSE_TOOL_CALLS_ATTRIBUTE,\n GEN_AI_SYSTEM_ATTRIBUTE,\n GEN_AI_SYSTEM_INSTRUCTIONS_ATTRIBUTE,\n GEN_AI_USAGE_CACHE_CREATION_INPUT_TOKENS_ATTRIBUTE,\n GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS_ATTRIBUTE,\n GEN_AI_USAGE_INPUT_TOKENS_ATTRIBUTE,\n GEN_AI_USAGE_OUTPUT_TOKENS_ATTRIBUTE,\n GEN_AI_USAGE_TOTAL_TOKENS_ATTRIBUTE,\n} from '../ai/gen-ai-attributes';\nimport { truncateGenAiMessages } from '../ai/messageTruncation';\nimport { extractSystemInstructions } from '../ai/utils';\nimport { LANGCHAIN_ORIGIN, ROLE_MAP } from './constants';\nimport type { LangChainLLMResult, LangChainMessage, LangChainSerialized } from './types';\n\n/**\n * Assigns an attribute only when the value is neither `undefined` nor `null`.\n *\n * We keep this tiny helper because call sites are repetitive and easy to miswrite.\n * It also preserves falsy-but-valid values like `0` and `\"\"`.\n */\nconst setIfDefined = (target: Record<string, SpanAttributeValue>, key: string, value: unknown): void => {\n if (value != null) target[key] = value as SpanAttributeValue;\n};\n\n/**\n * Like `setIfDefined`, but converts the value with `Number()` and skips only when the\n * result is `NaN`. This ensures numeric 0 makes it through (unlike truthy checks).\n */\nconst setNumberIfDefined = (target: Record<string, SpanAttributeValue>, key: string, value: unknown): void => {\n const n = Number(value);\n if (!Number.isNaN(n)) target[key] = n;\n};\n\n/**\n * Converts a value to a string. Avoids double-quoted JSON strings where a plain\n * string is desired, but still handles objects/arrays safely.\n */\nfunction asString(v: unknown): string {\n if (typeof v === 'string') return v;\n try {\n return JSON.stringify(v);\n } catch {\n return String(v);\n }\n}\n\n/**\n * Normalizes a single role token to our canonical set.\n *\n * @param role Incoming role value (free-form, any casing)\n * @returns Canonical role: 'user' | 'assistant' | 'system' | 'function' | 'tool' | <passthrough>\n */\nfunction normalizeMessageRole(role: string): string {\n const normalized = role.toLowerCase();\n return ROLE_MAP[normalized] ?? normalized;\n}\n\n/**\n * Infers a role from a LangChain message constructor name.\n *\n * Checks for substrings like \"System\", \"Human\", \"AI\", etc.\n */\nfunction normalizeRoleNameFromCtor(name: string): string {\n if (name.includes('System')) return 'system';\n if (name.includes('Human')) return 'user';\n if (name.includes('AI') || name.includes('Assistant')) return 'assistant';\n if (name.includes('Function')) return 'function';\n if (name.includes('Tool')) return 'tool';\n return 'user';\n}\n\n/**\n * Returns invocation params from a LangChain `tags` object.\n *\n * LangChain often passes runtime parameters (model, temperature, etc.) via the\n * `tags.invocation_params` bag. If `tags` is an array (LangChain sometimes uses\n * string tags), we return `undefined`.\n *\n * @param tags LangChain tags (string[] or record)\n * @returns The `invocation_params` object, if present\n */\nexport function getInvocationParams(tags?: string[] | Record<string, unknown>): Record<string, unknown> | undefined {\n if (!tags || Array.isArray(tags)) return undefined;\n return tags.invocation_params as Record<string, unknown> | undefined;\n}\n\n/**\n * Normalizes a heterogeneous set of LangChain messages to `{ role, content }`.\n *\n * Why so many branches? LangChain messages can arrive in several shapes:\n * - Message classes with `_getType()` (most reliable)\n * - Classes with meaningful constructor names (e.g. `SystemMessage`)\n * - Plain objects with `type`, or `{ role, content }`\n * - Serialized format with `{ lc: 1, id: [...], kwargs: { content } }`\n * We preserve the prioritization to minimize behavioral drift.\n *\n * @param messages Mixed LangChain messages\n * @returns Array of normalized `{ role, content }`\n */\nexport function normalizeLangChainMessages(messages: LangChainMessage[]): Array<{ role: string; content: string }> {\n return messages.map(message => {\n // 1) Prefer _getType() when present\n const maybeGetType = (message as { _getType?: () => string })._getType;\n if (typeof maybeGetType === 'function') {\n const messageType = maybeGetType.call(message);\n return {\n role: normalizeMessageRole(messageType),\n content: asString(message.content),\n };\n }\n\n // 2) Serialized LangChain format (lc: 1) - check before constructor name\n // This is more reliable than constructor.name which can be lost during serialization\n if (message.lc === 1 && message.kwargs) {\n const id = message.id;\n const messageType = Array.isArray(id) && id.length > 0 ? id[id.length - 1] : '';\n const role = typeof messageType === 'string' ? normalizeRoleNameFromCtor(messageType) : 'user';\n\n return {\n role: normalizeMessageRole(role),\n content: asString(message.kwargs?.content),\n };\n }\n\n // 3) Then objects with `type`\n if (message.type) {\n const role = String(message.type).toLowerCase();\n return {\n role: normalizeMessageRole(role),\n content: asString(message.content),\n };\n }\n\n // 4) Then objects with `{ role, content }` - check before constructor name\n // Plain objects have constructor.name=\"Object\" which would incorrectly default to \"user\"\n if (message.role) {\n return {\n role: normalizeMessageRole(String(message.role)),\n content: asString(message.content),\n };\n }\n\n // 5) Then try constructor name (SystemMessage / HumanMessage / ...)\n // Only use this if we haven't matched a more specific case\n const ctor = (message as { constructor?: { name?: string } }).constructor?.name;\n if (ctor && ctor !== 'Object') {\n return {\n role: normalizeMessageRole(normalizeRoleNameFromCtor(ctor)),\n content: asString(message.content),\n };\n }\n\n // 6) Fallback: treat as user text\n return {\n role: 'user',\n content: asString(message.content),\n };\n });\n}\n\n/**\n * Extracts request attributes common to both LLM and ChatModel invocations.\n *\n * Source precedence:\n * 1) `invocationParams` (highest)\n * 2) `langSmithMetadata`\n *\n * Numeric values are set even when 0 (e.g. `temperature: 0`), but skipped if `NaN`.\n */\nfunction extractCommonRequestAttributes(\n serialized: LangChainSerialized,\n invocationParams?: Record<string, unknown>,\n langSmithMetadata?: Record<string, unknown>,\n): Record<string, SpanAttributeValue> {\n const attrs: Record<string, SpanAttributeValue> = {};\n\n // Get kwargs if available (from constructor type)\n const kwargs = 'kwargs' in serialized ? serialized.kwargs : undefined;\n\n const temperature = invocationParams?.temperature ?? langSmithMetadata?.ls_temperature ?? kwargs?.temperature;\n setNumberIfDefined(attrs, GEN_AI_REQUEST_TEMPERATURE_ATTRIBUTE, temperature);\n\n const maxTokens = invocationParams?.max_tokens ?? langSmithMetadata?.ls_max_tokens ?? kwargs?.max_tokens;\n setNumberIfDefined(attrs, GEN_AI_REQUEST_MAX_TOKENS_ATTRIBUTE, maxTokens);\n\n const topP = invocationParams?.top_p ?? kwargs?.top_p;\n setNumberIfDefined(attrs, GEN_AI_REQUEST_TOP_P_ATTRIBUTE, topP);\n\n const frequencyPenalty = invocationParams?.frequency_penalty;\n setNumberIfDefined(attrs, GEN_AI_REQUEST_FREQUENCY_PENALTY_ATTRIBUTE, frequencyPenalty);\n\n const presencePenalty = invocationParams?.presence_penalty;\n setNumberIfDefined(attrs, GEN_AI_REQUEST_PRESENCE_PENALTY_ATTRIBUTE, presencePenalty);\n\n // LangChain uses `stream`. We only set the attribute if the key actually exists\n // (some callbacks report `false` even on streamed requests, this stems from LangChain's callback handler).\n if (invocationParams && 'stream' in invocationParams) {\n setIfDefined(attrs, GEN_AI_REQUEST_STREAM_ATTRIBUTE, Boolean(invocationParams.stream));\n }\n\n return attrs;\n}\n\n/**\n * Small helper to assemble boilerplate attributes shared by both request extractors.\n * Always uses 'chat' as the operation type for all LLM and chat model operations.\n */\nfunction baseRequestAttributes(\n system: unknown,\n modelName: unknown,\n serialized: LangChainSerialized,\n invocationParams?: Record<string, unknown>,\n langSmithMetadata?: Record<string, unknown>,\n): Record<string, SpanAttributeValue> {\n return {\n [GEN_AI_SYSTEM_ATTRIBUTE]: asString(system ?? 'langchain'),\n [GEN_AI_OPERATION_NAME_ATTRIBUTE]: 'chat',\n [GEN_AI_REQUEST_MODEL_ATTRIBUTE]: asString(modelName),\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: LANGCHAIN_ORIGIN,\n ...extractCommonRequestAttributes(serialized, invocationParams, langSmithMetadata),\n };\n}\n\n/**\n * Extracts attributes for plain LLM invocations (string prompts).\n *\n * - Operation is tagged as `chat` following OpenTelemetry semantic conventions.\n * LangChain LLM operations are treated as chat operations.\n * - When `recordInputs` is true, string prompts are wrapped into `{role:\"user\"}`\n * messages to align with the chat schema used elsewhere.\n */\nexport function extractLLMRequestAttributes(\n llm: LangChainSerialized,\n prompts: string[],\n recordInputs: boolean,\n invocationParams?: Record<string, unknown>,\n langSmithMetadata?: Record<string, unknown>,\n): Record<string, SpanAttributeValue> {\n const system = langSmithMetadata?.ls_provider;\n const modelName = invocationParams?.model ?? langSmithMetadata?.ls_model_name ?? 'unknown';\n\n const attrs = baseRequestAttributes(system, modelName, llm, invocationParams, langSmithMetadata);\n\n if (recordInputs && Array.isArray(prompts) && prompts.length > 0) {\n setIfDefined(attrs, GEN_AI_INPUT_MESSAGES_ORIGINAL_LENGTH_ATTRIBUTE, prompts.length);\n const messages = prompts.map(p => ({ role: 'user', content: p }));\n setIfDefined(attrs, GEN_AI_INPUT_MESSAGES_ATTRIBUTE, asString(messages));\n }\n\n return attrs;\n}\n\n/**\n * Extracts attributes for ChatModel invocations (array-of-arrays of messages).\n *\n * - Operation is tagged as `chat` following OpenTelemetry semantic conventions.\n * LangChain chat model operations are chat operations.\n * - We flatten LangChain's `LangChainMessage[][]` and normalize shapes into a\n * consistent `{ role, content }` array when `recordInputs` is true.\n * - Provider system value falls back to `serialized.id?.[2]`.\n */\nexport function extractChatModelRequestAttributes(\n llm: LangChainSerialized,\n langChainMessages: LangChainMessage[][],\n recordInputs: boolean,\n invocationParams?: Record<string, unknown>,\n langSmithMetadata?: Record<string, unknown>,\n): Record<string, SpanAttributeValue> {\n const system = langSmithMetadata?.ls_provider ?? llm.id?.[2];\n const modelName = invocationParams?.model ?? langSmithMetadata?.ls_model_name ?? 'unknown';\n\n const attrs = baseRequestAttributes(system, modelName, llm, invocationParams, langSmithMetadata);\n\n if (recordInputs && Array.isArray(langChainMessages) && langChainMessages.length > 0) {\n const normalized = normalizeLangChainMessages(langChainMessages.flat());\n\n const { systemInstructions, filteredMessages } = extractSystemInstructions(normalized);\n\n if (systemInstructions) {\n setIfDefined(attrs, GEN_AI_SYSTEM_INSTRUCTIONS_ATTRIBUTE, systemInstructions);\n }\n\n const filteredLength = Array.isArray(filteredMessages) ? filteredMessages.length : 0;\n setIfDefined(attrs, GEN_AI_INPUT_MESSAGES_ORIGINAL_LENGTH_ATTRIBUTE, filteredLength);\n\n const truncated = truncateGenAiMessages(filteredMessages as unknown[]);\n setIfDefined(attrs, GEN_AI_INPUT_MESSAGES_ATTRIBUTE, asString(truncated));\n }\n\n return attrs;\n}\n\n/**\n * Scans generations for Anthropic-style `tool_use` items and records them.\n *\n * LangChain represents some provider messages (e.g., Anthropic) with a `message.content`\n * array that may include objects `{ type: 'tool_use', ... }`. We collect and attach\n * them as a JSON array on `gen_ai.response.tool_calls` for downstream consumers.\n */\nfunction addToolCallsAttributes(generations: LangChainMessage[][], attrs: Record<string, SpanAttributeValue>): void {\n const toolCalls: unknown[] = [];\n const flatGenerations = generations.flat();\n\n for (const gen of flatGenerations) {\n const content = gen.message?.content;\n if (Array.isArray(content)) {\n for (const item of content) {\n const t = item as { type: string };\n if (t.type === 'tool_use') toolCalls.push(t);\n }\n }\n }\n\n if (toolCalls.length > 0) {\n setIfDefined(attrs, GEN_AI_RESPONSE_TOOL_CALLS_ATTRIBUTE, asString(toolCalls));\n }\n}\n\n/**\n * Adds token usage attributes, supporting both OpenAI (`tokenUsage`) and Anthropic (`usage`) formats.\n * - Preserve zero values (0 tokens) by avoiding truthy checks.\n * - Compute a total for Anthropic when not explicitly provided.\n * - Include cache token metrics when present.\n */\nfunction addTokenUsageAttributes(\n llmOutput: LangChainLLMResult['llmOutput'],\n attrs: Record<string, SpanAttributeValue>,\n): void {\n if (!llmOutput) return;\n\n const tokenUsage = llmOutput.tokenUsage as\n | { promptTokens?: number; completionTokens?: number; totalTokens?: number }\n | undefined;\n const anthropicUsage = llmOutput.usage as\n | {\n input_tokens?: number;\n output_tokens?: number;\n cache_creation_input_tokens?: number;\n cache_read_input_tokens?: number;\n }\n | undefined;\n\n if (tokenUsage) {\n setNumberIfDefined(attrs, GEN_AI_USAGE_INPUT_TOKENS_ATTRIBUTE, tokenUsage.promptTokens);\n setNumberIfDefined(attrs, GEN_AI_USAGE_OUTPUT_TOKENS_ATTRIBUTE, tokenUsage.completionTokens);\n setNumberIfDefined(attrs, GEN_AI_USAGE_TOTAL_TOKENS_ATTRIBUTE, tokenUsage.totalTokens);\n } else if (anthropicUsage) {\n setNumberIfDefined(attrs, GEN_AI_USAGE_INPUT_TOKENS_ATTRIBUTE, anthropicUsage.input_tokens);\n setNumberIfDefined(attrs, GEN_AI_USAGE_OUTPUT_TOKENS_ATTRIBUTE, anthropicUsage.output_tokens);\n\n // Compute total when not provided by the provider.\n const input = Number(anthropicUsage.input_tokens);\n const output = Number(anthropicUsage.output_tokens);\n const total = (Number.isNaN(input) ? 0 : input) + (Number.isNaN(output) ? 0 : output);\n if (total > 0) setNumberIfDefined(attrs, GEN_AI_USAGE_TOTAL_TOKENS_ATTRIBUTE, total);\n\n // Extra Anthropic cache metrics (present only when caching is enabled)\n if (anthropicUsage.cache_creation_input_tokens !== undefined)\n setNumberIfDefined(\n attrs,\n GEN_AI_USAGE_CACHE_CREATION_INPUT_TOKENS_ATTRIBUTE,\n anthropicUsage.cache_creation_input_tokens,\n );\n if (anthropicUsage.cache_read_input_tokens !== undefined)\n setNumberIfDefined(attrs, GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS_ATTRIBUTE, anthropicUsage.cache_read_input_tokens);\n }\n}\n\n/**\n * Extracts response-related attributes based on a `LangChainLLMResult`.\n *\n * - Records finish reasons when present on generations (e.g., OpenAI)\n * - When `recordOutputs` is true, captures textual response content and any\n * tool calls.\n * - Also propagates model name (`model_name` or `model`), response `id`, and\n * `stop_reason` (for providers that use it).\n */\nexport function extractLlmResponseAttributes(\n llmResult: LangChainLLMResult,\n recordOutputs: boolean,\n): Record<string, SpanAttributeValue> | undefined {\n if (!llmResult) return;\n\n const attrs: Record<string, SpanAttributeValue> = {};\n\n if (Array.isArray(llmResult.generations)) {\n const finishReasons = llmResult.generations\n .flat()\n .map(g => {\n // v1 uses generationInfo.finish_reason\n if (g.generationInfo?.finish_reason) {\n return g.generationInfo.finish_reason;\n }\n // v0.3+ uses generation_info.finish_reason\n if (g.generation_info?.finish_reason) {\n return g.generation_info.finish_reason;\n }\n return null;\n })\n .filter((r): r is string => typeof r === 'string');\n\n if (finishReasons.length > 0) {\n setIfDefined(attrs, GEN_AI_RESPONSE_FINISH_REASONS_ATTRIBUTE, asString(finishReasons));\n }\n\n // Tool calls metadata (names, IDs) are not PII, so capture them regardless of recordOutputs\n addToolCallsAttributes(llmResult.generations as LangChainMessage[][], attrs);\n\n if (recordOutputs) {\n const texts = llmResult.generations\n .flat()\n .map(gen => gen.text ?? gen.message?.content)\n .filter(t => typeof t === 'string');\n\n if (texts.length > 0) {\n setIfDefined(attrs, GEN_AI_RESPONSE_TEXT_ATTRIBUTE, asString(texts));\n }\n }\n }\n\n addTokenUsageAttributes(llmResult.llmOutput, attrs);\n\n const llmOutput = llmResult.llmOutput;\n\n // Extract from v1 generations structure if available\n const firstGeneration = llmResult.generations?.[0]?.[0];\n const v1Message = firstGeneration?.message;\n\n // Provider model identifier: `model_name` (OpenAI-style) or `model` (others)\n // v1 stores this in message.response_metadata.model_name\n const modelName = llmOutput?.model_name ?? llmOutput?.model ?? v1Message?.response_metadata?.model_name;\n if (modelName) setIfDefined(attrs, GEN_AI_RESPONSE_MODEL_ATTRIBUTE, modelName);\n\n // Response ID: v1 stores this in message.id\n const responseId = llmOutput?.id ?? v1Message?.id;\n if (responseId) {\n setIfDefined(attrs, GEN_AI_RESPONSE_ID_ATTRIBUTE, responseId);\n }\n\n // Stop reason: v1 stores this in message.response_metadata.finish_reason\n const stopReason = llmOutput?.stop_reason ?? v1Message?.response_metadata?.finish_reason;\n if (stopReason) {\n setIfDefined(attrs, GEN_AI_RESPONSE_STOP_REASON_ATTRIBUTE, asString(stopReason));\n }\n\n return attrs;\n}\n"],"names":[],"mappings":"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"}
@@ -2,8 +2,9 @@ import { captureException } from '../../exports.js';
2
2
  import { SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '../../semanticAttributes.js';
3
3
  import { SPAN_STATUS_ERROR } from '../spanstatus.js';
4
4
  import { startSpan } from '../trace.js';
5
- import { GEN_AI_AGENT_NAME_ATTRIBUTE, GEN_AI_OPERATION_NAME_ATTRIBUTE, GEN_AI_PIPELINE_NAME_ATTRIBUTE, GEN_AI_CONVERSATION_ID_ATTRIBUTE, GEN_AI_REQUEST_AVAILABLE_TOOLS_ATTRIBUTE, GEN_AI_REQUEST_MESSAGES_ORIGINAL_LENGTH_ATTRIBUTE, GEN_AI_REQUEST_MESSAGES_ATTRIBUTE, GEN_AI_INVOKE_AGENT_OPERATION_ATTRIBUTE } from '../ai/gen-ai-attributes.js';
5
+ import { GEN_AI_AGENT_NAME_ATTRIBUTE, GEN_AI_OPERATION_NAME_ATTRIBUTE, GEN_AI_PIPELINE_NAME_ATTRIBUTE, GEN_AI_CONVERSATION_ID_ATTRIBUTE, GEN_AI_REQUEST_AVAILABLE_TOOLS_ATTRIBUTE, GEN_AI_SYSTEM_INSTRUCTIONS_ATTRIBUTE, GEN_AI_INPUT_MESSAGES_ORIGINAL_LENGTH_ATTRIBUTE, GEN_AI_INPUT_MESSAGES_ATTRIBUTE, GEN_AI_INVOKE_AGENT_OPERATION_ATTRIBUTE } from '../ai/gen-ai-attributes.js';
6
6
  import { truncateGenAiMessages } from '../ai/messageTruncation.js';
7
+ import { extractSystemInstructions } from '../ai/utils.js';
7
8
  import { normalizeLangChainMessages } from '../langchain/utils.js';
8
9
  import { LANGGRAPH_ORIGIN } from './constants.js';
9
10
  import { extractToolsFromCompiledGraph, setResponseAttributes } from './utils.js';
@@ -127,10 +128,17 @@ function instrumentCompiledGraphInvoke(
127
128
 
128
129
  if (inputMessages && recordInputs) {
129
130
  const normalizedMessages = normalizeLangChainMessages(inputMessages);
130
- const truncatedMessages = truncateGenAiMessages(normalizedMessages);
131
+ const { systemInstructions, filteredMessages } = extractSystemInstructions(normalizedMessages);
132
+
133
+ if (systemInstructions) {
134
+ span.setAttribute(GEN_AI_SYSTEM_INSTRUCTIONS_ATTRIBUTE, systemInstructions);
135
+ }
136
+
137
+ const truncatedMessages = truncateGenAiMessages(filteredMessages );
138
+ const filteredLength = Array.isArray(filteredMessages) ? filteredMessages.length : 0;
131
139
  span.setAttributes({
132
- [GEN_AI_REQUEST_MESSAGES_ATTRIBUTE]: JSON.stringify(truncatedMessages),
133
- [GEN_AI_REQUEST_MESSAGES_ORIGINAL_LENGTH_ATTRIBUTE]: normalizedMessages.length,
140
+ [GEN_AI_INPUT_MESSAGES_ATTRIBUTE]: JSON.stringify(truncatedMessages),
141
+ [GEN_AI_INPUT_MESSAGES_ORIGINAL_LENGTH_ATTRIBUTE]: filteredLength,
134
142
  });
135
143
  }
136
144
 
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../../src/tracing/langgraph/index.ts"],"sourcesContent":["import { captureException } from '../../exports';\nimport { SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '../../semanticAttributes';\nimport { SPAN_STATUS_ERROR } from '../../tracing';\nimport {\n GEN_AI_AGENT_NAME_ATTRIBUTE,\n GEN_AI_CONVERSATION_ID_ATTRIBUTE,\n GEN_AI_INVOKE_AGENT_OPERATION_ATTRIBUTE,\n GEN_AI_OPERATION_NAME_ATTRIBUTE,\n GEN_AI_PIPELINE_NAME_ATTRIBUTE,\n GEN_AI_REQUEST_AVAILABLE_TOOLS_ATTRIBUTE,\n GEN_AI_REQUEST_MESSAGES_ATTRIBUTE,\n GEN_AI_REQUEST_MESSAGES_ORIGINAL_LENGTH_ATTRIBUTE,\n} from '../ai/gen-ai-attributes';\nimport { truncateGenAiMessages } from '../ai/messageTruncation';\nimport type { LangChainMessage } from '../langchain/types';\nimport { normalizeLangChainMessages } from '../langchain/utils';\nimport { startSpan } from '../trace';\nimport { LANGGRAPH_ORIGIN } from './constants';\nimport type { CompiledGraph, LangGraphOptions } from './types';\nimport { extractToolsFromCompiledGraph, setResponseAttributes } from './utils';\n\n/**\n * Instruments StateGraph's compile method to create spans for agent creation and invocation\n *\n * Wraps the compile() method to:\n * - Create a `gen_ai.create_agent` span when compile() is called\n * - Automatically wrap the invoke() method on the returned compiled graph with a `gen_ai.invoke_agent` span\n *\n */\nexport function instrumentStateGraphCompile(\n originalCompile: (...args: unknown[]) => CompiledGraph,\n options: LangGraphOptions,\n): (...args: unknown[]) => CompiledGraph {\n return new Proxy(originalCompile, {\n apply(target, thisArg, args: unknown[]): CompiledGraph {\n return startSpan(\n {\n op: 'gen_ai.create_agent',\n name: 'create_agent',\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: LANGGRAPH_ORIGIN,\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'gen_ai.create_agent',\n [GEN_AI_OPERATION_NAME_ATTRIBUTE]: 'create_agent',\n },\n },\n span => {\n try {\n const compiledGraph = Reflect.apply(target, thisArg, args);\n const compileOptions = args.length > 0 ? (args[0] as Record<string, unknown>) : {};\n\n // Extract graph name\n if (compileOptions?.name && typeof compileOptions.name === 'string') {\n span.setAttribute(GEN_AI_AGENT_NAME_ATTRIBUTE, compileOptions.name);\n span.updateName(`create_agent ${compileOptions.name}`);\n }\n\n // Instrument agent invoke method on the compiled graph\n const originalInvoke = compiledGraph.invoke;\n if (originalInvoke && typeof originalInvoke === 'function') {\n compiledGraph.invoke = instrumentCompiledGraphInvoke(\n originalInvoke.bind(compiledGraph) as (...args: unknown[]) => Promise<unknown>,\n compiledGraph,\n compileOptions,\n options,\n ) as typeof originalInvoke;\n }\n\n return compiledGraph;\n } catch (error) {\n span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });\n captureException(error, {\n mechanism: {\n handled: false,\n type: 'auto.ai.langgraph.error',\n },\n });\n throw error;\n }\n },\n );\n },\n }) as (...args: unknown[]) => CompiledGraph;\n}\n\n/**\n * Instruments CompiledGraph's invoke method to create spans for agent invocation\n *\n * Creates a `gen_ai.invoke_agent` span when invoke() is called\n */\nfunction instrumentCompiledGraphInvoke(\n originalInvoke: (...args: unknown[]) => Promise<unknown>,\n graphInstance: CompiledGraph,\n compileOptions: Record<string, unknown>,\n options: LangGraphOptions,\n): (...args: unknown[]) => Promise<unknown> {\n return new Proxy(originalInvoke, {\n apply(target, thisArg, args: unknown[]): Promise<unknown> {\n return startSpan(\n {\n op: 'gen_ai.invoke_agent',\n name: 'invoke_agent',\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: LANGGRAPH_ORIGIN,\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: GEN_AI_INVOKE_AGENT_OPERATION_ATTRIBUTE,\n [GEN_AI_OPERATION_NAME_ATTRIBUTE]: 'invoke_agent',\n },\n },\n async span => {\n try {\n const graphName = compileOptions?.name;\n\n if (graphName && typeof graphName === 'string') {\n span.setAttribute(GEN_AI_PIPELINE_NAME_ATTRIBUTE, graphName);\n span.setAttribute(GEN_AI_AGENT_NAME_ATTRIBUTE, graphName);\n span.updateName(`invoke_agent ${graphName}`);\n }\n\n // Extract thread_id from the config (second argument)\n // LangGraph uses config.configurable.thread_id for conversation/session linking\n const config = args.length > 1 ? (args[1] as Record<string, unknown> | undefined) : undefined;\n const configurable = config?.configurable as Record<string, unknown> | undefined;\n const threadId = configurable?.thread_id;\n if (threadId && typeof threadId === 'string') {\n span.setAttribute(GEN_AI_CONVERSATION_ID_ATTRIBUTE, threadId);\n }\n\n // Extract available tools from the graph instance\n const tools = extractToolsFromCompiledGraph(graphInstance);\n if (tools) {\n span.setAttribute(GEN_AI_REQUEST_AVAILABLE_TOOLS_ATTRIBUTE, JSON.stringify(tools));\n }\n\n // Parse input messages\n const recordInputs = options.recordInputs;\n const recordOutputs = options.recordOutputs;\n const inputMessages =\n args.length > 0 ? ((args[0] as { messages?: LangChainMessage[] }).messages ?? []) : [];\n\n if (inputMessages && recordInputs) {\n const normalizedMessages = normalizeLangChainMessages(inputMessages);\n const truncatedMessages = truncateGenAiMessages(normalizedMessages);\n span.setAttributes({\n [GEN_AI_REQUEST_MESSAGES_ATTRIBUTE]: JSON.stringify(truncatedMessages),\n [GEN_AI_REQUEST_MESSAGES_ORIGINAL_LENGTH_ATTRIBUTE]: normalizedMessages.length,\n });\n }\n\n // Call original invoke\n const result = await Reflect.apply(target, thisArg, args);\n\n // Set response attributes\n if (recordOutputs) {\n setResponseAttributes(span, inputMessages ?? null, result);\n }\n\n return result;\n } catch (error) {\n span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });\n captureException(error, {\n mechanism: {\n handled: false,\n type: 'auto.ai.langgraph.error',\n },\n });\n throw error;\n }\n },\n );\n },\n }) as (...args: unknown[]) => Promise<unknown>;\n}\n\n/**\n * Directly instruments a StateGraph instance to add tracing spans\n *\n * This function can be used to manually instrument LangGraph StateGraph instances\n * in environments where automatic instrumentation is not available or desired.\n *\n * @param stateGraph - The StateGraph instance to instrument\n * @param options - Optional configuration for recording inputs/outputs\n *\n * @example\n * ```typescript\n * import { instrumentLangGraph } from '@sentry/cloudflare';\n * import { StateGraph } from '@langchain/langgraph';\n *\n * const graph = new StateGraph(MessagesAnnotation)\n * .addNode('agent', mockLlm)\n * .addEdge(START, 'agent')\n * .addEdge('agent', END);\n *\n * instrumentLangGraph(graph, { recordInputs: true, recordOutputs: true });\n * const compiled = graph.compile({ name: 'my_agent' });\n * ```\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function instrumentLangGraph<T extends { compile: (...args: any[]) => any }>(\n stateGraph: T,\n options?: LangGraphOptions,\n): T {\n const _options: LangGraphOptions = options || {};\n\n stateGraph.compile = instrumentStateGraphCompile(stateGraph.compile.bind(stateGraph), _options);\n\n return stateGraph;\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../../../../src/tracing/langgraph/index.ts"],"sourcesContent":["import { captureException } from '../../exports';\nimport { SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '../../semanticAttributes';\nimport { SPAN_STATUS_ERROR } from '../../tracing';\nimport {\n GEN_AI_AGENT_NAME_ATTRIBUTE,\n GEN_AI_CONVERSATION_ID_ATTRIBUTE,\n GEN_AI_INPUT_MESSAGES_ATTRIBUTE,\n GEN_AI_INPUT_MESSAGES_ORIGINAL_LENGTH_ATTRIBUTE,\n GEN_AI_INVOKE_AGENT_OPERATION_ATTRIBUTE,\n GEN_AI_OPERATION_NAME_ATTRIBUTE,\n GEN_AI_PIPELINE_NAME_ATTRIBUTE,\n GEN_AI_REQUEST_AVAILABLE_TOOLS_ATTRIBUTE,\n GEN_AI_SYSTEM_INSTRUCTIONS_ATTRIBUTE,\n} from '../ai/gen-ai-attributes';\nimport { truncateGenAiMessages } from '../ai/messageTruncation';\nimport { extractSystemInstructions } from '../ai/utils';\nimport type { LangChainMessage } from '../langchain/types';\nimport { normalizeLangChainMessages } from '../langchain/utils';\nimport { startSpan } from '../trace';\nimport { LANGGRAPH_ORIGIN } from './constants';\nimport type { CompiledGraph, LangGraphOptions } from './types';\nimport { extractToolsFromCompiledGraph, setResponseAttributes } from './utils';\n\n/**\n * Instruments StateGraph's compile method to create spans for agent creation and invocation\n *\n * Wraps the compile() method to:\n * - Create a `gen_ai.create_agent` span when compile() is called\n * - Automatically wrap the invoke() method on the returned compiled graph with a `gen_ai.invoke_agent` span\n *\n */\nexport function instrumentStateGraphCompile(\n originalCompile: (...args: unknown[]) => CompiledGraph,\n options: LangGraphOptions,\n): (...args: unknown[]) => CompiledGraph {\n return new Proxy(originalCompile, {\n apply(target, thisArg, args: unknown[]): CompiledGraph {\n return startSpan(\n {\n op: 'gen_ai.create_agent',\n name: 'create_agent',\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: LANGGRAPH_ORIGIN,\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'gen_ai.create_agent',\n [GEN_AI_OPERATION_NAME_ATTRIBUTE]: 'create_agent',\n },\n },\n span => {\n try {\n const compiledGraph = Reflect.apply(target, thisArg, args);\n const compileOptions = args.length > 0 ? (args[0] as Record<string, unknown>) : {};\n\n // Extract graph name\n if (compileOptions?.name && typeof compileOptions.name === 'string') {\n span.setAttribute(GEN_AI_AGENT_NAME_ATTRIBUTE, compileOptions.name);\n span.updateName(`create_agent ${compileOptions.name}`);\n }\n\n // Instrument agent invoke method on the compiled graph\n const originalInvoke = compiledGraph.invoke;\n if (originalInvoke && typeof originalInvoke === 'function') {\n compiledGraph.invoke = instrumentCompiledGraphInvoke(\n originalInvoke.bind(compiledGraph) as (...args: unknown[]) => Promise<unknown>,\n compiledGraph,\n compileOptions,\n options,\n ) as typeof originalInvoke;\n }\n\n return compiledGraph;\n } catch (error) {\n span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });\n captureException(error, {\n mechanism: {\n handled: false,\n type: 'auto.ai.langgraph.error',\n },\n });\n throw error;\n }\n },\n );\n },\n }) as (...args: unknown[]) => CompiledGraph;\n}\n\n/**\n * Instruments CompiledGraph's invoke method to create spans for agent invocation\n *\n * Creates a `gen_ai.invoke_agent` span when invoke() is called\n */\nfunction instrumentCompiledGraphInvoke(\n originalInvoke: (...args: unknown[]) => Promise<unknown>,\n graphInstance: CompiledGraph,\n compileOptions: Record<string, unknown>,\n options: LangGraphOptions,\n): (...args: unknown[]) => Promise<unknown> {\n return new Proxy(originalInvoke, {\n apply(target, thisArg, args: unknown[]): Promise<unknown> {\n return startSpan(\n {\n op: 'gen_ai.invoke_agent',\n name: 'invoke_agent',\n attributes: {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: LANGGRAPH_ORIGIN,\n [SEMANTIC_ATTRIBUTE_SENTRY_OP]: GEN_AI_INVOKE_AGENT_OPERATION_ATTRIBUTE,\n [GEN_AI_OPERATION_NAME_ATTRIBUTE]: 'invoke_agent',\n },\n },\n async span => {\n try {\n const graphName = compileOptions?.name;\n\n if (graphName && typeof graphName === 'string') {\n span.setAttribute(GEN_AI_PIPELINE_NAME_ATTRIBUTE, graphName);\n span.setAttribute(GEN_AI_AGENT_NAME_ATTRIBUTE, graphName);\n span.updateName(`invoke_agent ${graphName}`);\n }\n\n // Extract thread_id from the config (second argument)\n // LangGraph uses config.configurable.thread_id for conversation/session linking\n const config = args.length > 1 ? (args[1] as Record<string, unknown> | undefined) : undefined;\n const configurable = config?.configurable as Record<string, unknown> | undefined;\n const threadId = configurable?.thread_id;\n if (threadId && typeof threadId === 'string') {\n span.setAttribute(GEN_AI_CONVERSATION_ID_ATTRIBUTE, threadId);\n }\n\n // Extract available tools from the graph instance\n const tools = extractToolsFromCompiledGraph(graphInstance);\n if (tools) {\n span.setAttribute(GEN_AI_REQUEST_AVAILABLE_TOOLS_ATTRIBUTE, JSON.stringify(tools));\n }\n\n // Parse input messages\n const recordInputs = options.recordInputs;\n const recordOutputs = options.recordOutputs;\n const inputMessages =\n args.length > 0 ? ((args[0] as { messages?: LangChainMessage[] }).messages ?? []) : [];\n\n if (inputMessages && recordInputs) {\n const normalizedMessages = normalizeLangChainMessages(inputMessages);\n const { systemInstructions, filteredMessages } = extractSystemInstructions(normalizedMessages);\n\n if (systemInstructions) {\n span.setAttribute(GEN_AI_SYSTEM_INSTRUCTIONS_ATTRIBUTE, systemInstructions);\n }\n\n const truncatedMessages = truncateGenAiMessages(filteredMessages as unknown[]);\n const filteredLength = Array.isArray(filteredMessages) ? filteredMessages.length : 0;\n span.setAttributes({\n [GEN_AI_INPUT_MESSAGES_ATTRIBUTE]: JSON.stringify(truncatedMessages),\n [GEN_AI_INPUT_MESSAGES_ORIGINAL_LENGTH_ATTRIBUTE]: filteredLength,\n });\n }\n\n // Call original invoke\n const result = await Reflect.apply(target, thisArg, args);\n\n // Set response attributes\n if (recordOutputs) {\n setResponseAttributes(span, inputMessages ?? null, result);\n }\n\n return result;\n } catch (error) {\n span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });\n captureException(error, {\n mechanism: {\n handled: false,\n type: 'auto.ai.langgraph.error',\n },\n });\n throw error;\n }\n },\n );\n },\n }) as (...args: unknown[]) => Promise<unknown>;\n}\n\n/**\n * Directly instruments a StateGraph instance to add tracing spans\n *\n * This function can be used to manually instrument LangGraph StateGraph instances\n * in environments where automatic instrumentation is not available or desired.\n *\n * @param stateGraph - The StateGraph instance to instrument\n * @param options - Optional configuration for recording inputs/outputs\n *\n * @example\n * ```typescript\n * import { instrumentLangGraph } from '@sentry/cloudflare';\n * import { StateGraph } from '@langchain/langgraph';\n *\n * const graph = new StateGraph(MessagesAnnotation)\n * .addNode('agent', mockLlm)\n * .addEdge(START, 'agent')\n * .addEdge('agent', END);\n *\n * instrumentLangGraph(graph, { recordInputs: true, recordOutputs: true });\n * const compiled = graph.compile({ name: 'my_agent' });\n * ```\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function instrumentLangGraph<T extends { compile: (...args: any[]) => any }>(\n stateGraph: T,\n options?: LangGraphOptions,\n): T {\n const _options: LangGraphOptions = options || {};\n\n stateGraph.compile = instrumentStateGraphCompile(stateGraph.compile.bind(stateGraph), _options);\n\n return stateGraph;\n}\n"],"names":[],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { DEBUG_BUILD } from '../debug-build.js';
2
2
  import { SEMANTIC_ATTRIBUTE_SENTRY_MEASUREMENT_UNIT, SEMANTIC_ATTRIBUTE_SENTRY_MEASUREMENT_VALUE } from '../semanticAttributes.js';
3
3
  import { debug } from '../utils/debug-logger.js';
4
- import { getActiveSpan, getRootSpan } from '../utils/spanUtils.js';
4
+ import { getRootSpan, getActiveSpan } from '../utils/spanUtils.js';
5
5
 
6
6
  /**
7
7
  * Adds a measurement to the active transaction on the current global scope. You can optionally pass in a different span
@@ -3,8 +3,8 @@ import { captureException } from '../../exports.js';
3
3
  import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '../../semanticAttributes.js';
4
4
  import { SPAN_STATUS_ERROR } from '../spanstatus.js';
5
5
  import { startSpanManual, startSpan } from '../trace.js';
6
- import { GEN_AI_OPERATION_NAME_ATTRIBUTE, GEN_AI_REQUEST_AVAILABLE_TOOLS_ATTRIBUTE, GEN_AI_REQUEST_MODEL_ATTRIBUTE, GEN_AI_REQUEST_MESSAGES_ATTRIBUTE, GEN_AI_REQUEST_MESSAGES_ORIGINAL_LENGTH_ATTRIBUTE, GEN_AI_RESPONSE_TEXT_ATTRIBUTE, GEN_AI_SYSTEM_ATTRIBUTE } from '../ai/gen-ai-attributes.js';
7
- import { getTruncatedJsonString } from '../ai/utils.js';
6
+ import { GEN_AI_OPERATION_NAME_ATTRIBUTE, GEN_AI_REQUEST_AVAILABLE_TOOLS_ATTRIBUTE, GEN_AI_REQUEST_MODEL_ATTRIBUTE, OPENAI_OPERATIONS, GEN_AI_EMBEDDINGS_INPUT_ATTRIBUTE, GEN_AI_SYSTEM_INSTRUCTIONS_ATTRIBUTE, GEN_AI_INPUT_MESSAGES_ATTRIBUTE, GEN_AI_INPUT_MESSAGES_ORIGINAL_LENGTH_ATTRIBUTE, GEN_AI_RESPONSE_TEXT_ATTRIBUTE, GEN_AI_SYSTEM_ATTRIBUTE } from '../ai/gen-ai-attributes.js';
7
+ import { extractSystemInstructions, getTruncatedJsonString } from '../ai/utils.js';
8
8
  import { instrumentStream } from './streaming.js';
9
9
  import { shouldInstrument, getOperationName, getSpanOperation, extractRequestParameters, isChatCompletionResponse, addChatCompletionAttributes, isResponsesApiResponse, addResponsesApiAttributes, isEmbeddingsResponse, addEmbeddingsAttributes, isConversationResponse, addConversationAttributes, buildMethodPath } from './utils.js';
10
10
 
@@ -76,16 +76,54 @@ function addResponseAttributes(span, result, recordOutputs) {
76
76
  }
77
77
 
78
78
  // Extract and record AI request inputs, if present. This is intentionally separate from response attributes.
79
- function addRequestAttributes(span, params) {
80
- const src = 'input' in params ? params.input : 'messages' in params ? params.messages : undefined;
81
- // typically an array, but can be other types. skip if an empty array.
82
- const length = Array.isArray(src) ? src.length : undefined;
83
- if (src && length !== 0) {
84
- const truncatedInput = getTruncatedJsonString(src);
85
- span.setAttribute(GEN_AI_REQUEST_MESSAGES_ATTRIBUTE, truncatedInput);
86
- if (length) {
87
- span.setAttribute(GEN_AI_REQUEST_MESSAGES_ORIGINAL_LENGTH_ATTRIBUTE, length);
79
+ function addRequestAttributes(span, params, operationName) {
80
+ // Store embeddings input on a separate attribute and do not truncate it
81
+ if (operationName === OPENAI_OPERATIONS.EMBEDDINGS && 'input' in params) {
82
+ const input = params.input;
83
+
84
+ // No input provided
85
+ if (input == null) {
86
+ return;
87
+ }
88
+
89
+ // Empty input string
90
+ if (typeof input === 'string' && input.length === 0) {
91
+ return;
92
+ }
93
+
94
+ // Empty array input
95
+ if (Array.isArray(input) && input.length === 0) {
96
+ return;
88
97
  }
98
+
99
+ // Store strings as-is, arrays/objects as JSON
100
+ span.setAttribute(GEN_AI_EMBEDDINGS_INPUT_ATTRIBUTE, typeof input === 'string' ? input : JSON.stringify(input));
101
+ return;
102
+ }
103
+
104
+ const src = 'input' in params ? params.input : 'messages' in params ? params.messages : undefined;
105
+
106
+ if (!src) {
107
+ return;
108
+ }
109
+
110
+ if (Array.isArray(src) && src.length === 0) {
111
+ return;
112
+ }
113
+
114
+ const { systemInstructions, filteredMessages } = extractSystemInstructions(src);
115
+
116
+ if (systemInstructions) {
117
+ span.setAttribute(GEN_AI_SYSTEM_INSTRUCTIONS_ATTRIBUTE, systemInstructions);
118
+ }
119
+
120
+ const truncatedInput = getTruncatedJsonString(filteredMessages);
121
+ span.setAttribute(GEN_AI_INPUT_MESSAGES_ATTRIBUTE, truncatedInput);
122
+
123
+ if (Array.isArray(filteredMessages)) {
124
+ span.setAttribute(GEN_AI_INPUT_MESSAGES_ORIGINAL_LENGTH_ATTRIBUTE, filteredMessages.length);
125
+ } else {
126
+ span.setAttribute(GEN_AI_INPUT_MESSAGES_ORIGINAL_LENGTH_ATTRIBUTE, 1);
89
127
  }
90
128
  }
91
129
 
@@ -119,7 +157,7 @@ function instrumentMethod(
119
157
  async (span) => {
120
158
  try {
121
159
  if (options.recordInputs && params) {
122
- addRequestAttributes(span, params);
160
+ addRequestAttributes(span, params, operationName);
123
161
  }
124
162
 
125
163
  const result = await originalMethod.apply(context, args);
@@ -158,7 +196,7 @@ function instrumentMethod(
158
196
  async (span) => {
159
197
  try {
160
198
  if (options.recordInputs && params) {
161
- addRequestAttributes(span, params);
199
+ addRequestAttributes(span, params, operationName);
162
200
  }
163
201
 
164
202
  const result = await originalMethod.apply(context, args);
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../../src/tracing/openai/index.ts"],"sourcesContent":["import { getClient } from '../../currentScopes';\nimport { captureException } from '../../exports';\nimport { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '../../semanticAttributes';\nimport { SPAN_STATUS_ERROR } from '../../tracing';\nimport { startSpan, startSpanManual } from '../../tracing/trace';\nimport type { Span, SpanAttributeValue } from '../../types-hoist/span';\nimport {\n GEN_AI_OPERATION_NAME_ATTRIBUTE,\n GEN_AI_REQUEST_AVAILABLE_TOOLS_ATTRIBUTE,\n GEN_AI_REQUEST_MESSAGES_ATTRIBUTE,\n GEN_AI_REQUEST_MESSAGES_ORIGINAL_LENGTH_ATTRIBUTE,\n GEN_AI_REQUEST_MODEL_ATTRIBUTE,\n GEN_AI_RESPONSE_TEXT_ATTRIBUTE,\n GEN_AI_SYSTEM_ATTRIBUTE,\n} from '../ai/gen-ai-attributes';\nimport { getTruncatedJsonString } from '../ai/utils';\nimport { instrumentStream } from './streaming';\nimport type {\n ChatCompletionChunk,\n InstrumentedMethod,\n OpenAiOptions,\n OpenAiResponse,\n OpenAIStream,\n ResponseStreamingEvent,\n} from './types';\nimport {\n addChatCompletionAttributes,\n addConversationAttributes,\n addEmbeddingsAttributes,\n addResponsesApiAttributes,\n buildMethodPath,\n extractRequestParameters,\n getOperationName,\n getSpanOperation,\n isChatCompletionResponse,\n isConversationResponse,\n isEmbeddingsResponse,\n isResponsesApiResponse,\n shouldInstrument,\n} from './utils';\n\n/**\n * Extract available tools from request parameters\n */\nfunction extractAvailableTools(params: Record<string, unknown>): string | undefined {\n const tools = Array.isArray(params.tools) ? params.tools : [];\n const hasWebSearchOptions = params.web_search_options && typeof params.web_search_options === 'object';\n const webSearchOptions = hasWebSearchOptions\n ? [{ type: 'web_search_options', ...(params.web_search_options as Record<string, unknown>) }]\n : [];\n\n const availableTools = [...tools, ...webSearchOptions];\n return availableTools.length > 0 ? JSON.stringify(availableTools) : undefined;\n}\n\n/**\n * Extract request attributes from method arguments\n */\nfunction extractRequestAttributes(args: unknown[], methodPath: string): Record<string, unknown> {\n const attributes: Record<string, unknown> = {\n [GEN_AI_SYSTEM_ATTRIBUTE]: 'openai',\n [GEN_AI_OPERATION_NAME_ATTRIBUTE]: getOperationName(methodPath),\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ai.openai',\n };\n\n if (args.length > 0 && typeof args[0] === 'object' && args[0] !== null) {\n const params = args[0] as Record<string, unknown>;\n\n const availableTools = extractAvailableTools(params);\n if (availableTools) {\n attributes[GEN_AI_REQUEST_AVAILABLE_TOOLS_ATTRIBUTE] = availableTools;\n }\n\n Object.assign(attributes, extractRequestParameters(params));\n } else {\n attributes[GEN_AI_REQUEST_MODEL_ATTRIBUTE] = 'unknown';\n }\n\n return attributes;\n}\n\n/**\n * Add response attributes to spans\n * This supports Chat Completion, Responses API, Embeddings, and Conversations API responses\n */\nfunction addResponseAttributes(span: Span, result: unknown, recordOutputs?: boolean): void {\n if (!result || typeof result !== 'object') return;\n\n const response = result as OpenAiResponse;\n\n if (isChatCompletionResponse(response)) {\n addChatCompletionAttributes(span, response, recordOutputs);\n if (recordOutputs && response.choices?.length) {\n const responseTexts = response.choices.map(choice => choice.message?.content || '');\n span.setAttributes({ [GEN_AI_RESPONSE_TEXT_ATTRIBUTE]: JSON.stringify(responseTexts) });\n }\n } else if (isResponsesApiResponse(response)) {\n addResponsesApiAttributes(span, response, recordOutputs);\n if (recordOutputs && response.output_text) {\n span.setAttributes({ [GEN_AI_RESPONSE_TEXT_ATTRIBUTE]: response.output_text });\n }\n } else if (isEmbeddingsResponse(response)) {\n addEmbeddingsAttributes(span, response);\n } else if (isConversationResponse(response)) {\n addConversationAttributes(span, response);\n }\n}\n\n// Extract and record AI request inputs, if present. This is intentionally separate from response attributes.\nfunction addRequestAttributes(span: Span, params: Record<string, unknown>): void {\n const src = 'input' in params ? params.input : 'messages' in params ? params.messages : undefined;\n // typically an array, but can be other types. skip if an empty array.\n const length = Array.isArray(src) ? src.length : undefined;\n if (src && length !== 0) {\n const truncatedInput = getTruncatedJsonString(src);\n span.setAttribute(GEN_AI_REQUEST_MESSAGES_ATTRIBUTE, truncatedInput);\n if (length) {\n span.setAttribute(GEN_AI_REQUEST_MESSAGES_ORIGINAL_LENGTH_ATTRIBUTE, length);\n }\n }\n}\n\n/**\n * Instrument a method with Sentry spans\n * Following Sentry AI Agents Manual Instrumentation conventions\n * @see https://docs.sentry.io/platforms/javascript/guides/node/tracing/instrumentation/ai-agents-module/#manual-instrumentation\n */\nfunction instrumentMethod<T extends unknown[], R>(\n originalMethod: (...args: T) => Promise<R>,\n methodPath: InstrumentedMethod,\n context: unknown,\n options: OpenAiOptions,\n): (...args: T) => Promise<R> {\n return async function instrumentedMethod(...args: T): Promise<R> {\n const requestAttributes = extractRequestAttributes(args, methodPath);\n const model = (requestAttributes[GEN_AI_REQUEST_MODEL_ATTRIBUTE] as string) || 'unknown';\n const operationName = getOperationName(methodPath);\n\n const params = args[0] as Record<string, unknown> | undefined;\n const isStreamRequested = params && typeof params === 'object' && params.stream === true;\n\n if (isStreamRequested) {\n // For streaming responses, use manual span management to properly handle the async generator lifecycle\n return startSpanManual(\n {\n name: `${operationName} ${model} stream-response`,\n op: getSpanOperation(methodPath),\n attributes: requestAttributes as Record<string, SpanAttributeValue>,\n },\n async (span: Span) => {\n try {\n if (options.recordInputs && params) {\n addRequestAttributes(span, params);\n }\n\n const result = await originalMethod.apply(context, args);\n\n return instrumentStream(\n result as OpenAIStream<ChatCompletionChunk | ResponseStreamingEvent>,\n span,\n options.recordOutputs ?? false,\n ) as unknown as R;\n } catch (error) {\n // For streaming requests that fail before stream creation, we still want to record\n // them as streaming requests but end the span gracefully\n span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });\n captureException(error, {\n mechanism: {\n handled: false,\n type: 'auto.ai.openai.stream',\n data: {\n function: methodPath,\n },\n },\n });\n span.end();\n throw error;\n }\n },\n );\n } else {\n // Non-streaming responses\n return startSpan(\n {\n name: `${operationName} ${model}`,\n op: getSpanOperation(methodPath),\n attributes: requestAttributes as Record<string, SpanAttributeValue>,\n },\n async (span: Span) => {\n try {\n if (options.recordInputs && params) {\n addRequestAttributes(span, params);\n }\n\n const result = await originalMethod.apply(context, args);\n addResponseAttributes(span, result, options.recordOutputs);\n return result;\n } catch (error) {\n captureException(error, {\n mechanism: {\n handled: false,\n type: 'auto.ai.openai',\n data: {\n function: methodPath,\n },\n },\n });\n throw error;\n }\n },\n );\n }\n };\n}\n\n/**\n * Create a deep proxy for OpenAI client instrumentation\n */\nfunction createDeepProxy<T extends object>(target: T, currentPath = '', options: OpenAiOptions): T {\n return new Proxy(target, {\n get(obj: object, prop: string): unknown {\n const value = (obj as Record<string, unknown>)[prop];\n const methodPath = buildMethodPath(currentPath, String(prop));\n\n if (typeof value === 'function' && shouldInstrument(methodPath)) {\n return instrumentMethod(value as (...args: unknown[]) => Promise<unknown>, methodPath, obj, options);\n }\n\n if (typeof value === 'function') {\n // Bind non-instrumented functions to preserve the original `this` context,\n // which is required for accessing private class fields (e.g. #baseURL) in OpenAI SDK v5.\n return value.bind(obj);\n }\n\n if (value && typeof value === 'object') {\n return createDeepProxy(value, methodPath, options);\n }\n\n return value;\n },\n }) as T;\n}\n\n/**\n * Instrument an OpenAI client with Sentry tracing\n * Can be used across Node.js, Cloudflare Workers, and Vercel Edge\n */\nexport function instrumentOpenAiClient<T extends object>(client: T, options?: OpenAiOptions): T {\n const sendDefaultPii = Boolean(getClient()?.getOptions().sendDefaultPii);\n\n const _options = {\n recordInputs: sendDefaultPii,\n recordOutputs: sendDefaultPii,\n ...options,\n };\n\n return createDeepProxy(client, '', _options);\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../../../../src/tracing/openai/index.ts"],"sourcesContent":["import { getClient } from '../../currentScopes';\nimport { captureException } from '../../exports';\nimport { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '../../semanticAttributes';\nimport { SPAN_STATUS_ERROR } from '../../tracing';\nimport { startSpan, startSpanManual } from '../../tracing/trace';\nimport type { Span, SpanAttributeValue } from '../../types-hoist/span';\nimport {\n GEN_AI_EMBEDDINGS_INPUT_ATTRIBUTE,\n GEN_AI_INPUT_MESSAGES_ATTRIBUTE,\n GEN_AI_INPUT_MESSAGES_ORIGINAL_LENGTH_ATTRIBUTE,\n GEN_AI_OPERATION_NAME_ATTRIBUTE,\n GEN_AI_REQUEST_AVAILABLE_TOOLS_ATTRIBUTE,\n GEN_AI_REQUEST_MODEL_ATTRIBUTE,\n GEN_AI_RESPONSE_TEXT_ATTRIBUTE,\n GEN_AI_SYSTEM_ATTRIBUTE,\n GEN_AI_SYSTEM_INSTRUCTIONS_ATTRIBUTE,\n OPENAI_OPERATIONS,\n} from '../ai/gen-ai-attributes';\nimport { extractSystemInstructions, getTruncatedJsonString } from '../ai/utils';\nimport { instrumentStream } from './streaming';\nimport type {\n ChatCompletionChunk,\n InstrumentedMethod,\n OpenAiOptions,\n OpenAiResponse,\n OpenAIStream,\n ResponseStreamingEvent,\n} from './types';\nimport {\n addChatCompletionAttributes,\n addConversationAttributes,\n addEmbeddingsAttributes,\n addResponsesApiAttributes,\n buildMethodPath,\n extractRequestParameters,\n getOperationName,\n getSpanOperation,\n isChatCompletionResponse,\n isConversationResponse,\n isEmbeddingsResponse,\n isResponsesApiResponse,\n shouldInstrument,\n} from './utils';\n\n/**\n * Extract available tools from request parameters\n */\nfunction extractAvailableTools(params: Record<string, unknown>): string | undefined {\n const tools = Array.isArray(params.tools) ? params.tools : [];\n const hasWebSearchOptions = params.web_search_options && typeof params.web_search_options === 'object';\n const webSearchOptions = hasWebSearchOptions\n ? [{ type: 'web_search_options', ...(params.web_search_options as Record<string, unknown>) }]\n : [];\n\n const availableTools = [...tools, ...webSearchOptions];\n return availableTools.length > 0 ? JSON.stringify(availableTools) : undefined;\n}\n\n/**\n * Extract request attributes from method arguments\n */\nfunction extractRequestAttributes(args: unknown[], methodPath: string): Record<string, unknown> {\n const attributes: Record<string, unknown> = {\n [GEN_AI_SYSTEM_ATTRIBUTE]: 'openai',\n [GEN_AI_OPERATION_NAME_ATTRIBUTE]: getOperationName(methodPath),\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.ai.openai',\n };\n\n if (args.length > 0 && typeof args[0] === 'object' && args[0] !== null) {\n const params = args[0] as Record<string, unknown>;\n\n const availableTools = extractAvailableTools(params);\n if (availableTools) {\n attributes[GEN_AI_REQUEST_AVAILABLE_TOOLS_ATTRIBUTE] = availableTools;\n }\n\n Object.assign(attributes, extractRequestParameters(params));\n } else {\n attributes[GEN_AI_REQUEST_MODEL_ATTRIBUTE] = 'unknown';\n }\n\n return attributes;\n}\n\n/**\n * Add response attributes to spans\n * This supports Chat Completion, Responses API, Embeddings, and Conversations API responses\n */\nfunction addResponseAttributes(span: Span, result: unknown, recordOutputs?: boolean): void {\n if (!result || typeof result !== 'object') return;\n\n const response = result as OpenAiResponse;\n\n if (isChatCompletionResponse(response)) {\n addChatCompletionAttributes(span, response, recordOutputs);\n if (recordOutputs && response.choices?.length) {\n const responseTexts = response.choices.map(choice => choice.message?.content || '');\n span.setAttributes({ [GEN_AI_RESPONSE_TEXT_ATTRIBUTE]: JSON.stringify(responseTexts) });\n }\n } else if (isResponsesApiResponse(response)) {\n addResponsesApiAttributes(span, response, recordOutputs);\n if (recordOutputs && response.output_text) {\n span.setAttributes({ [GEN_AI_RESPONSE_TEXT_ATTRIBUTE]: response.output_text });\n }\n } else if (isEmbeddingsResponse(response)) {\n addEmbeddingsAttributes(span, response);\n } else if (isConversationResponse(response)) {\n addConversationAttributes(span, response);\n }\n}\n\n// Extract and record AI request inputs, if present. This is intentionally separate from response attributes.\nfunction addRequestAttributes(span: Span, params: Record<string, unknown>, operationName: string): void {\n // Store embeddings input on a separate attribute and do not truncate it\n if (operationName === OPENAI_OPERATIONS.EMBEDDINGS && 'input' in params) {\n const input = params.input;\n\n // No input provided\n if (input == null) {\n return;\n }\n\n // Empty input string\n if (typeof input === 'string' && input.length === 0) {\n return;\n }\n\n // Empty array input\n if (Array.isArray(input) && input.length === 0) {\n return;\n }\n\n // Store strings as-is, arrays/objects as JSON\n span.setAttribute(GEN_AI_EMBEDDINGS_INPUT_ATTRIBUTE, typeof input === 'string' ? input : JSON.stringify(input));\n return;\n }\n\n const src = 'input' in params ? params.input : 'messages' in params ? params.messages : undefined;\n\n if (!src) {\n return;\n }\n\n if (Array.isArray(src) && src.length === 0) {\n return;\n }\n\n const { systemInstructions, filteredMessages } = extractSystemInstructions(src);\n\n if (systemInstructions) {\n span.setAttribute(GEN_AI_SYSTEM_INSTRUCTIONS_ATTRIBUTE, systemInstructions);\n }\n\n const truncatedInput = getTruncatedJsonString(filteredMessages);\n span.setAttribute(GEN_AI_INPUT_MESSAGES_ATTRIBUTE, truncatedInput);\n\n if (Array.isArray(filteredMessages)) {\n span.setAttribute(GEN_AI_INPUT_MESSAGES_ORIGINAL_LENGTH_ATTRIBUTE, filteredMessages.length);\n } else {\n span.setAttribute(GEN_AI_INPUT_MESSAGES_ORIGINAL_LENGTH_ATTRIBUTE, 1);\n }\n}\n\n/**\n * Instrument a method with Sentry spans\n * Following Sentry AI Agents Manual Instrumentation conventions\n * @see https://docs.sentry.io/platforms/javascript/guides/node/tracing/instrumentation/ai-agents-module/#manual-instrumentation\n */\nfunction instrumentMethod<T extends unknown[], R>(\n originalMethod: (...args: T) => Promise<R>,\n methodPath: InstrumentedMethod,\n context: unknown,\n options: OpenAiOptions,\n): (...args: T) => Promise<R> {\n return async function instrumentedMethod(...args: T): Promise<R> {\n const requestAttributes = extractRequestAttributes(args, methodPath);\n const model = (requestAttributes[GEN_AI_REQUEST_MODEL_ATTRIBUTE] as string) || 'unknown';\n const operationName = getOperationName(methodPath);\n\n const params = args[0] as Record<string, unknown> | undefined;\n const isStreamRequested = params && typeof params === 'object' && params.stream === true;\n\n if (isStreamRequested) {\n // For streaming responses, use manual span management to properly handle the async generator lifecycle\n return startSpanManual(\n {\n name: `${operationName} ${model} stream-response`,\n op: getSpanOperation(methodPath),\n attributes: requestAttributes as Record<string, SpanAttributeValue>,\n },\n async (span: Span) => {\n try {\n if (options.recordInputs && params) {\n addRequestAttributes(span, params, operationName);\n }\n\n const result = await originalMethod.apply(context, args);\n\n return instrumentStream(\n result as OpenAIStream<ChatCompletionChunk | ResponseStreamingEvent>,\n span,\n options.recordOutputs ?? false,\n ) as unknown as R;\n } catch (error) {\n // For streaming requests that fail before stream creation, we still want to record\n // them as streaming requests but end the span gracefully\n span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });\n captureException(error, {\n mechanism: {\n handled: false,\n type: 'auto.ai.openai.stream',\n data: {\n function: methodPath,\n },\n },\n });\n span.end();\n throw error;\n }\n },\n );\n } else {\n // Non-streaming responses\n return startSpan(\n {\n name: `${operationName} ${model}`,\n op: getSpanOperation(methodPath),\n attributes: requestAttributes as Record<string, SpanAttributeValue>,\n },\n async (span: Span) => {\n try {\n if (options.recordInputs && params) {\n addRequestAttributes(span, params, operationName);\n }\n\n const result = await originalMethod.apply(context, args);\n addResponseAttributes(span, result, options.recordOutputs);\n return result;\n } catch (error) {\n captureException(error, {\n mechanism: {\n handled: false,\n type: 'auto.ai.openai',\n data: {\n function: methodPath,\n },\n },\n });\n throw error;\n }\n },\n );\n }\n };\n}\n\n/**\n * Create a deep proxy for OpenAI client instrumentation\n */\nfunction createDeepProxy<T extends object>(target: T, currentPath = '', options: OpenAiOptions): T {\n return new Proxy(target, {\n get(obj: object, prop: string): unknown {\n const value = (obj as Record<string, unknown>)[prop];\n const methodPath = buildMethodPath(currentPath, String(prop));\n\n if (typeof value === 'function' && shouldInstrument(methodPath)) {\n return instrumentMethod(value as (...args: unknown[]) => Promise<unknown>, methodPath, obj, options);\n }\n\n if (typeof value === 'function') {\n // Bind non-instrumented functions to preserve the original `this` context,\n // which is required for accessing private class fields (e.g. #baseURL) in OpenAI SDK v5.\n return value.bind(obj);\n }\n\n if (value && typeof value === 'object') {\n return createDeepProxy(value, methodPath, options);\n }\n\n return value;\n },\n }) as T;\n}\n\n/**\n * Instrument an OpenAI client with Sentry tracing\n * Can be used across Node.js, Cloudflare Workers, and Vercel Edge\n */\nexport function instrumentOpenAiClient<T extends object>(client: T, options?: OpenAiOptions): T {\n const sendDefaultPii = Boolean(getClient()?.getOptions().sendDefaultPii);\n\n const _options = {\n recordInputs: sendDefaultPii,\n recordOutputs: sendDefaultPii,\n ...options,\n };\n\n return createDeepProxy(client, '', _options);\n}\n"],"names":[],"mappings":"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"}
@@ -2,20 +2,21 @@ import { OPENAI_OPERATIONS, GEN_AI_REQUEST_MODEL_ATTRIBUTE, GEN_AI_REQUEST_TEMPE
2
2
  import { INSTRUMENTED_METHODS } from './constants.js';
3
3
 
4
4
  /**
5
- * Maps OpenAI method paths to Sentry operation names
5
+ * Maps OpenAI method paths to OpenTelemetry semantic convention operation names
6
+ * @see https://opentelemetry.io/docs/specs/semconv/gen-ai/gen-ai-spans/#llm-request-spans
6
7
  */
7
8
  function getOperationName(methodPath) {
8
9
  if (methodPath.includes('chat.completions')) {
9
10
  return OPENAI_OPERATIONS.CHAT;
10
11
  }
11
12
  if (methodPath.includes('responses')) {
12
- return OPENAI_OPERATIONS.RESPONSES;
13
+ return OPENAI_OPERATIONS.CHAT;
13
14
  }
14
15
  if (methodPath.includes('embeddings')) {
15
16
  return OPENAI_OPERATIONS.EMBEDDINGS;
16
17
  }
17
18
  if (methodPath.includes('conversations')) {
18
- return OPENAI_OPERATIONS.CONVERSATIONS;
19
+ return OPENAI_OPERATIONS.CHAT;
19
20
  }
20
21
  return methodPath.split('.').pop() || 'unknown';
21
22
  }
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sources":["../../../../src/tracing/openai/utils.ts"],"sourcesContent":["import type { Span } from '../../types-hoist/span';\nimport {\n GEN_AI_CONVERSATION_ID_ATTRIBUTE,\n GEN_AI_REQUEST_DIMENSIONS_ATTRIBUTE,\n GEN_AI_REQUEST_ENCODING_FORMAT_ATTRIBUTE,\n GEN_AI_REQUEST_FREQUENCY_PENALTY_ATTRIBUTE,\n GEN_AI_REQUEST_MODEL_ATTRIBUTE,\n GEN_AI_REQUEST_PRESENCE_PENALTY_ATTRIBUTE,\n GEN_AI_REQUEST_STREAM_ATTRIBUTE,\n GEN_AI_REQUEST_TEMPERATURE_ATTRIBUTE,\n GEN_AI_REQUEST_TOP_P_ATTRIBUTE,\n GEN_AI_RESPONSE_FINISH_REASONS_ATTRIBUTE,\n GEN_AI_RESPONSE_ID_ATTRIBUTE,\n GEN_AI_RESPONSE_MODEL_ATTRIBUTE,\n GEN_AI_RESPONSE_TOOL_CALLS_ATTRIBUTE,\n GEN_AI_USAGE_INPUT_TOKENS_ATTRIBUTE,\n GEN_AI_USAGE_OUTPUT_TOKENS_ATTRIBUTE,\n GEN_AI_USAGE_TOTAL_TOKENS_ATTRIBUTE,\n OPENAI_OPERATIONS,\n OPENAI_RESPONSE_ID_ATTRIBUTE,\n OPENAI_RESPONSE_MODEL_ATTRIBUTE,\n OPENAI_RESPONSE_TIMESTAMP_ATTRIBUTE,\n OPENAI_USAGE_COMPLETION_TOKENS_ATTRIBUTE,\n OPENAI_USAGE_PROMPT_TOKENS_ATTRIBUTE,\n} from '../ai/gen-ai-attributes';\nimport { INSTRUMENTED_METHODS } from './constants';\nimport type {\n ChatCompletionChunk,\n InstrumentedMethod,\n OpenAiChatCompletionObject,\n OpenAIConversationObject,\n OpenAICreateEmbeddingsObject,\n OpenAIResponseObject,\n ResponseStreamingEvent,\n} from './types';\n\n/**\n * Maps OpenAI method paths to Sentry operation names\n */\nexport function getOperationName(methodPath: string): string {\n if (methodPath.includes('chat.completions')) {\n return OPENAI_OPERATIONS.CHAT;\n }\n if (methodPath.includes('responses')) {\n return OPENAI_OPERATIONS.RESPONSES;\n }\n if (methodPath.includes('embeddings')) {\n return OPENAI_OPERATIONS.EMBEDDINGS;\n }\n if (methodPath.includes('conversations')) {\n return OPENAI_OPERATIONS.CONVERSATIONS;\n }\n return methodPath.split('.').pop() || 'unknown';\n}\n\n/**\n * Get the span operation for OpenAI methods\n * Following Sentry's convention: \"gen_ai.{operation_name}\"\n */\nexport function getSpanOperation(methodPath: string): string {\n return `gen_ai.${getOperationName(methodPath)}`;\n}\n\n/**\n * Check if a method path should be instrumented\n */\nexport function shouldInstrument(methodPath: string): methodPath is InstrumentedMethod {\n return INSTRUMENTED_METHODS.includes(methodPath as InstrumentedMethod);\n}\n\n/**\n * Build method path from current traversal\n */\nexport function buildMethodPath(currentPath: string, prop: string): string {\n return currentPath ? `${currentPath}.${prop}` : prop;\n}\n\n/**\n * Check if response is a Chat Completion object\n */\nexport function isChatCompletionResponse(response: unknown): response is OpenAiChatCompletionObject {\n return (\n response !== null &&\n typeof response === 'object' &&\n 'object' in response &&\n (response as Record<string, unknown>).object === 'chat.completion'\n );\n}\n\n/**\n * Check if response is a Responses API object\n */\nexport function isResponsesApiResponse(response: unknown): response is OpenAIResponseObject {\n return (\n response !== null &&\n typeof response === 'object' &&\n 'object' in response &&\n (response as Record<string, unknown>).object === 'response'\n );\n}\n\n/**\n * Check if response is an Embeddings API object\n */\nexport function isEmbeddingsResponse(response: unknown): response is OpenAICreateEmbeddingsObject {\n if (response === null || typeof response !== 'object' || !('object' in response)) {\n return false;\n }\n const responseObject = response as Record<string, unknown>;\n return (\n responseObject.object === 'list' &&\n typeof responseObject.model === 'string' &&\n responseObject.model.toLowerCase().includes('embedding')\n );\n}\n\n/**\n * Check if response is a Conversations API object\n * @see https://platform.openai.com/docs/api-reference/conversations\n */\nexport function isConversationResponse(response: unknown): response is OpenAIConversationObject {\n return (\n response !== null &&\n typeof response === 'object' &&\n 'object' in response &&\n (response as Record<string, unknown>).object === 'conversation'\n );\n}\n\n/**\n * Check if streaming event is from the Responses API\n */\nexport function isResponsesApiStreamEvent(event: unknown): event is ResponseStreamingEvent {\n return (\n event !== null &&\n typeof event === 'object' &&\n 'type' in event &&\n typeof (event as Record<string, unknown>).type === 'string' &&\n ((event as Record<string, unknown>).type as string).startsWith('response.')\n );\n}\n\n/**\n * Check if streaming event is a chat completion chunk\n */\nexport function isChatCompletionChunk(event: unknown): event is ChatCompletionChunk {\n return (\n event !== null &&\n typeof event === 'object' &&\n 'object' in event &&\n (event as Record<string, unknown>).object === 'chat.completion.chunk'\n );\n}\n\n/**\n * Add attributes for Chat Completion responses\n */\nexport function addChatCompletionAttributes(\n span: Span,\n response: OpenAiChatCompletionObject,\n recordOutputs?: boolean,\n): void {\n setCommonResponseAttributes(span, response.id, response.model, response.created);\n if (response.usage) {\n setTokenUsageAttributes(\n span,\n response.usage.prompt_tokens,\n response.usage.completion_tokens,\n response.usage.total_tokens,\n );\n }\n if (Array.isArray(response.choices)) {\n const finishReasons = response.choices\n .map(choice => choice.finish_reason)\n .filter((reason): reason is string => reason !== null);\n if (finishReasons.length > 0) {\n span.setAttributes({\n [GEN_AI_RESPONSE_FINISH_REASONS_ATTRIBUTE]: JSON.stringify(finishReasons),\n });\n }\n\n // Extract tool calls from all choices (only if recordOutputs is true)\n if (recordOutputs) {\n const toolCalls = response.choices\n .map(choice => choice.message?.tool_calls)\n .filter(calls => Array.isArray(calls) && calls.length > 0)\n .flat();\n\n if (toolCalls.length > 0) {\n span.setAttributes({\n [GEN_AI_RESPONSE_TOOL_CALLS_ATTRIBUTE]: JSON.stringify(toolCalls),\n });\n }\n }\n }\n}\n\n/**\n * Add attributes for Responses API responses\n */\nexport function addResponsesApiAttributes(span: Span, response: OpenAIResponseObject, recordOutputs?: boolean): void {\n setCommonResponseAttributes(span, response.id, response.model, response.created_at);\n if (response.status) {\n span.setAttributes({\n [GEN_AI_RESPONSE_FINISH_REASONS_ATTRIBUTE]: JSON.stringify([response.status]),\n });\n }\n if (response.usage) {\n setTokenUsageAttributes(\n span,\n response.usage.input_tokens,\n response.usage.output_tokens,\n response.usage.total_tokens,\n );\n }\n\n // Extract function calls from output (only if recordOutputs is true)\n if (recordOutputs) {\n const responseWithOutput = response as OpenAIResponseObject & { output?: unknown[] };\n if (Array.isArray(responseWithOutput.output) && responseWithOutput.output.length > 0) {\n // Filter for function_call type objects in the output array\n const functionCalls = responseWithOutput.output.filter(\n (item): unknown =>\n typeof item === 'object' && item !== null && (item as Record<string, unknown>).type === 'function_call',\n );\n\n if (functionCalls.length > 0) {\n span.setAttributes({\n [GEN_AI_RESPONSE_TOOL_CALLS_ATTRIBUTE]: JSON.stringify(functionCalls),\n });\n }\n }\n }\n}\n\n/**\n * Add attributes for Embeddings API responses\n */\nexport function addEmbeddingsAttributes(span: Span, response: OpenAICreateEmbeddingsObject): void {\n span.setAttributes({\n [OPENAI_RESPONSE_MODEL_ATTRIBUTE]: response.model,\n [GEN_AI_RESPONSE_MODEL_ATTRIBUTE]: response.model,\n });\n\n if (response.usage) {\n setTokenUsageAttributes(span, response.usage.prompt_tokens, undefined, response.usage.total_tokens);\n }\n}\n\n/**\n * Add attributes for Conversations API responses\n * @see https://platform.openai.com/docs/api-reference/conversations\n */\nexport function addConversationAttributes(span: Span, response: OpenAIConversationObject): void {\n const { id, created_at } = response;\n\n span.setAttributes({\n [OPENAI_RESPONSE_ID_ATTRIBUTE]: id,\n [GEN_AI_RESPONSE_ID_ATTRIBUTE]: id,\n // The conversation id is used to link messages across API calls\n [GEN_AI_CONVERSATION_ID_ATTRIBUTE]: id,\n });\n\n if (created_at) {\n span.setAttributes({\n [OPENAI_RESPONSE_TIMESTAMP_ATTRIBUTE]: new Date(created_at * 1000).toISOString(),\n });\n }\n}\n\n/**\n * Set token usage attributes\n * @param span - The span to add attributes to\n * @param promptTokens - The number of prompt tokens\n * @param completionTokens - The number of completion tokens\n * @param totalTokens - The number of total tokens\n */\nexport function setTokenUsageAttributes(\n span: Span,\n promptTokens?: number,\n completionTokens?: number,\n totalTokens?: number,\n): void {\n if (promptTokens !== undefined) {\n span.setAttributes({\n [OPENAI_USAGE_PROMPT_TOKENS_ATTRIBUTE]: promptTokens,\n [GEN_AI_USAGE_INPUT_TOKENS_ATTRIBUTE]: promptTokens,\n });\n }\n if (completionTokens !== undefined) {\n span.setAttributes({\n [OPENAI_USAGE_COMPLETION_TOKENS_ATTRIBUTE]: completionTokens,\n [GEN_AI_USAGE_OUTPUT_TOKENS_ATTRIBUTE]: completionTokens,\n });\n }\n if (totalTokens !== undefined) {\n span.setAttributes({\n [GEN_AI_USAGE_TOTAL_TOKENS_ATTRIBUTE]: totalTokens,\n });\n }\n}\n\n/**\n * Set common response attributes\n * @param span - The span to add attributes to\n * @param id - The response id\n * @param model - The response model\n * @param timestamp - The response timestamp\n */\nexport function setCommonResponseAttributes(span: Span, id: string, model: string, timestamp: number): void {\n span.setAttributes({\n [OPENAI_RESPONSE_ID_ATTRIBUTE]: id,\n [GEN_AI_RESPONSE_ID_ATTRIBUTE]: id,\n });\n span.setAttributes({\n [OPENAI_RESPONSE_MODEL_ATTRIBUTE]: model,\n [GEN_AI_RESPONSE_MODEL_ATTRIBUTE]: model,\n });\n span.setAttributes({\n [OPENAI_RESPONSE_TIMESTAMP_ATTRIBUTE]: new Date(timestamp * 1000).toISOString(),\n });\n}\n\n/**\n * Extract conversation ID from request parameters\n * Supports both Conversations API and previous_response_id chaining\n * @see https://platform.openai.com/docs/guides/conversation-state\n */\nfunction extractConversationId(params: Record<string, unknown>): string | undefined {\n // Conversations API: conversation parameter (e.g., \"conv_...\")\n if ('conversation' in params && typeof params.conversation === 'string') {\n return params.conversation;\n }\n // Responses chaining: previous_response_id links to parent response\n if ('previous_response_id' in params && typeof params.previous_response_id === 'string') {\n return params.previous_response_id;\n }\n return undefined;\n}\n\n/**\n * Extract request parameters including model settings and conversation context\n */\nexport function extractRequestParameters(params: Record<string, unknown>): Record<string, unknown> {\n const attributes: Record<string, unknown> = {\n [GEN_AI_REQUEST_MODEL_ATTRIBUTE]: params.model ?? 'unknown',\n };\n\n if ('temperature' in params) attributes[GEN_AI_REQUEST_TEMPERATURE_ATTRIBUTE] = params.temperature;\n if ('top_p' in params) attributes[GEN_AI_REQUEST_TOP_P_ATTRIBUTE] = params.top_p;\n if ('frequency_penalty' in params) attributes[GEN_AI_REQUEST_FREQUENCY_PENALTY_ATTRIBUTE] = params.frequency_penalty;\n if ('presence_penalty' in params) attributes[GEN_AI_REQUEST_PRESENCE_PENALTY_ATTRIBUTE] = params.presence_penalty;\n if ('stream' in params) attributes[GEN_AI_REQUEST_STREAM_ATTRIBUTE] = params.stream;\n if ('encoding_format' in params) attributes[GEN_AI_REQUEST_ENCODING_FORMAT_ATTRIBUTE] = params.encoding_format;\n if ('dimensions' in params) attributes[GEN_AI_REQUEST_DIMENSIONS_ATTRIBUTE] = params.dimensions;\n\n // Capture conversation ID for linking messages across API calls\n const conversationId = extractConversationId(params);\n if (conversationId) {\n attributes[GEN_AI_CONVERSATION_ID_ATTRIBUTE] = conversationId;\n }\n\n return attributes;\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"utils.js","sources":["../../../../src/tracing/openai/utils.ts"],"sourcesContent":["import type { Span } from '../../types-hoist/span';\nimport {\n GEN_AI_CONVERSATION_ID_ATTRIBUTE,\n GEN_AI_REQUEST_DIMENSIONS_ATTRIBUTE,\n GEN_AI_REQUEST_ENCODING_FORMAT_ATTRIBUTE,\n GEN_AI_REQUEST_FREQUENCY_PENALTY_ATTRIBUTE,\n GEN_AI_REQUEST_MODEL_ATTRIBUTE,\n GEN_AI_REQUEST_PRESENCE_PENALTY_ATTRIBUTE,\n GEN_AI_REQUEST_STREAM_ATTRIBUTE,\n GEN_AI_REQUEST_TEMPERATURE_ATTRIBUTE,\n GEN_AI_REQUEST_TOP_P_ATTRIBUTE,\n GEN_AI_RESPONSE_FINISH_REASONS_ATTRIBUTE,\n GEN_AI_RESPONSE_ID_ATTRIBUTE,\n GEN_AI_RESPONSE_MODEL_ATTRIBUTE,\n GEN_AI_RESPONSE_TOOL_CALLS_ATTRIBUTE,\n GEN_AI_USAGE_INPUT_TOKENS_ATTRIBUTE,\n GEN_AI_USAGE_OUTPUT_TOKENS_ATTRIBUTE,\n GEN_AI_USAGE_TOTAL_TOKENS_ATTRIBUTE,\n OPENAI_OPERATIONS,\n OPENAI_RESPONSE_ID_ATTRIBUTE,\n OPENAI_RESPONSE_MODEL_ATTRIBUTE,\n OPENAI_RESPONSE_TIMESTAMP_ATTRIBUTE,\n OPENAI_USAGE_COMPLETION_TOKENS_ATTRIBUTE,\n OPENAI_USAGE_PROMPT_TOKENS_ATTRIBUTE,\n} from '../ai/gen-ai-attributes';\nimport { INSTRUMENTED_METHODS } from './constants';\nimport type {\n ChatCompletionChunk,\n InstrumentedMethod,\n OpenAiChatCompletionObject,\n OpenAIConversationObject,\n OpenAICreateEmbeddingsObject,\n OpenAIResponseObject,\n ResponseStreamingEvent,\n} from './types';\n\n/**\n * Maps OpenAI method paths to OpenTelemetry semantic convention operation names\n * @see https://opentelemetry.io/docs/specs/semconv/gen-ai/gen-ai-spans/#llm-request-spans\n */\nexport function getOperationName(methodPath: string): string {\n if (methodPath.includes('chat.completions')) {\n return OPENAI_OPERATIONS.CHAT;\n }\n if (methodPath.includes('responses')) {\n return OPENAI_OPERATIONS.CHAT;\n }\n if (methodPath.includes('embeddings')) {\n return OPENAI_OPERATIONS.EMBEDDINGS;\n }\n if (methodPath.includes('conversations')) {\n return OPENAI_OPERATIONS.CHAT;\n }\n return methodPath.split('.').pop() || 'unknown';\n}\n\n/**\n * Get the span operation for OpenAI methods\n * Following Sentry's convention: \"gen_ai.{operation_name}\"\n */\nexport function getSpanOperation(methodPath: string): string {\n return `gen_ai.${getOperationName(methodPath)}`;\n}\n\n/**\n * Check if a method path should be instrumented\n */\nexport function shouldInstrument(methodPath: string): methodPath is InstrumentedMethod {\n return INSTRUMENTED_METHODS.includes(methodPath as InstrumentedMethod);\n}\n\n/**\n * Build method path from current traversal\n */\nexport function buildMethodPath(currentPath: string, prop: string): string {\n return currentPath ? `${currentPath}.${prop}` : prop;\n}\n\n/**\n * Check if response is a Chat Completion object\n */\nexport function isChatCompletionResponse(response: unknown): response is OpenAiChatCompletionObject {\n return (\n response !== null &&\n typeof response === 'object' &&\n 'object' in response &&\n (response as Record<string, unknown>).object === 'chat.completion'\n );\n}\n\n/**\n * Check if response is a Responses API object\n */\nexport function isResponsesApiResponse(response: unknown): response is OpenAIResponseObject {\n return (\n response !== null &&\n typeof response === 'object' &&\n 'object' in response &&\n (response as Record<string, unknown>).object === 'response'\n );\n}\n\n/**\n * Check if response is an Embeddings API object\n */\nexport function isEmbeddingsResponse(response: unknown): response is OpenAICreateEmbeddingsObject {\n if (response === null || typeof response !== 'object' || !('object' in response)) {\n return false;\n }\n const responseObject = response as Record<string, unknown>;\n return (\n responseObject.object === 'list' &&\n typeof responseObject.model === 'string' &&\n responseObject.model.toLowerCase().includes('embedding')\n );\n}\n\n/**\n * Check if response is a Conversations API object\n * @see https://platform.openai.com/docs/api-reference/conversations\n */\nexport function isConversationResponse(response: unknown): response is OpenAIConversationObject {\n return (\n response !== null &&\n typeof response === 'object' &&\n 'object' in response &&\n (response as Record<string, unknown>).object === 'conversation'\n );\n}\n\n/**\n * Check if streaming event is from the Responses API\n */\nexport function isResponsesApiStreamEvent(event: unknown): event is ResponseStreamingEvent {\n return (\n event !== null &&\n typeof event === 'object' &&\n 'type' in event &&\n typeof (event as Record<string, unknown>).type === 'string' &&\n ((event as Record<string, unknown>).type as string).startsWith('response.')\n );\n}\n\n/**\n * Check if streaming event is a chat completion chunk\n */\nexport function isChatCompletionChunk(event: unknown): event is ChatCompletionChunk {\n return (\n event !== null &&\n typeof event === 'object' &&\n 'object' in event &&\n (event as Record<string, unknown>).object === 'chat.completion.chunk'\n );\n}\n\n/**\n * Add attributes for Chat Completion responses\n */\nexport function addChatCompletionAttributes(\n span: Span,\n response: OpenAiChatCompletionObject,\n recordOutputs?: boolean,\n): void {\n setCommonResponseAttributes(span, response.id, response.model, response.created);\n if (response.usage) {\n setTokenUsageAttributes(\n span,\n response.usage.prompt_tokens,\n response.usage.completion_tokens,\n response.usage.total_tokens,\n );\n }\n if (Array.isArray(response.choices)) {\n const finishReasons = response.choices\n .map(choice => choice.finish_reason)\n .filter((reason): reason is string => reason !== null);\n if (finishReasons.length > 0) {\n span.setAttributes({\n [GEN_AI_RESPONSE_FINISH_REASONS_ATTRIBUTE]: JSON.stringify(finishReasons),\n });\n }\n\n // Extract tool calls from all choices (only if recordOutputs is true)\n if (recordOutputs) {\n const toolCalls = response.choices\n .map(choice => choice.message?.tool_calls)\n .filter(calls => Array.isArray(calls) && calls.length > 0)\n .flat();\n\n if (toolCalls.length > 0) {\n span.setAttributes({\n [GEN_AI_RESPONSE_TOOL_CALLS_ATTRIBUTE]: JSON.stringify(toolCalls),\n });\n }\n }\n }\n}\n\n/**\n * Add attributes for Responses API responses\n */\nexport function addResponsesApiAttributes(span: Span, response: OpenAIResponseObject, recordOutputs?: boolean): void {\n setCommonResponseAttributes(span, response.id, response.model, response.created_at);\n if (response.status) {\n span.setAttributes({\n [GEN_AI_RESPONSE_FINISH_REASONS_ATTRIBUTE]: JSON.stringify([response.status]),\n });\n }\n if (response.usage) {\n setTokenUsageAttributes(\n span,\n response.usage.input_tokens,\n response.usage.output_tokens,\n response.usage.total_tokens,\n );\n }\n\n // Extract function calls from output (only if recordOutputs is true)\n if (recordOutputs) {\n const responseWithOutput = response as OpenAIResponseObject & { output?: unknown[] };\n if (Array.isArray(responseWithOutput.output) && responseWithOutput.output.length > 0) {\n // Filter for function_call type objects in the output array\n const functionCalls = responseWithOutput.output.filter(\n (item): unknown =>\n typeof item === 'object' && item !== null && (item as Record<string, unknown>).type === 'function_call',\n );\n\n if (functionCalls.length > 0) {\n span.setAttributes({\n [GEN_AI_RESPONSE_TOOL_CALLS_ATTRIBUTE]: JSON.stringify(functionCalls),\n });\n }\n }\n }\n}\n\n/**\n * Add attributes for Embeddings API responses\n */\nexport function addEmbeddingsAttributes(span: Span, response: OpenAICreateEmbeddingsObject): void {\n span.setAttributes({\n [OPENAI_RESPONSE_MODEL_ATTRIBUTE]: response.model,\n [GEN_AI_RESPONSE_MODEL_ATTRIBUTE]: response.model,\n });\n\n if (response.usage) {\n setTokenUsageAttributes(span, response.usage.prompt_tokens, undefined, response.usage.total_tokens);\n }\n}\n\n/**\n * Add attributes for Conversations API responses\n * @see https://platform.openai.com/docs/api-reference/conversations\n */\nexport function addConversationAttributes(span: Span, response: OpenAIConversationObject): void {\n const { id, created_at } = response;\n\n span.setAttributes({\n [OPENAI_RESPONSE_ID_ATTRIBUTE]: id,\n [GEN_AI_RESPONSE_ID_ATTRIBUTE]: id,\n // The conversation id is used to link messages across API calls\n [GEN_AI_CONVERSATION_ID_ATTRIBUTE]: id,\n });\n\n if (created_at) {\n span.setAttributes({\n [OPENAI_RESPONSE_TIMESTAMP_ATTRIBUTE]: new Date(created_at * 1000).toISOString(),\n });\n }\n}\n\n/**\n * Set token usage attributes\n * @param span - The span to add attributes to\n * @param promptTokens - The number of prompt tokens\n * @param completionTokens - The number of completion tokens\n * @param totalTokens - The number of total tokens\n */\nexport function setTokenUsageAttributes(\n span: Span,\n promptTokens?: number,\n completionTokens?: number,\n totalTokens?: number,\n): void {\n if (promptTokens !== undefined) {\n span.setAttributes({\n [OPENAI_USAGE_PROMPT_TOKENS_ATTRIBUTE]: promptTokens,\n [GEN_AI_USAGE_INPUT_TOKENS_ATTRIBUTE]: promptTokens,\n });\n }\n if (completionTokens !== undefined) {\n span.setAttributes({\n [OPENAI_USAGE_COMPLETION_TOKENS_ATTRIBUTE]: completionTokens,\n [GEN_AI_USAGE_OUTPUT_TOKENS_ATTRIBUTE]: completionTokens,\n });\n }\n if (totalTokens !== undefined) {\n span.setAttributes({\n [GEN_AI_USAGE_TOTAL_TOKENS_ATTRIBUTE]: totalTokens,\n });\n }\n}\n\n/**\n * Set common response attributes\n * @param span - The span to add attributes to\n * @param id - The response id\n * @param model - The response model\n * @param timestamp - The response timestamp\n */\nexport function setCommonResponseAttributes(span: Span, id: string, model: string, timestamp: number): void {\n span.setAttributes({\n [OPENAI_RESPONSE_ID_ATTRIBUTE]: id,\n [GEN_AI_RESPONSE_ID_ATTRIBUTE]: id,\n });\n span.setAttributes({\n [OPENAI_RESPONSE_MODEL_ATTRIBUTE]: model,\n [GEN_AI_RESPONSE_MODEL_ATTRIBUTE]: model,\n });\n span.setAttributes({\n [OPENAI_RESPONSE_TIMESTAMP_ATTRIBUTE]: new Date(timestamp * 1000).toISOString(),\n });\n}\n\n/**\n * Extract conversation ID from request parameters\n * Supports both Conversations API and previous_response_id chaining\n * @see https://platform.openai.com/docs/guides/conversation-state\n */\nfunction extractConversationId(params: Record<string, unknown>): string | undefined {\n // Conversations API: conversation parameter (e.g., \"conv_...\")\n if ('conversation' in params && typeof params.conversation === 'string') {\n return params.conversation;\n }\n // Responses chaining: previous_response_id links to parent response\n if ('previous_response_id' in params && typeof params.previous_response_id === 'string') {\n return params.previous_response_id;\n }\n return undefined;\n}\n\n/**\n * Extract request parameters including model settings and conversation context\n */\nexport function extractRequestParameters(params: Record<string, unknown>): Record<string, unknown> {\n const attributes: Record<string, unknown> = {\n [GEN_AI_REQUEST_MODEL_ATTRIBUTE]: params.model ?? 'unknown',\n };\n\n if ('temperature' in params) attributes[GEN_AI_REQUEST_TEMPERATURE_ATTRIBUTE] = params.temperature;\n if ('top_p' in params) attributes[GEN_AI_REQUEST_TOP_P_ATTRIBUTE] = params.top_p;\n if ('frequency_penalty' in params) attributes[GEN_AI_REQUEST_FREQUENCY_PENALTY_ATTRIBUTE] = params.frequency_penalty;\n if ('presence_penalty' in params) attributes[GEN_AI_REQUEST_PRESENCE_PENALTY_ATTRIBUTE] = params.presence_penalty;\n if ('stream' in params) attributes[GEN_AI_REQUEST_STREAM_ATTRIBUTE] = params.stream;\n if ('encoding_format' in params) attributes[GEN_AI_REQUEST_ENCODING_FORMAT_ATTRIBUTE] = params.encoding_format;\n if ('dimensions' in params) attributes[GEN_AI_REQUEST_DIMENSIONS_ATTRIBUTE] = params.dimensions;\n\n // Capture conversation ID for linking messages across API calls\n const conversationId = extractConversationId(params);\n if (conversationId) {\n attributes[GEN_AI_CONVERSATION_ID_ATTRIBUTE] = conversationId;\n }\n\n return attributes;\n}\n"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { getAsyncContextStrategy } from '../asyncContext/index.js';
2
2
  import { getMainCarrier } from '../carrier.js';
3
- import { withScope, getCurrentScope, getClient, getIsolationScope } from '../currentScopes.js';
3
+ import { getClient, withScope, getCurrentScope, getIsolationScope } from '../currentScopes.js';
4
4
  import { DEBUG_BUILD } from '../debug-build.js';
5
5
  import { SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE } from '../semanticAttributes.js';
6
6
  import { baggageHeaderToDynamicSamplingContext } from '../utils/baggage.js';
@@ -10,8 +10,8 @@ import { hasSpansEnabled } from '../utils/hasSpansEnabled.js';
10
10
  import { parseSampleRate } from '../utils/parseSampleRate.js';
11
11
  import { generateTraceId } from '../utils/propagationContext.js';
12
12
  import { safeMathRandom } from '../utils/randomSafeContext.js';
13
- import { _setSpanForScope, _getSpanForScope } from '../utils/spanOnScope.js';
14
- import { spanToJSON, spanTimeInputToSeconds, getRootSpan, addChildSpanToSpan, spanIsSampled } from '../utils/spanUtils.js';
13
+ import { _getSpanForScope, _setSpanForScope } from '../utils/spanOnScope.js';
14
+ import { spanTimeInputToSeconds, getRootSpan, addChildSpanToSpan, spanIsSampled, spanToJSON } from '../utils/spanUtils.js';
15
15
  import { shouldContinueTrace, propagationContextFromHeaders } from '../utils/tracing.js';
16
16
  import { getDynamicSamplingContextFromSpan, freezeDscOnSpan } from './dynamicSamplingContext.js';
17
17
  import { logSpanStart } from './logSpans.js';
@@ -2,5 +2,24 @@
2
2
  // This allows us to capture tool errors and link them to the correct span
3
3
  const toolCallSpanMap = new Map();
4
4
 
5
- export { toolCallSpanMap };
5
+ // Operation sets for efficient mapping to OpenTelemetry semantic convention values
6
+ const INVOKE_AGENT_OPS = new Set([
7
+ 'ai.generateText',
8
+ 'ai.streamText',
9
+ 'ai.generateObject',
10
+ 'ai.streamObject',
11
+ 'ai.embed',
12
+ 'ai.embedMany',
13
+ ]);
14
+
15
+ const GENERATE_CONTENT_OPS = new Set([
16
+ 'ai.generateText.doGenerate',
17
+ 'ai.streamText.doStream',
18
+ 'ai.generateObject.doGenerate',
19
+ 'ai.streamObject.doStream',
20
+ ]);
21
+
22
+ const EMBEDDINGS_OPS = new Set(['ai.embed.doEmbed', 'ai.embedMany.doEmbed']);
23
+
24
+ export { EMBEDDINGS_OPS, GENERATE_CONTENT_OPS, INVOKE_AGENT_OPS, toolCallSpanMap };
6
25
  //# sourceMappingURL=constants.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.js","sources":["../../../../src/tracing/vercel-ai/constants.ts"],"sourcesContent":["import type { Span } from '../../types-hoist/span';\n\n// Global Map to track tool call IDs to their corresponding spans\n// This allows us to capture tool errors and link them to the correct span\nexport const toolCallSpanMap = new Map<string, Span>();\n"],"names":[],"mappings":"AAEA;AACA;MACa,eAAA,GAAkB,IAAI,GAAG;;;;"}
1
+ {"version":3,"file":"constants.js","sources":["../../../../src/tracing/vercel-ai/constants.ts"],"sourcesContent":["import type { Span } from '../../types-hoist/span';\n\n// Global Map to track tool call IDs to their corresponding spans\n// This allows us to capture tool errors and link them to the correct span\nexport const toolCallSpanMap = new Map<string, Span>();\n\n// Operation sets for efficient mapping to OpenTelemetry semantic convention values\nexport const INVOKE_AGENT_OPS = new Set([\n 'ai.generateText',\n 'ai.streamText',\n 'ai.generateObject',\n 'ai.streamObject',\n 'ai.embed',\n 'ai.embedMany',\n]);\n\nexport const GENERATE_CONTENT_OPS = new Set([\n 'ai.generateText.doGenerate',\n 'ai.streamText.doStream',\n 'ai.generateObject.doGenerate',\n 'ai.streamObject.doStream',\n]);\n\nexport const EMBEDDINGS_OPS = new Set(['ai.embed.doEmbed', 'ai.embedMany.doEmbed']);\n"],"names":[],"mappings":"AAEA;AACA;MACa,eAAA,GAAkB,IAAI,GAAG;;AAEtC;AACO,MAAM,gBAAA,GAAmB,IAAI,GAAG,CAAC;AACxC,EAAE,iBAAiB;AACnB,EAAE,eAAe;AACjB,EAAE,mBAAmB;AACrB,EAAE,iBAAiB;AACnB,EAAE,UAAU;AACZ,EAAE,cAAc;AAChB,CAAC;;AAEM,MAAM,oBAAA,GAAuB,IAAI,GAAG,CAAC;AAC5C,EAAE,4BAA4B;AAC9B,EAAE,wBAAwB;AAC1B,EAAE,8BAA8B;AAChC,EAAE,0BAA0B;AAC5B,CAAC;;AAEM,MAAM,cAAA,GAAiB,IAAI,GAAG,CAAC,CAAC,kBAAkB,EAAE,sBAAsB,CAAC;;;;"}