Skip to content

Commit 318dd52

Browse files
authored
Merge branch 'develop' into test/ai-pii-absence-assertions
2 parents a987ded + 7267c25 commit 318dd52

1,633 files changed

Lines changed: 53836 additions & 25238 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.agents/skills/add-ai-integration/SKILL.md

Lines changed: 41 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -6,116 +6,61 @@ argument-hint: <provider-name>
66

77
# Adding a New AI Integration
88

9-
## Decision Tree
9+
## Conventions First
1010

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)
11+
Span ops and attributes are specified outside this repo. Never invent or hardcode either:
4712

48-
## Token Accumulation
13+
- [gen_ai attributes](https://getsentry.github.io/sentry-conventions/attributes/gen_ai/) and [gen_ai ops](https://getsentry.github.io/sentry-conventions/ops/#gen_ai) — normative; import from `@sentry/conventions/attributes` and `@sentry/conventions/op`
14+
- [RFC 0153](https://github.com/getsentry/rfcs/blob/main/text/0153-decoupling-sentrys-generative-ai-conventions-from-open-telemetry.md) — why Sentry's gen-AI conventions diverge from the [OTel gen-ai semconv](https://opentelemetry.io/docs/specs/semconv/gen-ai/)
4915

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/`)
16+
Derive the op with `getGenAiSpanOp()` from `ai/core/utils.ts` rather than picking one by hand. `ai/core/gen-ai-attributes.ts` is for gap-fillers only — keys with no `@sentry/conventions` equivalent — so check it last, not first.
5217

53-
## Pattern 1: OTel Span Processors
18+
## Which Pattern
5419

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.
20+
```
21+
Does the SDK publish its own `diagnostics_channel` telemetry?
22+
|- YES (ai >= 7) -> Pattern 1: Native tracing channel
23+
+- NO -> Does the SDK expose callback/exporter hooks?
24+
|- YES (LangChain, Mastra) -> Pattern 3: Callback/Exporter
25+
+- NO (OpenAI, Anthropic, Google GenAI, ai < 7) -> Pattern 2: Orchestrion-injected channels
26+
```
7927

80-
Reference: `packages/node/src/integrations/tracing/langchain/`
28+
| Pattern | Use when | Reference |
29+
| -------------------------- | ------------------------------------------ | --------------------------------------------------------------- |
30+
| 1 — Native tracing channel | the SDK publishes to `diagnostics_channel` | `integrations/vercel-ai/vercel-ai-dc-subscriber.ts` |
31+
| 2 — Orchestrion channels | the SDK has no telemetry of its own | `integrations/openai.ts` + `orchestrion/config/openai.ts` |
32+
| 3 — Callback/exporter | the SDK exposes hooks or an exporter | `ai/langchain/`, `ai/mastra/` (exporter-shaped agent framework) |
8133

82-
## Auto-Instrumentation (Node.js)
34+
What the reference files won't tell you:
8335

84-
**Mandatory** for Node.js AI integrations. OTel only patches when the package is imported (zero cost if unused).
36+
- A provider can need two patterns at once: `vercelAIIntegration` subscribes to native `ai:telemetry` for `ai` >= 7 _and_ runs orchestrion injection for v4-v6.
37+
- Pattern 1 subscribers are safe to register unconditionally — subscribing is a no-op on SDK versions that never publish.
8538

86-
### Steps
39+
## Where The Code Goes
8740

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}/`
41+
- **Instrumentation** -> `packages/server-utils/src/ai/{provider}/`
42+
- **Integration** -> `packages/server-utils/src/integrations/{provider}.ts`
43+
- Runtime packages (`node`, `cloudflare`, `bun`, ...) re-export from `@sentry/server-utils` — they never define their own
44+
- Exception: Workers AI is client-wrapped in `packages/cloudflare/src/instrumentations/worker/instrumentEnv.ts`
9545

96-
## Key Rules
46+
## Gotchas
9747

98-
1. Respect `dataCollection.genAI` for recording input and output messages
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
48+
1. **Detect streaming from the result shape** — an async-iterable or the SDK's stream object — not from `params.stream`. Only the manual `instrument{Provider}Client()` API keys off `params.stream === true`.
49+
2. **Never set streamed response attributes by hand.** Accumulate into a `StreamResponseState` and call `endStreamSpan()` (`ai/openai/streaming.ts` for an async generator, `ai/anthropic-ai/streaming.ts` for a listener-based stream).
50+
3. **Never truncate message payloads.** Truncation was removed in v11 (#23045) and nothing downstream caps them; size limiting is server-side.
51+
4. **Never roll child token usage up onto parent spans.** Tree totals are computed product-side from the full span tree.
52+
5. **Never read `dataCollection.genAI` directly.** Gate input/output recording on `resolveAIRecordingOptions()`.
53+
6. **LangChain must be registered first** in `getTracingIntegrations()`, so it can disable the provider integrations before they instrument.
54+
7. Set `SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN = 'auto.ai.{provider}'` (alphanumerics, `_`, `.` only).
10255

10356
## Checklist
10457

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/`
58+
- [ ] Instrumentation in `src/ai/`, integration in `src/integrations/`, registered in `getTracingIntegrations()` (LangChain first)
59+
- [ ] Exported from `packages/server-utils/src/index.ts`, re-exported from the supported runtime packages
60+
- [ ] E2E tests in `dev-packages/node-integration-tests/suites/tracing/{provider}/` (and `cloudflare-integration-tests/` if supported)
61+
- [ ] Ops and attributes from `@sentry/conventions`, op derived via `getGenAiSpanOp()`
62+
- [ ] Recording gated on `resolveAIRecordingOptions()`; no truncation, no token rollup
63+
- [ ] JSDoc names the channels subscribed to, the supported SDK versions, and — for Pattern 2 — that it requires the Sentry runtime hook or bundler plugin
64+
- [ ] Patching happens only once the target package is imported (zero cost if unused)
12065

12166
**When in doubt, follow the pattern of the most similar existing integration.**

.cursor/BUGBOT.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,11 @@ Keep reviews high-signal. Prefer actionable, high-confidence findings over specu
6868
- `consoleSandbox(() => { console.warn(...) })` for intentional user-facing warnings (e.g. init-time misconfiguration messages). The `consoleSandbox` wrapper prevents the SDK's own console instrumentation from intercepting the call. Bare `console.*` calls outside very early init paths (e.g. before the logger is available) should be flagged.
6969
- Flag `url.full`, `url.query`, `http.target` or `request.query_string` being set from a URL that isn't filtered. Wrap the value in `filterCollectedUrl()` (or `filterCollectedUrlQuery()` for a bare query string), passing the `client` if one is in scope, so `dataCollection.urlQueryParams` applies. Values that can't contain a query (a bare pathname, a queue URL) are fine. The `sdk/no-unfiltered-url-attributes` lint rule catches direct attribute writes, so look for what it can't: URLs passed through a helper or variable first, deprecated aliases set next to a filtered attribute, and URLs on breadcrumbs or events instead of spans.
7070
- Flag span names built from a raw URL. Names follow `METHOD scheme://host/path` and must never contain a query string, so they need `stripUrlQueryAndFragment()`, not `filterCollectedUrl()`.
71+
- Flag a SQL statement that reaches telemetry unsanitized. `db.query.text`, `db.query.summary`, a DB span name, and a breadcrumb carrying a statement all have to come from `sanitizeSqlQuery()` or `sanitizeSqlQueryWithSummary()` (`@sentry/server-utils`). Inline literals are user data, and OTel allows collecting query text only once they are replaced with `?`. This is deliberately not gated on `dataCollection.databaseQueryData`, which does not cover query text.
72+
- Cover every place the statement lands, not only the span attribute. The two that get forgotten are the breadcrumb beside the span and the span name used when span streaming is off.
73+
- Sanitize each statement of a batch before joining them.
74+
- Pass the dialect. `toSqlDialect()` maps a driver or `db.system.name` value to one, and a missing dialect leaves MySQL and SQL Server values in the statement.
75+
- Leave Redis command text alone. It is not SQL and has its own redaction path.
7176
- Flag usage of the following APIs: `getCurrentScope()`, `getIsolationScope()`, `getClient()` if they are avoidable. Flag it with severity Low and acknowledge from the start that this is more a "is this necessary" check, rather than a rule violation.
7277
- Reason for flagging: Usage of these APIs is problematic for multi-client setups where either there is no "current" client/scope, or the wrong client might be used. Calling these APIs would create a current scope, thereby misleading any future calls to these APIs.
7378
- What to do instead: Use an existing reference to the scope or client. For example, this is possible in most `Integration` hooks.

.github/workflows/auto-fix-issue.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ jobs:
7777
7878
- name: Try to fix the issue with Claude
7979
id: triage
80-
uses: anthropics/claude-code-action@6b082c41935b4c8a3b8b0ef85ba4ba4d9eeb8975 # v1
80+
uses: anthropics/claude-code-action@a874e9ecd7bb36efdad65429c6b35815f5a08f10 # v1
8181
env:
8282
ANTHROPIC_BASE_URL: https://openrouter.ai/api
8383
with:

.github/workflows/auto-release.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ jobs:
4646
echo "version=$version" >> $GITHUB_OUTPUT
4747
4848
- name: Set up Node
49-
uses: actions/setup-node@v6
49+
uses: actions/setup-node@v7
5050
with:
5151
node-version-file: 'package.json'
5252

0 commit comments

Comments
 (0)