Skip to content

feat: collect final output from beta Agents streams - #4004

Merged
apcha-oai merged 7 commits into
mainfrom
apcha/beta-agents-final-result
Sep 30, 2026
Merged

apcha-oai merged 7 commits into
mainfrom
apcha/beta-agents-final-result

Conversation

@apcha-oai

@apcha-oai apcha-oai commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Collect a hosted agent's final answer directly from the stream, whether it creates a session or continues one. Applications can still display every event, but no longer need their own turn tracking and final-message accumulator.

Before:

with client.beta.agents.sessions.create(
    agent={"model": MODEL}, environment={"type": "none"},
    input="Explain this policy.", stream=True,
) as stream:
    for event in stream:
        # Track the root turn, gather completed final messages,
        # and distinguish completion from interrupted delivery.
        collect_event(event)

print(collected_answer())

After:

with client.beta.agents.sessions.create(
    agent={"model": MODEL}, environment={"type": "none"},
    input="Explain this policy.", stream=True,
) as stream:
    result = stream.get_final_result()

print(result.output_text)

with client.beta.agents.sessions.stream(
    result.session_id, input="Give me an example.", tool_handlers=handlers,
).with_result_collection() as stream:
    for event in stream:
        show_progress(event)
    followup = stream.get_final_result()

print(followup.output_text)

The beta result retains the generated turn and ordered final messages, including their content and annotations. Sync and async getters share selection rules and preserve existing tool dispatch. An unsuccessful turn, required action, or incomplete observation raises a beta result error with available partial state; closing the stream does not cancel hosted execution.

Ordinary event iteration remains incremental; progress consumers opt into result retention with with_result_collection(). Creation streams retain their existing Stream interface. Collection lives under openai.lib.beta.agents; generated resource wiring only selects the specialized stream type.

Stack:

@apcha-oai
apcha-oai marked this pull request as ready for review September 30, 2026 22:04
@apcha-oai
apcha-oai requested a review from a team as a code owner September 30, 2026 22:04
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-30T23:39:30.991143Z 33c9a07 New commits
🔒 Security Review ✅ Completed 2026-09-30T23:38:54.948346Z 33c9a07 New commits

Security findings

Advisory findings (1)

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@openai-sdks

openai-sdks Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

OkTest Summary

✅ 236/236 SDK tests passed in 10.378s for Python SDK PR #4004.

Test results — 42 files
Test Result Time
tests/chat-completions-complex-body.test.ts ✅ Passed 142ms
tests/chat-completions-create.test.ts ✅ Passed 256ms
tests/chat-completions-stream.test.ts ✅ Passed 227ms
tests/files-content-binary.test.ts ✅ Passed 149ms
tests/files-create-multipart.test.ts ✅ Passed 159ms
tests/files-list-pagination.test.ts ✅ Passed 266ms
tests/initialize-config.test.ts ✅ Passed 164ms
tests/instance-isolation.test.ts ✅ Passed 136ms
tests/models-list.test.ts ✅ Passed 182ms
tests/responses-background-lifecycle.test.ts ✅ Passed 164ms
tests/responses-body-method-errors.test.ts ✅ Passed 369ms
tests/responses-cancel-timeout.test.ts ✅ Passed 191ms
tests/responses-cancel.test.ts ✅ Passed 188ms
tests/responses-compact-retries.test.ts ✅ Passed 301ms
tests/responses-compact.test.ts ✅ Passed 208ms
tests/responses-create-advanced-stream.test.ts ✅ Passed 210ms
tests/responses-create-advanced.test.ts ✅ Passed 239ms
tests/responses-create-disconnect.test.ts ✅ Passed 1.114s
tests/responses-create-errors.test.ts ✅ Passed 159ms
tests/responses-create-malformed-api-responses.test.ts ✅ Passed 176ms
tests/responses-create-retries.test.ts ✅ Passed 189ms
tests/responses-create-stream-failures.test.ts ✅ Passed 856ms
tests/responses-create-stream-timeout.test.ts ✅ Passed 205ms
tests/responses-create-stream-wire.test.ts ✅ Passed 3.11s
tests/responses-create-stream.test.ts ✅ Passed 156ms
tests/responses-create-terminal-states.test.ts ✅ Passed 258ms
tests/responses-create-timeout.test.ts ✅ Passed 182ms
tests/responses-create.test.ts ✅ Passed 190ms
tests/responses-delete.test.ts ✅ Passed 165ms
tests/responses-input-items-errors.test.ts ✅ Passed 538ms
tests/responses-input-items-list.test.ts ✅ Passed 321ms
tests/responses-input-items-options.test.ts ✅ Passed 307ms
tests/responses-input-tokens-count-timeout.test.ts ✅ Passed 241ms
tests/responses-input-tokens-count.test.ts ✅ Passed 156ms
tests/responses-malformed-inputs.test.ts ✅ Passed 2.086s
tests/responses-not-found-errors.test.ts ✅ Passed 274ms
tests/responses-parse.test.ts ✅ Passed 210ms
tests/responses-retrieve-retries.test.ts ✅ Passed 426ms
tests/responses-retrieve.test.ts ✅ Passed 217ms
tests/responses-stored-method-errors.test.ts ✅ Passed 553ms
tests/retry-behavior.test.ts ✅ Passed 2.905s
tests/sdk-error-shape.test.ts ✅ Passed 315ms

View OkTest run #36791938747

SDK merge (04a981803fed) · head (33c9a07e0151) · base (8a136c203062) · OkTest (e7cf6535e0ca)

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e541046804

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/openai/lib/beta/agents/_result.py
Comment thread src/openai/lib/streaming/agents/_streams.py Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛡️ Codex Security Review · Automatically triggered

Here are some automated security review suggestions for this pull request.

Reviewed commit: e541046804

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

Comment thread src/openai/lib/beta/agents/_result.py
Comment thread src/openai/lib/beta/agents/_result.py
@apcha-oai

Copy link
Copy Markdown
Contributor Author

@codex security review

@chatgpt-codex-connector

Copy link
Copy Markdown

🛡️ Codex Security Review

Security review completed. No security issues were found in this pull request.

Reviewed commit: bf515f44ee

View security finding report

Only the user who started this review can view the report in Codex.

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

@github-actions

github-actions Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

Castiron custom code

✅ No new custom-code files detected.

49 mixed files remain; 1 existing customization changed.

Compared 8a136c203062 → 33c9a07e0151. Generated baselines verified.

File Result Current custom patch
src/openai/resources/beta/agents/sessions/sessions.py Existing customization changed +63 / −11
48 existing customizations unchanged
  • api.md
  • scripts/castiron/README.md
  • scripts/castiron/custom_code_report.py
  • scripts/castiron/test_custom_code_report.py
  • src/openai/init.py
  • src/openai/_client.py
  • src/openai/resources/audio/transcriptions.py
  • src/openai/resources/audio/translations.py
  • src/openai/resources/beta/beta.py
  • src/openai/resources/beta/responses/responses.py
  • src/openai/resources/beta/threads/runs/runs.py
  • src/openai/resources/beta/threads/threads.py
  • src/openai/resources/chat/completions/completions.py
  • src/openai/resources/embeddings.py
  • src/openai/resources/files.py
  • src/openai/resources/live/forks.py
  • src/openai/resources/live/live.py
  • src/openai/resources/live/sideband.py
  • src/openai/resources/realtime/api.md
  • src/openai/resources/realtime/realtime.py
  • src/openai/resources/responses/responses.py
  • src/openai/resources/uploads/uploads.py
  • src/openai/resources/vector_stores/file_batches.py
  • src/openai/resources/vector_stores/files.py
  • src/openai/resources/videos.py
  • src/openai/resources/webhooks/init.py
  • src/openai/resources/webhooks/webhooks.py
  • src/openai/types/beta/agent_session_message.py
  • src/openai/types/beta/session_turn_error.py
  • src/openai/types/chat/init.py
  • src/openai/types/chat/chat_completion_message_tool_call.py
  • src/openai/types/fine_tuning/fine_tuning_job_integration.py
  • src/openai/types/realtime/conversation_item_input_audio_transcription_delta_event.py
  • src/openai/types/realtime/realtime_error_event.py
  • src/openai/types/responses/init.py
  • src/openai/types/responses/response.py
  • src/openai/types/responses/response_function_web_search.py
  • src/openai/types/responses/response_function_web_search_param.py
  • src/openai/types/responses/responses_client_event.py
  • src/openai/types/responses/responses_client_event_param.py

8 more in the full report.

A changed generated baseline means this report cannot reliably identify which handwritten lines changed.

Inspect the custom-code diff

Download the exact patch produced by this run (requires repository access):

gh run download 36791993867 --repo openai/openai-python \
  --name castiron-custom-code-36791993867-1 --dir /tmp/castiron-custom-code-36791993867-1
git apply --stat /tmp/castiron-custom-code-36791993867-1/custom-code.patch
cat /tmp/castiron-custom-code-36791993867-1/custom-code.patch

Or reproduce it from an SDK checkout containing the vendored reporter:

git fetch --no-tags origin 8a136c203062b999349da745226ba5299709c7be 33c9a07e0151a1b776648a9eacc739485fab795c
python3 scripts/castiron/custom_code_report.py report \
  --base 8a136c203062b999349da745226ba5299709c7be \
  --head 33c9a07e0151a1b776648a9eacc739485fab795c --fetch --require-head-hash --public \
  --out /tmp/castiron-custom-code-33c9a07e0151
cat /tmp/castiron-custom-code-33c9a07e0151/custom-code.patch

This is the current full custom patch for mixed files, not an attribution of only the handwritten lines changed by this PR.

Full report and patch

@apcha-oai
apcha-oai enabled auto-merge September 30, 2026 23:36
@apcha-oai
apcha-oai added this pull request to the merge queue Sep 30, 2026
Merged via the queue into main with commit 157ac4c Sep 30, 2026
26 checks passed
@apcha-oai
apcha-oai deleted the apcha/beta-agents-final-result branch September 30, 2026 23:49
github-merge-queue Bot pushed a commit that referenced this pull request Oct 1, 2026
## Summary

Bind a Pydantic output model to the Agents request and its completed
result, so applications can use a typed answer without maintaining
separate schema and parsing code.

Before:
```python
with client.beta.agents.sessions.create(
    agent={"model": MODEL, "text": {"format": {
        "type": "json_schema", "schema": schema,
    }}},
    environment={"type": "none"}, input=QUESTION, stream=True,
) as stream:
    result = stream.get_final_result()
report = Report.model_validate_json(result.output_text)
```

After:
```python
with client.beta.agents.sessions.create(
    agent={"model": MODEL}, environment={"type": "none"},
    input=QUESTION, stream=True, output_type=Report,
) as stream:
    result = stream.get_final_result()
report = result.output_parsed
```

The same `output_type` works on follow-up streams as a local parser for
an already-configured session. `output_parsed` returns the first parsed
final text part; all final text parts are validated. Raw output remains
available; parsing failures expose the completed result through
`AgentOutputParseError.result`. Sync and async helpers share the
existing Pydantic schema normalization and result collection.

Schema conversion follows the existing Responses helper; API errors
report unsupported schema features. Typed tools and outputs can reuse
the same model without output normalization changing its tool argument
schema.

### Stack

- #4004 (merged
prerequisite)
- #4007 👈 this PR
- #4008
- #4009
gh-actions-shared Bot pushed a commit to xf-qubit/openai-python that referenced this pull request Oct 1, 2026
## Summary

Stage selected local files and read an artifact from the exact completed
turn, either in memory or streamed to a local path. The helpers keep
upload IDs available for explicit cleanup and stream downloads to a
caller-chosen path.

Before:

```python
uploaded = client.files.create(file=Path("source.pdf"), purpose="user_data")
inputs = [{"type": "file_id", "file_id": uploaded.id, "path": "/workspace/source.pdf"}]
# Pass inputs when creating the hosted environment, then run the session.
artifact = next(a for a in client.beta.agents.sessions.artifacts.list(result.session_id)
                if a.turn_id == result.turn_id and a.path == "/workspace/outputs/report.md")
with client.beta.agents.sessions.artifacts.with_streaming_response.content(
    artifact.id, session_id=result.session_id,
) as content:
    content.stream_to_file("report.md")
```

After:

```python
prepared = client.beta.agents.environments.files.prepare({
    "/workspace/source.pdf": Path("source.pdf"),
})
# Pass prepared.files when creating the hosted environment, then run the session.
artifacts = client.beta.agents.sessions.artifacts.for_result(result)
report_bytes = artifacts.content("/workspace/outputs/report.md").content
# Or stream to an application-owned path:
artifact = artifacts.download("/workspace/outputs/report.md", to=Path("report.md"))
```

`prepare_directory(..., include=[...])` selects a directory snapshot;
`files.upload(...)` uploads and stages one file in an existing
environment. Sync and async helpers live under the beta Agents
namespace. They preflight selected files before uploading, preserve
partial upload ownership on errors, and detect missing or ambiguous
artifacts across all pages.

Local path selection is intended for static application-owned files and
stable directories; it is not a filesystem sandbox for untrusted paths
or hostile local writers.

This PR now targets `main` directly. Reattachment/result recovery is
deferred: a silent attachment cannot reliably distinguish pending work
from an already-completed turn, so these helpers do not depend on
SDK-side recovery heuristics.

### Stack

- openai#4004 (merged
prerequisite)
- openai#4007 (merged
prerequisite)
- openai#4008 (closed/deferred;
not a dependency)
- openai#4009 👈 this PR
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants