|
| 1 | +--- |
| 2 | +name: add-ai-integration |
| 3 | +description: Add a new AI provider integration to the Sentry JavaScript SDK. Use when contributing a new AI instrumentation (OpenAI, Anthropic, Vercel AI, LangChain, etc.) or modifying an existing one. |
| 4 | +argument-hint: <provider-name> |
| 5 | +--- |
| 6 | + |
| 7 | +# Adding a New AI Integration |
| 8 | + |
| 9 | +## Decision Tree |
| 10 | + |
| 11 | +``` |
| 12 | +Does the AI SDK have native OpenTelemetry support? |
| 13 | +|- YES -> Does it emit OTel spans automatically? |
| 14 | +| |- YES (like Vercel AI) -> Pattern 1: OTel Span Processors |
| 15 | +| +- NO -> Pattern 2: OTel Instrumentation (wrap client) |
| 16 | ++- NO -> Does the SDK provide hooks/callbacks? |
| 17 | + |- YES (like LangChain) -> Pattern 3: Callback/Hook Based |
| 18 | + +- NO -> Pattern 4: Client Wrapping |
| 19 | +``` |
| 20 | + |
| 21 | +## Runtime-Specific Placement |
| 22 | + |
| 23 | +If an AI SDK only works in one runtime, code lives exclusively in that runtime's package. Do NOT add it to `packages/core/`. |
| 24 | + |
| 25 | +- **Node.js-only** -> `packages/node/src/integrations/tracing/{provider}/` |
| 26 | +- **Cloudflare-only** -> `packages/cloudflare/src/integrations/tracing/{provider}.ts` |
| 27 | +- **Browser-only** -> `packages/browser/src/integrations/tracing/{provider}/` |
| 28 | +- **Multi-runtime** -> shared core in `packages/core/src/tracing/{provider}/` with runtime-specific wrappers |
| 29 | + |
| 30 | +## Span Hierarchy |
| 31 | + |
| 32 | +- `gen_ai.invoke_agent` — parent/pipeline spans (chains, agents, orchestration) |
| 33 | +- `gen_ai.chat`, `gen_ai.generate_text`, etc. — child spans (actual LLM calls) |
| 34 | + |
| 35 | +## Shared Utilities (`packages/core/src/tracing/ai/`) |
| 36 | + |
| 37 | +- `gen-ai-attributes.ts` — OTel Semantic Convention attribute constants. **Always use these, never hardcode.** |
| 38 | +- `utils.ts` — `setTokenUsageAttributes()`, `getTruncatedJsonString()`, `truncateGenAiMessages()`, `buildMethodPath()` |
| 39 | +- Only use attributes from [Sentry Gen AI Conventions](https://getsentry.github.io/sentry-conventions/attributes/gen_ai/). |
| 40 | + |
| 41 | +## Streaming |
| 42 | + |
| 43 | +- **Non-streaming:** `startSpan()`, set attributes from response |
| 44 | +- **Streaming:** `startSpanManual()`, accumulate state via async generator or event listeners, set `GEN_AI_RESPONSE_STREAMING_ATTRIBUTE: true`, call `span.end()` in finally block |
| 45 | +- Detect via `params.stream === true` |
| 46 | +- References: `openai/streaming.ts` (async generator), `anthropic-ai/streaming.ts` (event listeners) |
| 47 | + |
| 48 | +## Token Accumulation |
| 49 | + |
| 50 | +- **Child spans:** Set tokens directly from API response via `setTokenUsageAttributes()` |
| 51 | +- **Parent spans (`invoke_agent`):** Accumulate from children using event processor (see `vercel-ai/`) |
| 52 | + |
| 53 | +## Pattern 1: OTel Span Processors |
| 54 | + |
| 55 | +**Use when:** SDK emits OTel spans automatically (Vercel AI) |
| 56 | + |
| 57 | +1. **Core:** Create `add{Provider}Processors()` in `packages/core/src/tracing/{provider}/index.ts` — registers `spanStart` listener + event processor |
| 58 | +2. **Node.js:** Add `callWhenPatched()` optimization in `packages/node/src/integrations/tracing/{provider}/index.ts` — defers registration until package is imported |
| 59 | +3. **Edge:** Direct registration in `packages/cloudflare/src/integrations/tracing/{provider}.ts` — no OTel, call processors immediately |
| 60 | + |
| 61 | +Reference: `packages/node/src/integrations/tracing/vercelai/` |
| 62 | + |
| 63 | +## Pattern 2: OTel Instrumentation (Client Wrapping) |
| 64 | + |
| 65 | +**Use when:** SDK has no native OTel support (OpenAI, Anthropic, Google GenAI) |
| 66 | + |
| 67 | +1. **Core:** Create `instrument{Provider}Client()` in `packages/core/src/tracing/{provider}/index.ts` — Proxy to wrap client methods, create spans manually |
| 68 | +2. **Node.js `instrumentation.ts`:** Patch module exports, wrap client constructor. Check `_INTERNAL_shouldSkipAiProviderWrapping()` for LangChain compatibility. |
| 69 | +3. **Node.js `index.ts`:** Export integration function using `generateInstrumentOnce()` helper |
| 70 | + |
| 71 | +Reference: `packages/node/src/integrations/tracing/openai/` |
| 72 | + |
| 73 | +## Pattern 3: Callback/Hook Based |
| 74 | + |
| 75 | +**Use when:** SDK provides lifecycle hooks (LangChain, LangGraph) |
| 76 | + |
| 77 | +1. **Core:** Create `create{Provider}CallbackHandler()` — implement SDK's callback interface, create spans in callbacks |
| 78 | +2. **Node.js `instrumentation.ts`:** Auto-inject callbacks by patching runnable methods. Disable underlying AI provider wrapping. |
| 79 | + |
| 80 | +Reference: `packages/node/src/integrations/tracing/langchain/` |
| 81 | + |
| 82 | +## Auto-Instrumentation (Node.js) |
| 83 | + |
| 84 | +**Mandatory** for Node.js AI integrations. OTel only patches when the package is imported (zero cost if unused). |
| 85 | + |
| 86 | +### Steps |
| 87 | + |
| 88 | +1. **Add to `getAutoPerformanceIntegrations()`** in `packages/node/src/integrations/tracing/index.ts` — LangChain MUST come first |
| 89 | +2. **Add to `getOpenTelemetryInstrumentationToPreload()`** for OTel-based integrations |
| 90 | +3. **Export from `packages/node/src/index.ts`**: integration function + options type |
| 91 | +4. **Add E2E tests:** |
| 92 | + - Node.js: `dev-packages/node-integration-tests/suites/tracing/{provider}/` |
| 93 | + - Cloudflare: `dev-packages/cloudflare-integration-tests/suites/tracing/{provider}/` |
| 94 | + - Browser: `dev-packages/browser-integration-tests/suites/tracing/ai-providers/{provider}/` |
| 95 | + |
| 96 | +## Key Rules |
| 97 | + |
| 98 | +1. Respect `sendDefaultPii` for `recordInputs`/`recordOutputs` |
| 99 | +2. Set `SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN = 'auto.ai.{provider}'` (alphanumerics, `_`, `.` only) |
| 100 | +3. Truncate large data with helper functions from `utils.ts` |
| 101 | +4. `gen_ai.invoke_agent` for parent ops, `gen_ai.chat` for child ops |
| 102 | + |
| 103 | +## Checklist |
| 104 | + |
| 105 | +- [ ] Runtime-specific code placed only in that runtime's package |
| 106 | +- [ ] Added to `getAutoPerformanceIntegrations()` in correct order (Node.js) |
| 107 | +- [ ] Added to `getOpenTelemetryInstrumentationToPreload()` (Node.js with OTel) |
| 108 | +- [ ] Exported from appropriate package index |
| 109 | +- [ ] E2E tests added and verifying auto-instrumentation |
| 110 | +- [ ] Only used attributes from [Sentry Gen AI Conventions](https://getsentry.github.io/sentry-conventions/attributes/gen_ai/) |
| 111 | +- [ ] JSDoc says "enabled by default" or "not enabled by default" |
| 112 | +- [ ] Documented how to disable (if auto-enabled) |
| 113 | +- [ ] Verified OTel only patches when package imported (Node.js) |
| 114 | + |
| 115 | +## Reference Implementations |
| 116 | + |
| 117 | +- **Pattern 1 (Span Processors):** `packages/node/src/integrations/tracing/vercelai/` |
| 118 | +- **Pattern 2 (Client Wrapping):** `packages/node/src/integrations/tracing/openai/` |
| 119 | +- **Pattern 3 (Callback/Hooks):** `packages/node/src/integrations/tracing/langchain/` |
| 120 | + |
| 121 | +**When in doubt, follow the pattern of the most similar existing integration.** |
0 commit comments