Skip to content

fix(openai-chat): ignore empty data: frames; tighten unspaced [DONE] test - #1249

Merged
lidge-jun merged 1 commit into
lidge-jun:devfrom
Yuxin-Qiao:fix/openai-chat-empty-data-frame
Aug 8, 2026
Merged

fix(openai-chat): ignore empty data: frames; tighten unspaced [DONE] test#1249
lidge-jun merged 1 commit into
lidge-jun:devfrom
Yuxin-Qiao:fix/openai-chat-empty-data-frame

Conversation

@Yuxin-Qiao

@Yuxin-Qiao Yuxin-Qiao commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Summary

Small follow-up to #1194, carrying forward the two pieces of the superseded #1188 that the maintainer review called out as worth keeping (see the closing comment on #1188):

  1. Ignore empty data: frames in the OpenAI Chat adapter. sseFieldValue (from fix(sse): accept unspaced data: fields across six parsers (#1170) #1194) correctly yields an empty value for a bare data: line, but handleDataLine then falls into JSON.parse("") and terminates the stream with malformed upstream SSE data frame. Some gateways emit bare data: lines as heartbeat keep-alives; they now short-circuit as nothing-to-parse, matching the tolerant behavior of the other SSE call sites (claude-messages.ts, chat/outbound.ts, which catch { continue }).
  2. Tighten the unspaced [DONE] regression test to sentinel-only. The merged accepts an unspaced [DONE] sentinel test carries a preceding answer frame, so the finish-less EOF fallback would emit done even if unspaced [DONE] parsing regressed. The test now drives the parser with the data:[DONE] frame alone, so it fails unless the sentinel is actually recognized (this is the same weakness CodeRabbit flagged on fix(openai-chat): accept SSE data fields without the optional space (#1170) #1188).

No other parser behavior changes.

Verification

  • bun x tsc --noEmit — clean.
  • bun test tests/sse-unspaced-data-fields.test.ts tests/openai-chat-eof.test.ts — 41/41 pass (1 new test, 1 tightened).
  • bun run privacy:scan — passed.
  • Rebased onto the current integration head per maintainer review (517f4460, 0 commits behind at push time). Diff remains the same two files.

Checklist

  • Scope stays focused and avoids unrelated cleanup.
  • Docs or release notes were updated when needed.
  • Security-sensitive changes were reviewed for secrets, auth, and unsafe defaults.

Review readiness checklist

This PR stays in draft until every box below is ticked. Tick all four boxes once the requirements are met:

  • All CI tests are green on my local testing.
  • I pushed my PR to the latest dev commit.
  • I resolved all correct Codex and CodeRabbit findings.
  • My PR is ready for review.

Summary by CodeRabbit

  • Bug Fixes
    • Improved streaming responses by safely ignoring empty server-sent event frames.
    • Prevented malformed-frame errors when receiving blank data: messages.
    • Ensured valid streamed content and completion signals continue to work correctly.

@github-actions

github-actions Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Deterministic PR hygiene checks passed.

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

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The OpenAI SSE parser now ignores empty data: frames. Tests separately verify unspaced [DONE] handling and continued processing after a bare data: frame.

Changes

OpenAI SSE empty-frame handling

Layer / File(s) Summary
Skip empty SSE data frames
src/adapters/openai-chat.ts, tests/sse-unspaced-data-fields.test.ts
The parser skips empty data: payloads. Tests verify that a later valid frame emits text, completes, and produces no error. The [DONE] sentinel test remains separate.

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

Possibly related PRs

Suggested reviewers: lidge-jun, wibias, ingwannu

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the primary fix: ignoring empty SSE data frames, and names the related [DONE] test refinement.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions

github-actions Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

✅ READY

  • all PR quality gates passed; the review readiness checklist is complete.

Review readiness checklist

  • ✅ All CI tests are green on my local testing.
  • ✅ I pushed my PR to the latest dev commit.
  • ✅ I resolved all correct Codex and CodeRabbit findings.
  • ✅ My PR is ready for review.

4/4 boxes ticked.

This pull request is already Ready for Review.
The review-ready label marks this PR as ready; review automation runs independently. If no CodeRabbit review appears, comment @coderabbitai review to request one.
Maintainers: @lidge-jun @Ingwannu @Wibias

Repository owner deleted a comment from coderabbitai Bot Aug 8, 2026
Repository owner deleted a comment from coderabbitai Bot Aug 8, 2026
Repository owner deleted a comment from coderabbitai Bot Aug 8, 2026
Repository owner deleted a comment from coderabbitai Bot Aug 8, 2026
Repository owner deleted a comment from coderabbitai Bot Aug 8, 2026
Repository owner deleted a comment from coderabbitai Bot Aug 8, 2026
Repository owner deleted a comment from coderabbitai Bot Aug 8, 2026
Repository owner deleted a comment from coderabbitai Bot Aug 8, 2026
Repository owner deleted a comment from coderabbitai Bot Aug 8, 2026
Repository owner deleted a comment from coderabbitai Bot Aug 8, 2026
Repository owner deleted a comment from coderabbitai Bot Aug 8, 2026
Repository owner deleted a comment from coderabbitai Bot Aug 8, 2026
Repository owner deleted a comment from coderabbitai Bot Aug 8, 2026
Repository owner deleted a comment from coderabbitai Bot Aug 8, 2026
Repository owner deleted a comment from Yuxin-Qiao Aug 8, 2026
Repository owner deleted a comment from coderabbitai Bot Aug 8, 2026
Repository owner deleted a comment from coderabbitai Bot Aug 8, 2026
Repository owner deleted a comment from coderabbitai Bot Aug 8, 2026
Repository owner deleted a comment from coderabbitai Bot Aug 8, 2026
Repository owner deleted a comment from coderabbitai Bot Aug 8, 2026
Repository owner deleted a comment from coderabbitai Bot Aug 8, 2026
Repository owner deleted a comment from coderabbitai Bot Aug 8, 2026
Repository owner deleted a comment from coderabbitai Bot Aug 8, 2026
Repository owner deleted a comment from coderabbitai Bot Aug 8, 2026
Repository owner deleted a comment from coderabbitai Bot Aug 8, 2026
Repository owner deleted a comment from coderabbitai Bot Aug 8, 2026
Repository owner deleted a comment from coderabbitai Bot Aug 8, 2026
Repository owner deleted a comment from coderabbitai Bot Aug 8, 2026
Repository owner deleted a comment from coderabbitai Bot Aug 8, 2026
Repository owner deleted a comment from coderabbitai Bot Aug 8, 2026
@Yuxin-Qiao

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor
⚠️ Action not completed

Already reviewed.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@Ingwannu Ingwannu left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

The code change itself is focused and correct: I independently passed the 41 affected SSE/EOF tests, bun run typecheck, and bun run privacy:scan on 20c7afb5. Ignoring an empty data: value and making the sentinel-only regression test non-vacuous are both worth keeping.

The remaining blocker is branch freshness. This head is now 22 commits behind current dev (d55b903d), so it is outside the repository readiness window. Please rebase onto the current integration head, rerun the same focused checks, and let the required CI complete. I will approve the exact rebased head if the diff remains this two-file change.

@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '=== PR diff ==='
git diff --find-renames origin/dev...HEAD -- src/adapters/openai-chat.ts tests/sse-unspaced-data-fields.test.ts

echo
echo '=== Adapter outline ==='
ast-grep outline src/adapters/openai-chat.ts --items all

echo
echo '=== Changed adapter context ==='
git diff --unified=80 origin/dev...HEAD -- src/adapters/openai-chat.ts

echo
echo '=== Changed test context ==='
git diff --unified=100 origin/dev...HEAD -- tests/sse-unspaced-data-fields.test.ts

echo
echo '=== Related SSE parser and test references ==='
rg -n -C 3 --glob '*.{ts,tsx,js,mjs,cjs}' \
  'data:\[DONE\]|data:\s*\[DONE\]|JSON\.parse\(|text/event-stream|unspaced' \
  src tests

Length of output: 1060


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '=== Current checkout and available refs ==='
git status --short --branch
git branch -a
git remote -v
git show-ref --heads --remotes 2>/dev/null || true

echo
echo '=== Pull request metadata ==='
gh pr view 1249 --repo lidge-jun/opencodex \
  --json baseRefName,baseRefOid,headRefName,headRefOid,url

echo
echo '=== Current changed files relative to HEAD parent ==='
git diff --name-status HEAD^ HEAD -- \
  src/adapters/openai-chat.ts tests/sse-unspaced-data-fields.test.ts

echo
echo '=== Current commit diff ==='
git diff --find-renames --unified=100 HEAD^ HEAD -- \
  src/adapters/openai-chat.ts tests/sse-unspaced-data-fields.test.ts

echo
echo '=== Adapter source map and relevant context ==='
ast-grep outline src/adapters/openai-chat.ts --items all
rg -n -C 12 --glob '*.ts' \
  'JSON\.parse\(|\[DONE\]|data:|event-stream' \
  src/adapters/openai-chat.ts tests/sse-unspaced-data-fields.test.ts

Length of output: 57488


🏁 Script executed:

#!/bin/bash
set -euo pipefail

base='517f4460408bd871b40c3650261b23f7c542a873'
head='cf390cdba7ee1625b33e4a37cf6abde0966364fc'

echo '=== Exact PR file summary ==='
git diff --stat "$base" "$head"
git diff --name-status "$base" "$head"

echo
echo '=== Exact PR diff ==='
git diff --check "$base" "$head"
git diff --find-renames --unified=40 "$base" "$head"

echo
echo '=== OpenAI stream line-splitting and EOF handling ==='
sed -n '1040,1155p' src/adapters/openai-chat.ts

echo
echo '=== SSE field decoder implementation ==='
ast-grep outline src/lib/sse-decoder.ts --items all
sed -n '1,220p' src/lib/sse-decoder.ts

echo
echo '=== Current line-numbered changed regions ==='
nl -ba src/adapters/openai-chat.ts | sed -n '946,972p'
nl -ba tests/sse-unspaced-data-fields.test.ts | sed -n '100,145p'

Length of output: 114078


🏁 Script executed:

#!/bin/bash
set -euo pipefail

repo='lidge-jun/opencodex'
pr='1249'

echo '=== Canonical PR metadata ==='
gh api "repos/$repo/pulls/$pr" \
  --jq '{number, state, draft, base: .base.sha, head: .head.sha, changed_files, commits, additions, deletions}'

echo
echo '=== Canonical PR changed files ==='
gh api --paginate "repos/$repo/pulls/$pr/files?per_page=100" \
  --jq '.[] | {filename, status, additions, deletions, changes, patch}'

echo
echo '=== PR commits ==='
gh api --paginate "repos/$repo/pulls/$pr/commits?per_page=100" \
  --jq '.[] | {sha, message: .commit.message}'

echo
echo '=== Check status ==='
gh pr checks "$pr" --repo "$repo" || true

echo
echo '=== Current source and test line ranges ==='
awk 'NR >= 946 && NR <= 972 { printf "%d:%s\n", NR, $0 }' src/adapters/openai-chat.ts
awk 'NR >= 100 && NR <= 145 { printf "%d:%s\n", NR, $0 }' tests/sse-unspaced-data-fields.test.ts

Length of output: 8816


@github-actions[bot] Review complete. I found no actionable issues in #1249.

The empty-payload guard at src/adapters/openai-chat.ts Lines 951-956 runs before JSON.parse at Line 966. It prevents the empty-frame failure and preserves processing for non-empty JSON frames and [DONE].

The regression tests are effective:

  • tests/sse-unspaced-data-fields.test.ts Lines 114-120 test data:[DONE] without content that could activate the EOF fallback.
  • Lines 123-131 confirm that a bare data: frame is ignored and that the following valid frame completes without an error.

GitHub reports all current PR checks as passing.

You are interacting with an AI system.

…test

Follow-up to lidge-jun#1194 carrying forward the two pieces of lidge-jun#1188 that the
superseding PR did not take, per maintainer review:

- A bare `data:` line (heartbeat-style keep-alive on some gateways) now
  yields no payload and is skipped, instead of falling into JSON.parse("")
  and terminating the stream as a malformed frame.
- The unspaced [DONE] regression test is driven by the sentinel frame
  alone: with a preceding answer frame, the finish-less EOF fallback
  could emit done even if unspaced [DONE] parsing were broken.
@Yuxin-Qiao
Yuxin-Qiao force-pushed the fix/openai-chat-empty-data-frame branch from cf390cd to 8fd93a1 Compare August 8, 2026 08:15
@github-actions
github-actions Bot marked this pull request as ready for review August 8, 2026 08:17
@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

@github-actions[bot] Acknowledged. Pull request #1249 is ready for review. The existing CodeRabbit review found no actionable issues.

You are interacting with an AI system.

@Yuxin-Qiao

Copy link
Copy Markdown
Contributor Author

Temporarily closing and reopening to re-trigger the Cross-platform CI run, which was created without any jobs after the force-push (conclusion: action_required). No code change; head stays 8fd93a1.

@Yuxin-Qiao Yuxin-Qiao closed this Aug 8, 2026
@Yuxin-Qiao Yuxin-Qiao reopened this Aug 8, 2026
@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

@github-actions[bot] Acknowledged. Pull request #1249 is marked ready for review. The readiness checklist and hygiene checks passed.

You are interacting with an AI system.

@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

@github-actions[bot] Acknowledged. Pull request #1249 is ready for review at 8fd93a11bccd52ef5d753a3360e77dc070ae7e32. The readiness gate reports 4/4 checks complete.

You are interacting with an AI system.

@Yuxin-Qiao

Copy link
Copy Markdown
Contributor Author

Rebase done per review; CI runs need an approval/rerun

@Ingwannu @lidge-jun @Wibias — the head is rebased onto the current integration head (517f4460, 0 commits behind dev at push time) and the diff is still exactly the same two files (src/adapters/openai-chat.ts, tests/sse-unspaced-data-fields.test.ts).

Local verification on the rebased head 8fd93a11:

  • bun x tsc --noEmit — clean
  • bun test tests/sse-unspaced-data-fields.test.ts tests/openai-chat-eof.test.ts — 41/41 pass
  • bun run privacy:scan — passed

Checklist re-ticked, gate marked the PR ready, maintainers notified.

One blocker left: the Cross-platform CI and React Doctor runs for this head were created without any jobs and concluded action_required (runs 31248014592 / 31248014594; same after the reopen: 31248617054 / 31248617065). gh run rerun reports it needs admin rights. This looks like the fork-PR approval gate — the previous run for 20c7afb also only started its jobs after approval (~06:40). Could you approve/rerun the two runs? Happy to re-push if a fresh event is easier.

@lidge-jun
lidge-jun merged commit 30308dc into lidge-jun:dev Aug 8, 2026
29 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working review-ready

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants