Nested handoff history: speaker names in summary records break Chat Completions agents when a handoff_history_mapper forwards the transcript
Los mantenedores suelen responder en 1 día
Nadie ha tomado este issue todavía.
Evaluación
- Dificultad
- 2/5
- Tiempo estimado
- 1-3 horas
- Aptitud para principiantes
- 85/100
- Tipo de issue
- Error
- Claridad
- Bien especificado
- Estado de actividad
- Activo
- Stack tecnológico
- python
- Área
- backend-api-design
Línea de trabajo
Lee src/agents/models/chatcmpl_converter.py, especialmente Converter.maybe_easy_input_message, y compara los campos de mensaje aceptados con el campo name restaurado por _parse_summary_line en src/agents/handoffs/history.py. Añade una prueba de regresión para un elemento anidado del historial de handoff con un nombre de hablante reenviado a un modelo de Chat Completions mediante handoff_history_mapper. El trabajo estará terminado cuando la reproducción funcione y pasen las pruebas existentes del converter.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
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.
- Lenguaje dominante
- Python
- Estrellas
- 30.5k
- Forks
- 5k
- Merge medio
- 16 h 15 min
- PR fusionados (30 d)
- 154
Preparar el entorno
- Sin Dockerfile ni archivo de Docker Compose
- Tiene una plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de openai/openai-agents-python
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 82/100
openai/openai-agents-python#5594 ·
Los mantenedores suelen responder en 1 día
-
Default tool error text tells the model to "try again" after a call that may already have taken effectPosiblemente ocupada @Anurag07-crypto la tomó hoy. Abierto
Dificultad 1/5 Menos de una hora Aptitud para principiantes 72/100
openai/openai-agents-python#5573 · 3 comentarios ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 66/100
openai/openai-agents-python#5550 · 3 comentarios ·
Los mantenedores suelen responder en 1 día
-
Dificultad 1/5 Menos de una hora Aptitud para principiantes 88/100
openai/openai-agents-python#5529 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 1/5 Menos de una hora Aptitud para principiantes 88/100
openai/openai-agents-python#5498 ·
Los mantenedores suelen responder en 1 día
Todos los issues de openai/openai-agents-python
Issues similares
-
integration: logger
Dificultad 2/5 1-3 horas Aptitud para principiantes 68/100
home-assistant/core#191147 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 76/100
Los mantenedores suelen responder en 1 día
-
documentation p4
Dificultad 1/5 1-3 horas Aptitud para principiantes 88/100
MillenniumDawn/focus-tree-creation-tool#417 ·
Los mantenedores suelen responder en 3 días
-
bug
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
CharlesP44/Beem_Energy#96 ·
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 88/100
rheeloaded/paperpull#153 ·
Los mantenedores suelen responder en 1 día