Nested handoff history: speaker names in summary records break Chat Completions agents when a handoff_history_mapper forwards the transcript
Maintainer thường phản hồi trong vòng 1 ngày
Chưa có ai nhận issue này.
Đánh giá
- Độ khó
- 2/5
- Thời gian dự kiến
- 1-3 giờ
- Mức phù hợp với người mới
- 85/100
- Loại issue
- Lỗi
- Độ rõ ràng
- Đặc tả rõ ràng
- Mức độ hoạt động
- Sôi nổi
- Công nghệ
- python
- Lĩnh vực
- backend-api-design
Hướng nghiên cứu
Đọc src/agents/models/chatcmpl_converter.py, đặc biệt là Converter.maybe_easy_input_message, và so sánh các trường thông điệp được chấp nhận với trường name được khôi phục bởi _parse_summary_line trong src/agents/handoffs/history.py. Thêm một kiểm thử hồi quy cho mục lịch sử handoff lồng nhau có tên người nói được chuyển tiếp qua handoff_history_mapper đến một mô hình Chat Completions. Hoàn tất khi có thể tái hiện lỗi thành công và các kiểm thử converter hiện có đều đạt.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
Please read this first
- Have you read the docs? Agents SDK docs Yes
- Have you searched for related issues? Yes. Related but different: #4230 (keeps empty turns in nested history), #3319 and #3814 (other nested-history parsing issues).
Describe the bug
With RunConfig(nest_handoff_history=True), conversation history is carried in <CONVERSATION HISTORY> summary records, and the next handoff parses those records back into input items. The record format includes an optional speaker name, role (name): content: the summary writer emits it for items that have a name (_format_transcript_item_legacy in src/agents/handoffs/history.py), and _parse_summary_line restores it as a name key, e.g. {'role': 'user', 'name': 'Dana', 'content': '...'}.
When a RunConfig.handoff_history_mapper returns the normalized transcript to the next agent (the documented way to "return the exact list of input items to forward to the next agent"), and that agent uses a Chat Completions model, the run fails before the request is sent:
UserError: Unhandled item type or structure: {'role': 'user', 'name': 'Dana', 'content': 'Hi, I need my subscription history.'}
The same conversation without speaker names works. The cause is that Converter.maybe_easy_input_message (src/agents/models/chatcmpl_converter.py) only accepts the keys content, role, type and phase, so the items produced by the SDK's own parser are rejected, even though Chat Completions messages support a name field.
Debug information
- Agents SDK version: v0.24.0 (also reproduced on
mainat 27c75c5) - Related library versions (optional, e.g.
any-llm,litellm, orpydantic): openai 3.29.0, pydantic 2.12.3 - Python version: 3.12.13
- Operating system: Linux (aarch64)
- Model and model provider: Chat Completions (
OpenAIChatCompletionsModel); the repro stubs the HTTP transport - Does the issue reproduce with the latest Agents SDK release? Yes (v0.24.0, and
mainat 27c75c5) - Does the issue occur consistently or intermittently? Consistently
without speaker names: Your plan changed in May.
Traceback (most recent call last):
...
File "04_named_history_chat_completions.py", line 40, in run
result = await Runner.run(agents(), [{"role": "assistant", "content": summary},
File "agents/run.py", line 352, in run
return await runner.run(
File "agents/run.py", line 610, in run
return await self._run_impl(starting_agent, input, **kwargs)
File "agents/run.py", line 1909, in _run_impl
turn_result = await run_single_turn(
File "agents/run_internal/run_loop.py", line 2883, in run_single_turn
new_response = await get_new_response(
File "agents/run_internal/run_loop.py", line 3046, in get_new_response
new_response = await get_response_with_retry(
File "agents/run_internal/model_retry.py", line 612, in get_response_with_retry
response = await _await_model_attempt(get_response(), timeout)
File "agents/run_internal/model_retry.py", line 296, in _await_model_attempt
return await awaitable
File "agents/models/openai_chatcompletions.py", line 249, in get_response
response = await self._fetch_response(
File "agents/models/openai_chatcompletions.py", line 621, in _fetch_response
converted_messages = Converter.items_to_messages(
File "agents/models/chatcmpl_converter.py", line 1026, in items_to_messages
raise UserError(f"Unhandled item type or structure: {item}")
agents.exceptions.UserError: Unhandled item type or structure: {'role': 'user', 'name': 'Dana', 'content': 'Hi, I need my subscription history.'}
Repro steps
Self-contained; the model is a stubbed Chat Completions HTTP transport, so no API key or network is needed.
import asyncio
import httpx
from openai import AsyncOpenAI
from agents import Agent, OpenAIChatCompletionsModel, RunConfig, Runner, get_conversation_history_wrappers, handoff
from agents import set_tracing_disabled
set_tracing_disabled(True)
def chat_model(*messages):
"""A Chat Completions model whose HTTP endpoint returns the given assistant messages in order."""
replies = iter(messages)
def respond(request):
return httpx.Response(200, json={"id": "chatcmpl-1", "object": "chat.completion", "created": 0, "model": "gpt-4.1",
"choices": [{"index": 0, "finish_reason": "stop", "message": next(replies)}]})
client = AsyncOpenAI(api_key="test", http_client=httpx.AsyncClient(transport=httpx.MockTransport(respond)))
return OpenAIChatCompletionsModel("gpt-4.1", client)
def agents():
billing = Agent(name="Billing", model=chat_model({"role": "assistant", "content": "Your plan changed in May."}))
return Agent(name="Triage", handoffs=[handoff(billing)], model=chat_model(
{"role": "assistant", "content": None, "tool_calls": [{"id": "call_1", "type": "function", "function": {
"name": "transfer_to_billing", "arguments": "{}"}}]}))
def earlier(user, assistant):
"""An earlier conversation carried over in the nested-history summary format."""
start, end = get_conversation_history_wrappers()
return ("For context, here is the conversation so far between the user and the previous agent:\n"
f"{start}\n1. {user}: Hi, I need my subscription history.\n2. {assistant}: Sure, let me look into it.\n{end}")
async def run(summary):
config = RunConfig(nest_handoff_history=True, handoff_history_mapper=lambda transcript: transcript)
result = await Runner.run(agents(), [{"role": "assistant", "content": summary},
{"role": "user", "content": "When did my plan change?"}], run_config=config)
return result.final_output
async def main():
print("without speaker names:", await run(earlier("user", "assistant")))
print("with speaker names: ", await run(earlier("user (Dana)", "assistant (Support)")))
asyncio.run(main())
Expected behavior
Items recovered from nested-history summaries can be sent to any supported model: either the Chat Completions converter maps name to the message's name field, or the parser keeps the speaker name in a form the converters accept. Both lines of the script print Your plan changed in May.
- Ngôn ngữ chính
- Python
- Star
- 30.5k
- Fork
- 5k
- Merge trung bình
- 16 giờ 15 phút
- Pull request đã merge (30 ngày)
- 154
Chuẩn bị môi trường
- Không có Dockerfile hay tệp Docker Compose
- Có mẫu pull request
- Đọc hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của openai/openai-agents-python
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 82/100
openai/openai-agents-python#5594 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Default tool error text tells the model to "try again" after a call that may already have taken effectCó thể đã có người làm @Anurag07-crypto đã nhận hôm nay. Đang mở
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 72/100
openai/openai-agents-python#5573 · 3 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 66/100
openai/openai-agents-python#5550 · 3 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 88/100
openai/openai-agents-python#5529 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 1/5 Dưới một giờ Mức phù hợp với người mới 88/100
openai/openai-agents-python#5498 ·
Maintainer thường phản hồi trong vòng 1 ngày
Tất cả issue của openai/openai-agents-python
Issue tương tự
-
integration: logger
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 68/100
home-assistant/core#191147 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 76/100
Maintainer thường phản hồi trong vòng 1 ngày
-
documentation p4
Độ khó 1/5 1-3 giờ Mức phù hợp với người mới 88/100
MillenniumDawn/focus-tree-creation-tool#417 ·
Maintainer thường phản hồi trong vòng 3 ngày
-
bug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
CharlesP44/Beem_Energy#96 ·
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 88/100
rheeloaded/paperpull#153 ·
Maintainer thường phản hồi trong vòng 1 ngày