Skip to content

fix(streaming): handle anthropic ping chunk silently - #2089

Merged
steebchen merged 2 commits into
mainfrom
anthropic-ping-chunk
Apr 26, 2026
Merged

steebchen merged 2 commits into
mainfrom
anthropic-ping-chunk

Conversation

@steebchen

@steebchen steebchen commented Apr 26, 2026 •

Copy link
Copy Markdown
Member

Summary

  • Recognize Anthropic streaming ping keepalive events as a known chunk type and emit a no-op delta, eliminating the noisy Unrecognized Anthropic chunk warn log seen on claude-haiku-4-5.

Test plan

  • Trigger an Anthropic streaming completion and confirm no Unrecognized Anthropic chunk warnings for type: "ping" are logged.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Improved streaming handling for external AI responses: lightweight "ping" heartbeat messages are now ignored rather than being treated as data, reducing spurious warnings and preventing placeholder content from appearing in streams.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings April 26, 2026 12:56
@coderabbitai

coderabbitai Bot commented Apr 26, 2026 •

Copy link
Copy Markdown
Contributor

Walkthrough

The change updates transformStreamingToOpenai to explicitly handle Anthropic ping streaming chunks by returning null for data.type === "ping" instead of producing an OpenAI-compatible completion chunk; other unrecognized types still log a warning.

Changes

Cohort / File(s) Summary
Anthropic Ping Handling
apps/gateway/src/chat/tools/transform-streaming-to-openai.ts
Added explicit branch for data.type === "ping" that returns null (skips emitting a completion chunk). Unrecognized-type logging remains for other cases.

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~2 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: handling Anthropic ping chunks silently to prevent noisy warning logs.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch anthropic-ping-chunk

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 and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
apps/gateway/src/chat/tools/transform-streaming-to-openai.ts (1)

307-323: Consider suppressing ping chunks entirely (transformedData = null) to match the OpenAI keepalive precedent.

Anthropic ping events are pure connection keepalives with no payload. Synthesizing a full chat.completion.chunk with role: "assistant" for each one forwards a redundant SSE event to the client on every keepalive and is inconsistent with how the analogous OpenAI keepalive type is handled in this same file (lines 699-701 set transformedData = null).

The PR's goal — silencing the "Unrecognized Anthropic chunk" warn — is achieved either way, but null-suppression aligns with the existing pattern (already confirmed safe by the caller's if (!transformedData) continue; guard at line 6277-6280) and avoids emitting empty assistant-role deltas to downstream SSE consumers.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/gateway/src/chat/tools/transform-streaming-to-openai.ts` around lines
307 - 323, Replace the synthesized chat.completion.chunk for Anthropic
keepalives by setting transformedData = null when data.type === "ping" (instead
of building an assistant-role delta); update the ping branch in
transform-streaming-to-openai so it mirrors the OpenAI keepalive handling and
returns null (the caller already skips null transformedData), and remove the
normalizeAnthropicUsage(...) usage for ping since there is no payload to
forward.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@apps/gateway/src/chat/tools/transform-streaming-to-openai.ts`:
- Around line 307-323: Replace the synthesized chat.completion.chunk for
Anthropic keepalives by setting transformedData = null when data.type === "ping"
(instead of building an assistant-role delta); update the ping branch in
transform-streaming-to-openai so it mirrors the OpenAI keepalive handling and
returns null (the caller already skips null transformedData), and remove the
normalizeAnthropicUsage(...) usage for ping since there is no payload to
forward.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: cabdf8a9-20a2-419c-8900-7c520e543a2e

📥 Commits

Reviewing files that changed from the base of the PR and between fa4b184 and 947e87c.

📒 Files selected for processing (1)
  • apps/gateway/src/chat/tools/transform-streaming-to-openai.ts

Copilot AI 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.

Pull request overview

Updates Anthropic streaming-to-OpenAI transformation to treat type: "ping" keepalive events as a known chunk type, eliminating noisy “Unrecognized Anthropic chunk” warnings.

Changes:

  • Adds explicit handling for Anthropic streaming ping chunks to avoid warning logs.
  • Emits an OpenAI-compatible no-op chunk for ping events instead of falling through to the “unrecognized” warning path.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +308 to +323
transformedData = {
id: data.id ?? `chatcmpl-${Date.now()}`,
object: "chat.completion.chunk",
created: data.created ?? Math.floor(Date.now() / 1000),
model: data.model ?? usedModel,
choices: [
{
index: 0,
delta: {
role: "assistant",
},
finish_reason: null,
},
],
usage: normalizeAnthropicUsage(data.usage),
};

Copilot AI Apr 26, 2026

Copy link

Choose a reason for hiding this comment

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

Anthropic ping is a keepalive with no user-visible delta; transforming it into an OpenAI chat.completion.chunk adds extra downstream SSE traffic and can interfere with buffering logic that forwards chunks when delta.role is present (see apps/gateway/src/chat/chat.ts:6443-6453). Consider returning null here (like the OpenAI keepalive case) or emitting an actually-empty delta (no role) so ping events are ignored instead of forwarded.

Suggested change
transformedData = {
id: data.id ?? `chatcmpl-${Date.now()}`,
object: "chat.completion.chunk",
created: data.created ?? Math.floor(Date.now() / 1000),
model: data.model ?? usedModel,
choices: [
{
index: 0,
delta: {
role: "assistant",
},
finish_reason: null,
},
],
usage: normalizeAnthropicUsage(data.usage),
};
transformedData = null;

Copilot uses AI. Check for mistakes.
Comment on lines +307 to +323
} else if (data.type === "ping") {
transformedData = {
id: data.id ?? `chatcmpl-${Date.now()}`,
object: "chat.completion.chunk",
created: data.created ?? Math.floor(Date.now() / 1000),
model: data.model ?? usedModel,
choices: [
{
index: 0,
delta: {
role: "assistant",
},
finish_reason: null,
},
],
usage: normalizeAnthropicUsage(data.usage),
};

Copilot AI Apr 26, 2026

Copy link

Choose a reason for hiding this comment

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

This new anthropic ping-handling branch isn’t covered by existing unit tests for transformStreamingToOpenai (there are streaming transform tests in transform-streaming-to-openai.spec.ts). Add a test that passes { type: "ping" } for provider anthropic and asserts no warning is logged and that the event is handled as intended (e.g., returns null if treated as a keepalive, or returns the expected no-op chunk).

Copilot uses AI. Check for mistakes.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
apps/gateway/src/chat/tools/transform-streaming-to-openai.ts (1)

307-308: Targeted handling of ping chunks looks correct.

Returning null here is safely handled by the caller in apps/gateway/src/chat/chat.ts (the if (!transformedData) { … continue; } guard), so this cleanly silences the spurious Unrecognized Anthropic chunk warnings for claude-haiku-4-5 keepalives without altering downstream stream semantics.

One small stylistic note (optional): every other branch in this switch assigns to transformedData and falls through to the single return transformedData at the end of the function (e.g. AWS Bedrock uses transformedData = null; for analogous no-op events at lines 1124, 1126, 1203). Using an early return null here is functionally equivalent but inconsistent with the surrounding pattern. Feel free to align if you prefer uniformity:

♻️ Optional consistency tweak
-			} else if (data.type === "ping") {
-				return null;
+			} else if (data.type === "ping") {
+				transformedData = null;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/gateway/src/chat/tools/transform-streaming-to-openai.ts` around lines
307 - 308, The ping branch currently does an early "return null;" which is
functionally fine but inconsistent with the surrounding pattern; change the
branch handling for data.type === "ping" to set transformedData = null (instead
of returning) and fall through to the function's single "return transformedData"
at the end so it matches other branches (e.g., AWS Bedrock branches) — update
the branch in the same switch handling data.type, referencing transformedData
and data.type to locate the code.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@apps/gateway/src/chat/tools/transform-streaming-to-openai.ts`:
- Around line 307-308: The ping branch currently does an early "return null;"
which is functionally fine but inconsistent with the surrounding pattern; change
the branch handling for data.type === "ping" to set transformedData = null
(instead of returning) and fall through to the function's single "return
transformedData" at the end so it matches other branches (e.g., AWS Bedrock
branches) — update the branch in the same switch handling data.type, referencing
transformedData and data.type to locate the code.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 4c94cf33-0b50-4789-a02f-73c6ec892af1

📥 Commits

Reviewing files that changed from the base of the PR and between 947e87c and 6be63dd.

📒 Files selected for processing (1)
  • apps/gateway/src/chat/tools/transform-streaming-to-openai.ts

@steebchen
steebchen enabled auto-merge April 26, 2026 13:08
@steebchen
steebchen added this pull request to the merge queue Apr 26, 2026
Merged via the queue into main with commit 71f9fde Apr 26, 2026
17 checks passed
@steebchen
steebchen deleted the anthropic-ping-chunk branch April 26, 2026 13:20
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