Skip to content

fix(anthropic): cover retry stream recreation#1588

Open
rosetta-livekit-bot[bot] wants to merge 12 commits into
mainfrom
fix/anthropic-retry-fresh-stream
Open

fix(anthropic): cover retry stream recreation#1588
rosetta-livekit-bot[bot] wants to merge 12 commits into
mainfrom
fix/anthropic-retry-fresh-stream

Conversation

@rosetta-livekit-bot
Copy link
Copy Markdown
Contributor

@rosetta-livekit-bot rosetta-livekit-bot Bot commented May 24, 2026

Summary

  • create a fresh Anthropic streaming request for each retry attempt
  • avoid re-awaiting the same coroutine after a transient stream creation failure
  • add a no-network regression test for the retry path

Fixes #5805.

Tests

  • uv run pytest tests\test_plugin_anthropic.py -q
  • uv run ruff check livekit-plugins\livekit-plugins-anthropic\livekit\plugins\anthropic\llm.py tests\test_plugin_anthropic.py
  • python -m py_compile livekit-plugins\livekit-plugins-anthropic\livekit\plugins\anthropic\llm.py tests\test_plugin_anthropic.py
  • git diff --check

@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 24, 2026

🦋 Changeset detected

Latest commit: 9f8f651

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 32 packages
Name Type
@livekit/agents-plugin-anthropic Patch
@livekit/agents Patch
@livekit/agents-plugin-anam Patch
@livekit/agents-plugin-assemblyai Patch
@livekit/agents-plugin-baseten Patch
@livekit/agents-plugin-bey Patch
@livekit/agents-plugin-cartesia Patch
@livekit/agents-plugin-cerebras Patch
@livekit/agents-plugin-deepgram Patch
@livekit/agents-plugin-elevenlabs Patch
@livekit/agents-plugin-fishaudio Patch
@livekit/agents-plugin-google Patch
@livekit/agents-plugin-hedra Patch
@livekit/agents-plugin-hume Patch
@livekit/agents-plugin-inworld Patch
@livekit/agents-plugin-lemonslice Patch
@livekit/agents-plugin-liveavatar Patch
@livekit/agents-plugin-livekit Patch
@livekit/agents-plugin-minimax Patch
@livekit/agents-plugin-mistral Patch
@livekit/agents-plugin-mistralai Patch
@livekit/agents-plugin-neuphonic Patch
@livekit/agents-plugin-openai Patch
@livekit/agents-plugin-phonic Patch
@livekit/agents-plugin-resemble Patch
@livekit/agents-plugin-rime Patch
@livekit/agents-plugin-runway Patch
@livekit/agents-plugin-sarvam Patch
@livekit/agents-plugin-silero Patch
@livekit/agents-plugin-trugen Patch
@livekit/agents-plugin-xai Patch
@livekit/agents-plugins-test Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@davidzhao davidzhao self-assigned this May 25, 2026
devin-ai-integration[bot]

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

Copy link
Copy Markdown
Contributor

@devin-ai-integration devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

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

Devin Review found 1 new potential issue.

View 12 additional findings in Devin Review.

Open in Devin Review

Comment on lines +359 to +367
if (this.#ignoringCoT) {
continue;
}

this.queue.put({
id: this.#requestId,
delta: { role: 'assistant', content: text },
});
retryable = false;
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.

🟡 Empty text emitted and retryable set to false after thinking block is completely stripped

When a text delta consists entirely of a <thinking>...</thinking> block (or when the </thinking> tag appears at the very end of a delta leaving nothing after it), the remaining text after filtering is "". The code falls through to emit this empty string as a content chunk and sets retryable = false on line 367. This has two consequences: (1) an empty content delta is needlessly queued to the consumer, and (2) the stream becomes non-retryable despite no meaningful content being delivered, so subsequent API errors will not trigger a retry.

Trace for text delta containing only a thinking block

For text = "<thinking>hidden</thinking>":

  1. thinkingStart = 0, preThinking = "" (not emitted because of the if (preThinking) guard)
  2. text = "hidden</thinking>" after slicing past <thinking>
  3. #ignoringCoT = true
  4. thinkingEnd = 6, text = "" after slicing past </thinking>
  5. #ignoringCoT = false
  6. Falls through to line 363, emits { content: "" } and sets retryable = false
Suggested change
if (this.#ignoringCoT) {
continue;
}
this.queue.put({
id: this.#requestId,
delta: { role: 'assistant', content: text },
});
retryable = false;
if (this.#ignoringCoT) {
continue;
}
if (!text) {
continue;
}
this.queue.put({
id: this.#requestId,
delta: { role: 'assistant', content: text },
});
retryable = false;
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

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