Skip to content

feat(node): Add Mistral AI integration - #24243

Open
nicohrubec wants to merge 14 commits into
developfrom
nh/mistral-integration
Open

feat(node): Add Mistral AI integration#24243
nicohrubec wants to merge 14 commits into
developfrom
nh/mistral-integration

Conversation

@nicohrubec

@nicohrubec nicohrubec commented Sep 9, 2026

Copy link
Copy Markdown
Member

Adds a gen_ai integration for the @mistralai/mistralai v2 SDK.

  • Automatic: Sentry.mistralAIIntegration()
  • Manual: Sentry.instrumentMistralAiClient(client)

Instruments chat.complete/stream (gen_ai.chat), embeddings.create (gen_ai.embeddings), and agents.complete/stream (gen_ai.invoke_agent), including streaming, with recordInputs/recordOutputs (PII) controls. Provider mistral, origin auto.ai.mistral.

Trace from my local sample app:
Screenshot 2026-09-11 at 11 21 50

Sorry for the large PR and the AI integrations more generally could also use some refactors. However, to get this out soon I suggest to follow up on this with a broader sweep.

Add span-streaming (`traceLifecycle: 'stream'`) node integration tests
for a planned `@mistralai/mistralai` gen_ai integration, mirroring the
OpenAI suite. Covers chat, embeddings, agents (invoke_agent) and fim
(text_completion), across PII-off, PII-on and explicit-integration-option
variants.

These tests are expected to fail until the `mistralAIIntegration` /
`instrumentMistralClient` instrumentation is implemented (TDD step 1).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

size-limit report 📦

Path Size % Change Change
@sentry/browser 28.96 kB - -
@sentry/browser - with treeshaking flags 27.26 kB - -
@sentry/browser - with treeshaking flags tracing without tracing 27.15 kB - -
@sentry/browser (incl. Tracing) 50.48 kB - -
@sentry/browser (incl. Tracing + Span Streaming) 50.48 kB - -
@sentry/browser (incl. Tracing, Profiling) 53.47 kB - -
@sentry/browser (incl. Tracing, Replay) 89.98 kB - -
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 79.1 kB - -
@sentry/browser (incl. Tracing, Replay with Canvas) 94.68 kB - -
@sentry/browser (incl. Tracing, Replay, Feedback) 107.69 kB - -
@sentry/browser (incl. Feedback) 46.46 kB - -
@sentry/browser (incl. sendFeedback) 34.01 kB - -
@sentry/browser (incl. FeedbackAsync) 39.12 kB - -
@sentry/browser (incl. Metrics) 29.98 kB - -
@sentry/browser (incl. Logs) 30.24 kB - -
@sentry/browser (incl. Metrics & Logs) 30.91 kB - -
@sentry/react 30.72 kB - -
@sentry/react (incl. Tracing) 52.77 kB - -
@sentry/vue 36.2 kB - -
@sentry/vue (incl. Tracing) 52.71 kB - -
@sentry/svelte 28.98 kB - -
CDN Bundle 30.7 kB - -
CDN Bundle (incl. Tracing) 50.97 kB - -
CDN Bundle (incl. Logs, Metrics) 32.98 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) 52.95 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) 73.67 kB - -
CDN Bundle (incl. Tracing, Replay) 88.52 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) 90.48 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) 94.59 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) 96.59 kB - -
CDN Bundle - uncompressed 90.83 kB - -
CDN Bundle (incl. Tracing) - uncompressed 152.27 kB - -
CDN Bundle (incl. Logs, Metrics) - uncompressed 97.41 kB - -
CDN Bundle (incl. Tracing, Logs, Metrics) - uncompressed 158.24 kB - -
CDN Bundle (incl. Replay, Logs, Metrics) - uncompressed 226.82 kB - -
CDN Bundle (incl. Tracing, Replay) - uncompressed 271.84 kB - -
CDN Bundle (incl. Tracing, Replay, Logs, Metrics) - uncompressed 277.79 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 285.54 kB - -
CDN Bundle (incl. Tracing, Replay, Feedback, Logs, Metrics) - uncompressed 291.48 kB - -
@sentry/nextjs (client) 55.09 kB - -
@sentry/sveltekit (client) 50.88 kB - -
@sentry/core/server 37.13 kB - -
@sentry/core/browser 13.66 kB - -
@sentry/node 130.52 kB +0.56% +722 B 🔺
@sentry/node/import (ESM hook with diagnostics-channel injection) 81.94 kB +0.14% +110 B 🔺
@sentry/node - without tracing 89.72 kB +0.15% +131 B 🔺
@sentry/node - without channel injection 109.42 kB +0.69% +741 B 🔺
@sentry/aws-serverless 97.97 kB +0.16% +151 B 🔺
@sentry/cloudflare (withSentry) - minified 203.21 kB - -
@sentry/cloudflare (withSentry) 505.9 kB - -

View base workflow run

Instrument `@mistralai/mistralai` v2 with gen_ai spans, turning the
previously-failing integration tests green.

- Automatic `mistralAIIntegration()` via the orchestrion diagnostics
  channels (default-on in Node)
- Manual `instrumentMistralClient()` proxy for edge runtimes
- Covers chat, embeddings, agents (invoke_agent) and fim
  (text_completion), including streaming, with `recordInputs` /
  `recordOutputs` controls

Mistral's typed responses/usage are camelCase, so the response/stream
mapping reads `promptTokens`/`completionTokens`/`totalTokens` and
`choices[].finishReason` directly. `@mistralai/mistralai` v2 is ESM-only,
so the CJS test variants are marked `failsOnCjs`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread packages/server-utils/src/ai/mistral/streaming.ts
- Remove `failsOnCjs` from the Mistral suite: CJS auto-instrumentation
  works on a full build (it only failed under a partial local rebuild),
  so the tests pass in both ESM and CJS.
- Re-export `mistralAIIntegration` / `instrumentMistralClient` from the
  dependent SDK packages (aws-serverless, bun, elysia, deno,
  google-cloud-serverless, astro, cloudflare, vercel-edge) so the
  node-exports consistency check passes.
- Add `Mistral` to the Deno default-integrations snapshot.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread dev-packages/node-integration-tests/suites/tracing/mistral/test.ts
@nicohrubec nicohrubec changed the title test(node): Add failing integration tests for Mistral AI SDK feat(node): Add Mistral AI integration Sep 9, 2026
nicohrubec and others added 9 commits September 9, 2026 15:31
Exclude the fim endpoints for now — `text_completion` is not yet used by
any other AI integration, so defer it to a follow-up. Remaining scope:
chat, embeddings, and agents.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
`@mistralai/mistralai` v2 ships no CJS build, so CJS consumers load it via
`require(esm)`, whose auto-instrumentation is inconsistent across Node
versions (works on 24/26, fails on 22). The SDK's native mode is ESM, so
use `createEsmTests` and cover it there only.

Also give the embeddings mock a distinct id per call shape so the
single-input span is targeted unambiguously.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
`@langchain/mistralai` drives the `@mistralai/mistralai` SDK under the
hood, so with the LangChain integration active both it and
`mistralAIIntegration` would instrument the same call, producing two
spans. Add `Mistral` to LangChain's `SKIPPED_PROVIDERS`, matching the
existing OpenAI/Anthropic/Google handling. This also puts the previously
unused `MISTRAL_INTEGRATION_NAME` constant to use.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Record request tool definitions on `gen_ai.tool.definitions` (gated by
  recordInputs), restoring the `recordInputs` arg to
  `extractRequestAttributes`.
- Accumulate streamed tool calls by index (concatenating fragmented
  `function.arguments`) and emit them on `gen_ai.response.tool_calls`;
  non-streaming tool calls were already captured.
- Add `scenario-tools.mjs` + a test asserting tool definitions and tool
  calls for both streaming and non-streaming chat.

Brings Mistral to parity with the OpenAI integration for function tools.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Read the span-name token from the already-extracted request attributes
(model, or the agent name for invoke_agent spans) instead of re-reading
the raw params — mirroring the OpenAI integration. Removes the
`getModelForSpanName` helper. No change to emitted attributes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Hoist the single `recordOutputs` check to wrap both content and tool
  calls (was checked twice), matching the OpenAI streaming path.
- Drop the dead `?? ''` in tool-call argument accumulation (the first
  chunk already seeds it to '').
- Simplify the chunk unwrap to a single object guard.

No behavior change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Align `gen_ai.provider.name` (and the matching `sentry.origin`) with the
package-scope slug `mistralai` — consistent with how `@google/genai`
maps to `google_genai`, and with the existing LangChain path which
already reports Mistral as `mistralai`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…lAiClient

Align the manual instrumentation API with the other providers, which all
use the `instrument<Provider>AiClient` form (`instrumentOpenAiClient`,
`instrumentAnthropicAiClient`, `instrumentWorkersAiClient`). Renamed the
export and all per-package re-exports.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…tion

# Conflicts:
#	packages/deno/test/__snapshots__/mod.test.ts.snap

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 3 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit df92709. Configure here.

Comment thread packages/server-utils/src/ai/mistral/index.ts
Comment thread packages/server-utils/src/ai/mistral/index.ts
Comment thread packages/server-utils/src/ai/mistral/streaming.ts
nicohrubec and others added 2 commits September 11, 2026 15:32
- Set error status on the span when a Mistral stream throws mid-iteration,
  so failed streams no longer end as successful gen_ai spans.
- Decide streaming from the SDK method alone; `stream: true` on `complete`
  still returns a completion in v2, so it must not be wrapped as a stream.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The default-on Mistral AI integration grows the @sentry/node bundle past the current budgets (130.52 kB > 130, 109.42 kB > 109). Raise both limits.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@nicohrubec
nicohrubec marked this pull request as ready for review September 11, 2026 14:17
@nicohrubec
nicohrubec requested review from a team as code owners September 11, 2026 14:17
@nicohrubec
nicohrubec requested review from JPeer264, RulaKhaled, andreiborza, mydea and s1gr1d and removed request for a team September 11, 2026 14:17
Comment on lines +151 to +161
options.recordOutputs ?? false,
) as unknown as R;
} catch (error) {
span.setStatus({ code: SPAN_STATUS_ERROR, message: 'internal_error' });
span.end();
throw error;
}
})();
});

return wrapPromiseWithMethods(originalResult, instrumentedPromise);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: The manual instrumentation for chat.stream() returns a Promise instead of a direct async-iterable, breaking the native SDK contract and causing a TypeError for users who don't await the result.
Severity: HIGH

Suggested Fix

The instrumentation should return the async-iterable stream directly, without wrapping it in a Promise. The behavior should match the orchestrion-based integration (mistralAIIntegration), which patches the stream in-place and returns the original EventStream, preserving the native SDK's API contract.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.

Location: packages/server-utils/src/ai/mistral/index.ts#L135-L161

Potential issue: The manual instrumentation for the Mistral AI client,
`instrumentMistralAiClient`, alters the return type of streaming methods like
`chat.stream()`. The native SDK returns an async-iterable `EventStream` that can be
iterated over directly. However, the instrumented version wraps this in a promise,
returning a `Promise<AsyncGenerator>`. This change is not backward-compatible. Code that
follows the native SDK's usage pattern, such as `for await (const event of
client.chat.stream(...))`, will fail with a `TypeError` at runtime because the promise
returned by the instrumented function is not directly iterable. Users are forced to
change their code to `await` the result first before iterating.

Did we get this right? 👍 / 👎 to inform future reviews.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant