Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions libs/partners/openai/langchain_openai/chat_models/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -4233,6 +4233,13 @@ def _construct_responses_api_payload(
else:
payload["text"] = {"verbosity": verbosity}

return_logprobs = payload.pop("logprobs", None)
if return_logprobs:
if "include" in payload:
payload["include"].append("message.output_text.logprobs")
else:
payload["include"] = ["message.output_text.logprobs"]

return payload


Expand Down Expand Up @@ -4648,6 +4655,22 @@ def _construct_lc_result_from_responses_api(
content_blocks.append(block)
if hasattr(content, "parsed"):
additional_kwargs["parsed"] = content.parsed
if hasattr(content, "logprobs") and content.logprobs:
if "logprobs" in response_metadata:
# only keep the latest message logprobs

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

should this go in response_metadata, or on the associated content block?

@HOZHENWAI HOZHENWAI Apr 14, 2026

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

well, is that a recommandation or a question? The default pattern without the response api when using logprobs is to have it in the response_metadata. I just copied the pattern to make sure the outputs were compatible between use_responses_api=True and use_responses_api=False . Should this be changed?
Tbh, I don't think I tested the case with multi-turn conversation when logprobs=True and use_response_api=False, so I don't know if the pattern is different in that case.

response_metadata["logprobs"]["content"] = [
logprob.model_dump() for logprob in content.logprobs
]
response_metadata["logprobs"]["refusal"] = (
response_metadata["logprobs"].get("refusal", None)
)
else:
response_metadata["logprobs"] = {
"content": [
logprob.model_dump() for logprob in content.logprobs
],
"refusal": None,
}
if content.type == "refusal":
refusal_block = {
"type": "refusal",
Expand Down
87 changes: 87 additions & 0 deletions libs/partners/openai/tests/unit_tests/chat_models/test_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -3803,3 +3803,90 @@ def test_defer_loading_in_responses_api_payload() -> None:
assert weather_tool["defer_loading"] is True
assert weather_tool["type"] == "function"
assert {"type": "tool_search"} in result["tools"]


def test_logprobs_with_responses_api_parameter_handling() -> None:
"""Test that the `logprobs` parameter is correctly handled with the response api."""
from openai.types.responses.response_create_params import (
ResponseCreateParamsNonStreaming,
)

chat = ChatOpenAI(
model="gpt-4o",
use_responses_api=True,
logprobs=True,
output_version="responses/v1",
max_completion_tokens=10,
)
messages = [HumanMessage("Hello")]
payload = chat._get_request_payload(messages)
allowed_keys = set(ResponseCreateParamsNonStreaming.__annotations__.keys())
payload_keys = set(payload.keys())
assert payload_keys.issubset(allowed_keys)
assert "include" in payload
assert "message.output_text.logprobs" in payload["include"]


def test___construct_lc_result_from_responses_api_logprobs() -> None:
"""Test that the logprobs are correctly parsed from the response api."""
from openai.types.responses.response_output_text import Logprob

response = Response(
id="resp_123",
created_at=1234567890,
model="gpt-4o",
object="response",
parallel_tool_calls=True,
tools=[],
tool_choice="auto",
output=[
ResponseOutputMessage(
type="message",
id="msg_123",
content=[
ResponseOutputText(
type="output_text",
text="Hello, world!",
annotations=[],
logprobs=[
Logprob(
token="Hello", # noqa: S106
bytes=[72, 101, 108, 108, 111],
logprob=8e-06,
top_logprobs=[],
),
Logprob(
token=",", # noqa: S106
bytes=[44],
logprob=3.8e-05,
top_logprobs=[],
),
Logprob(
token="world", # noqa: S106
bytes=[32, 119, 111, 114, 108, 100],
logprob=-5.3e-05,
top_logprobs=[],
),
Logprob(
token="!", # noqa: S106
bytes=[33],
logprob=-8e-06,
top_logprobs=[],
),
],
),
],
role="assistant",
status="completed",
)
],
)

chat_result = _construct_lc_result_from_responses_api(response)
assert "logprobs" in chat_result.generations[0].message.response_metadata

metadata = chat_result.generations[0].message.response_metadata
assert "content" in metadata["logprobs"]
assert len(metadata["logprobs"]["content"]) == 4
assert metadata["logprobs"]["content"][0]["token"] == "Hello" # noqa: S105
assert metadata["logprobs"]["content"][0]["logprob"] == 8e-06
Loading