From 7e585f601f7327785862155bdc049fe13770585d Mon Sep 17 00:00:00 2001 From: Simon Van de Kerckhove Date: Fri, 27 Mar 2026 15:02:10 +0000 Subject: [PATCH 1/5] fix(otel): record cached prompt tokens in spans Co-authored-by: mistral-hydra[bot] --- src/mistralai/extra/observability/otel.py | 37 ++++-- .../extra/tests/test_otel_tracing.py | 114 +++++++++++++++++- 2 files changed, 141 insertions(+), 10 deletions(-) diff --git a/src/mistralai/extra/observability/otel.py b/src/mistralai/extra/observability/otel.py index 6ea37389..b701ee15 100644 --- a/src/mistralai/extra/observability/otel.py +++ b/src/mistralai/extra/observability/otel.py @@ -40,6 +40,9 @@ os.getenv("MISTRAL_SDK_DEBUG_TRACING", "false").lower() == "true" ) DEBUG_HINT: str = "To see detailed tracing logs, set MISTRAL_SDK_DEBUG_TRACING=true." +# As of 2026-03-27: in GenAI semantic conventions, but not yet in +# opentelemetry-semantic-conventions for Python. +GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS = "gen_ai.usage.cache_read.input_tokens" class MistralAIAttributes: @@ -251,20 +254,36 @@ def _enrich_response_genai_attrs( # Usage usage = response_data.get("usage", {}) if usage: - attributes.update( - { - gen_ai_attributes.GEN_AI_USAGE_INPUT_TOKENS: usage.get( - "prompt_tokens", 0 - ), - gen_ai_attributes.GEN_AI_USAGE_OUTPUT_TOKENS: usage.get( - "completion_tokens", 0 - ), - } + attributes[gen_ai_attributes.GEN_AI_USAGE_INPUT_TOKENS] = usage.get( + "prompt_tokens", 0 ) + attributes[gen_ai_attributes.GEN_AI_USAGE_OUTPUT_TOKENS] = usage.get( + "completion_tokens", 0 + ) + + cached_input_tokens = _extract_cached_input_tokens(usage) + if cached_input_tokens is not None: + attributes[GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS] = cached_input_tokens set_available_attributes(span, attributes) +def _extract_cached_input_tokens(usage: dict[str, Any]) -> int | None: + prompt_token_details = usage.get("prompt_tokens_details") or usage.get( + "prompt_token_details" + ) + if isinstance(prompt_token_details, dict): + cached_tokens = prompt_token_details.get("cached_tokens") + if isinstance(cached_tokens, int): + return cached_tokens + + num_cached_tokens = usage.get("num_cached_tokens") + if isinstance(num_cached_tokens, int): + return num_cached_tokens + + return None + + def _enrich_create_agent(span: Span, response_data: dict[str, Any]) -> None: """Set agent-specific attributes from create_agent response. diff --git a/src/mistralai/extra/tests/test_otel_tracing.py b/src/mistralai/extra/tests/test_otel_tracing.py index ff30ba0c..ab09ff4d 100644 --- a/src/mistralai/extra/tests/test_otel_tracing.py +++ b/src/mistralai/extra/tests/test_otel_tracing.py @@ -462,6 +462,54 @@ def test_chat_completion_with_tool_calls(self): ], ) + def test_chat_completion_with_cached_prompt_tokens(self): + request = ChatCompletionRequest( + model="mistral-large-latest", + messages=[ + UserMessage(content="Summarize this document."), + ], + ) + response = { + "id": "cmpl-cache-001", + "object": "chat.completion", + "model": "mistral-large-latest", + "created": 1700000002, + "choices": [ + { + "index": 0, + "message": { + "role": "assistant", + "content": "Here is the summary.", + }, + "finish_reason": "stop", + } + ], + "usage": { + "prompt_tokens": 42, + "completion_tokens": 9, + "total_tokens": 51, + "prompt_tokens_details": { + "cached_tokens": 12, + }, + }, + } + + self._run_hook_lifecycle( + "chat_completion_v1_chat_completions_post", + request, + response, + ) + span = self._get_single_span() + + self.assertSpanAttributes( + span, + { + "gen_ai.usage.input_tokens": 42, + "gen_ai.usage.output_tokens": 9, + "gen_ai.usage.cache_read.input_tokens": 12, + }, + ) + # -- Embeddings ------------------------------------------------------------ def test_embeddings(self): @@ -1440,6 +1488,71 @@ def test_streaming_chat_completion_enriches_span(self): ], ) + def test_streaming_chat_completion_with_num_cached_tokens(self): + request = ChatCompletionRequest( + model="mistral-large-latest", + messages=[ + UserMessage(content="Continue."), + ], + ) + response_events = [ + CompletionEvent( + data=CompletionChunk( + id="cmpl-stream-cache-001", + model="mistral-large-latest", + object="chat.completion.chunk", + created=1700000000, + choices=[ + CompletionResponseStreamChoice( + index=0, + delta=DeltaMessage(role="assistant", content="Done."), + finish_reason=None, + ), + ], + ), + ), + CompletionEvent( + data=CompletionChunk( + id="cmpl-stream-cache-001", + model="mistral-large-latest", + object="chat.completion.chunk", + created=1700000000, + choices=[ + CompletionResponseStreamChoice( + index=0, + delta=DeltaMessage(content=""), + finish_reason="stop", + ), + ], + usage=UsageInfo.model_validate( + { + "prompt_tokens": 24, + "completion_tokens": 3, + "total_tokens": 27, + "num_cached_tokens": 10, + } + ), + ), + ), + ] + + self._run_hook_lifecycle( + "chat_completion_v1_chat_completions_post", + request, + response_events, + streaming=True, + ) + span = self._get_single_span() + + self.assertSpanAttributes( + span, + { + "gen_ai.usage.input_tokens": 24, + "gen_ai.usage.output_tokens": 3, + "gen_ai.usage.cache_read.input_tokens": 10, + }, + ) + # -- create_function_result (client-side tool execution) ------------------- def test_create_function_result_span_attributes(self): @@ -1526,7 +1639,6 @@ def failing_tool(x: int) -> str: "Expected an exception event on the span", ) - # -- Baggage propagation: gen_ai.conversation.id --------------------------- def test_conversation_id_from_baggage(self): From b6cce3daa9d28fc62aed4692d73a522b38996fa7 Mon Sep 17 00:00:00 2001 From: "mistral-hydra[bot]" Date: Fri, 27 Mar 2026 16:50:23 +0000 Subject: [PATCH 2/5] fix(otel): address cached token review feedback Co-authored-by: mistral-hydra[bot] --- src/mistralai/extra/observability/otel.py | 8 +- .../extra/tests/test_otel_tracing.py | 131 +++--------------- 2 files changed, 20 insertions(+), 119 deletions(-) diff --git a/src/mistralai/extra/observability/otel.py b/src/mistralai/extra/observability/otel.py index b701ee15..946bb7e8 100644 --- a/src/mistralai/extra/observability/otel.py +++ b/src/mistralai/extra/observability/otel.py @@ -40,9 +40,6 @@ os.getenv("MISTRAL_SDK_DEBUG_TRACING", "false").lower() == "true" ) DEBUG_HINT: str = "To see detailed tracing logs, set MISTRAL_SDK_DEBUG_TRACING=true." -# As of 2026-03-27: in GenAI semantic conventions, but not yet in -# opentelemetry-semantic-conventions for Python. -GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS = "gen_ai.usage.cache_read.input_tokens" class MistralAIAttributes: @@ -263,12 +260,15 @@ def _enrich_response_genai_attrs( cached_input_tokens = _extract_cached_input_tokens(usage) if cached_input_tokens is not None: - attributes[GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS] = cached_input_tokens + attributes["gen_ai.usage.cache_read.input_tokens"] = cached_input_tokens set_available_attributes(span, attributes) def _extract_cached_input_tokens(usage: dict[str, Any]) -> int | None: + # The generated usage schema currently exposes both plural/singular + # prompt token details names, plus the legacy top-level cached token count. + # Prefer the nested cached_tokens value when present. prompt_token_details = usage.get("prompt_tokens_details") or usage.get( "prompt_token_details" ) diff --git a/src/mistralai/extra/tests/test_otel_tracing.py b/src/mistralai/extra/tests/test_otel_tracing.py index ab09ff4d..ea0aa511 100644 --- a/src/mistralai/extra/tests/test_otel_tracing.py +++ b/src/mistralai/extra/tests/test_otel_tracing.py @@ -272,7 +272,16 @@ def test_simple_chat_completion(self): finish_reason="stop", ), ], - usage=UsageInfo(prompt_tokens=20, completion_tokens=25, total_tokens=45), + usage=UsageInfo.model_validate( + { + "prompt_tokens": 20, + "completion_tokens": 25, + "total_tokens": 45, + "prompt_tokens_details": { + "cached_tokens": 12, + }, + } + ), ) self._run_hook_lifecycle( @@ -301,6 +310,7 @@ def test_simple_chat_completion(self): "gen_ai.response.finish_reasons": ("stop",), "gen_ai.usage.input_tokens": 20, "gen_ai.usage.output_tokens": 25, + "gen_ai.usage.cache_read.input_tokens": 12, }, ) @@ -462,54 +472,6 @@ def test_chat_completion_with_tool_calls(self): ], ) - def test_chat_completion_with_cached_prompt_tokens(self): - request = ChatCompletionRequest( - model="mistral-large-latest", - messages=[ - UserMessage(content="Summarize this document."), - ], - ) - response = { - "id": "cmpl-cache-001", - "object": "chat.completion", - "model": "mistral-large-latest", - "created": 1700000002, - "choices": [ - { - "index": 0, - "message": { - "role": "assistant", - "content": "Here is the summary.", - }, - "finish_reason": "stop", - } - ], - "usage": { - "prompt_tokens": 42, - "completion_tokens": 9, - "total_tokens": 51, - "prompt_tokens_details": { - "cached_tokens": 12, - }, - }, - } - - self._run_hook_lifecycle( - "chat_completion_v1_chat_completions_post", - request, - response, - ) - span = self._get_single_span() - - self.assertSpanAttributes( - span, - { - "gen_ai.usage.input_tokens": 42, - "gen_ai.usage.output_tokens": 9, - "gen_ai.usage.cache_read.input_tokens": 12, - }, - ) - # -- Embeddings ------------------------------------------------------------ def test_embeddings(self): @@ -1439,7 +1401,10 @@ def test_streaming_chat_completion_enriches_span(self): ), ], usage=UsageInfo( - prompt_tokens=20, completion_tokens=8, total_tokens=28 + prompt_tokens=20, + completion_tokens=8, + total_tokens=28, + num_cached_tokens=10, ), ), ), @@ -1467,6 +1432,7 @@ def test_streaming_chat_completion_enriches_span(self): "gen_ai.response.model": "mistral-large-latest", "gen_ai.usage.input_tokens": 20, "gen_ai.usage.output_tokens": 8, + "gen_ai.usage.cache_read.input_tokens": 10, "gen_ai.response.finish_reasons": ("stop",), }, ) @@ -1488,71 +1454,6 @@ def test_streaming_chat_completion_enriches_span(self): ], ) - def test_streaming_chat_completion_with_num_cached_tokens(self): - request = ChatCompletionRequest( - model="mistral-large-latest", - messages=[ - UserMessage(content="Continue."), - ], - ) - response_events = [ - CompletionEvent( - data=CompletionChunk( - id="cmpl-stream-cache-001", - model="mistral-large-latest", - object="chat.completion.chunk", - created=1700000000, - choices=[ - CompletionResponseStreamChoice( - index=0, - delta=DeltaMessage(role="assistant", content="Done."), - finish_reason=None, - ), - ], - ), - ), - CompletionEvent( - data=CompletionChunk( - id="cmpl-stream-cache-001", - model="mistral-large-latest", - object="chat.completion.chunk", - created=1700000000, - choices=[ - CompletionResponseStreamChoice( - index=0, - delta=DeltaMessage(content=""), - finish_reason="stop", - ), - ], - usage=UsageInfo.model_validate( - { - "prompt_tokens": 24, - "completion_tokens": 3, - "total_tokens": 27, - "num_cached_tokens": 10, - } - ), - ), - ), - ] - - self._run_hook_lifecycle( - "chat_completion_v1_chat_completions_post", - request, - response_events, - streaming=True, - ) - span = self._get_single_span() - - self.assertSpanAttributes( - span, - { - "gen_ai.usage.input_tokens": 24, - "gen_ai.usage.output_tokens": 3, - "gen_ai.usage.cache_read.input_tokens": 10, - }, - ) - # -- create_function_result (client-side tool execution) ------------------- def test_create_function_result_span_attributes(self): From 8dd088e21b4d65bf6e685f86c4514dc7a89654e6 Mon Sep 17 00:00:00 2001 From: "mistral-hydra[bot]" Date: Fri, 27 Mar 2026 16:54:00 +0000 Subject: [PATCH 3/5] fix(otel): satisfy pyright for cached token usage Co-authored-by: mistral-hydra[bot] --- src/mistralai/extra/tests/test_otel_tracing.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/mistralai/extra/tests/test_otel_tracing.py b/src/mistralai/extra/tests/test_otel_tracing.py index ea0aa511..1aa0dd66 100644 --- a/src/mistralai/extra/tests/test_otel_tracing.py +++ b/src/mistralai/extra/tests/test_otel_tracing.py @@ -1400,11 +1400,13 @@ def test_streaming_chat_completion_enriches_span(self): finish_reason="stop", ), ], - usage=UsageInfo( - prompt_tokens=20, - completion_tokens=8, - total_tokens=28, - num_cached_tokens=10, + usage=UsageInfo.model_validate( + { + "prompt_tokens": 20, + "completion_tokens": 8, + "total_tokens": 28, + "num_cached_tokens": 10, + } ), ), ), From 06f680e890969fffac383579218b3b6bb620f465 Mon Sep 17 00:00:00 2001 From: "mistral-hydra[bot]" Date: Mon, 30 Mar 2026 08:21:43 +0000 Subject: [PATCH 4/5] fix(otel): relax semconv dependency range Co-authored-by: mistral-hydra[bot] --- pyproject.toml | 2 +- src/mistralai/extra/observability/otel.py | 15 ++++++++++++--- uv.lock | 20 ++++++++++---------- 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 7be2c9e6..2cfd934b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,7 +12,7 @@ dependencies = [ "python-dateutil >=2.8.2", "typing-inspection >=0.4.0", "opentelemetry-api (>=1.33.1,<2.0.0)", - "opentelemetry-semantic-conventions (>=0.60b1,<0.61)", + "opentelemetry-semantic-conventions (>=0.60b1,<0.62)", "jsonpath-python >=1.0.6", # required for speakeasy generated path with pagination ] diff --git a/src/mistralai/extra/observability/otel.py b/src/mistralai/extra/observability/otel.py index 946bb7e8..bf484b4f 100644 --- a/src/mistralai/extra/observability/otel.py +++ b/src/mistralai/extra/observability/otel.py @@ -40,6 +40,16 @@ os.getenv("MISTRAL_SDK_DEBUG_TRACING", "false").lower() == "true" ) DEBUG_HINT: str = "To see detailed tracing logs, set MISTRAL_SDK_DEBUG_TRACING=true." +GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS = getattr( + gen_ai_attributes, + "GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS", + "gen_ai.usage.cache_read.input_tokens", +) +GEN_AI_AGENT_VERSION = getattr( + gen_ai_attributes, + "GEN_AI_AGENT_VERSION", + "gen_ai.agent.version", +) class MistralAIAttributes: @@ -260,7 +270,7 @@ def _enrich_response_genai_attrs( cached_input_tokens = _extract_cached_input_tokens(usage) if cached_input_tokens is not None: - attributes["gen_ai.usage.cache_read.input_tokens"] = cached_input_tokens + attributes[GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS] = cached_input_tokens set_available_attributes(span, attributes) @@ -293,8 +303,7 @@ def _enrich_create_agent(span: Span, response_data: dict[str, Any]) -> None: gen_ai_attributes.GEN_AI_AGENT_DESCRIPTION: response_data.get("description"), gen_ai_attributes.GEN_AI_AGENT_ID: response_data.get("id"), gen_ai_attributes.GEN_AI_AGENT_NAME: response_data.get("name"), - # As of 2026-03-02: in convention, but not yet in opentelemetry-semantic-conventions - "gen_ai.agent.version": str(response_data.get("version")), + GEN_AI_AGENT_VERSION: str(response_data.get("version")), gen_ai_attributes.GEN_AI_REQUEST_MODEL: response_data.get("model"), gen_ai_attributes.GEN_AI_SYSTEM_INSTRUCTIONS: response_data.get("instructions"), } diff --git a/uv.lock b/uv.lock index 3769cd61..c3970947 100644 --- a/uv.lock +++ b/uv.lock @@ -620,7 +620,7 @@ requires-dist = [ { name = "jsonpath-python", specifier = ">=1.0.6" }, { name = "mcp", marker = "extra == 'agents'", specifier = ">=1.0,<2.0" }, { name = "opentelemetry-api", specifier = ">=1.33.1,<2.0.0" }, - { name = "opentelemetry-semantic-conventions", specifier = ">=0.60b1,<0.61" }, + { name = "opentelemetry-semantic-conventions", specifier = ">=0.60b1,<0.62" }, { name = "pydantic", specifier = ">=2.11.2" }, { name = "python-dateutil", specifier = ">=2.8.2" }, { name = "requests", marker = "extra == 'gcp'", specifier = ">=2.32.3" }, @@ -710,42 +710,42 @@ wheels = [ [[package]] name = "opentelemetry-api" -version = "1.39.1" +version = "1.40.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "importlib-metadata" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/97/b9/3161be15bb8e3ad01be8be5a968a9237c3027c5be504362ff800fca3e442/opentelemetry_api-1.39.1.tar.gz", hash = "sha256:fbde8c80e1b937a2c61f20347e91c0c18a1940cecf012d62e65a7caf08967c9c", size = 65767, upload-time = "2025-12-11T13:32:39.182Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2c/1d/4049a9e8698361cc1a1aa03a6c59e4fa4c71e0c0f94a30f988a6876a2ae6/opentelemetry_api-1.40.0.tar.gz", hash = "sha256:159be641c0b04d11e9ecd576906462773eb97ae1b657730f0ecf64d32071569f", size = 70851, upload-time = "2026-03-04T14:17:21.555Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cf/df/d3f1ddf4bb4cb50ed9b1139cc7b1c54c34a1e7ce8fd1b9a37c0d1551a6bd/opentelemetry_api-1.39.1-py3-none-any.whl", hash = "sha256:2edd8463432a7f8443edce90972169b195e7d6a05500cd29e6d13898187c9950", size = 66356, upload-time = "2025-12-11T13:32:17.304Z" }, + { url = "https://files.pythonhosted.org/packages/5f/bf/93795954016c522008da367da292adceed71cca6ee1717e1d64c83089099/opentelemetry_api-1.40.0-py3-none-any.whl", hash = "sha256:82dd69331ae74b06f6a874704be0cfaa49a1650e1537d4a813b86ecef7d0ecf9", size = 68676, upload-time = "2026-03-04T14:17:01.24Z" }, ] [[package]] name = "opentelemetry-sdk" -version = "1.39.1" +version = "1.40.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, { name = "opentelemetry-semantic-conventions" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/eb/fb/c76080c9ba07e1e8235d24cdcc4d125ef7aa3edf23eb4e497c2e50889adc/opentelemetry_sdk-1.39.1.tar.gz", hash = "sha256:cf4d4563caf7bff906c9f7967e2be22d0d6b349b908be0d90fb21c8e9c995cc6", size = 171460, upload-time = "2025-12-11T13:32:49.369Z" } +sdist = { url = "https://files.pythonhosted.org/packages/58/fd/3c3125b20ba18ce2155ba9ea74acb0ae5d25f8cd39cfd37455601b7955cc/opentelemetry_sdk-1.40.0.tar.gz", hash = "sha256:18e9f5ec20d859d268c7cb3c5198c8d105d073714db3de50b593b8c1345a48f2", size = 184252, upload-time = "2026-03-04T14:17:31.87Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7c/98/e91cf858f203d86f4eccdf763dcf01cf03f1dae80c3750f7e635bfa206b6/opentelemetry_sdk-1.39.1-py3-none-any.whl", hash = "sha256:4d5482c478513ecb0a5d938dcc61394e647066e0cc2676bee9f3af3f3f45f01c", size = 132565, upload-time = "2025-12-11T13:32:35.069Z" }, + { url = "https://files.pythonhosted.org/packages/2c/c5/6a852903d8bfac758c6dc6e9a68b015d3c33f2f1be5e9591e0f4b69c7e0a/opentelemetry_sdk-1.40.0-py3-none-any.whl", hash = "sha256:787d2154a71f4b3d81f20524a8ce061b7db667d24e46753f32a7bc48f1c1f3f1", size = 141951, upload-time = "2026-03-04T14:17:17.961Z" }, ] [[package]] name = "opentelemetry-semantic-conventions" -version = "0.60b1" +version = "0.61b0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "opentelemetry-api" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/91/df/553f93ed38bf22f4b999d9be9c185adb558982214f33eae539d3b5cd0858/opentelemetry_semantic_conventions-0.60b1.tar.gz", hash = "sha256:87c228b5a0669b748c76d76df6c364c369c28f1c465e50f661e39737e84bc953", size = 137935, upload-time = "2025-12-11T13:32:50.487Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6d/c0/4ae7973f3c2cfd2b6e321f1675626f0dab0a97027cc7a297474c9c8f3d04/opentelemetry_semantic_conventions-0.61b0.tar.gz", hash = "sha256:072f65473c5d7c6dc0355b27d6c9d1a679d63b6d4b4b16a9773062cb7e31192a", size = 145755, upload-time = "2026-03-04T14:17:32.664Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/7a/5e/5958555e09635d09b75de3c4f8b9cae7335ca545d77392ffe7331534c402/opentelemetry_semantic_conventions-0.60b1-py3-none-any.whl", hash = "sha256:9fa8c8b0c110da289809292b0591220d3a7b53c1526a23021e977d68597893fb", size = 219982, upload-time = "2025-12-11T13:32:36.955Z" }, + { url = "https://files.pythonhosted.org/packages/b2/37/cc6a55e448deaa9b27377d087da8615a3416d8ad523d5960b78dbeadd02a/opentelemetry_semantic_conventions-0.61b0-py3-none-any.whl", hash = "sha256:fa530a96be229795f8cef353739b618148b0fe2b4b3f005e60e262926c4d38e2", size = 231621, upload-time = "2026-03-04T14:17:19.33Z" }, ] [[package]] From c3bf8965ba6c326bac9f414e2225b9325975f6fc Mon Sep 17 00:00:00 2001 From: "mistral-hydra[bot]" Date: Mon, 30 Mar 2026 08:38:18 +0000 Subject: [PATCH 5/5] fix(otel): require semconv 0.61b0 Co-authored-by: mistral-hydra[bot] --- pyproject.toml | 2 +- src/mistralai/extra/observability/otel.py | 16 ++++------------ uv.lock | 2 +- 3 files changed, 6 insertions(+), 14 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 2cfd934b..b4ea7713 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,7 +12,7 @@ dependencies = [ "python-dateutil >=2.8.2", "typing-inspection >=0.4.0", "opentelemetry-api (>=1.33.1,<2.0.0)", - "opentelemetry-semantic-conventions (>=0.60b1,<0.62)", + "opentelemetry-semantic-conventions (>=0.61b0,<0.62)", "jsonpath-python >=1.0.6", # required for speakeasy generated path with pagination ] diff --git a/src/mistralai/extra/observability/otel.py b/src/mistralai/extra/observability/otel.py index bf484b4f..34de57b4 100644 --- a/src/mistralai/extra/observability/otel.py +++ b/src/mistralai/extra/observability/otel.py @@ -40,16 +40,6 @@ os.getenv("MISTRAL_SDK_DEBUG_TRACING", "false").lower() == "true" ) DEBUG_HINT: str = "To see detailed tracing logs, set MISTRAL_SDK_DEBUG_TRACING=true." -GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS = getattr( - gen_ai_attributes, - "GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS", - "gen_ai.usage.cache_read.input_tokens", -) -GEN_AI_AGENT_VERSION = getattr( - gen_ai_attributes, - "GEN_AI_AGENT_VERSION", - "gen_ai.agent.version", -) class MistralAIAttributes: @@ -270,7 +260,9 @@ def _enrich_response_genai_attrs( cached_input_tokens = _extract_cached_input_tokens(usage) if cached_input_tokens is not None: - attributes[GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS] = cached_input_tokens + attributes[ + gen_ai_attributes.GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS + ] = cached_input_tokens set_available_attributes(span, attributes) @@ -303,7 +295,7 @@ def _enrich_create_agent(span: Span, response_data: dict[str, Any]) -> None: gen_ai_attributes.GEN_AI_AGENT_DESCRIPTION: response_data.get("description"), gen_ai_attributes.GEN_AI_AGENT_ID: response_data.get("id"), gen_ai_attributes.GEN_AI_AGENT_NAME: response_data.get("name"), - GEN_AI_AGENT_VERSION: str(response_data.get("version")), + gen_ai_attributes.GEN_AI_AGENT_VERSION: str(response_data.get("version")), gen_ai_attributes.GEN_AI_REQUEST_MODEL: response_data.get("model"), gen_ai_attributes.GEN_AI_SYSTEM_INSTRUCTIONS: response_data.get("instructions"), } diff --git a/uv.lock b/uv.lock index c3970947..f20e973d 100644 --- a/uv.lock +++ b/uv.lock @@ -620,7 +620,7 @@ requires-dist = [ { name = "jsonpath-python", specifier = ">=1.0.6" }, { name = "mcp", marker = "extra == 'agents'", specifier = ">=1.0,<2.0" }, { name = "opentelemetry-api", specifier = ">=1.33.1,<2.0.0" }, - { name = "opentelemetry-semantic-conventions", specifier = ">=0.60b1,<0.62" }, + { name = "opentelemetry-semantic-conventions", specifier = ">=0.61b0,<0.62" }, { name = "pydantic", specifier = ">=2.11.2" }, { name = "python-dateutil", specifier = ">=2.8.2" }, { name = "requests", marker = "extra == 'gcp'", specifier = ">=2.32.3" },