Skip to content

fix(openai-chat): complete streams that end after output without terminal - #793

Merged
Wibias merged 5 commits into
lidge-jun:devfrom
Wibias:restore/773-openai-chat-eof
Jul 31, 2026
Merged

fix(openai-chat): complete streams that end after output without terminal#793
Wibias merged 5 commits into
lidge-jun:devfrom
Wibias:restore/773-openai-chat-eof

Conversation

@Wibias

@Wibias Wibias commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator

Summary

Test plan

  • bun test tests/openai-chat-eof.test.ts
  • Confirm EOF-after-text still completes successfully
  • Confirm EOF with pending tool calls still errors as truncated

Summary by CodeRabbit

  • Bug Fixes
    • Improved handling of streamed chat responses that end after delivering content, including responses without trailing newlines or terminal markers.
    • Prevented valid content responses from being incorrectly reported as truncated.
    • Continued rejecting empty, incomplete, reasoning-only, or usage-only responses, including those with pending tool calls.
    • Ensured flushed tool calls alone are not treated as user-facing answer content.

@github-actions github-actions Bot added the bug Something isn't working label Jul 30, 2026
@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 24e76b7a-21e6-4e49-b1ab-8cb3e46f20b6

📥 Commits

Reviewing files that changed from the base of the PR and between 0c829dc and 413027e.

📒 Files selected for processing (2)
  • src/adapters/openai-chat.ts
  • tests/openai-chat-eof.test.ts

📝 Walkthrough

Walkthrough

OpenAI chat EOF handling now distinguishes answer text from reasoning, usage, and buffered tool calls. Text-bearing streams without terminal signals can complete; streams with pending tool calls or no answer text fail closed. Tests cover newline-free, empty, reasoning-only, usage-only, and incomplete tool-call streams.

Changes

OpenAI stream EOF handling

Layer / File(s) Summary
Track answer output
src/adapters/openai-chat.ts
The parser records whether non-empty answer text was emitted; reasoning-only and flushed tool-call output do not satisfy the answer-output condition.
Validate unterminated streams
src/adapters/openai-chat.ts, tests/openai-chat-eof.test.ts
EOF validation runs before pending tool calls are flushed, rejects incomplete or outputless streams, and allows content-bearing streams—including frames without trailing newlines—to finish as done. Tests cover pending tool calls, reasoning-only output, usage-only output, and empty EOF cases.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

Suggested reviewers: ingwannu, lidge-jun

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: completing OpenAI chat streams after output when no terminal signal is received.
Linked Issues check ✅ Passed The changes satisfy #735 by completing text-producing EOF streams while rejecting pending tool-call, reasoning-only, and usage-only truncations.
Out of Scope Changes check ✅ Passed All code and test changes are directly focused on EOF handling for OpenAI chat streams and the linked issue’s truncation requirements.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/adapters/openai-chat.ts`:
- Around line 807-823: Update the EOF handling around flushToolCalls in the
stream processing flow to detect pending tool calls before flushing them; when
the stream lacks a terminal signal, emit the truncation error without yielding
any tool_call_end events. Ensure usage-only EOF frames cannot bypass this
pending-call validation and incorrectly produce done, and add regression
coverage asserting the error response contains no tool_call_end.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 31e56125-7204-4ea5-86ec-f408148c0b00

📥 Commits

Reviewing files that changed from the base of the PR and between 440f363 and 0c829dc.

📒 Files selected for processing (2)
  • src/adapters/openai-chat.ts
  • tests/openai-chat-eof.test.ts

Comment thread src/adapters/openai-chat.ts 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 Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0c829dc5a2

ℹ️ 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/adapters/openai-chat.ts Outdated
Comment thread src/adapters/openai-chat.ts Outdated
Fail pending-tool and reasoning/usage-only truncations before flushing
tool calls, and only complete when answer text or finish_reason is present.
@Wibias

Wibias commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator Author

Verdict: approve-comment

lidge-jun added a commit that referenced this pull request Jul 31, 2026
…complete

At reader EOF the adapter flushed buffered tool calls and only then judged
whether the stream had terminated cleanly. `flushToolCalls()` emits
`tool_call_end`, so a socket that died mid-argument handed the client
`{"cmd":"l` as a COMPLETED call: a truncation reported as a successful tool
invocation, which is worse than an error because nothing downstream can tell
it apart from a real one.

The integrity check now runs before the flush. This adapter can still decide
at that point because it buffers tool calls; the Anthropic adapter forwards
fragments live and genuinely cannot, which is why the equivalent fix there
was reverted (see devlog 260731_pr_merge_round/021).

A usage frame does not launder it either. Usage counts as a terminal signal
for text streams, and without the separate branch a provider that sent usage
after dying mid-tool would still flush the partial call.

What this deliberately does NOT do is the other half of #735. PR #793 (and
#773 before it, merged then reverted by #792 with no recorded reason)
additionally completes text-only streams that end without a terminal frame.
That is a policy change, not a bug fix: it inverts two existing fail-closed
assertions in tests/openai-chat-eof.test.ts, which currently pin "content
without a terminator is truncation". Whether a relay's dropped terminal frame
should be forgiven is the maintainer's call, and the missing revert rationale
on #792 is exactly the context needed to make it. Attempting both halves here
is how the previous round burned three repair rounds on the Anthropic side.

So the narrowing half lands and the widening half does not. #735 stays open.

Tests, verified by ablation (disable the guard -> 13 pass / 2 fail, restore ->
15/15):

- a half-assembled tool call at EOF errors, emitting no tool_call_end or done
- a trailing usage frame does not rescue it
- a tool call closed by finish_reason still completes normally -- the control,
  without which the guard could fire on every well-formed tool turn

Adjacent suites green: adapter-usage, openai-chat-parallel-stream.
Wibias added 3 commits July 31, 2026 08:28
Keep the restored lidge-jun#773 finish-less EOF rules (answer text required;
pending tool calls fail closed before flush) while taking tip's clearer
mid-tool-call truncation message.
Windows CI flakes when Bun's default 5s test budget races star-state's
5s auth kill on a slow credential helper. Raise only these two route
tests to cover AUTH+API budgets.
@Wibias

Wibias commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator Author

Merging this.

Why it helps: Providers that close a chat stream after emitting answer text without [DONE] / inish_reason no longer hang the client, while mid-assembly tool-call EOFs still fail closed before a fabricated ool_call_end.

@Wibias
Wibias merged commit 679eda0 into lidge-jun:dev Jul 31, 2026
9 checks passed
@Wibias
Wibias deleted the restore/773-openai-chat-eof branch July 31, 2026 07:24
@jinbaizhe

Copy link
Copy Markdown

@Wibias @lidge-jun need your help bro。not only openai-chat adapter has this question,but also openai-response adapter have。

@Wibias

Wibias commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator Author

@Wibias @lidge-jun need your help bro。not only openai-chat adapter has this question,but also openai-response adapter have。

Huh? What do you mean exactly?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants