Skip to content

fix(mistralai): instrumentation for version 1.9+ compatibility#3376

Merged
nirga merged 4 commits intomainfrom
mistral-bug
Sep 14, 2025
Merged

fix(mistralai): instrumentation for version 1.9+ compatibility#3376
nirga merged 4 commits intomainfrom
mistral-bug

Conversation

@nirga
Copy link
Copy Markdown
Member

@nirga nirga commented Sep 13, 2025

Summary

Fixes #3375 - Resolves crash after mistralai package naming change in version 1.9+

Breaking Changes

  • ⚠️ Drop support for mistralai 0.x versions
  • ⚠️ Require mistralai >= 1.0.0

Changes Made

Import Updates

  • ✅ Updated imports from mistralai.models.chat_completion to mistralai.models
  • ✅ Updated all model imports: ChatCompletionResponse, AssistantMessage, UserMessage, etc.

Client API Migration

  • ✅ Replace deprecated MistralClient/MistralAsyncClient with unified Mistral client
  • ✅ Update method calls:
    • client.chat()client.chat.complete()
    • client.chat_stream()client.chat.stream()
    • client.embeddings(input=...)client.embeddings.create(inputs=...)
  • ✅ Add async method support: complete_async(), stream_async(), create_async()

Message Handling

  • ✅ Replace single ChatMessage with specific message types: UserMessage, AssistantMessage, SystemMessage
  • ✅ Update instrumentation to handle multiple message types

Test Updates

  • ✅ Update all test fixtures to use new Mistral() client
  • ✅ Update all test method calls to use new API structure
  • ✅ Fix parameter names (inputinputs for embeddings)

Test Plan

  • Basic instrumentation imports work
  • Client instrumentation setup succeeds
  • Basic chat completion tests pass
  • All streaming tests pass (may need cassette re-recording)
  • All async tests pass (may need cassette re-recording)
  • All embedding tests pass (may need cassette re-recording)

Note: Some tests may need cassette re-recording with the new API since the request/response format has changed in mistralai 1.9+.

🤖 Generated with Claude Code


Important

Update opentelemetry-instrumentation-mistralai for Mistral AI SDK 1.9+ compatibility, including import, client API, and test changes.

  • Breaking Changes:
    • Drop support for Mistral AI SDK 0.x versions.
    • Require Mistral AI SDK >= 1.0.0.
  • Import Updates:
    • Update imports from mistralai.models.chat_completion to mistralai.models.
    • Update model imports: ChatCompletionResponse, AssistantMessage, UserMessage, etc.
  • Client API Migration:
    • Replace MistralClient/MistralAsyncClient with Mistral client.
    • Update method calls: client.chat()client.chat.complete(), client.chat_stream()client.chat.stream(), client.embeddings(input=...)client.embeddings.create(inputs=...).
    • Add async method support: complete_async(), stream_async(), create_async().
  • Message Handling:
    • Replace ChatMessage with UserMessage, AssistantMessage, SystemMessage.
    • Update instrumentation for multiple message types.
  • Test Updates:
    • Update test fixtures to use new Mistral() client.
    • Update test method calls to new API structure.
    • Fix parameter names (inputinputs for embeddings).

This description was created by Ellipsis for 55175e3. You can customize this summary. It will automatically update as commits are pushed.


Summary by CodeRabbit

  • New Features

    • Support for Mistral AI SDK v1.x: updated chat and embeddings entrypoints, new message role types (user/assistant/system), and unified public models surface.
  • Bug Fixes

    • Streaming responses and event emission adapted to new chunk format and accumulate content correctly; request type mapping aligned for chat vs embeddings.
  • Chores

    • Bumped test/dev dependency to mistralai ^1.0.0 and refreshed test fixtures/cassettes to match new API shapes.

Fixes #3375

Breaking changes:
- Drop support for mistralai 0.x versions
- Require mistralai >= 1.0.0

Changes:
- Update imports from mistralai.models.* to mistralai.models
- Replace deprecated MistralClient with new Mistral client
- Update API method calls:
  - chat() → chat.complete()
  - chat_stream() → chat.stream()
  - embeddings(input=) → embeddings.create(inputs=)
- Add support for async methods: complete_async, stream_async, create_async
- Update message type handling for UserMessage, AssistantMessage, SystemMessage
- Update instrumentation to wrap new client structure
- Update tests to use new API

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Sep 13, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Updates instrumentation and tests for mistralai >= 1.0.0: replace legacy chat/embedding types and call sites with new mistralai.models types and methods (chat.complete/stream, embeddings.create), generalize wrapper targets for sync/async, and refresh test fixtures and cassettes to match new client wire formats.

Changes

Cohort / File(s) Change Summary
Instrumentation core
packages/opentelemetry-instrumentation-mistralai/opentelemetry/instrumentation/mistralai/__init__.py
Bump version gate to mistralai >= 1.0.0; switch imports to from mistralai.models import (...) (ChatCompletionChoice, AssistantMessage, UserMessage, SystemMessage, UsageInfo, EmbeddingResponse); update streaming accumulation and event emission to use new models; remap methods (complete/stream → CHAT, create → EMBEDDING); generalize wrapper/unwrap to dynamic mistralai.{module_name} and {ModuleName} with async variants.
Project test deps
packages/opentelemetry-instrumentation-mistralai/pyproject.toml
Test dependency bumped: mistralai = "^1.0.0" (was ^0.2.0).
Test setup / fixtures
packages/opentelemetry-instrumentation-mistralai/tests/conftest.py
Replace separate MistralClient/MistralAsyncClient with unified Mistral instantiation in fixtures; use API key from env.
Chat tests & cassettes
packages/opentelemetry-instrumentation-mistralai/tests/test_chat.py
packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/*
Replace ChatMessageUserMessage and import from mistralai.models; update synchronous/asynchronous call sites to chat.complete/chat.complete_async and streaming to chat.stream/chat.stream_async; adjust streaming assembly/data access (e.g., res.data.choices[0].delta.content), expected IDs/content, emitted gen_ai events, and cassette request/response formats/headers to match client v1.x and new streaming layout.
Embeddings tests & cassettes
packages/opentelemetry-instrumentation-mistralai/tests/test_embeddings.py
packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_embeddings/*
Change embeddings calls from embeddings(...)embeddings.create(...); rename arg inputinputs; update async variants to create_async; update expected response IDs and cassette bodies/headers to client v1.x formatting.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant App as Application
  participant Instr as OTEL Mistral Instrumentation
  participant Mistral as mistralai.Mistral
  participant Chat as mistralai.chat
  participant Emb as mistralai.embeddings

  rect #F5F8FF
    note right of Instr: Dynamic wrapping at import-time\n(chat.complete/stream, embeddings.create)
  end

  App->>Mistral: chat.complete(messages)
  Mistral->>Instr: intercepted sync call
  Instr->>Chat: complete(messages)
  Chat-->>Instr: response (ChatCompletionChoice, UsageInfo)
  Instr->>Instr: build events from AssistantMessage/UserMessage/SystemMessage
  Instr-->>App: response with emitted events

  App->>Mistral: chat.stream(messages)
  Mistral->>Instr: intercepted streaming call
  loop streaming chunks
    Chat-->>Instr: chunk (delta / p fields)
    Instr->>Instr: accumulate AssistantMessage (handle delta/p)
    Instr-->>App: partial events (optional)
  end
  Instr-->>App: final assembled result

  App->>Mistral: embeddings.create(inputs)
  Mistral->>Instr: intercepted call
  Instr->>Emb: create(inputs)
  Emb-->>Instr: EmbeddingResponse (UsageInfo)
  Instr-->>App: response with emitted events
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45–60 minutes

Possibly related PRs

Suggested reviewers

  • doronkopit5
  • galkleinman

Poem

Thump-thump I hop, my whiskers twitch,
New models land and methods switch.
Streams arrive in tidy parts,
Embeds create and log their starts.
A carrot patch of imports neat — hooray, the rabbit drums a beat! 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 4.55% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "fix(mistralai): instrumentation for version 1.9+ compatibility" succinctly summarizes the PR's primary purpose—making the instrumentation compatible with newer mistralai naming and API changes—and clearly signals the main intent to reviewers. It is concise, focused on the main change, and does not include extraneous noise.
Linked Issues Check ✅ Passed The diff implements the linked-issue requirements: imports were moved to mistralai.models, the instrumentation was migrated to the unified Mistral client and updated method names (chat.complete/stream, embeddings.create with inputs), async variants were added, ChatMessage usage was replaced with UserMessage/AssistantMessage/SystemMessage, and tests/cassettes were updated to reflect the new API. These code changes address the import crash and API-mapping objectives in #3375 at the code level. Per the PR notes and cassette diffs, streaming/async fixtures may still need CI verification and possible re-recording, but the core coding objectives appear implemented.
Out of Scope Changes Check ✅ Passed All modified files are confined to the mistralai instrumentation package and its tests/cassettes and are directly related to the migration (imports, client API calls, message typing, fixtures, and cassettes); I do not see unrelated modules or functionality touched by this PR. The pyproject dependency adjustment and cassette rewrites are consistent with the stated objective rather than scope creep.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch mistral-bug

Comment @coderabbitai help to get the list of available commands and usage tips.

@nirga nirga changed the title 🐛 Fix mistralai instrumentation for version 1.9+ compatibility 🐛 fix(mistralai): instrumentation for version 1.9+ compatibility Sep 13, 2025
Copy link
Copy Markdown
Contributor

@ellipsis-dev ellipsis-dev Bot left a comment

Choose a reason for hiding this comment

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

Important

Looks good to me! 👍

Reviewed everything up to 5c5f746 in 1 minute and 4 seconds. Click for details.
  • Reviewed 416 lines of code in 5 files
  • Skipped 1 files when reviewing.
  • Skipped posting 2 draft comments. View those below.
  • Modify your settings and rules to customize what types of comments Ellipsis leaves. And don't forget to react with 👍 or 👎 to teach Ellipsis.
1. packages/opentelemetry-instrumentation-mistralai/opentelemetry/instrumentation/mistralai/__init__.py:100
  • Draft comment:
    For embedding requests, the parameter name is now 'inputs' in the new API. Please update this line from using kwargs.get('input') to kwargs.get('inputs') (and update subsequent usage accordingly).
  • Reason this comment was not posted:
    Comment was not on a location in the diff, so it can't be submitted as a review comment.
2. packages/opentelemetry-instrumentation-mistralai/opentelemetry/instrumentation/mistralai/__init__.py:321
  • Draft comment:
    Update the key from 'input' to 'inputs' when retrieving the embedding input in _emit_message_events to match the updated API.
  • Reason this comment was not posted:
    Comment was not on a location in the diff, so it can't be submitted as a review comment.

Workflow ID: wflow_oYN8GbLwMIntejVo

You can customize Ellipsis by changing your verbosity settings, reacting with 👍 or 👎, replying to comments, or adding code review rules.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
packages/opentelemetry-instrumentation-mistralai/pyproject.toml (1)

21-27: Pin the optional runtime extra to mistralai>=1.0.0 to prevent accidental 0.x installs.

Right now only the test group constrains mistralai. If users install with the “instruments” extra, Poetry may resolve mistralai 0.x. Add an optional dependency with a version floor and keep the extra referencing it.

 [tool.poetry.dependencies]
 python = ">=3.9,<4"
 opentelemetry-api = "^1.28.0"
 opentelemetry-instrumentation = ">=0.50b0"
 opentelemetry-semantic-conventions = ">=0.50b0"
 opentelemetry-semantic-conventions-ai = "^0.4.13"
+mistralai = { version = ">=1.0.0", optional = true }

 [tool.poetry.extras]
-instruments = ["mistralai"]
+instruments = ["mistralai"]

Also applies to: 47-51

packages/opentelemetry-instrumentation-mistralai/opentelemetry/instrumentation/mistralai/__init__.py (2)

100-121: Embeddings input key mismatch: use inputs (1.x) instead of input.

This breaks spans’ prompt attributes in legacy mode and user-message events in event mode for embeddings. Support inputs and keep a fallback for input.

-        else:
-            input = kwargs.get("input")
-
-            if isinstance(input, str):
+        else:
+            # mistralai>=1.0.0 uses 'inputs'; keep 'input' as fallback
+            inputs = kwargs.get("inputs", kwargs.get("input"))
+
+            if isinstance(inputs, str):
                 _set_span_attribute(
                     span, f"{SpanAttributes.LLM_PROMPTS}.0.role", "user"
                 )
                 _set_span_attribute(
-                    span, f"{SpanAttributes.LLM_PROMPTS}.0.content", input
+                    span, f"{SpanAttributes.LLM_PROMPTS}.0.content", inputs
                 )
-            else:
-                for index, prompt in enumerate(input):
+            elif isinstance(inputs, (list, tuple)):
+                for index, prompt in enumerate(inputs):
                     _set_span_attribute(
                         span,
                         f"{SpanAttributes.LLM_PROMPTS}.{index}.role",
                         "user",
                     )
                     _set_span_attribute(
                         span,
                         f"{SpanAttributes.LLM_PROMPTS}.{index}.content",
                         prompt,
                     )
+            else:
+                return

321-327: Emit embeddings user-message events from inputs and handle positional args.

args[0] is the model when passed positionally; the actual inputs would be args[1]. Also migrate to inputs with a fallback to input.

-    elif method_wrapped == "mistralai.embeddings":
-        embedding_input = args[0] if len(args) > 0 else kwargs.get("input", [])
-        if isinstance(embedding_input, str):
-            emit_event(MessageEvent(content=embedding_input, role="user"), event_logger)
-        elif isinstance(embedding_input, list):
-            for prompt in embedding_input:
-                emit_event(MessageEvent(content=prompt, role="user"), event_logger)
+    elif method_wrapped == "mistralai.embeddings":
+        # Embeddings.create(model, inputs, ...)
+        embedding_inputs = kwargs.get("inputs", kwargs.get("input"))
+        if embedding_inputs is None and len(args) >= 2:
+            embedding_inputs = args[1]
+        if isinstance(embedding_inputs, str):
+            emit_event(MessageEvent(content=embedding_inputs, role="user"), event_logger)
+        elif isinstance(embedding_inputs, (list, tuple)):
+            for prompt in embedding_inputs:
+                emit_event(MessageEvent(content=prompt, role="user"), event_logger)
🧹 Nitpick comments (4)
packages/opentelemetry-instrumentation-mistralai/tests/conftest.py (1)

55-55: Trim trailing whitespace in decorator.

Minor flake8 nit on Line 55 (@pytest.fixture ).

-@pytest.fixture  
+@pytest.fixture
packages/opentelemetry-instrumentation-mistralai/opentelemetry/instrumentation/mistralai/__init__.py (3)

231-234: Don’t overwrite assistant role with None during streaming accumulation.

Streaming deltas often omit role after the first chunk. Preserve the existing role unless a new non-empty role is provided.

-            accumulated_response.choices[idx].message.role = choice.delta.role
+            if getattr(choice.delta, "role", None):
+                accumulated_response.choices[idx].message.role = choice.delta.role

Apply the same change in the async accumulator.

Also applies to: 273-276


305-313: More robust chat message extraction when callers use positional args.

If model is passed positionally, messages is args[1]. Current code reads args[0].

-        messages = args[0] if len(args) > 0 else kwargs.get("messages", [])
+        messages = kwargs.get("messages")
+        if messages is None:
+            messages = args[1] if len(args) >= 2 else []

518-525: Satisfy linter: don’t keep an unused kwargs in _uninstrument.

Also makes the intent clear.

-    def _uninstrument(self, **kwargs):
+    def _uninstrument(self, **_):
         for wrapped_method in WRAPPED_METHODS:
             wrap_method = wrapped_method.get("method")
             module_name = wrapped_method.get("module")
             
             unwrap(f"mistralai.{module_name}.{module_name.capitalize()}", wrap_method)
             unwrap(f"mistralai.{module_name}.{module_name.capitalize()}", f"{wrap_method}_async")
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 3a5a517 and 5c5f746.

⛔ Files ignored due to path filters (1)
  • packages/opentelemetry-instrumentation-mistralai/poetry.lock is excluded by !**/*.lock
📒 Files selected for processing (5)
  • packages/opentelemetry-instrumentation-mistralai/opentelemetry/instrumentation/mistralai/__init__.py (6 hunks)
  • packages/opentelemetry-instrumentation-mistralai/pyproject.toml (1 hunks)
  • packages/opentelemetry-instrumentation-mistralai/tests/conftest.py (2 hunks)
  • packages/opentelemetry-instrumentation-mistralai/tests/test_chat.py (13 hunks)
  • packages/opentelemetry-instrumentation-mistralai/tests/test_embeddings.py (6 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: Store API keys only in environment variables/secure vaults; never hardcode secrets in code
Use Flake8 for code linting and adhere to its rules

Files:

  • packages/opentelemetry-instrumentation-mistralai/tests/conftest.py
  • packages/opentelemetry-instrumentation-mistralai/opentelemetry/instrumentation/mistralai/__init__.py
  • packages/opentelemetry-instrumentation-mistralai/tests/test_embeddings.py
  • packages/opentelemetry-instrumentation-mistralai/tests/test_chat.py
🧬 Code graph analysis (3)
packages/opentelemetry-instrumentation-mistralai/opentelemetry/instrumentation/mistralai/__init__.py (5)
packages/opentelemetry-semantic-conventions-ai/opentelemetry/semconv_ai/__init__.py (1)
  • LLMRequestTypeValues (293-298)
packages/opentelemetry-instrumentation-google-generativeai/opentelemetry/instrumentation/google_generativeai/__init__.py (2)
  • _wrap (192-246)
  • _uninstrument (291-298)
packages/opentelemetry-instrumentation-vertexai/opentelemetry/instrumentation/vertexai/__init__.py (2)
  • _wrap (264-314)
  • _uninstrument (359-366)
packages/opentelemetry-instrumentation-ollama/opentelemetry/instrumentation/ollama/__init__.py (2)
  • _wrap (275-345)
  • _uninstrument (526-537)
packages/opentelemetry-instrumentation-watsonx/opentelemetry/instrumentation/watsonx/__init__.py (2)
  • _wrap (539-621)
  • _uninstrument (712-717)
packages/opentelemetry-instrumentation-mistralai/tests/test_embeddings.py (1)
packages/opentelemetry-instrumentation-mistralai/tests/conftest.py (2)
  • mistralai_client (51-52)
  • mistralai_async_client (56-57)
packages/opentelemetry-instrumentation-mistralai/tests/test_chat.py (1)
packages/opentelemetry-instrumentation-mistralai/tests/conftest.py (2)
  • mistralai_client (51-52)
  • mistralai_async_client (56-57)
🪛 Ruff (0.12.2)
packages/opentelemetry-instrumentation-mistralai/opentelemetry/instrumentation/mistralai/__init__.py

518-518: Unused method argument: kwargs

(ARG002)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Lint
  • GitHub Check: Test Packages (3.12)
  • GitHub Check: Test Packages (3.10)
  • GitHub Check: Test Packages (3.11)
  • GitHub Check: Build Packages (3.11)
🔇 Additional comments (4)
packages/opentelemetry-instrumentation-mistralai/pyproject.toml (1)

35-35: Version floor bump to mistralai>=1.0.0 for tests is correct.

Matches the PR’s objective to drop 0.x. No concerns here.

packages/opentelemetry-instrumentation-mistralai/tests/conftest.py (1)

6-6: Unified Mistral client import LGTM.

Switching to from mistralai import Mistral aligns with 1.x.

packages/opentelemetry-instrumentation-mistralai/tests/test_embeddings.py (1)

16-19: API migration to embeddings.create(inputs=...) looks good.

Calls and parameter names match mistralai 1.x.

Also applies to: 52-55, 95-98, 139-142, 176-179, 233-236

packages/opentelemetry-instrumentation-mistralai/tests/test_chat.py (1)

2-2: Chat API migration (UserMessage, .complete/.stream and async variants) LGTM.

Changes are consistent and assertions align with the new surface.

Also applies to: 17-22, 63-68, 116-121, 165-170, 215-220, 272-277, 326-331, 374-379, 428-433, 479-484, 527-532, 583-588

@nirga nirga changed the title 🐛 fix(mistralai): instrumentation for version 1.9+ compatibility fix(mistralai): instrumentation for version 1.9+ compatibility Sep 13, 2025
…ibility

- Update imports from deprecated mistralai.models.chat_completion to mistralai.models
- Replace deprecated MistralClient/MistralAsyncClient with unified Mistral client
- Fix streaming response handling for new CompletionEvent.data structure
- Resolve async streaming coroutine issues by properly awaiting stream_async calls
- Add support for both 'input' and 'inputs' parameters in embeddings API
- Update all tests and VCR cassettes for new API structure
- Fix async embeddings event logging by using create_async method
- Update dependency from mistralai ^0.2.0 to ^1.0.0
- All 18 tests passing with full instrumentation functionality preserved

Fixes #3375

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@ellipsis-dev ellipsis-dev Bot left a comment

Choose a reason for hiding this comment

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

Caution

Changes requested ❌

Reviewed f2bec07 in 3 minutes and 26 seconds. Click for details.
  • Reviewed 5735 lines of code in 21 files
  • Skipped 1 files when reviewing.
  • Skipped posting 7 draft comments. View those below.
  • Modify your settings and rules to customize what types of comments Ellipsis leaves. And don't forget to react with 👍 or 👎 to teach Ellipsis.
1. packages/opentelemetry-instrumentation-mistralai/opentelemetry/instrumentation/mistralai/__init__.py:100
  • Draft comment:
    Consider renaming the local variable 'input' to avoid shadowing the built‐in function. For example, use 'prompt_input' instead.
  • Reason this comment was not posted:
    Comment was on unchanged code.
2. packages/opentelemetry-instrumentation-mistralai/opentelemetry/instrumentation/mistralai/__init__.py:214
  • Draft comment:
    The check 'chunk_data = res.data if hasattr(res, 'data') else res' handles both new and legacy response formats. Adding a clarifying comment here would help future maintainers understand this dual handling.
  • Reason this comment was not posted:
    Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 0% vs. threshold = 50% While the code does handle two different response formats, the code is already quite clear with its comment "Handle new CompletionEvent structure with .data attribute". The pattern is simple and self-documenting. Adding more documentation would be redundant and not add significant value. The comment is purely informational and doesn't suggest any actual code changes. The comment does point out an important compatibility feature that future maintainers should be aware of. Documentation can be valuable for complex compatibility handling. However, the existing inline comment already explains the purpose clearly and concisely. The code pattern is simple enough that additional documentation would be redundant. Delete the comment since it's purely informational and the code already has a clear explanatory comment.
3. packages/opentelemetry-instrumentation-mistralai/tests/test_embeddings.py:38
  • Draft comment:
    Embeddings tests now compare a new static response ID. Ensure these values are updated with cassette re‐recordings; consider if a more flexible check might be less brittle.
  • Reason this comment was not posted:
    Comment looked like it was already resolved.
4. packages/opentelemetry-instrumentation-mistralai/tests/test_chat.py:621
  • Draft comment:
    The helper 'assert_message_in_logs' checks for empty expected content by evaluating the dict’s truthiness. Confirm that using an empty dict to indicate no log body is intentionally designed and robust.
  • Reason this comment was not posted:
    Comment was not on a location in the diff, so it can't be submitted as a review comment.
5. packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_embeddings/test_mistral_async_embeddings_with_events_with_content.yaml:3
  • Draft comment:
    The JSON string in the body field is split across two lines ("Tell" on line 3 and "me a joke about Traceloop" on line 4). Please ensure that this newline is intentional, as it may cause parsing issues.
  • Reason this comment was not posted:
    Comment looked like it was already resolved.
6. packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_embeddings/test_mistral_async_embeddings_with_events_with_no_content.yaml:3
  • Draft comment:
    The JSON string in the body field appears to be split across lines ("Tell" on line 3 and "me a joke about Traceloop" on line 4). This might cause parsing issues. Please ensure the string is kept as a single continuous line.
  • Reason this comment was not posted:
    Comment was on unchanged code.
7. packages/opentelemetry-instrumentation-mistralai/tests/test_chat.py:412
  • Draft comment:
    Typographical/Graammatical Issue: The list of actions in the joke description uses inconsistent verb forms: 'monitor', 'span', and 'distributed'. Consider changing 'distributed' to 'distribute' to maintain parallel structure.
  • Reason this comment was not posted:
    Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 10% vs. threshold = 50% This is a test file containing mock responses. The grammatical issue is real but very minor. The inconsistent verb form doesn't affect functionality. Since this is test data and not user-facing content, the grammatical precision is not critical. The rules say not to make comments that are obvious or unimportant. The grammar issue could make the test less readable or professional. Bad grammar in tests could set a poor example for code quality standards. While code quality is important, this is a very minor grammatical issue in test data that doesn't affect functionality or test clarity. The rules specifically say not to make unimportant comments. Delete this comment as it points out a minor grammatical issue in test data that doesn't meaningfully impact code quality or functionality.

Workflow ID: wflow_DFw1ccFIZo9WH7xj

You can customize Ellipsis by changing your verbosity settings, reacting with 👍 or 👎, replying to comments, or adding code review rules.

assert (
mistral_span.attributes.get("gen_ai.response.id")
== "d5f25c4c1e29441db526ce7db3400010"
== "e9459fcd56c742e0875167c9926c6aae"
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.

The test asserts a hard-coded response ID. These static values may change with upstream API modifications, so be sure to re-record cassettes as needed or consider a less brittle comparison.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 17

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
packages/opentelemetry-instrumentation-mistralai/opentelemetry/instrumentation/mistralai/__init__.py (1)

213-237: Streaming assembly can crash on None deltas and may overwrite role with None

choice.delta.content may be None (or absent) on some chunks; concatenating into a str raises TypeError. Also, unconditionally setting role can erase the initial "assistant" role. Guard both.

Apply:

-            accumulated_response.choices[idx].finish_reason = choice.finish_reason
-            accumulated_response.choices[idx].message.content += choice.delta.content
-            accumulated_response.choices[idx].message.role = choice.delta.role
+            accumulated_response.choices[idx].finish_reason = choice.finish_reason
+            # Some chunks omit content/role; guard to avoid TypeError and role loss.
+            delta = getattr(choice, "delta", None)
+            if delta is not None:
+                piece = getattr(delta, "content", None)
+                if piece:
+                    accumulated_response.choices[idx].message.content += piece
+                role_piece = getattr(delta, "role", None)
+                if role_piece is not None:
+                    accumulated_response.choices[idx].message.role = role_piece

Repeat the same change in the async accumulator.

Also applies to: 258-282

packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_async_streaming_chat_legacy.yaml (2)

1-21: Add VCR scrubbers globally for cassettes.

Enforce scrubbing at the framework level so future recordings are safe.

Example (pytest + vcrpy, in tests/conftest.py):

import json, re

def _strip_stream_noise(body: str) -> str:
    # Redact SSE JSON lines
    out = []
    for line in body.splitlines():
        if line.startswith("data: "):
            try:
                obj = json.loads(line[len("data: "):])
                for k in ("p",):
                    obj.pop(k, None)
                if "id" in obj:
                    obj["id"] = "<redacted-id>"
                if "created" in obj:
                    obj["created"] = 0
                line = "data: " + json.dumps(obj, separators=(",", ":"))
            except Exception:
                pass
        out.append(line)
    return "\n".join(out)

def before_record_response(response):
    headers = response["headers"]
    for h in ["set-cookie", "mistral-correlation-id", "x-kong-request-id",
              "cf-ray", "x-envoy-upstream-service-time",
              "x-kong-proxy-latency", "x-kong-upstream-latency"]:
        headers.pop(h, None)
    # Normalize event-stream body if present
    body = response.get("body", {}).get("string")
    if body and isinstance(body, str) and "text/event-stream" in headers.get("content-type", [""])[0]:
        response["body"]["string"] = _strip_stream_noise(body)
    return response

def before_record_request(request):
    request["headers"].pop("user-agent", None)
    request["headers"].pop("authorization", None)  # defensive
    return request

1-363: Critical: exposed secret(s) & sensitive headers found in VCR cassettes — immediate remediation required

  • Definitive leak: packages/opentelemetry-instrumentation-haystack/tests/cassettes/test_simple_pipeline/test_haystack.yaml (line 14) contains a non-redacted api_key — rotate/cancel the key now, remove it from the file, and purge it from git history.
  • Widespread sensitive headers/IDs (Set-Cookie, CF-RAY, mistral-correlation-id, x-kong-request-id, Authorization-like tokens, api_key/access_token, sk-*) appear across many cassettes under packages/**/tests/cassettes (example: packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_async_streaming_chat_legacy.yaml lines ~323–355).
  • Required actions: rotate exposed credentials; redact/replace sensitive values in all affected cassettes (use FILTERED placeholders); rewrite/regenerate cassettes; purge secrets from git history (git-filter-repo/BFG); add VCR sanitizers and CI/pre-commit checks to prevent future commits of secrets.
♻️ Duplicate comments (9)
packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_async_streaming_chat_with_events_with_no_content.yaml (1)

212-237: Duplicate: scrub Set-Cookie and volatile headers.

Same concern and fix as noted in the embeddings cassette.

packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_chat_with_events_with_no_content.yaml (1)

46-85: Duplicate: scrub Set-Cookie and volatile headers.

Same concern and fix as above.

packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_chat_with_events_with_content.yaml (1)

45-83: Duplicate: scrub Set-Cookie and volatile headers.

Same concern and fix as above.

packages/opentelemetry-instrumentation-mistralai/tests/test_embeddings.py (1)

16-19: Verify instrumentor extracts embeddings “inputs” (with fallback to “input”).

Past issue: extraction only checked kwargs["input"], missing “inputs”. Please confirm the fix in the instrumentor to avoid empty prompts.

#!/bin/bash
# Verify embeddings argument extraction supports both "inputs" and "input"
rg -nC2 -g 'packages/opentelemetry-instrumentation-mistralai/**/__init__.py' \
  -e 'kwargs\.get\(\s*[\'"]inputs[\'"]\s*\)' \
  -e 'kwargs\.get\(\s*[\'"]input[\'"]\s*\)'

Also applies to: 52-55, 95-98, 139-142, 176-179, 233-236

packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_streaming_chat_with_events_with_no_content.yaml (2)

405-429: Duplicate: scrub Set-Cookie and volatile headers.

Same concern and fix as above.


20-20: Duplicate: ignore “#stream” URI fragment in matching.

Same stabilization advice as the other streaming cassette.

packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_async_streaming_chat_with_events_with_content.yaml (2)

307-332: Duplicate: scrub Set-Cookie and volatile headers.

Same concern and fix as above.


20-20: Duplicate: ignore “#stream” URI fragment in matching.

Same stabilization advice as above.

packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_async_chat_with_events_with_content.yaml (1)

48-86: Duplicate: scrub Set-Cookie and volatile headers.

Same concern and fix as above.

🧹 Nitpick comments (16)
packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_streaming_chat_legacy.yaml (1)

329-331: Optional: reduce cassette churn by normalizing volatile headers.

Date, cf-cache-status, and latency headers change frequently and create noisy diffs. Consider filtering or normalizing them.

Example:

-      Date:
-      - Sat, 13 Sep 2025 10:31:32 GMT
+      Date:
+      - <REDACTED>
-      cf-cache-status:
-      - DYNAMIC
+      cf-cache-status:
+      - <REDACTED>

Also applies to: 347-352

packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_embeddings/test_mistral_embeddings_with_events_with_content.yaml (3)

18-18: Stabilize/strip User-Agent in recorded requests.

The pinned UA (mistral-client-python/1.9.10) will churn on client bumps. Configure VCR to filter this header or normalize it in a before_record hook.


109-117: Strip volatile infra headers to reduce churn.

CF-RAY and Date vary per request; exclude them via filter_headers to stabilize replays.


136-159: Scrub correlation/latency/rate-limit headers.

mistral-correlation-id, x-kong-, x-envoy-, and x-ratelimit-* are volatile and unnecessary for assertions. Filter them out globally.

I can open a follow-up PR to add a shared VCR config that filters these headers repo-wide.

packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_chat_legacy.yaml (1)

3-4: Confirm embeddings request param naming is aligned (input vs inputs).

The PR notes embeddings.create(inputs=...), but several cassettes still show "input". If the client maps correctly at transport, fine—otherwise re-record after ensuring inputs is used (or adapt assertions).

packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_async_streaming_chat_with_events_with_no_content.yaml (1)

20-20: Stabilize URI matching for “#stream” fragments.

Fragments aren’t sent in HTTP requests; relying on them in recorded URIs can break on client changes. Consider a VCR matcher that ignores fragments and matches on method/host/path/query/body only.

packages/opentelemetry-instrumentation-mistralai/tests/test_embeddings.py (2)

38-40: Avoid brittle assertions on gen_ai.response.id (stable across re-recordings).

Exact IDs change whenever cassettes are re-recorded. Assert presence/shape instead of hardcoding values.

-    assert (
-        mistral_span.attributes.get("gen_ai.response.id")
-        == "cc93a06d10244e07a4b2604c20855c61"
-    )
+    resp_id = mistral_span.attributes.get("gen_ai.response.id")
+    assert isinstance(resp_id, str) and len(resp_id) >= 16
@@
-    assert (
-        mistral_span.attributes.get("gen_ai.response.id")
-        == "e87386ab58e64d8c8c9f5b5175d7b1a9"
-    )
+    resp_id = mistral_span.attributes.get("gen_ai.response.id")
+    assert isinstance(resp_id, str) and len(resp_id) >= 16
@@
-    assert (
-        mistral_span.attributes.get("gen_ai.response.id")
-        == "233321a98ca941b1a412c4c2a74e6a8d"
-    )
+    resp_id = mistral_span.attributes.get("gen_ai.response.id")
+    assert isinstance(resp_id, str) and len(resp_id) >= 16
@@
-    assert (
-        mistral_span.attributes.get("gen_ai.response.id")
-        == "d5656d6c70804024b2e0729a2f30ad55"
-    )
+    resp_id = mistral_span.attributes.get("gen_ai.response.id")
+    assert isinstance(resp_id, str) and len(resp_id) >= 16
@@
-    assert (
-        mistral_span.attributes.get("gen_ai.response.id")
-        == "fd94466331664a4d8b2e3ff9bedd24f2"
-    )
+    resp_id = mistral_span.attributes.get("gen_ai.response.id")
+    assert isinstance(resp_id, str) and len(resp_id) >= 16
@@
-    assert (
-        mistral_span.attributes.get("gen_ai.response.id")
-        == "41c2e6bc6d4a463884a0729196c565b8"
-    )
+    resp_id = mistral_span.attributes.get("gen_ai.response.id")
+    assert isinstance(resp_id, str) and len(resp_id) >= 16

Also applies to: 70-72, 113-115, 161-163, 194-196, 251-253


52-56: Consider not asserting full embedding vectors in logs (size/noise).

Asserting equality on entire embeddings is heavy and fragile. Prefer asserting type/shape (e.g., list with length > 0) or hashed preview.

Also applies to: 176-180

packages/opentelemetry-instrumentation-mistralai/tests/test_chat.py (2)

347-349: Fix copy/paste: references to “async ollama chat”

These comments should say “async mistral chat,” not “ollama.” Keeps tests accurate and avoids confusion.

Apply:

-    # For some reason, async ollama chat doesn't report prompt token usage back
+    # For some reason, async mistral chat doesn't report prompt token usage back

Also applies to: 387-389, 440-442


349-354: Guard arithmetic when prompt tokens may be missing

If LLM_USAGE_PROMPT_TOKENS isn’t set (None), adding it will raise TypeError. Make the assertion resilient.

Apply:

-    ) == mistral_span.attributes.get(
-        SpanAttributes.LLM_USAGE_COMPLETION_TOKENS
-    ) + mistral_span.attributes.get(SpanAttributes.LLM_USAGE_PROMPT_TOKENS)
+    ) == (mistral_span.attributes.get(SpanAttributes.LLM_USAGE_COMPLETION_TOKENS) or 0) \
+        + (mistral_span.attributes.get(SpanAttributes.LLM_USAGE_PROMPT_TOKENS) or 0)

Also applies to: 389-394, 443-447, 501-506, 541-546, 595-599

packages/opentelemetry-instrumentation-mistralai/opentelemetry/instrumentation/mistralai/__init__.py (3)

465-469: Redundant await branches in _awrap

Both branches await the same call. Simplify.

Apply:

-    if to_wrap.get("streaming"):
-        response = await wrapped(*args, **kwargs)
-    else:
-        response = await wrapped(*args, **kwargs)
+    response = await wrapped(*args, **kwargs)

421-433: Consider recording exceptions and setting span status on errors

Currently, exceptions bypass status/exception recording for non-streaming paths; spans may end without error status. Wrap handling with try/except/finally for non-streaming; for streaming, record+end on exception before re-raise.

Would you like me to draft a safe try/except structure that avoids ending spans prematurely for streaming?

Also applies to: 476-482


60-62: Trailing whitespace/style nit

Minor trailing space after "chat", keep Flake8/Ruff happy.

-        "module": "chat", 
+        "module": "chat",
packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_async_streaming_chat_legacy.yaml (3)

26-315: Optional: strip volatile “p” fields and dynamic ids/timestamps from SSE chunks for stability.

These vary run-to-run and bloat diffs; unless asserted by tests, remove or redact via VCR hooks.

I can add a VCR before_record_response hook to JSON-parse SSE lines and drop keys like "p", and redact "id"/"created".


330-352: Optional: drop highly volatile headers (Date, cf-cache-status, latency headers).

They add churn without test value.

-      Date:
-      - Sat, 13 Sep 2025 10:31:32 GMT
-      cf-cache-status:
-      - DYNAMIC
-      x-envoy-upstream-service-time:
-      - '61'
-      x-kong-proxy-latency:
-      - '7'
-      x-kong-upstream-latency:
-      - '61'

Also applies to: 347-355, 358-358


18-18: Optional: filter User-Agent from matching to avoid re-recording when client version changes.

Helps keep cassettes stable across mistral-client-python upgrades.

I can update VCR config to filter_headers=["user-agent"] and exclude it from matchers.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 5c5f746 and f2bec07.

⛔ Files ignored due to path filters (1)
  • packages/opentelemetry-instrumentation-mistralai/poetry.lock is excluded by !**/*.lock
📒 Files selected for processing (21)
  • packages/opentelemetry-instrumentation-mistralai/opentelemetry/instrumentation/mistralai/__init__.py (10 hunks)
  • packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_async_chat_legacy.yaml (2 hunks)
  • packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_async_chat_with_events_with_content.yaml (2 hunks)
  • packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_async_chat_with_events_with_no_content.yaml (2 hunks)
  • packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_async_streaming_chat_legacy.yaml (2 hunks)
  • packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_async_streaming_chat_with_events_with_content.yaml (2 hunks)
  • packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_async_streaming_chat_with_events_with_no_content.yaml (2 hunks)
  • packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_chat_legacy.yaml (2 hunks)
  • packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_chat_with_events_with_content.yaml (2 hunks)
  • packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_chat_with_events_with_no_content.yaml (2 hunks)
  • packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_streaming_chat_legacy.yaml (2 hunks)
  • packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_streaming_chat_with_events_with_content.yaml (2 hunks)
  • packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_streaming_chat_with_events_with_no_content.yaml (2 hunks)
  • packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_embeddings/test_mistral_async_embeddings_legacy.yaml (2 hunks)
  • packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_embeddings/test_mistral_async_embeddings_with_events_with_content.yaml (2 hunks)
  • packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_embeddings/test_mistral_async_embeddings_with_events_with_no_content.yaml (2 hunks)
  • packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_embeddings/test_mistral_embeddings_legacy.yaml (2 hunks)
  • packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_embeddings/test_mistral_embeddings_with_events_with_content.yaml (2 hunks)
  • packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_embeddings/test_mistral_embeddings_with_events_with_no_content.yaml (2 hunks)
  • packages/opentelemetry-instrumentation-mistralai/tests/test_chat.py (29 hunks)
  • packages/opentelemetry-instrumentation-mistralai/tests/test_embeddings.py (12 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/cassettes/**/*.{yaml,yml,json}

📄 CodeRabbit inference engine (CLAUDE.md)

Never commit secrets or PII in VCR cassettes; scrub sensitive data

Files:

  • packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_embeddings/test_mistral_async_embeddings_legacy.yaml
  • packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_chat_with_events_with_content.yaml
  • packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_embeddings/test_mistral_embeddings_with_events_with_content.yaml
  • packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_streaming_chat_legacy.yaml
  • packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_async_streaming_chat_with_events_with_content.yaml
  • packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_chat_with_events_with_no_content.yaml
  • packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_async_streaming_chat_with_events_with_no_content.yaml
  • packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_embeddings/test_mistral_embeddings_legacy.yaml
  • packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_async_chat_with_events_with_no_content.yaml
  • packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_async_chat_legacy.yaml
  • packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_chat_legacy.yaml
  • packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_async_streaming_chat_legacy.yaml
  • packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_embeddings/test_mistral_embeddings_with_events_with_no_content.yaml
  • packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_streaming_chat_with_events_with_no_content.yaml
  • packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_async_chat_with_events_with_content.yaml
  • packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_streaming_chat_with_events_with_content.yaml
  • packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_embeddings/test_mistral_async_embeddings_with_events_with_content.yaml
  • packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_embeddings/test_mistral_async_embeddings_with_events_with_no_content.yaml
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: Store API keys only in environment variables/secure vaults; never hardcode secrets in code
Use Flake8 for code linting and adhere to its rules

Files:

  • packages/opentelemetry-instrumentation-mistralai/tests/test_chat.py
  • packages/opentelemetry-instrumentation-mistralai/opentelemetry/instrumentation/mistralai/__init__.py
  • packages/opentelemetry-instrumentation-mistralai/tests/test_embeddings.py
🧬 Code graph analysis (3)
packages/opentelemetry-instrumentation-mistralai/tests/test_chat.py (1)
packages/opentelemetry-instrumentation-mistralai/tests/conftest.py (2)
  • mistralai_client (51-52)
  • mistralai_async_client (56-57)
packages/opentelemetry-instrumentation-mistralai/opentelemetry/instrumentation/mistralai/__init__.py (3)
packages/opentelemetry-semantic-conventions-ai/opentelemetry/semconv_ai/__init__.py (1)
  • LLMRequestTypeValues (293-298)
packages/opentelemetry-instrumentation-google-generativeai/opentelemetry/instrumentation/google_generativeai/__init__.py (3)
  • _wrap (192-246)
  • _awrap (134-188)
  • _uninstrument (291-298)
packages/opentelemetry-instrumentation-vertexai/opentelemetry/instrumentation/vertexai/__init__.py (3)
  • _wrap (264-314)
  • _awrap (215-260)
  • _uninstrument (359-366)
packages/opentelemetry-instrumentation-mistralai/tests/test_embeddings.py (1)
packages/opentelemetry-instrumentation-mistralai/tests/conftest.py (2)
  • mistralai_client (51-52)
  • mistralai_async_client (56-57)
🪛 Ruff (0.12.2)
packages/opentelemetry-instrumentation-mistralai/opentelemetry/instrumentation/mistralai/__init__.py

524-524: Unused method argument: kwargs

(ARG002)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Test Packages (3.10)
  • GitHub Check: Test Packages (3.12)
  • GitHub Check: Test Packages (3.11)
  • GitHub Check: Build Packages (3.11)
  • GitHub Check: Lint
🔇 Additional comments (6)
packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_streaming_chat_legacy.yaml (1)

3-3: Request/streaming shape looks correct for mistralai >= 1.x.

Model/messages schema, stream: true, Accept: text/event-stream, and initial delta chunk are consistent. LGTM.

Also applies to: 6-7, 18-18, 23-23

packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_embeddings/test_mistral_embeddings_with_events_with_content.yaml (1)

24-106: ```shell
#!/bin/bash
set -euo pipefail

echo "1) List cassette files (up to 200 paths):"
if command -v rg >/dev/null 2>&1; then
rg --files --glob '/cassettes/' | sed -n '1,200p' || true
else
find . -path '/cassettes/' -type f | sed -n '1,200p' || true
fi

echo
echo "2) Search for gzip-base64 marker 'H4sI' inside cassettes (shows filename + context):"
if command -v rg >/dev/null 2>&1; then
rg -n --glob '/cassettes/' -F 'H4sI' -C3 || true
else
grep -n -F 'H4sI' $(find . -path '/cassettes/' -type f) 2>/dev/null || true
fi

echo
echo "3) Search for YAML binary tag '!!binary |' (literal) inside cassettes:"
if command -v rg >/dev/null 2>&1; then
rg -n --glob '/cassettes/' -F '!!binary |' -C3 || true
else
grep -n -F '!!binary |' $(find . -path '/cassettes/' -type f) 2>/dev/null || true
fi

echo
echo "4) If the specific file from the review exists, print its first 160 lines for inspection:"
target="packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_embeddings/test_mistral_embeddings_with_events_with_content.yaml"
if [ -f "$target" ]; then
echo "FOUND: $target"
sed -n '1,160p' "$target" || true
else
echo "Not found: $target"
fi


</blockquote></details>
<details>
<summary>packages/opentelemetry-instrumentation-mistralai/tests/test_chat.py (1)</summary><blockquote>

`2-2`: **API migration to UserMessage and chat.complete/stream looks correct**

Imports, callsites, and streaming chunk handling (res.data.choices[0].delta.content) align with mistralai ≥1.0 semantics. Good coverage across sync/async and streaming variants. 



Also applies to: 17-22, 63-68, 114-119, 163-168, 171-173, 213-218, 221-223, 268-273, 276-278, 322-327, 370-375, 423-428, 474-479, 522-527, 575-580

</blockquote></details>
<details>
<summary>packages/opentelemetry-instrumentation-mistralai/opentelemetry/instrumentation/mistralai/__init__.py (3)</summary><blockquote>

`38-46`: **Model import surface updated correctly for mistralai ≥1.0**

Switch to mistralai.models and new types (ChatCompletionChoice, Assistant/User/SystemMessage, UsageInfo, EmbeddingResponse) matches the new SDK.

---

`50-50`: **Version gate tightened to mistralai ≥1.0.0**

Matches the PR’s stated breaking change and avoids 0.x import paths.

---

`100-121`: **inputs vs input compatibility looks good**

Handling both input and inputs for embeddings and emitting user message events is correct for 1.x while staying backward tolerant.



Also applies to: 327-333

</blockquote></details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

Comment on lines 507 to 523
for wrapped_method in WRAPPED_METHODS:
wrap_method = wrapped_method.get("method")
module_name = wrapped_method.get("module")

# Wrap sync methods on the class
wrap_function_wrapper(
"mistralai.client",
f"MistralClient.{wrap_method}",
f"mistralai.{module_name}",
f"{module_name.capitalize()}.{wrap_method}",
_wrap(tracer, event_logger, wrapped_method),
)
# Wrap async methods on the class
wrap_function_wrapper(
"mistralai.async_client",
f"MistralAsyncClient.{wrap_method}",
f"mistralai.{module_name}",
f"{module_name.capitalize()}.{wrap_method}_async",
_awrap(tracer, event_logger, wrapped_method),
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Unwrap target mismatch prevents proper uninstrumentation

You wrap with wrap_function_wrapper("mistralai.{module}", "{Class}.{method}", ...), but unwrap uses "mistralai.{module}.{Class}", "method". This won’t remove the wrapper, risking double-wrapping.

Apply:

-            unwrap(f"mistralai.{module_name}.{module_name.capitalize()}", wrap_method)
-            unwrap(f"mistralai.{module_name}.{module_name.capitalize()}", f"{wrap_method}_async")
+            unwrap(f"mistralai.{module_name}", f"{module_name.capitalize()}.{wrap_method}")
+            unwrap(f"mistralai.{module_name}", f"{module_name.capitalize()}.{wrap_method}_async")

Also applies to: 524-531

🤖 Prompt for AI Agents
In
packages/opentelemetry-instrumentation-mistralai/opentelemetry/instrumentation/mistralai/__init__.py
around lines 507-523 (and similarly 524-531), the wrap targets use
wrap_function_wrapper("mistralai.{module}", "{Class}.{method}", ...) but
unwrapping expects ("mistralai.{module}.{Class}", "method"), so change the wrap
calls to use the same module/attribute split as unwrap: call
wrap_function_wrapper(f"mistralai.{module_name}.{module_name.capitalize()}",
f"{wrap_method}", _wrap(...)) and for async use f"{wrap_method}_async" as the
attribute, ensuring the module string includes the Class and the attribute is
only the method name so wrapping and unwrapping targets match.

Comment on lines +47 to +53
- __cf_bm=6ZqZIvXWzd4GTFqHj7hxkZFQaT1cE01tK8xOzhGSriE-1757759495-1.0.1.1-LEMmduP3325yDDh4LHHCQgbgRy5aDSUM6EuzzvoE5n2SGH2ylTkPKSGMcGGBZ1sYJA.hCtuLWgaN_S4NzOxRRIVgMG..puEYRHa63zCjd5I;
path=/; expires=Sat, 13-Sep-25 11:01:35 GMT; domain=.mistral.ai; HttpOnly;
Secure; SameSite=None
- _cfuvid=X6iv5AeiSObKKPE0EoN0aVf6OxBU06bCiqkhUOlv6_s-1757759495029-0.0.1.1-604800000;
path=/; domain=.mistral.ai; HttpOnly; Secure; SameSite=None
Strict-Transport-Security:
- max-age=15552000; includeSubDomains
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

Sanitize cassette headers (cookies, IDs, rate-limit counts).

Please redact Set-Cookie, CF-RAY, mistral-correlation-id, x-kong-request-id, and all x-ratelimit-*.

-      Set-Cookie:
-      - __cf_bm=...
-      - _cfuvid=...
+      Set-Cookie:
+      - "<redacted>"
-      CF-RAY:
-      - 97e6f9c6f8965ae2-VIE
+      CF-RAY:
+      - "<redacted>"
-      mistral-correlation-id:
-      - 019942a1-58a5-7472-861d-2c7176f8439d
+      mistral-correlation-id:
+      - "<redacted>"
-      x-kong-request-id:
-      - 019942a1-58a5-7472-861d-2c7176f8439d
+      x-kong-request-id:
+      - "<redacted>"

Also applies to: 60-85, 35-35, 63-71

🤖 Prompt for AI Agents
packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_async_chat_legacy.yaml
lines 47-53: sanitize the cassette HTTP headers by redacting sensitive
values—replace full Set-Cookie header values with a placeholder like
"<REDACTED_COOKIE>", replace CF-RAY, mistral-correlation-id, x-kong-request-id
with "<REDACTED>", and replace all x-ratelimit-* headers (e.g.,
x-ratelimit-limit, x-ratelimit-remaining, x-ratelimit-reset) with "<REDACTED>"
or a generic "<REDACTED_RATE_LIMIT>" token; apply the same redaction rules to
the other mentioned ranges (lines 60-85, 35-35, 63-71) so no cookies, CF
identifiers, mistral/kong request IDs, or rate limit counts remain in the
cassette.

Comment on lines +46 to 53
- __cf_bm=4O8DJ8mkzFjYcdV.vzn032Fv25NCGCjTtL5P7FPgCq4-1757759496-1.0.1.1-9snhFQCGv5G.HUJYV92QhVYZrONPCNy3JphujYdbBxCFhSJWUtIs0IbccgEgV_pJdKw1hHZmLf9ZeVPsMm8JjzKYbRAS0M5zWdrpEwW3Ptg;
path=/; expires=Sat, 13-Sep-25 11:01:36 GMT; domain=.mistral.ai; HttpOnly;
Secure; SameSite=None
- _cfuvid=77QsBdxDqjX_ghHJbaPcvNsOMgIQI_M5elYCNEiBkns-1757759496782-0.0.1.1-604800000;
path=/; domain=.mistral.ai; HttpOnly; Secure; SameSite=None
Strict-Transport-Security:
- max-age=15552000; includeSubDomains
Transfer-Encoding:
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

Scrub cookies and transient headers to reduce PII/brittleness.

Set-Cookie, CF-RAY, mistral-correlation-id, x-kong-request-id, and x-ratelimit-* should be redacted.

-      Set-Cookie:
-      - __cf_bm=...
-      - _cfuvid=...
+      Set-Cookie:
+      - "<redacted>"
-      CF-RAY:
-      - 97e6f9d33d360d50-VIE
+      CF-RAY:
+      - "<redacted>"
-      mistral-correlation-id:
-      - 019942a1-6052-7921-b641-1ffed226bec6
+      mistral-correlation-id:
+      - "<redacted>"
-      x-kong-request-id:
-      - 019942a1-6052-7921-b641-1ffed226bec6
+      x-kong-request-id:
+      - "<redacted>"
# Redact x-ratelimit-* numeric values

Also applies to: 60-85, 34-35, 62-69

🤖 Prompt for AI Agents
In
packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_async_chat_with_events_with_no_content.yaml
around lines 46-53 (and also apply same changes to lines 60-85, 34-35, 62-69),
scrub PII and transient headers by redacting Set-Cookie header values and
removing or replacing values for CF-RAY, mistral-correlation-id,
x-kong-request-id, and any x-ratelimit-* headers with a stable placeholder
(e.g., "<REDACTED>" or remove the header value entirely) so cassettes are
deterministic and do not leak cookies or request-specific IDs; update each
occurrence in the listed line ranges accordingly.

- mistral-client-python/1.9.10
method: POST
uri: https://api.mistral.ai/v1/chat/completions
uri: https://api.mistral.ai/v1/chat/completions#stream
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Verify URI fragment “#stream” — likely incorrect for HTTP requests.

Fragments aren’t sent in HTTP; leaving it may break replay matching. Prefer the canonical endpoint without the fragment.

-    uri: https://api.mistral.ai/v1/chat/completions#stream
+    uri: https://api.mistral.ai/v1/chat/completions

🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_async_streaming_chat_legacy.yaml"

if [ -f "$FILE" ]; then
  echo "== FILE CONTENT ($FILE) =="
  nl -ba "$FILE" | sed -n '1,120p'
else
  echo "File not found: $FILE" >&2
fi

echo
echo "== SEARCH FOR '#stream' IN REPO =="
rg -n --hidden --no-ignore -S "#stream" || true

echo
echo "== SEARCH FOR 'completions' IN 'packages' =="
rg -n --hidden --no-ignore -S "completions" packages || true

Length of output: 184


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_async_streaming_chat_legacy.yaml"

if [ -f "$FILE" ]; then
  echo "== FILE: $FILE =="
  sed -n '1,240p' "$FILE" || cat "$FILE"
else
  echo "File not found: $FILE" >&2
fi

echo
echo "== SEARCH for '#stream' in repo =="
if command -v rg >/dev/null 2>&1; then
  rg -n --hidden --no-ignore -S "#stream" || true
else
  grep -R --line-number "#stream" . || true
fi

echo
echo "== SEARCH for 'completions#stream' in repo =="
if command -v rg >/dev/null 2>&1; then
  rg -n --hidden --no-ignore -S "completions#stream" || true
else
  grep -R --line-number "completions#stream" . || true
fi

echo
echo "== SEARCH for 'completions' under packages =="
if command -v rg >/dev/null 2>&1; then
  rg -n --hidden --no-ignore -S "completions" packages || true
else
  grep -R --line-number "completions" packages || true
fi

Length of output: 227349


Remove “#stream” fragment from recorded Mistral streaming URIs

Fragments aren’t sent in HTTP requests and will break cassette/replay matching — use the canonical endpoint.

Files to update (line ~20):

  • packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_streaming_chat_with_events_with_content.yaml
  • packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_streaming_chat_with_events_with_no_content.yaml
  • packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_async_streaming_chat_with_events_with_content.yaml
  • packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_async_streaming_chat_with_events_with_no_content.yaml
  • packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_streaming_chat_legacy.yaml
  • packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_async_streaming_chat_legacy.yaml
-    uri: https://api.mistral.ai/v1/chat/completions#stream
+    uri: https://api.mistral.ai/v1/chat/completions
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
uri: https://api.mistral.ai/v1/chat/completions#stream
uri: https://api.mistral.ai/v1/chat/completions
🤖 Prompt for AI Agents
In
packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_async_streaming_chat_legacy.yaml
around line 20 (and the other listed cassette files), the recorded URI includes
the fragment "#stream" which is not part of HTTP requests and breaks cassette
replay matching; remove the "#stream" fragment from the URI so it reads
"https://api.mistral.ai/v1/chat/completions" instead, and apply the same
replacement to the other affected files listed in the comment.

- DYNAMIC
CF-RAY:
- 88987a0c7bcf8e4e-TLV
- 97e6f9b9acb3c25b-VIE
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Scrub correlation/trace identifiers (not needed for test; reduce PII/telemetry leakage).

Remove CF-RAY, mistral-correlation-id, and x-kong-request-id headers.

-      CF-RAY:
-      - 97e6f9b9acb3c25b-VIE
-      mistral-correlation-id:
-      - 019942a1-5022-73ce-9715-3f9e881e7cb1
-      x-kong-request-id:
-      - 019942a1-5022-73ce-9715-3f9e881e7cb1

Also applies to: 349-356

🤖 Prompt for AI Agents
packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_async_streaming_chat_legacy.yaml
around line 324 (also apply same changes to lines 349-356): the cassette
contains real telemetry identifiers (CF-RAY, mistral-correlation-id,
x-kong-request-id) such as "97e6f9b9acb3c25b-VIE"; remove or replace these
header values with scrubbed placeholders (e.g., "<REDACTED>" or generic IDs) to
eliminate PII/telemetry leakage, ensuring all occurrences in the specified
ranges are sanitized consistently.

Comment on lines +189 to 196
- __cf_bm=Oel3X_u9ZS.OZmt9BWXIbi5HqNEl1ZEUpvERIwx1M2w-1757761132-1.0.1.1-WNMNl9pJf5BNRrLFJdAqUIXnVJOn3k2iJ1mux6WFa_aOZkG2tVyCMrQHirsbhQrP5AnY0UIfLaDisP54gb6gGK62s3z.yJMHBjxlayiTMdk;
path=/; expires=Sat, 13-Sep-25 11:28:52 GMT; domain=.mistral.ai; HttpOnly;
Secure; SameSite=None
- _cfuvid=71zBd1qCtqZxWiCKPJ3lfPN4qO8oup7V56sQJxoO404-1757761132879-0.0.1.1-604800000;
path=/; domain=.mistral.ai; HttpOnly; Secure; SameSite=None
Strict-Transport-Security:
- max-age=15552000; includeSubDomains
Transfer-Encoding:
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

Scrub cookies and unique IDs per cassette policy.

Same redaction as other cassettes: Set-Cookie, CF-RAY, mistral-correlation-id, x-kong-request-id, and x-ratelimit-*.

-      Set-Cookie:
-      - __cf_bm=...
-      - _cfuvid=...
+      Set-Cookie:
+      - "<redacted>"
-      CF-RAY:
-      - 97e721c75ddd5db9-VIE
+      CF-RAY:
+      - "<redacted>"
-      mistral-correlation-id:
-      - 019942ba-58de-7b9b-b023-07e61ba103b5
+      mistral-correlation-id:
+      - "<redacted>"
-      x-kong-request-id:
-      - 019942ba-58de-7b9b-b023-07e61ba103b5
+      x-kong-request-id:
+      - "<redacted>"

Also applies to: 176-228

🤖 Prompt for AI Agents
In
packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_embeddings/test_mistral_async_embeddings_with_events_with_no_content.yaml
around lines 189 to 196 (and also apply same changes to lines 176–228), scrub
sensitive cookie and unique ID headers to match other cassettes: replace
Set-Cookie values and any __cf_bm/_cfuvid entries with a redacted placeholder,
remove or redact CF-RAY, mistral-correlation-id, x-kong-request-id, and any
x-ratelimit-* header values (use the same redaction format used in other
cassettes), ensuring no real cookie/token/ID values remain in those header
lines.

Comment on lines +121 to +127
- __cf_bm=30bOTm5Cet.pOHkZj37epBkDRaGz1_fS3Vd1cJs6J7g-1757759497-1.0.1.1-1Hqc6IFughuGVShT4dY4f_CWCTfSkuBqpTPRaK8F7vt6oqExf_Ghxb8b0IZe4CJlqIB5lf1MYhFfIQYHsGz.K79uW537_iqsvaMhkcdb6cs;
path=/; expires=Sat, 13-Sep-25 11:01:37 GMT; domain=.mistral.ai; HttpOnly;
Secure; SameSite=None
- _cfuvid=BrLS77zsgcEUvrK4zU6kTG_NxIKPJm9HUBcX4KJLVrY-1757759497180-0.0.1.1-604800000;
path=/; domain=.mistral.ai; HttpOnly; Secure; SameSite=None
Strict-Transport-Security:
- max-age=15552000; includeSubDomains
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

Scrub Set-Cookie and transient headers.

Remove/replace Set-Cookie, CF-RAY, mistral-correlation-id, x-kong-request-id, and x-ratelimit-*.

-      Set-Cookie:
-      - __cf_bm=...
-      - _cfuvid=...
+      Set-Cookie:
+      - "<redacted>"
-      CF-RAY:
-      - 97e6f9d7f8b3a464-VIE
+      CF-RAY:
+      - "<redacted>"
-      mistral-correlation-id:
-      - 019942a1-634e-70b1-b771-1115f59f287a
+      mistral-correlation-id:
+      - "<redacted>"
-      x-kong-request-id:
-      - 019942a1-634e-70b1-b771-1115f59f287a
+      x-kong-request-id:
+      - "<redacted>"

Also applies to: 134-159, 109-110, 137-145

🤖 Prompt for AI Agents
In
packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_embeddings/test_mistral_embeddings_legacy.yaml
around lines 121-127, scrub any transient/secret response headers by removing or
replacing Set-Cookie, CF-RAY, mistral-correlation-id, x-kong-request-id, and any
x-ratelimit-* headers with stable placeholders (e.g., "<redacted>" or remove the
header entries entirely) so cassettes are deterministic; apply the same changes
to the other affected ranges noted in the comment (lines 109-110, 134-159,
137-145).

interactions:
- request:
body: '{"model": "mistral-embed", "input": "Tell me a joke about OpenTelemetry"}'
body: '{"model":"mistral-embed","input":"Tell me a joke about OpenTelemetry"}'
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Cassette still uses "input" instead of "inputs" (API mismatch).

PR objective says embeddings.create(inputs=...), but the recorded request body uses "input". This will cause VCR mismatches if the code now sends "inputs". Re-record this cassette with the new client call or update VCR matchers to ignore body. Prefer re-recording.

To find other cassettes with the old field:


🏁 Script executed:

#!/bin/bash
rg -n --glob 'packages/**/cassettes/**' -C2 '"input":'

Length of output: 312021


Re-record VCR cassettes: request bodies still use "input" (should be "inputs")

PR switched embeddings to embeddings.create(inputs=...), but many recorded cassettes still contain "input" in the request body and will cause VCR mismatches. Re-record affected cassettes with the new client calls (preferred) or relax VCR body matching.

Examples found:

  • packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_embeddings/test_mistral_embeddings_with_events_with_content.yaml — body: '{"model":"mistral-embed","input":"Tell me a joke about OpenTelemetry"}'
  • packages/opentelemetry-instrumentation-openai/tests/metrics/cassettes/test_openai_metrics/test_embeddings_metrics.yaml
  • packages/opentelemetry-instrumentation-pinecone/tests/cassettes/test_query/test_pinecone_retrieval.yaml
  • packages/opentelemetry-instrumentation-replicate/tests/cassettes/test_llama/test_replicate_llama_stream_with_events_with_no_content.yaml
🤖 Prompt for AI Agents
In
packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_embeddings/test_mistral_embeddings_with_events_with_content.yaml
around line 3 the recorded request body still uses "input" instead of the new
"inputs" key; re-record this VCR cassette (and the other listed cassettes) using
the updated embeddings client call embeddings.create(inputs=...) so the request
bodies contain "inputs": [...], or alternatively relax the VCR body matching for
these tests to ignore the input/inputs key; update all affected cassette files
(examples listed in the review) so their request bodies match the new client
behavior.

Comment on lines +120 to +125
Set-Cookie:
- __cf_bm=0Ok6fRf3UQ9sB85qUZZ6PGNLYroXDSfl_Ccr6aiSwk8-1757759497-1.0.1.1-OJ5.WvPIvKmiLOJlpeuKpZE8DGWBOlRObCdhnnB9jxdjKP9nI8m8LYWnOjuFL0L2Q16Mk1l9zcAdWQcr0_BAURKOFsP2d_JFlTwx40GXnxM;
path=/; expires=Sat, 13-Sep-25 11:01:37 GMT; domain=.mistral.ai; HttpOnly;
Secure; SameSite=None
- _cfuvid=LCh_oFa4EozQ1vWi_f5sVX7oI0x0Mgj6tU7FC4gy.hc-1757759497590-0.0.1.1-604800000;
path=/; domain=.mistral.ai; HttpOnly; Secure; SameSite=None
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Do not commit Set-Cookie values in cassettes.

These are session identifiers and must be scrubbed. Filter them at record time and remove from existing cassettes.

Apply this diff (safe to commit now; also add header filtering to your VCR config):

-      Set-Cookie:
-      - __cf_bm=0Ok6fRf3UQ9sB85qUZZ6PGNLYroXDSfl_Ccr6aiSwk8-1757759497-1.0.1.1-OJ5.WvPIvKmiLOJlpeuKpZE8DGWBOlRObCdhnnB9jxdjKP9nI8m8LYWnOjuFL0L2Q16Mk1l9zcAdWQcr0_BAURKOFsP2d_JFlTwx40GXnxM;
-        path=/; expires=Sat, 13-Sep-25 11:01:37 GMT; domain=.mistral.ai; HttpOnly;
-        Secure; SameSite=None
-      - _cfuvid=LCh_oFa4EozQ1vWi_f5sVX7oI0x0Mgj6tU7FC4gy.hc-1757759497590-0.0.1.1-604800000;
-        path=/; domain=.mistral.ai; HttpOnly; Secure; SameSite=None
+      # Set-Cookie stripped by VCR filter
+      Set-Cookie:
+      - "[REDACTED]"

Also add to VCR config: filter_headers=['authorization','set-cookie','user-agent'].

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
Set-Cookie:
- __cf_bm=0Ok6fRf3UQ9sB85qUZZ6PGNLYroXDSfl_Ccr6aiSwk8-1757759497-1.0.1.1-OJ5.WvPIvKmiLOJlpeuKpZE8DGWBOlRObCdhnnB9jxdjKP9nI8m8LYWnOjuFL0L2Q16Mk1l9zcAdWQcr0_BAURKOFsP2d_JFlTwx40GXnxM;
path=/; expires=Sat, 13-Sep-25 11:01:37 GMT; domain=.mistral.ai; HttpOnly;
Secure; SameSite=None
- _cfuvid=LCh_oFa4EozQ1vWi_f5sVX7oI0x0Mgj6tU7FC4gy.hc-1757759497590-0.0.1.1-604800000;
path=/; domain=.mistral.ai; HttpOnly; Secure; SameSite=None
# Set-Cookie stripped by VCR filter
Set-Cookie:
- "[REDACTED]"
🤖 Prompt for AI Agents
In
packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_embeddings/test_mistral_embeddings_with_events_with_content.yaml
around lines 120-125, this cassette currently contains sensitive Set-Cookie
session identifiers which must be removed; delete the Set-Cookie header entries
from this cassette and any other existing cassettes, and update the VCR
recording configuration to filter these headers at record time by adding
filter_headers=['authorization','set-cookie','user-agent'] (or the equivalent
config in your test suite) so future recordings never capture these values.

Comment on lines 108 to +159
CF-RAY:
- 88988070bc8d8e4e-TLV
- 97e6f9dc7c77325c-VIE
Connection:
- keep-alive
Content-Encoding:
- gzip
Content-Type:
- application/json
Date:
- Sat, 25 May 2024 21:08:36 GMT
- Sat, 13 Sep 2025 10:31:37 GMT
Server:
- cloudflare
Set-Cookie:
- __cf_bm=mm7uCH_8rU9ID.OnNjZq3eOX_2zF8XAsPcNiNC1uFkQ-1757759497-1.0.1.1-RJ.HV4u8FhnlwkMloWN1_8DDEjbWZ19BN35LaecZ4XKUa6Lk0dlqtQXrKgqbKAPlZh64GhkNfxdxeZH6O0rXU7iaA651KMCfShZUyiRcfjk;
path=/; expires=Sat, 13-Sep-25 11:01:37 GMT; domain=.mistral.ai; HttpOnly;
Secure; SameSite=None
- _cfuvid=3J87G2ON_lK56cmk_DlIiu7LUZLPff.tUocFCSiUEow-1757759497842-0.0.1.1-604800000;
path=/; domain=.mistral.ai; HttpOnly; Secure; SameSite=None
Strict-Transport-Security:
- max-age=15552000; includeSubDomains
Transfer-Encoding:
- chunked
access-control-allow-origin:
- '*'
alt-svc:
- h3=":443"; ma=86400
cf-cache-status:
- DYNAMIC
mistral-correlation-id:
- 019942a1-65ec-7b6c-b402-f06c89844891
x-envoy-upstream-service-time:
- '94'
x-kong-proxy-latency:
- '0'
- '8'
x-kong-request-id:
- 165aca3313b7431c0a82f33993c61255
- 019942a1-65ec-7b6c-b402-f06c89844891
x-kong-upstream-latency:
- '111'
- '95'
x-ratelimit-limit-req-10-second:
- '60'
x-ratelimit-limit-tokens-minute:
- '20000000'
x-ratelimit-limit-tokens-month:
- '200000000000'
x-ratelimit-remaining-req-10-second:
- '48'
x-ratelimit-remaining-tokens-minute:
- '19999970'
x-ratelimit-remaining-tokens-month:
- '199999999970'
x-ratelimit-tokens-query-cost:
- '10'
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue

Scrub Set-Cookie and volatile tracing headers from cassettes (secrets/PII + churn).

Response records include Set-Cookie (Cloudflare cookies), CF-RAY, mistral-correlation-id, x-kong-request-id, latencies, Date, etc. Cookies are sensitive; IDs/timestamps cause unnecessary cassette churn. Please filter them at record time.

Apply in tests/conftest.py:

@@
+import pytest
+
+@pytest.fixture(scope="session")
+def vcr_config():
+    def _scrub_response(response):
+        headers = response.get("headers", {})
+        for h in [
+            "Set-Cookie", "set-cookie", "CF-RAY", "cf-ray", "Date", "date",
+            "mistral-correlation-id", "x-kong-request-id",
+            "x-envoy-upstream-service-time", "x-kong-proxy-latency", "x-kong-upstream-latency",
+            "cf-cache-status",
+        ]:
+            headers.pop(h, None)
+        response["headers"] = headers
+        return response
+    return {
+        "filter_headers": ["authorization", "cookie", "user-agent"],
+        "before_record_response": _scrub_response,
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
CF-RAY:
- 88988070bc8d8e4e-TLV
- 97e6f9dc7c77325c-VIE
Connection:
- keep-alive
Content-Encoding:
- gzip
Content-Type:
- application/json
Date:
- Sat, 25 May 2024 21:08:36 GMT
- Sat, 13 Sep 2025 10:31:37 GMT
Server:
- cloudflare
Set-Cookie:
- __cf_bm=mm7uCH_8rU9ID.OnNjZq3eOX_2zF8XAsPcNiNC1uFkQ-1757759497-1.0.1.1-RJ.HV4u8FhnlwkMloWN1_8DDEjbWZ19BN35LaecZ4XKUa6Lk0dlqtQXrKgqbKAPlZh64GhkNfxdxeZH6O0rXU7iaA651KMCfShZUyiRcfjk;
path=/; expires=Sat, 13-Sep-25 11:01:37 GMT; domain=.mistral.ai; HttpOnly;
Secure; SameSite=None
- _cfuvid=3J87G2ON_lK56cmk_DlIiu7LUZLPff.tUocFCSiUEow-1757759497842-0.0.1.1-604800000;
path=/; domain=.mistral.ai; HttpOnly; Secure; SameSite=None
Strict-Transport-Security:
- max-age=15552000; includeSubDomains
Transfer-Encoding:
- chunked
access-control-allow-origin:
- '*'
alt-svc:
- h3=":443"; ma=86400
cf-cache-status:
- DYNAMIC
mistral-correlation-id:
- 019942a1-65ec-7b6c-b402-f06c89844891
x-envoy-upstream-service-time:
- '94'
x-kong-proxy-latency:
- '0'
- '8'
x-kong-request-id:
- 165aca3313b7431c0a82f33993c61255
- 019942a1-65ec-7b6c-b402-f06c89844891
x-kong-upstream-latency:
- '111'
- '95'
x-ratelimit-limit-req-10-second:
- '60'
x-ratelimit-limit-tokens-minute:
- '20000000'
x-ratelimit-limit-tokens-month:
- '200000000000'
x-ratelimit-remaining-req-10-second:
- '48'
x-ratelimit-remaining-tokens-minute:
- '19999970'
x-ratelimit-remaining-tokens-month:
- '199999999970'
x-ratelimit-tokens-query-cost:
- '10'
import pytest
@pytest.fixture(scope="session")
def vcr_config():
def _scrub_response(response):
headers = response.get("headers", {})
for h in [
"Set-Cookie", "set-cookie", "CF-RAY", "cf-ray", "Date", "date",
"mistral-correlation-id", "x-kong-request-id",
"x-envoy-upstream-service-time", "x-kong-proxy-latency", "x-kong-upstream-latency",
"cf-cache-status",
]:
headers.pop(h, None)
response["headers"] = headers
return response
return {
"filter_headers": ["authorization", "cookie", "user-agent"],
"before_record_response": _scrub_response,
}
🤖 Prompt for AI Agents
In
packages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_embeddings/test_mistral_embeddings_with_events_with_no_content.yaml
around lines 108-159, the cassette contains Set-Cookie and volatile
tracing/latency headers (CF-RAY, mistral-correlation-id, x-kong-request-id,
x-envoy-upstream-service-time, x-kong-proxy-latency, x-kong-upstream-latency,
Date, cf-cache-status, and similar rate-limit tokens) that leak sensitive
cookies/IDs and cause churn; update tests/conftest.py to strip or normalize
these headers at recording time by adding a response header scrubber (or
configure the VCR/recorder) to remove Set-Cookie entirely and either remove or
replace values for CF-RAY, mistral-correlation-id, x-kong-request-id, x-envoy-*,
x-kong-*, Date, cf-cache-status and x-ratelimit-* with stable placeholders so
recorded cassettes contain no PII/volatile IDs or timestamps.

nirga and others added 2 commits September 13, 2025 13:53
- Remove trailing whitespace in __init__.py and conftest.py
- Clean up blank lines containing whitespace
- Break long string literals in test_chat.py to comply with 120 char limit

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@ellipsis-dev ellipsis-dev Bot left a comment

Choose a reason for hiding this comment

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

Important

Looks good to me! 👍

Reviewed 55175e3 in 1 minute and 32 seconds. Click for details.
  • Reviewed 121 lines of code in 3 files
  • Skipped 0 files when reviewing.
  • Skipped posting 4 draft comments. View those below.
  • Modify your settings and rules to customize what types of comments Ellipsis leaves. And don't forget to react with 👍 or 👎 to teach Ellipsis.
1. packages/opentelemetry-instrumentation-mistralai/opentelemetry/instrumentation/mistralai/__init__.py:213
  • Draft comment:
    Removed an extraneous blank line in the streaming response accumulator; no functional change.
  • Reason this comment was not posted:
    Comment did not seem useful. Confidence is useful = 0% <= threshold 50% This comment is purely informative, stating that a blank line was removed without any functional change. It doesn't provide any actionable feedback or suggestions for improvement.
2. packages/opentelemetry-instrumentation-mistralai/opentelemetry/instrumentation/mistralai/__init__.py:507
  • Draft comment:
    Removed unnecessary whitespace before the wrap_function_wrapper calls; cleanup only.
  • Reason this comment was not posted:
    Comment did not seem useful. Confidence is useful = 0% <= threshold 50% This comment is purely informative, indicating a cleanup of whitespace. It doesn't provide any actionable feedback or suggestions for improvement.
3. packages/opentelemetry-instrumentation-mistralai/tests/conftest.py:51
  • Draft comment:
    Removed redundant trailing whitespace in the pytest.fixture decorator.
  • Reason this comment was not posted:
    Comment was not on a location in the diff, so it can't be submitted as a review comment.
4. packages/opentelemetry-instrumentation-mistralai/tests/test_chat.py:105
  • Draft comment:
    Refactored long string literals into multiline concatenation for improved readability; the expected content remains unchanged.
  • Reason this comment was not posted:
    Comment did not seem useful. Confidence is useful = 0% <= threshold 50% This comment is purely informative, explaining a change that was made without suggesting any action or asking for confirmation. It doesn't provide any actionable feedback or raise any concerns about the code.

Workflow ID: wflow_N0rCjA5Fj5Ugam9d

You can customize Ellipsis by changing your verbosity settings, reacting with 👍 or 👎, replying to comments, or adding code review rules.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
packages/opentelemetry-instrumentation-mistralai/opentelemetry/instrumentation/mistralai/__init__.py (4)

521-527: Unwrap target mismatch prevents proper uninstrumentation

You wrap using module="mistralai.{module}", attribute="{Class}.{method}", but unwrap expects object="mistralai.{module}.{Class}", attr="method". This won’t remove wrappers and risks double-wrapping.

     def _uninstrument(self, **kwargs):
         for wrapped_method in WRAPPED_METHODS:
             wrap_method = wrapped_method.get("method")
             module_name = wrapped_method.get("module")
-            unwrap(f"mistralai.{module_name}.{module_name.capitalize()}", wrap_method)
-            unwrap(f"mistralai.{module_name}.{module_name.capitalize()}", f"{wrap_method}_async")
+            unwrap(f"mistralai.{module_name}", f"{module_name.capitalize()}.{wrap_method}")
+            unwrap(f"mistralai.{module_name}", f"{module_name.capitalize()}.{wrap_method}_async")

200-240: Streaming spans can leak if consumer stops early; also guard None deltas

Ensure spans are ended even if the stream consumer breaks early or an error occurs, and safely handle missing/None delta content/role.

 def _accumulate_streaming_response(span, event_logger, llm_request_type, response):
     accumulated_response = ChatCompletionResponse(
         id="",
         object="",
         created=0,
         model="",
         choices=[],
         usage=UsageInfo(prompt_tokens=0, total_tokens=0, completion_tokens=0),
     )
 
-    for res in response:
-        yield res
-
-        # Handle new CompletionEvent structure with .data attribute
-        chunk_data = res.data if hasattr(res, 'data') else res
-        if chunk_data.model:
-            accumulated_response.model = chunk_data.model
-        if chunk_data.usage:
-            accumulated_response.usage = chunk_data.usage
-        # Id is the same for all chunks, so it's safe to overwrite it every time
-        if chunk_data.id:
-            accumulated_response.id = chunk_data.id
-
-        for idx, choice in enumerate(chunk_data.choices):
-            if len(accumulated_response.choices) <= idx:
-                accumulated_response.choices.append(
-                    ChatCompletionChoice(
-                        index=idx,
-                        message=AssistantMessage(role="assistant", content=""),
-                        finish_reason=None,
-                    )
-                )
-
-            accumulated_response.choices[idx].finish_reason = choice.finish_reason
-            accumulated_response.choices[idx].message.content += choice.delta.content
-            accumulated_response.choices[idx].message.role = choice.delta.role
-
-    _handle_response(span, event_logger, llm_request_type, accumulated_response)
-
-    span.end()
+    try:
+        for res in response:
+            yield res
+
+            # Handle new CompletionEvent structure with .data attribute
+            chunk_data = res.data if hasattr(res, "data") else res
+            if getattr(chunk_data, "model", None):
+                accumulated_response.model = chunk_data.model
+            if getattr(chunk_data, "usage", None):
+                accumulated_response.usage = chunk_data.usage
+            # Id is the same for all chunks, safe to overwrite
+            if getattr(chunk_data, "id", None):
+                accumulated_response.id = chunk_data.id
+
+            for idx, choice in enumerate(getattr(chunk_data, "choices", []) or []):
+                if len(accumulated_response.choices) <= idx:
+                    accumulated_response.choices.append(
+                        ChatCompletionChoice(
+                            index=idx,
+                            message=AssistantMessage(role="assistant", content=""),
+                            finish_reason=None,
+                        )
+                    )
+                delta = getattr(choice, "delta", None)
+                if delta:
+                    content_part = getattr(delta, "content", "") or ""
+                    if content_part:
+                        accumulated_response.choices[idx].message.content += content_part
+                    role_part = getattr(delta, "role", None)
+                    if role_part:
+                        accumulated_response.choices[idx].message.role = role_part
+                accumulated_response.choices[idx].finish_reason = getattr(choice, "finish_reason", None)
+    finally:
+        _handle_response(span, event_logger, llm_request_type, accumulated_response)
+        if span.is_recording():
+            span.set_status(Status(StatusCode.OK))
+        span.end()

242-284: Async streaming: same span-leak and None-delta concerns

Mirror the sync fixes in the async generator.

 async def _aaccumulate_streaming_response(
     span, event_logger, llm_request_type, response
 ):
     accumulated_response = ChatCompletionResponse(
         id="",
         object="",
         created=0,
         model="",
         choices=[],
         usage=UsageInfo(prompt_tokens=0, total_tokens=0, completion_tokens=0),
     )
 
-    async for res in response:
-        yield res
-
-        # Handle new CompletionEvent structure with .data attribute
-        chunk_data = res.data if hasattr(res, 'data') else res
-        if chunk_data.model:
-            accumulated_response.model = chunk_data.model
-        if chunk_data.usage:
-            accumulated_response.usage = chunk_data.usage
-        # Id is the same for all chunks, so it's safe to overwrite it every time
-        if chunk_data.id:
-            accumulated_response.id = chunk_data.id
-
-        for idx, choice in enumerate(chunk_data.choices):
-            if len(accumulated_response.choices) <= idx:
-                accumulated_response.choices.append(
-                    ChatCompletionChoice(
-                        index=idx,
-                        message=AssistantMessage(role="assistant", content=""),
-                        finish_reason=None,
-                    )
-                )
-
-            accumulated_response.choices[idx].finish_reason = choice.finish_reason
-            accumulated_response.choices[idx].message.content += choice.delta.content
-            accumulated_response.choices[idx].message.role = choice.delta.role
-
-    _handle_response(span, event_logger, llm_request_type, accumulated_response)
-
-    span.end()
+    try:
+        async for res in response:
+            yield res
+
+            # Handle new CompletionEvent structure with .data attribute
+            chunk_data = res.data if hasattr(res, "data") else res
+            if getattr(chunk_data, "model", None):
+                accumulated_response.model = chunk_data.model
+            if getattr(chunk_data, "usage", None):
+                accumulated_response.usage = chunk_data.usage
+            if getattr(chunk_data, "id", None):
+                accumulated_response.id = chunk_data.id
+
+            for idx, choice in enumerate(getattr(chunk_data, "choices", []) or []):
+                if len(accumulated_response.choices) <= idx:
+                    accumulated_response.choices.append(
+                        ChatCompletionChoice(
+                            index=idx,
+                            message=AssistantMessage(role="assistant", content=""),
+                            finish_reason=None,
+                        )
+                    )
+                delta = getattr(choice, "delta", None)
+                if delta:
+                    content_part = getattr(delta, "content", "") or ""
+                    if content_part:
+                        accumulated_response.choices[idx].message.content += content_part
+                    role_part = getattr(delta, "role", None)
+                    if role_part:
+                        accumulated_response.choices[idx].message.role = role_part
+                accumulated_response.choices[idx].finish_reason = getattr(choice, "finish_reason", None)
+    finally:
+        _handle_response(span, event_logger, llm_request_type, accumulated_response)
+        if span.is_recording():
+            span.set_status(Status(StatusCode.OK))
+        span.end()

86-99: Don’t hardcode first prompt role; avoid shadowing built-in input

  • Hardcoding prompts.0.role="user" is wrong if the first message is System/Assistant. Let enumeration define roles.
  • Rename local input to avoid shadowing Python’s built-in.
-        if llm_request_type == LLMRequestTypeValues.CHAT:
-            _set_span_attribute(span, f"{SpanAttributes.LLM_PROMPTS}.0.role", "user")
-            for index, message in enumerate(kwargs.get("messages")):
+        if llm_request_type == LLMRequestTypeValues.CHAT:
+            for index, message in enumerate(kwargs.get("messages", [])):
                 _set_span_attribute(
                     span,
                     f"{SpanAttributes.LLM_PROMPTS}.{index}.content",
                     message.content,
                 )
                 _set_span_attribute(
                     span,
                     f"{SpanAttributes.LLM_PROMPTS}.{index}.role",
                     message.role,
                 )
         else:
-            input = kwargs.get("input") or kwargs.get("inputs")
+            inputs = kwargs.get("inputs") or kwargs.get("input")
 
-            if isinstance(input, str):
+            if isinstance(inputs, str):
                 _set_span_attribute(
                     span, f"{SpanAttributes.LLM_PROMPTS}.0.role", "user"
                 )
                 _set_span_attribute(
-                    span, f"{SpanAttributes.LLM_PROMPTS}.0.content", input
+                    span, f"{SpanAttributes.LLM_PROMPTS}.0.content", inputs
                 )
-            elif input:
-                for index, prompt in enumerate(input):
+            elif inputs:
+                for index, prompt in enumerate(inputs):
                     _set_span_attribute(
                         span,
                         f"{SpanAttributes.LLM_PROMPTS}.{index}.role",
                         "user",
                     )
                     _set_span_attribute(
                         span,
                         f"{SpanAttributes.LLM_PROMPTS}.{index}.content",
                         prompt,
                     )

Also applies to: 100-121

♻️ Duplicate comments (1)
packages/opentelemetry-instrumentation-mistralai/tests/test_chat.py (1)

49-51: Avoid brittle assertions for response IDs

IDs are opaque and may change across SDK/server versions or cassette re-recordings. Assert presence/shape instead of exact value. from Line 50 in prior review.

-    assert (
-        mistral_span.attributes.get("gen_ai.response.id")
-        == "e9459fcd56c742e0875167c9926c6aae"
-    )
+    resp_id = mistral_span.attributes.get("gen_ai.response.id")
+    assert isinstance(resp_id, str) and resp_id

Repeat the replacement for all occurrences listed in the line ranges of this comment.

Also applies to: 87-89, 143-145, 204-206, 246-248, 305-307, 364-366, 404-406, 466-468, 525-527, 565-567, 624-626

🧹 Nitpick comments (4)
packages/opentelemetry-instrumentation-mistralai/tests/test_chat.py (1)

356-357: Fix copy/paste in comments: “ollama” → “Mistral”

These comments refer to Ollama but the tests target Mistral.

-    # For some reason, async ollama chat doesn't report prompt token usage back
+    # For some reason, async Mistral chat doesn't report prompt token usage back

Also applies to: 396-398, 458-460

packages/opentelemetry-instrumentation-mistralai/opentelemetry/instrumentation/mistralai/__init__.py (3)

463-467: Remove redundant async call branch

Both branches call the same expression.

-    if to_wrap.get("streaming"):
-        response = await wrapped(*args, **kwargs)
-    else:
-        response = await wrapped(*args, **kwargs)
+    response = await wrapped(*args, **kwargs)

352-365: Avoid logging full embedding vectors in events

Emitting raw vectors can bloat logs and leak data. Prefer metadata (dimension, count) or omit content entirely when events are enabled.

If you want, I can draft an event schema for embeddings that records sizes only (e.g., message={"embedding_dim": N, "role": "assistant"}).


521-521: Silence Ruff ARG002 for unused kwargs

Keep signature compatible with BaseInstrumentor but avoid the warning.

-    def _uninstrument(self, **kwargs):
+    def _uninstrument(self, **_kwargs):

Or add # noqa: ARG002 to the line.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between f2bec07 and 0252f22.

📒 Files selected for processing (3)
  • packages/opentelemetry-instrumentation-mistralai/opentelemetry/instrumentation/mistralai/__init__.py (10 hunks)
  • packages/opentelemetry-instrumentation-mistralai/tests/conftest.py (2 hunks)
  • packages/opentelemetry-instrumentation-mistralai/tests/test_chat.py (29 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/opentelemetry-instrumentation-mistralai/tests/conftest.py
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.py: Store API keys only in environment variables/secure vaults; never hardcode secrets in code
Use Flake8 for code linting and adhere to its rules

Files:

  • packages/opentelemetry-instrumentation-mistralai/opentelemetry/instrumentation/mistralai/__init__.py
  • packages/opentelemetry-instrumentation-mistralai/tests/test_chat.py
🧬 Code graph analysis (2)
packages/opentelemetry-instrumentation-mistralai/opentelemetry/instrumentation/mistralai/__init__.py (3)
packages/opentelemetry-semantic-conventions-ai/opentelemetry/semconv_ai/__init__.py (1)
  • LLMRequestTypeValues (293-298)
packages/opentelemetry-instrumentation-google-generativeai/opentelemetry/instrumentation/google_generativeai/__init__.py (2)
  • _wrap (192-246)
  • _uninstrument (291-298)
packages/opentelemetry-instrumentation-ollama/opentelemetry/instrumentation/ollama/__init__.py (2)
  • _wrap (275-345)
  • _uninstrument (526-537)
packages/opentelemetry-instrumentation-mistralai/tests/test_chat.py (1)
packages/opentelemetry-instrumentation-mistralai/tests/conftest.py (2)
  • mistralai_client (51-52)
  • mistralai_async_client (56-57)
🪛 Ruff (0.12.2)
packages/opentelemetry-instrumentation-mistralai/opentelemetry/instrumentation/mistralai/__init__.py

521-521: Unused method argument: kwargs

(ARG002)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Test Packages (3.11)
  • GitHub Check: Test Packages (3.12)
  • GitHub Check: Test Packages (3.10)
  • GitHub Check: Lint
  • GitHub Check: Build Packages (3.11)

Comment on lines 176 to 179
for res in gen:
response += res.choices[0].delta.content
response += res.data.choices[0].delta.content

spans = span_exporter.get_finished_spans()
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Guard streaming chunks with optional content; avoid TypeError on None

Some stream chunks can have delta without content. Concatenate defensively.

-    for res in gen:
-        response += res.data.choices[0].delta.content
+    for res in gen:
+        chunk = res.data if hasattr(res, "data") else res
+        if getattr(chunk, "choices", None):
+            delta = getattr(chunk.choices[0], "delta", None)
+            if delta and getattr(delta, "content", None):
+                response += delta.content

Apply the same change to all streaming tests shown in this comment’s range.

Also applies to: 225-228, 284-287, 499-502, 547-550, 606-609

🤖 Prompt for AI Agents
In packages/opentelemetry-instrumentation-mistralai/tests/test_chat.py around
lines 176-179 (and similarly at 225-228, 284-287, 499-502, 547-550, 606-609),
the test assumes streaming chunk delta.content is always present and
concatenates directly, causing TypeError when content is None; update each loop
to defensively extract the content into a local variable and only append it if
it is not None (e.g., content = res.data.choices[0].delta.content; if content is
not None: response += content), applying the same guarded check to all the
listed line ranges.

@nirga nirga merged commit 5ac6e6a into main Sep 14, 2025
12 checks passed
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.

🐛 Bug Report: Mistral AI crash after package naming change

2 participants