fix(mistralai): instrumentation for version 1.9+ compatibility#3376
fix(mistralai): instrumentation for version 1.9+ compatibility#3376
Conversation
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>
|
Note Other AI code review bot(s) detectedCodeRabbit 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. WalkthroughUpdates 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
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45–60 minutes Possibly related PRs
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing touches
🧪 Generate unit tests
Comment |
There was a problem hiding this comment.
Important
Looks good to me! 👍
Reviewed everything up to 5c5f746 in 1 minute and 4 seconds. Click for details.
- Reviewed
416lines of code in5files - Skipped
1files when reviewing. - Skipped posting
2draft 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 by changing your verbosity settings, reacting with 👍 or 👎, replying to comments, or adding code review rules.
There was a problem hiding this comment.
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: useinputs(1.x) instead ofinput.This breaks spans’ prompt attributes in legacy mode and user-message events in event mode for embeddings. Support
inputsand keep a fallback forinput.- 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 frominputsand handle positional args.
args[0]is themodelwhen passed positionally; the actualinputswould beargs[1]. Also migrate toinputswith a fallback toinput.- 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.fixturepackages/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.roleApply 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
modelis passed positionally,messagesisargs[1]. Current code readsargs[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 unusedkwargsin_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.
⛔ Files ignored due to path filters (1)
packages/opentelemetry-instrumentation-mistralai/poetry.lockis 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.pypackages/opentelemetry-instrumentation-mistralai/opentelemetry/instrumentation/mistralai/__init__.pypackages/opentelemetry-instrumentation-mistralai/tests/test_embeddings.pypackages/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 Mistralaligns 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
…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>
There was a problem hiding this comment.
Caution
Changes requested ❌
Reviewed f2bec07 in 3 minutes and 26 seconds. Click for details.
- Reviewed
5735lines of code in21files - Skipped
1files when reviewing. - Skipped posting
7draft 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 thebodyfield 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 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" |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 Nonechoice.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_pieceRepeat 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) >= 16Also 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 backAlso applies to: 387-389, 440-442
349-354: Guard arithmetic when prompt tokens may be missingIf 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 _awrapBoth 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 errorsCurrently, 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 nitMinor 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.
⛔ Files ignored due to path filters (1)
packages/opentelemetry-instrumentation-mistralai/poetry.lockis 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.yamlpackages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_chat_with_events_with_content.yamlpackages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_embeddings/test_mistral_embeddings_with_events_with_content.yamlpackages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_streaming_chat_legacy.yamlpackages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_async_streaming_chat_with_events_with_content.yamlpackages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_chat_with_events_with_no_content.yamlpackages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_async_streaming_chat_with_events_with_no_content.yamlpackages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_embeddings/test_mistral_embeddings_legacy.yamlpackages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_async_chat_with_events_with_no_content.yamlpackages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_async_chat_legacy.yamlpackages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_chat_legacy.yamlpackages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_async_streaming_chat_legacy.yamlpackages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_embeddings/test_mistral_embeddings_with_events_with_no_content.yamlpackages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_streaming_chat_with_events_with_no_content.yamlpackages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_async_chat_with_events_with_content.yamlpackages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_chat/test_mistralai_streaming_chat_with_events_with_content.yamlpackages/opentelemetry-instrumentation-mistralai/tests/cassettes/test_embeddings/test_mistral_async_embeddings_with_events_with_content.yamlpackages/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.pypackages/opentelemetry-instrumentation-mistralai/opentelemetry/instrumentation/mistralai/__init__.pypackages/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 pipefailecho "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
fiecho
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
fiecho
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
fiecho
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 -->
| 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), | ||
| ) | ||
|
|
There was a problem hiding this comment.
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.
| - __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 |
There was a problem hiding this comment.
🛠️ Refactor suggestion
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.
| - __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: |
There was a problem hiding this comment.
🛠️ Refactor suggestion
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 valuesAlso 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 |
There was a problem hiding this comment.
💡 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 || trueLength 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
fiLength 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.
| 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 |
There was a problem hiding this comment.
🛠️ 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-3f9e881e7cb1Also 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.
| - __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: |
There was a problem hiding this comment.
🛠️ Refactor suggestion
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.
| - __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 |
There was a problem hiding this comment.
🛠️ Refactor suggestion
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"}' |
There was a problem hiding this comment.
💡 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.
| 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 |
There was a problem hiding this comment.
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.
| 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.
| 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' |
There was a problem hiding this comment.
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.
| 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.
- 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>
There was a problem hiding this comment.
Important
Looks good to me! 👍
Reviewed 55175e3 in 1 minute and 32 seconds. Click for details.
- Reviewed
121lines of code in3files - Skipped
0files when reviewing. - Skipped posting
4draft 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%<= threshold50%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%<= threshold50%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%<= threshold50%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 by changing your verbosity settings, reacting with 👍 or 👎, replying to comments, or adding code review rules.
There was a problem hiding this comment.
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 uninstrumentationYou 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 deltasEnsure 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 concernsMirror 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-ininput
- Hardcoding prompts.0.role="user" is wrong if the first message is System/Assistant. Let enumeration define roles.
- Rename local
inputto 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 IDsIDs 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_idRepeat 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 backAlso applies to: 396-398, 458-460
packages/opentelemetry-instrumentation-mistralai/opentelemetry/instrumentation/mistralai/__init__.py (3)
463-467: Remove redundant async call branchBoth 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 eventsEmitting 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 kwargsKeep signature compatible with BaseInstrumentor but avoid the warning.
- def _uninstrument(self, **kwargs): + def _uninstrument(self, **_kwargs):Or add
# noqa: ARG002to 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.
📒 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__.pypackages/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)
| for res in gen: | ||
| response += res.choices[0].delta.content | ||
| response += res.data.choices[0].delta.content | ||
|
|
||
| spans = span_exporter.get_finished_spans() |
There was a problem hiding this comment.
🛠️ 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.contentApply 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.
Summary
Fixes #3375 - Resolves crash after mistralai package naming change in version 1.9+
Breaking Changes
Changes Made
Import Updates
mistralai.models.chat_completiontomistralai.modelsChatCompletionResponse,AssistantMessage,UserMessage, etc.Client API Migration
MistralClient/MistralAsyncClientwith unifiedMistralclientclient.chat()→client.chat.complete()client.chat_stream()→client.chat.stream()client.embeddings(input=...)→client.embeddings.create(inputs=...)complete_async(),stream_async(),create_async()Message Handling
ChatMessagewith specific message types:UserMessage,AssistantMessage,SystemMessageTest Updates
Mistral()clientinput→inputsfor embeddings)Test Plan
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-mistralaifor Mistral AI SDK 1.9+ compatibility, including import, client API, and test changes.mistralai.models.chat_completiontomistralai.models.ChatCompletionResponse,AssistantMessage,UserMessage, etc.MistralClient/MistralAsyncClientwithMistralclient.client.chat()→client.chat.complete(),client.chat_stream()→client.chat.stream(),client.embeddings(input=...)→client.embeddings.create(inputs=...).complete_async(),stream_async(),create_async().ChatMessagewithUserMessage,AssistantMessage,SystemMessage.Mistral()client.input→inputsfor embeddings).This description was created by
for 55175e3. You can customize this summary. It will automatically update as commits are pushed.
Summary by CodeRabbit
New Features
Bug Fixes
Chores