mirror of
https://github.com/hwchase17/langchain.git
synced 2025-09-08 22:42:05 +00:00
openai[patch]: allow specification of output format for Responses API (#31686)
This commit is contained in:
@@ -649,6 +649,25 @@ class BaseChatOpenAI(BaseChatModel):
|
||||
.. versionadded:: 0.3.9
|
||||
"""
|
||||
|
||||
output_version: Literal["v0", "responses/v1"] = "v0"
|
||||
"""Version of AIMessage output format to use.
|
||||
|
||||
This field is used to roll-out new output formats for chat model AIMessages
|
||||
in a backwards-compatible way.
|
||||
|
||||
Supported values:
|
||||
|
||||
- ``"v0"``: AIMessage format as of langchain-openai 0.3.x.
|
||||
- ``"responses/v1"``: Formats Responses API output
|
||||
items into AIMessage content blocks.
|
||||
|
||||
Currently only impacts the Responses API. ``output_version="responses/v1"`` is
|
||||
recommended.
|
||||
|
||||
.. versionadded:: 0.3.25
|
||||
|
||||
"""
|
||||
|
||||
model_config = ConfigDict(populate_by_name=True)
|
||||
|
||||
@model_validator(mode="before")
|
||||
@@ -903,6 +922,7 @@ class BaseChatOpenAI(BaseChatModel):
|
||||
schema=original_schema_obj,
|
||||
metadata=metadata,
|
||||
has_reasoning=has_reasoning,
|
||||
output_version=self.output_version,
|
||||
)
|
||||
if generation_chunk:
|
||||
if run_manager:
|
||||
@@ -957,6 +977,7 @@ class BaseChatOpenAI(BaseChatModel):
|
||||
schema=original_schema_obj,
|
||||
metadata=metadata,
|
||||
has_reasoning=has_reasoning,
|
||||
output_version=self.output_version,
|
||||
)
|
||||
if generation_chunk:
|
||||
if run_manager:
|
||||
@@ -1096,7 +1117,10 @@ class BaseChatOpenAI(BaseChatModel):
|
||||
else:
|
||||
response = self.root_client.responses.create(**payload)
|
||||
return _construct_lc_result_from_responses_api(
|
||||
response, schema=original_schema_obj, metadata=generation_info
|
||||
response,
|
||||
schema=original_schema_obj,
|
||||
metadata=generation_info,
|
||||
output_version=self.output_version,
|
||||
)
|
||||
elif self.include_response_headers:
|
||||
raw_response = self.client.with_raw_response.create(**payload)
|
||||
@@ -1109,6 +1133,8 @@ class BaseChatOpenAI(BaseChatModel):
|
||||
def _use_responses_api(self, payload: dict) -> bool:
|
||||
if isinstance(self.use_responses_api, bool):
|
||||
return self.use_responses_api
|
||||
elif self.output_version == "responses/v1":
|
||||
return True
|
||||
elif self.include is not None:
|
||||
return True
|
||||
elif self.reasoning is not None:
|
||||
@@ -1327,7 +1353,10 @@ class BaseChatOpenAI(BaseChatModel):
|
||||
else:
|
||||
response = await self.root_async_client.responses.create(**payload)
|
||||
return _construct_lc_result_from_responses_api(
|
||||
response, schema=original_schema_obj, metadata=generation_info
|
||||
response,
|
||||
schema=original_schema_obj,
|
||||
metadata=generation_info,
|
||||
output_version=self.output_version,
|
||||
)
|
||||
elif self.include_response_headers:
|
||||
raw_response = await self.async_client.with_raw_response.create(**payload)
|
||||
@@ -3540,6 +3569,7 @@ def _construct_lc_result_from_responses_api(
|
||||
response: Response,
|
||||
schema: Optional[type[_BM]] = None,
|
||||
metadata: Optional[dict] = None,
|
||||
output_version: Literal["v0", "responses/v1"] = "v0",
|
||||
) -> ChatResult:
|
||||
"""Construct ChatResponse from OpenAI Response API response."""
|
||||
if response.error:
|
||||
@@ -3676,7 +3706,10 @@ def _construct_lc_result_from_responses_api(
|
||||
tool_calls=tool_calls,
|
||||
invalid_tool_calls=invalid_tool_calls,
|
||||
)
|
||||
message = _convert_to_v03_ai_message(message)
|
||||
if output_version == "v0":
|
||||
message = _convert_to_v03_ai_message(message)
|
||||
else:
|
||||
pass
|
||||
return ChatResult(generations=[ChatGeneration(message=message)])
|
||||
|
||||
|
||||
@@ -3688,6 +3721,7 @@ def _convert_responses_chunk_to_generation_chunk(
|
||||
schema: Optional[type[_BM]] = None,
|
||||
metadata: Optional[dict] = None,
|
||||
has_reasoning: bool = False,
|
||||
output_version: Literal["v0", "responses/v1"] = "v0",
|
||||
) -> tuple[int, int, int, Optional[ChatGenerationChunk]]:
|
||||
def _advance(output_idx: int, sub_idx: Optional[int] = None) -> None:
|
||||
"""Advance indexes tracked during streaming.
|
||||
@@ -3756,12 +3790,15 @@ def _convert_responses_chunk_to_generation_chunk(
|
||||
elif chunk.type == "response.output_text.done":
|
||||
content.append({"id": chunk.item_id, "index": current_index})
|
||||
elif chunk.type == "response.created":
|
||||
response_metadata["id"] = chunk.response.id
|
||||
id = chunk.response.id
|
||||
response_metadata["id"] = chunk.response.id # Backwards compatibility
|
||||
elif chunk.type == "response.completed":
|
||||
msg = cast(
|
||||
AIMessage,
|
||||
(
|
||||
_construct_lc_result_from_responses_api(chunk.response, schema=schema)
|
||||
_construct_lc_result_from_responses_api(
|
||||
chunk.response, schema=schema, output_version=output_version
|
||||
)
|
||||
.generations[0]
|
||||
.message
|
||||
),
|
||||
@@ -3773,7 +3810,10 @@ def _convert_responses_chunk_to_generation_chunk(
|
||||
k: v for k, v in msg.response_metadata.items() if k != "id"
|
||||
}
|
||||
elif chunk.type == "response.output_item.added" and chunk.item.type == "message":
|
||||
id = chunk.item.id
|
||||
if output_version == "v0":
|
||||
id = chunk.item.id
|
||||
else:
|
||||
pass
|
||||
elif (
|
||||
chunk.type == "response.output_item.added"
|
||||
and chunk.item.type == "function_call"
|
||||
@@ -3868,9 +3908,13 @@ def _convert_responses_chunk_to_generation_chunk(
|
||||
additional_kwargs=additional_kwargs,
|
||||
id=id,
|
||||
)
|
||||
message = cast(
|
||||
AIMessageChunk, _convert_to_v03_ai_message(message, has_reasoning=has_reasoning)
|
||||
)
|
||||
if output_version == "v0":
|
||||
message = cast(
|
||||
AIMessageChunk,
|
||||
_convert_to_v03_ai_message(message, has_reasoning=has_reasoning),
|
||||
)
|
||||
else:
|
||||
pass
|
||||
return (
|
||||
current_index,
|
||||
current_output_index,
|
||||
|
Reference in New Issue
Block a user