From 1905da5e4e148f9034fd39b5eac308c70eb92541 Mon Sep 17 00:00:00 2001 From: Radhika Gupta Date: Fri, 11 Sep 2026 14:10:38 -0700 Subject: [PATCH 01/10] Capture top_k and choice count on chat in langchain --- .../genai/langchain/callback_handler.py | 6 ++++ .../tests/test_callback_handler.py | 36 +++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/instrumentation/opentelemetry-instrumentation-genai-langchain/src/opentelemetry/instrumentation/genai/langchain/callback_handler.py b/instrumentation/opentelemetry-instrumentation-genai-langchain/src/opentelemetry/instrumentation/genai/langchain/callback_handler.py index 5ce311542..c74d9ffad 100644 --- a/instrumentation/opentelemetry-instrumentation-genai-langchain/src/opentelemetry/instrumentation/genai/langchain/callback_handler.py +++ b/instrumentation/opentelemetry-instrumentation-genai-langchain/src/opentelemetry/instrumentation/genai/langchain/callback_handler.py @@ -294,6 +294,8 @@ def on_chat_model_start( request_model = request_model.removeprefix("models/") # Initialize variables with default values to avoid "possibly unbound" errors + request_choice_count = None + top_k = None top_p = None frequency_penalty = None presence_penalty = None @@ -303,6 +305,8 @@ def on_chat_model_start( max_tokens = None if params is not None: + request_choice_count = params.get("n") + top_k = params.get("top_k") top_p = params.get("top_p") frequency_penalty = params.get("frequency_penalty") presence_penalty = params.get("presence_penalty") @@ -346,6 +350,8 @@ def on_chat_model_start( llm_invocation.conversation_id = _conversation_id(metadata) llm_invocation.input_messages = input_messages llm_invocation.top_p = top_p + llm_invocation.top_k = top_k + llm_invocation.request_choice_count = request_choice_count llm_invocation.frequency_penalty = frequency_penalty llm_invocation.presence_penalty = presence_penalty llm_invocation.stop_sequences = stop_sequences diff --git a/instrumentation/opentelemetry-instrumentation-genai-langchain/tests/test_callback_handler.py b/instrumentation/opentelemetry-instrumentation-genai-langchain/tests/test_callback_handler.py index 214dea68e..ee79ee4ee 100644 --- a/instrumentation/opentelemetry-instrumentation-genai-langchain/tests/test_callback_handler.py +++ b/instrumentation/opentelemetry-instrumentation-genai-langchain/tests/test_callback_handler.py @@ -2800,6 +2800,42 @@ def test_on_chat_model_start_captures_input_messages_when_content_enabled(): assert blob.content == _REAL_PNG_BYTES +def test_on_chat_model_start_captures_top_k_and_choice_count(): + run_id = _run_id() + handler, _, llm_inv = _make_handler_with_llm_invocation(run_id) + + handler.on_chat_model_start( + serialized={}, + messages=[[HumanMessage(content="Hello")]], + run_id=run_id, + invocation_params={ + "params": { + "model_name": "gpt-4o", + "top_k": 40, + "n": 3, + } + }, + ) + + assert llm_inv.top_k == 40 + assert llm_inv.request_choice_count == 3 + + +def test_on_chat_model_start_defaults_top_k_and_choice_count_to_none(): + run_id = _run_id() + handler, _, llm_inv = _make_handler_with_llm_invocation(run_id) + + handler.on_chat_model_start( + serialized={}, + messages=[[HumanMessage(content="Hello")]], + run_id=run_id, + invocation_params={"model_name": "gpt-4o"}, + ) + + assert llm_inv.top_k is None + assert llm_inv.request_choice_count is None + + def test_on_chat_model_start_preserves_message_name(): run_id = _run_id() handler, telemetry, llm_inv = _make_handler_with_llm_invocation(run_id) From 6b51e2d2abda9453f05c3415ee466edec393d088 Mon Sep 17 00:00:00 2001 From: Radhika Gupta Date: Fri, 11 Sep 2026 14:13:17 -0700 Subject: [PATCH 02/10] Added CHANGELOG --- .../.changelog/684.added | 1 + 1 file changed, 1 insertion(+) create mode 100644 instrumentation/opentelemetry-instrumentation-genai-langchain/.changelog/684.added diff --git a/instrumentation/opentelemetry-instrumentation-genai-langchain/.changelog/684.added b/instrumentation/opentelemetry-instrumentation-genai-langchain/.changelog/684.added new file mode 100644 index 000000000..6722344bb --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-genai-langchain/.changelog/684.added @@ -0,0 +1 @@ +Capture top_k - `gen_ai.request.top_k` and choice count - `gen_ai.request.choice.count` on chat From 77cc36a1f825f987fea7b5067cf1908086594929 Mon Sep 17 00:00:00 2001 From: Radhika Gupta Date: Mon, 14 Sep 2026 07:36:05 -0700 Subject: [PATCH 03/10] Remove unit tests and add integration tests instead --- .../tests/test_callback_handler.py | 36 ------------- .../tests/test_llm_call.py | 53 +++++++++++++++++++ 2 files changed, 53 insertions(+), 36 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-genai-langchain/tests/test_callback_handler.py b/instrumentation/opentelemetry-instrumentation-genai-langchain/tests/test_callback_handler.py index ee79ee4ee..214dea68e 100644 --- a/instrumentation/opentelemetry-instrumentation-genai-langchain/tests/test_callback_handler.py +++ b/instrumentation/opentelemetry-instrumentation-genai-langchain/tests/test_callback_handler.py @@ -2800,42 +2800,6 @@ def test_on_chat_model_start_captures_input_messages_when_content_enabled(): assert blob.content == _REAL_PNG_BYTES -def test_on_chat_model_start_captures_top_k_and_choice_count(): - run_id = _run_id() - handler, _, llm_inv = _make_handler_with_llm_invocation(run_id) - - handler.on_chat_model_start( - serialized={}, - messages=[[HumanMessage(content="Hello")]], - run_id=run_id, - invocation_params={ - "params": { - "model_name": "gpt-4o", - "top_k": 40, - "n": 3, - } - }, - ) - - assert llm_inv.top_k == 40 - assert llm_inv.request_choice_count == 3 - - -def test_on_chat_model_start_defaults_top_k_and_choice_count_to_none(): - run_id = _run_id() - handler, _, llm_inv = _make_handler_with_llm_invocation(run_id) - - handler.on_chat_model_start( - serialized={}, - messages=[[HumanMessage(content="Hello")]], - run_id=run_id, - invocation_params={"model_name": "gpt-4o"}, - ) - - assert llm_inv.top_k is None - assert llm_inv.request_choice_count is None - - def test_on_chat_model_start_preserves_message_name(): run_id = _run_id() handler, telemetry, llm_inv = _make_handler_with_llm_invocation(run_id) diff --git a/instrumentation/opentelemetry-instrumentation-genai-langchain/tests/test_llm_call.py b/instrumentation/opentelemetry-instrumentation-genai-langchain/tests/test_llm_call.py index a417bf0d6..11dd79b54 100644 --- a/instrumentation/opentelemetry-instrumentation-genai-langchain/tests/test_llm_call.py +++ b/instrumentation/opentelemetry-instrumentation-genai-langchain/tests/test_llm_call.py @@ -804,6 +804,59 @@ def _identifying_params(self): assert output_messages[0]["name"] == "assistant_bob" assert output_messages[0]["role"] == "assistant" + assert gen_ai_attributes.GEN_AI_REQUEST_TOP_K not in span.attributes + assert ( + gen_ai_attributes.GEN_AI_REQUEST_CHOICE_COUNT not in span.attributes + ) + + +def test_chat_model_captures_top_k_and_choice_count( + span_exporter, + log_exporter, + tracer_provider, + meter_provider, + logger_provider, +): + class _TestModel(FakeMessagesListChatModel): + model_name: str = "test-model" + top_k: int = 40 + n: int = 3 + + @property + def _identifying_params(self): + return { + "model_name": self.model_name, + "top_k": self.top_k, + "n": self.n, + } + + with instrument( + LangChainInstrumentor(), + tracer_provider=tracer_provider, + meter_provider=meter_provider, + logger_provider=logger_provider, + content_capture="SPAN_AND_EVENT", + ): + model = _TestModel(responses=[AIMessage(content="Hello there!")]) + model.invoke([HumanMessage(content="Hi!")]) + + (span,) = span_exporter.get_finished_spans() + assert span.attributes[gen_ai_attributes.GEN_AI_REQUEST_TOP_K] == 40 + assert ( + span.attributes[gen_ai_attributes.GEN_AI_REQUEST_CHOICE_COUNT] == 3 + ) + + (log,) = log_exporter.get_finished_logs() + assert ( + log.log_record.attributes[gen_ai_attributes.GEN_AI_REQUEST_TOP_K] == 40 + ) + assert ( + log.log_record.attributes[ + gen_ai_attributes.GEN_AI_REQUEST_CHOICE_COUNT + ] + == 3 + ) + def test_chat_model_uses_ls_model_name_from_metadata( span_exporter, From dbd88886cabf5d774e895d41780aae07a9f4a938 Mon Sep 17 00:00:00 2001 From: Radhika Gupta Date: Mon, 14 Sep 2026 07:52:28 -0700 Subject: [PATCH 04/10] Fix formatting --- .../tests/test_llm_call.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-genai-langchain/tests/test_llm_call.py b/instrumentation/opentelemetry-instrumentation-genai-langchain/tests/test_llm_call.py index 11dd79b54..e9affa2d0 100644 --- a/instrumentation/opentelemetry-instrumentation-genai-langchain/tests/test_llm_call.py +++ b/instrumentation/opentelemetry-instrumentation-genai-langchain/tests/test_llm_call.py @@ -805,9 +805,7 @@ def _identifying_params(self): assert output_messages[0]["role"] == "assistant" assert gen_ai_attributes.GEN_AI_REQUEST_TOP_K not in span.attributes - assert ( - gen_ai_attributes.GEN_AI_REQUEST_CHOICE_COUNT not in span.attributes - ) + assert gen_ai_attributes.GEN_AI_REQUEST_CHOICE_COUNT not in span.attributes def test_chat_model_captures_top_k_and_choice_count( @@ -842,9 +840,7 @@ def _identifying_params(self): (span,) = span_exporter.get_finished_spans() assert span.attributes[gen_ai_attributes.GEN_AI_REQUEST_TOP_K] == 40 - assert ( - span.attributes[gen_ai_attributes.GEN_AI_REQUEST_CHOICE_COUNT] == 3 - ) + assert span.attributes[gen_ai_attributes.GEN_AI_REQUEST_CHOICE_COUNT] == 3 (log,) = log_exporter.get_finished_logs() assert ( From 7f8c0f012c92ab5a3bfb98f87416ffa86bd125c1 Mon Sep 17 00:00:00 2001 From: Radhika Gupta Date: Mon, 14 Sep 2026 08:08:45 -0700 Subject: [PATCH 05/10] Retrigger CI/CD pipeline From 0497c2e923cce5322ab9abf9f5fe0fce126e414e Mon Sep 17 00:00:00 2001 From: Radhika Gupta Date: Mon, 14 Sep 2026 10:56:52 -0700 Subject: [PATCH 06/10] Add VCR cassettes --- ...ropic_captures_top_k_and_choice_count.yaml | 45 ++++++ ...est_chat_openai_captures_choice_count.yaml | 152 ++++++++++++++++++ .../tests/test_llm_call.py | 84 ++++++++-- 3 files changed, 267 insertions(+), 14 deletions(-) create mode 100644 instrumentation/opentelemetry-instrumentation-genai-langchain/tests/cassettes/test_chat_anthropic_captures_top_k_and_choice_count.yaml create mode 100644 instrumentation/opentelemetry-instrumentation-genai-langchain/tests/cassettes/test_chat_openai_captures_choice_count.yaml diff --git a/instrumentation/opentelemetry-instrumentation-genai-langchain/tests/cassettes/test_chat_anthropic_captures_top_k_and_choice_count.yaml b/instrumentation/opentelemetry-instrumentation-genai-langchain/tests/cassettes/test_chat_anthropic_captures_top_k_and_choice_count.yaml new file mode 100644 index 000000000..941550dd3 --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-genai-langchain/tests/cassettes/test_chat_anthropic_captures_top_k_and_choice_count.yaml @@ -0,0 +1,45 @@ +# TODO: this is generated by AI, re-record +interactions: +- request: + body: |- + {"model":"claude-sonnet-4-5","max_tokens":32,"messages":[{"role":"user","content":"Reply with one short word."}],"top_k":40,"n":3} + headers: + Content-Type: + - application/json + User-Agent: + - !!binary | + QW50aHJvcGljL1B5dGhvbiAxLjAuMA== + anthropic-version: + - '2023-06-01' + x-api-key: + - test_key + method: POST + uri: https://api.anthropic.com/v1/messages + response: + body: + string: |- + { + "id": "msg_01TopKChoiceCountPlaceholder", + "type": "message", + "role": "assistant", + "model": "claude-sonnet-4-5", + "content": [ + { + "type": "text", + "text": "Okay" + } + ], + "stop_reason": "end_turn", + "stop_sequence": null, + "usage": { + "input_tokens": 12, + "output_tokens": 1 + } + } + headers: + Content-Type: + - application/json + status: + code: 200 + message: OK +version: 1 diff --git a/instrumentation/opentelemetry-instrumentation-genai-langchain/tests/cassettes/test_chat_openai_captures_choice_count.yaml b/instrumentation/opentelemetry-instrumentation-genai-langchain/tests/cassettes/test_chat_openai_captures_choice_count.yaml new file mode 100644 index 000000000..902350040 --- /dev/null +++ b/instrumentation/opentelemetry-instrumentation-genai-langchain/tests/cassettes/test_chat_openai_captures_choice_count.yaml @@ -0,0 +1,152 @@ +interactions: +- request: + body: |- + { + "messages": [ + { + "content": "Reply with one short word.", + "role": "user" + } + ], + "model": "gpt-5.1", + "n": 3, + "stream": false + } + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate, zstd + authorization: + - Bearer test_openai_api_key + connection: + - keep-alive + content-length: + - '108' + content-type: + - application/json + host: + - api.openai.com + user-agent: + - OpenAI/Python 2.48.0 + x-stainless-arch: + - other:amd64 + x-stainless-async: + - 'false' + x-stainless-lang: + - python + x-stainless-os: + - Windows + x-stainless-package-version: + - 3.7.0 + x-stainless-raw-response: + - 'true' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.13.15 + method: POST + uri: https://api.openai.com/v1/chat/completions + response: + body: + string: |- + { + "choices": [ + { + "finish_reason": "stop", + "index": 0, + "logprobs": null, + "message": { + "annotations": [], + "content": "Okay", + "refusal": null, + "role": "assistant" + } + }, + { + "finish_reason": "stop", + "index": 1, + "logprobs": null, + "message": { + "annotations": [], + "content": "Okay", + "refusal": null, + "role": "assistant" + } + }, + { + "finish_reason": "stop", + "index": 2, + "logprobs": null, + "message": { + "annotations": [], + "content": "Okay", + "refusal": null, + "role": "assistant" + } + } + ], + "created": 1789402853, + "id": "chatcmpl-EO3iH8BjWLEPGDghInTosUBN4mUPS", + "model": "gpt-5.1-2025-11-13", + "object": "chat.completion", + "service_tier": "default", + "system_fingerprint": null, + "usage": { + "completion_tokens": 33, + "completion_tokens_details": { + "accepted_prediction_tokens": 0, + "audio_tokens": 0, + "reasoning_tokens": 0, + "rejected_prediction_tokens": 0 + }, + "prompt_tokens": 12, + "prompt_tokens_details": { + "audio_tokens": 0, + "cached_tokens": 0 + }, + "total_tokens": 45 + } + } + headers: + Set-Cookie: test_set_cookie + content-length: + - '2710' + content-type: + - application/json + date: + - Mon, 14 Sep 2026 16:20:53 GMT + openai-organization: test_openai_org_id + openai-project: test_openai_project_id + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + x-accel-buffering: + - 'no' + x-content-type-options: + - nosniff + x-ratelimit-abusepenalty-active: + - 'False' + x-ratelimit-key: + - gpt-5.1 + x-ratelimit-limit-requests: + - '5000' + x-ratelimit-limit-tokens: + - '500000' + x-ratelimit-remaining-requests: + - '4999' + x-ratelimit-remaining-tokens: + - '499988' + x-ratelimit-renewalperiod-requests: + - '60' + x-ratelimit-renewalperiod-tokens: + - '60' + x-ratelimit-reset-requests: + - '0' + x-ratelimit-reset-tokens: + - '0' + status: + code: 200 + message: OK +version: 1 diff --git a/instrumentation/opentelemetry-instrumentation-genai-langchain/tests/test_llm_call.py b/instrumentation/opentelemetry-instrumentation-genai-langchain/tests/test_llm_call.py index e9affa2d0..0f38913e5 100644 --- a/instrumentation/opentelemetry-instrumentation-genai-langchain/tests/test_llm_call.py +++ b/instrumentation/opentelemetry-instrumentation-genai-langchain/tests/test_llm_call.py @@ -808,25 +808,77 @@ def _identifying_params(self): assert gen_ai_attributes.GEN_AI_REQUEST_CHOICE_COUNT not in span.attributes -def test_chat_model_captures_top_k_and_choice_count( +@pytest.mark.skipif( + _langchain_openai_version() < (1, 0, 0), + reason="cassette was recorded with langchain-openai 1.x", +) +def test_chat_openai_captures_choice_count( span_exporter, log_exporter, tracer_provider, meter_provider, logger_provider, + vcr, ): - class _TestModel(FakeMessagesListChatModel): - model_name: str = "test-model" - top_k: int = 40 - n: int = 3 + model = ChatOpenAI( + model="gpt-5.1", + api_key="test_openai_api_key", + n=3, + ) - @property - def _identifying_params(self): - return { - "model_name": self.model_name, - "top_k": self.top_k, - "n": self.n, - } + with instrument( + LangChainInstrumentor(), + tracer_provider=tracer_provider, + meter_provider=meter_provider, + logger_provider=logger_provider, + content_capture="SPAN_AND_EVENT", + ): + with vcr.use_cassette( + "test_chat_openai_captures_choice_count.yaml" + ): + model.invoke( + [HumanMessage(content="Reply with one short word.")] + ) + + (span,) = span_exporter.get_finished_spans() + assert span.attributes[gen_ai_attributes.GEN_AI_REQUEST_CHOICE_COUNT] == 3 + + (log,) = log_exporter.get_finished_logs() + assert ( + log.log_record.attributes[ + gen_ai_attributes.GEN_AI_REQUEST_CHOICE_COUNT + ] + == 3 + ) + + +def test_chat_anthropic_captures_top_k_and_choice_count( + span_exporter, + log_exporter, + tracer_provider, + meter_provider, + logger_provider, + monkeypatch, + vcr, +): + model = ChatAnthropic( + model="claude-sonnet-4-5", + api_key="test_key", + max_tokens=32, + top_k=40, + ) + create_message = model._client.messages.create + + def create_message_with_choice_count(**kwargs): + choice_count = kwargs.pop("n") + kwargs["extra_body"] = {"n": choice_count} + return create_message(**kwargs) + + monkeypatch.setattr( + model._client.messages, + "create", + create_message_with_choice_count, + ) with instrument( LangChainInstrumentor(), @@ -835,8 +887,12 @@ def _identifying_params(self): logger_provider=logger_provider, content_capture="SPAN_AND_EVENT", ): - model = _TestModel(responses=[AIMessage(content="Hello there!")]) - model.invoke([HumanMessage(content="Hi!")]) + with vcr.use_cassette( + "test_chat_anthropic_captures_top_k_and_choice_count.yaml" + ): + model.invoke( + [HumanMessage(content="Reply with one short word.")], n=3 + ) (span,) = span_exporter.get_finished_spans() assert span.attributes[gen_ai_attributes.GEN_AI_REQUEST_TOP_K] == 40 From 65451c88c7e79db2eb4df7ad9536b7c1ef89ee59 Mon Sep 17 00:00:00 2001 From: Radhika Gupta Date: Mon, 14 Sep 2026 11:33:01 -0700 Subject: [PATCH 07/10] Fix formatting --- .../tests/test_llm_call.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-genai-langchain/tests/test_llm_call.py b/instrumentation/opentelemetry-instrumentation-genai-langchain/tests/test_llm_call.py index 0f38913e5..827c0d712 100644 --- a/instrumentation/opentelemetry-instrumentation-genai-langchain/tests/test_llm_call.py +++ b/instrumentation/opentelemetry-instrumentation-genai-langchain/tests/test_llm_call.py @@ -833,12 +833,8 @@ def test_chat_openai_captures_choice_count( logger_provider=logger_provider, content_capture="SPAN_AND_EVENT", ): - with vcr.use_cassette( - "test_chat_openai_captures_choice_count.yaml" - ): - model.invoke( - [HumanMessage(content="Reply with one short word.")] - ) + with vcr.use_cassette("test_chat_openai_captures_choice_count.yaml"): + model.invoke([HumanMessage(content="Reply with one short word.")]) (span,) = span_exporter.get_finished_spans() assert span.attributes[gen_ai_attributes.GEN_AI_REQUEST_CHOICE_COUNT] == 3 From 8aeae87f266c5c8e384a13ca62de6ae213d6f0f7 Mon Sep 17 00:00:00 2001 From: Radhika Gupta Date: Mon, 14 Sep 2026 12:21:22 -0700 Subject: [PATCH 08/10] Fix test --- .../tests/test_llm_call.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-genai-langchain/tests/test_llm_call.py b/instrumentation/opentelemetry-instrumentation-genai-langchain/tests/test_llm_call.py index 827c0d712..11a507677 100644 --- a/instrumentation/opentelemetry-instrumentation-genai-langchain/tests/test_llm_call.py +++ b/instrumentation/opentelemetry-instrumentation-genai-langchain/tests/test_llm_call.py @@ -848,6 +848,10 @@ def test_chat_openai_captures_choice_count( ) +@pytest.mark.vcr() +@pytest.mark.cassette( + "test_chat_anthropic_captures_top_k_and_choice_count" +) def test_chat_anthropic_captures_top_k_and_choice_count( span_exporter, log_exporter, @@ -855,7 +859,6 @@ def test_chat_anthropic_captures_top_k_and_choice_count( meter_provider, logger_provider, monkeypatch, - vcr, ): model = ChatAnthropic( model="claude-sonnet-4-5", @@ -883,12 +886,9 @@ def create_message_with_choice_count(**kwargs): logger_provider=logger_provider, content_capture="SPAN_AND_EVENT", ): - with vcr.use_cassette( - "test_chat_anthropic_captures_top_k_and_choice_count.yaml" - ): - model.invoke( - [HumanMessage(content="Reply with one short word.")], n=3 - ) + model.invoke( + [HumanMessage(content="Reply with one short word.")], n=3 + ) (span,) = span_exporter.get_finished_spans() assert span.attributes[gen_ai_attributes.GEN_AI_REQUEST_TOP_K] == 40 From 9da0b73ba82ef64bfd95ce155e8b1c54e7bc9d8e Mon Sep 17 00:00:00 2001 From: Radhika Gupta Date: Mon, 14 Sep 2026 12:26:20 -0700 Subject: [PATCH 09/10] Fix formatting --- .../tests/test_llm_call.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/instrumentation/opentelemetry-instrumentation-genai-langchain/tests/test_llm_call.py b/instrumentation/opentelemetry-instrumentation-genai-langchain/tests/test_llm_call.py index 11a507677..64613dd71 100644 --- a/instrumentation/opentelemetry-instrumentation-genai-langchain/tests/test_llm_call.py +++ b/instrumentation/opentelemetry-instrumentation-genai-langchain/tests/test_llm_call.py @@ -849,9 +849,7 @@ def test_chat_openai_captures_choice_count( @pytest.mark.vcr() -@pytest.mark.cassette( - "test_chat_anthropic_captures_top_k_and_choice_count" -) +@pytest.mark.cassette("test_chat_anthropic_captures_top_k_and_choice_count") def test_chat_anthropic_captures_top_k_and_choice_count( span_exporter, log_exporter, @@ -886,9 +884,7 @@ def create_message_with_choice_count(**kwargs): logger_provider=logger_provider, content_capture="SPAN_AND_EVENT", ): - model.invoke( - [HumanMessage(content="Reply with one short word.")], n=3 - ) + model.invoke([HumanMessage(content="Reply with one short word.")], n=3) (span,) = span_exporter.get_finished_spans() assert span.attributes[gen_ai_attributes.GEN_AI_REQUEST_TOP_K] == 40 From ad517fc2a42e15f7caf6ab0b37dccdcf1f89c9a9 Mon Sep 17 00:00:00 2001 From: Radhika Gupta Date: Mon, 14 Sep 2026 13:11:46 -0700 Subject: [PATCH 10/10] Fix anthropic cassette --- ...> test_chat_anthropic_captures_top_k.yaml} | 4 +-- .../tests/test_llm_call.py | 25 ++----------------- 2 files changed, 4 insertions(+), 25 deletions(-) rename instrumentation/opentelemetry-instrumentation-genai-langchain/tests/cassettes/{test_chat_anthropic_captures_top_k_and_choice_count.yaml => test_chat_anthropic_captures_top_k.yaml} (98%) diff --git a/instrumentation/opentelemetry-instrumentation-genai-langchain/tests/cassettes/test_chat_anthropic_captures_top_k_and_choice_count.yaml b/instrumentation/opentelemetry-instrumentation-genai-langchain/tests/cassettes/test_chat_anthropic_captures_top_k.yaml similarity index 98% rename from instrumentation/opentelemetry-instrumentation-genai-langchain/tests/cassettes/test_chat_anthropic_captures_top_k_and_choice_count.yaml rename to instrumentation/opentelemetry-instrumentation-genai-langchain/tests/cassettes/test_chat_anthropic_captures_top_k.yaml index 941550dd3..f1de19a58 100644 --- a/instrumentation/opentelemetry-instrumentation-genai-langchain/tests/cassettes/test_chat_anthropic_captures_top_k_and_choice_count.yaml +++ b/instrumentation/opentelemetry-instrumentation-genai-langchain/tests/cassettes/test_chat_anthropic_captures_top_k.yaml @@ -2,7 +2,7 @@ interactions: - request: body: |- - {"model":"claude-sonnet-4-5","max_tokens":32,"messages":[{"role":"user","content":"Reply with one short word."}],"top_k":40,"n":3} + {"model":"claude-sonnet-4-5","max_tokens":32,"messages":[{"role":"user","content":"Reply with one short word."}],"top_k":40} headers: Content-Type: - application/json @@ -42,4 +42,4 @@ interactions: status: code: 200 message: OK -version: 1 +version: 1 \ No newline at end of file diff --git a/instrumentation/opentelemetry-instrumentation-genai-langchain/tests/test_llm_call.py b/instrumentation/opentelemetry-instrumentation-genai-langchain/tests/test_llm_call.py index 64613dd71..b1b1995ba 100644 --- a/instrumentation/opentelemetry-instrumentation-genai-langchain/tests/test_llm_call.py +++ b/instrumentation/opentelemetry-instrumentation-genai-langchain/tests/test_llm_call.py @@ -849,14 +849,12 @@ def test_chat_openai_captures_choice_count( @pytest.mark.vcr() -@pytest.mark.cassette("test_chat_anthropic_captures_top_k_and_choice_count") -def test_chat_anthropic_captures_top_k_and_choice_count( +def test_chat_anthropic_captures_top_k( span_exporter, log_exporter, tracer_provider, meter_provider, logger_provider, - monkeypatch, ): model = ChatAnthropic( model="claude-sonnet-4-5", @@ -864,18 +862,6 @@ def test_chat_anthropic_captures_top_k_and_choice_count( max_tokens=32, top_k=40, ) - create_message = model._client.messages.create - - def create_message_with_choice_count(**kwargs): - choice_count = kwargs.pop("n") - kwargs["extra_body"] = {"n": choice_count} - return create_message(**kwargs) - - monkeypatch.setattr( - model._client.messages, - "create", - create_message_with_choice_count, - ) with instrument( LangChainInstrumentor(), @@ -884,22 +870,15 @@ def create_message_with_choice_count(**kwargs): logger_provider=logger_provider, content_capture="SPAN_AND_EVENT", ): - model.invoke([HumanMessage(content="Reply with one short word.")], n=3) + model.invoke([HumanMessage(content="Reply with one short word.")]) (span,) = span_exporter.get_finished_spans() assert span.attributes[gen_ai_attributes.GEN_AI_REQUEST_TOP_K] == 40 - assert span.attributes[gen_ai_attributes.GEN_AI_REQUEST_CHOICE_COUNT] == 3 (log,) = log_exporter.get_finished_logs() assert ( log.log_record.attributes[gen_ai_attributes.GEN_AI_REQUEST_TOP_K] == 40 ) - assert ( - log.log_record.attributes[ - gen_ai_attributes.GEN_AI_REQUEST_CHOICE_COUNT - ] - == 3 - ) def test_chat_model_uses_ls_model_name_from_metadata(