Skip to content

fix: complete Cohere streaming Responses terminal events with the output array and reasoning payloads - #5386

Merged
akshaydeo merged 2 commits into
maximhq:devfrom
fus3r:fix-cohere-responses-stream-terminal-events
Jul 20, 2026
Merged

fix: complete Cohere streaming Responses terminal events with the output array and reasoning payloads#5386
akshaydeo merged 2 commits into
maximhq:devfrom
fus3r:fix-cohere-responses-stream-terminal-events

Conversation

@fus3r

@fus3r fus3r commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Summary

Streaming /v1/responses on Cohere never completed its terminal events: response.completed carried no output array at all ("output":null, every item type missing from the final snapshot) and no stop_reason, reasoning done events were empty shells (thinking deltas were never buffered, unlike text deltas since #3838), and citation annotation events carried no item_id. The non-streaming path returns the output array and the reasoning content correctly, and the Anthropic stream ingress already populates Output from per-item bookkeeping, so these were Cohere-only gaps. The streamed reasoning shape was also not replayable: it had neither the content blocks the request converter reads nor a filled summary, so conversations coming from streaming silently lost their thinking blocks on the next turn. Fixes #5385.

Changes

  • Added output-item bookkeeping to CohereResponsesStreamState (OutputItems map fed by a small trackOutputItems helper wherever output_item.added/output_item.done are emitted) and populate response.completed.response.output from it, sorted by output index, mirroring the Anthropic ingress.
  • Buffer thinking deltas in TextBuffers the way text deltas already are, and emit reasoning_summary_text.done, the reasoning content_part.done, and the reasoning output_item.done with the accumulated text. The done item carries the text as a reasoning content block, the exact shape the non-streaming converter produces, which is what convertBifrostReasoningToCohereThinking replays. The reasoning delta and done events also carry summary_index (0; Cohere thinking has a single summary part per item), which the official OpenAI event types require.
  • Record citation annotations per output index, set item_id on output_text.annotation.added/done, include the already-streamed annotations in the text item's done events, and defensively fold annotations into the completed snapshot regardless of whether they arrive before or after content-end, with clone-on-write so already-emitted events are never mutated.
  • Map delta.finish_reason on message-end (and finish_reason on the non-streaming Responses conversion) through the existing ConvertCohereFinishReasonToBifrost, so response.completed and the non-streaming response carry stop_reason the way the chat surface already does. This also fixes the Anthropic-format egress synthesizing end_turn for Cohere tool-use turns.
  • Extracted the tool-plan close block (duplicated verbatim in content-start and tool-call-start) into a closeToolPlanItem helper and also call it on message-end, so a stream ending right after a tool plan completes the item before response.completed.
  • Added a changelog entry.

Type of change

  • Bug fix
  • Feature
  • Refactor
  • Documentation
  • Chore/CI

Affected areas

  • Core (Go)
  • Transports (HTTP)
  • Providers/Integrations
  • Plugins
  • UI (React)
  • Docs

How to test

cd core
go test ./providers/cohere/ -run 'TestCohere(ResponsesStream|NonStreamResponses)' -v
go test ./providers/cohere/

The nine new tests in streamterminalevents_test.go construct Cohere v2 stream event sequences (thinking deltas, text, tool plan, tool calls, citations arriving both during and after their content block) and drive them through ToBifrostResponsesStream, pinning: done events carrying the accumulated thinking text, response.completed carrying the full output array with the actual payloads and the mapped stop_reason, item_id on annotation events, citations present in the done events and the final snapshot, the tool-plan item completed at message-end, no state leakage across pooled-state recycling, a full replay chain (completed reasoning item JSON-echoed back through ConvertBifrostMessagesToCohereMessages recovers the thinking block), and the non-streaming stop_reason mapping. All nine fail on dev without the fix with the exact symptoms above.

Screenshots/Recordings

Not a UI change.

Breaking changes

  • Yes
  • No

Terminal events that previously carried empty payloads now carry the accumulated ones, and response.completed now includes the output array and stop_reason, matching the non-streaming path. The framework stream accumulator folds deltas and ignores done events, so logged messages are unchanged (verified). The Anthropic-format egress ignores the enriched done payloads and only gains the correct terminal stop_reason.

Related issues

Closes #5385

Security considerations

None. No new inputs are parsed; the change only carries already-received stream content through to the terminal events.

Checklist

  • I read docs/contributing/README.md and followed the guidelines
  • I added/updated tests where appropriate
  • I updated documentation where needed
  • I verified builds succeed (Go and UI)
  • I verified the CI pipeline passes locally if applicable

@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 38ce85c6-d768-40b4-8970-544cdda6b510

📥 Commits

Reviewing files that changed from the base of the PR and between 329f840 and 1658e79.

📒 Files selected for processing (2)
  • core/providers/cohere/responses.go
  • core/providers/cohere/streamterminalevents_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • core/providers/cohere/responses.go

📝 Walkthrough

Summary by CodeRabbit

  • Bug Fixes
    • Cohere streaming responses now include complete output items and stop reasons in response.completed.
    • Thinking terminal events now retain accumulated thinking text in the final reasoning-related payloads.
    • Citation annotations now include the associated text item ID and are reflected reliably in the final snapshot (including when citations arrive after content ends).
    • Tool plans are properly closed at message end and handled correctly when followed by content.
    • Non-streaming responses now correctly report the stop reason.
  • Tests
    • Added streaming terminal-events coverage for completed payloads, ordering, citations, tool plans, and state recycling.

Walkthrough

Cohere Responses terminal events now preserve reasoning text, citations, output items, tool-plan completion, usage, and stop reasons. Non-streaming responses map stop reasons, with tests covering terminal snapshots, ordering, recycling, and replayability.

Changes

Cohere Responses terminal event handling

Layer / File(s) Summary
Stream state tracking and shared helpers
core/providers/cohere/responses.go
Tracks output items and annotations, resets pooled state, and centralizes tool-plan closure and tracked emission.
Streamed item and annotation events
core/providers/cohere/responses.go
Preserves accumulated reasoning and text content, attaches citation metadata and item IDs, and tracks completed text, reasoning, and function-call items.
Completed response assembly
core/providers/cohere/responses.go
Maps finish reasons, closes open tool plans, folds late citations, and builds an ordered response.completed output array. Non-streaming conversion also maps stop reasons.
Terminal behavior validation and changelog
core/providers/cohere/streamterminalevents_test.go, core/changelog.md
Adds coverage for terminal output, reasoning, citations, tool plans, state recycling, replayability, and stop reasons, plus the changelog entry.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CohereStreamEvent
  participant CohereResponsesStreamState
  participant response_completed
  CohereStreamEvent->>CohereResponsesStreamState: process streamed content and citations
  CohereStreamEvent->>CohereResponsesStreamState: process MessageEnd and FinishReason
  CohereResponsesStreamState->>CohereResponsesStreamState: close tool plan and fold annotations
  CohereResponsesStreamState->>response_completed: emit ordered output and stop reason
Loading

Possibly related issues

  • maximhq/bifrost#5259: Covers similar missing completed output and incomplete reasoning terminal events in a different provider implementation.

Suggested reviewers: akshaydeo, tejasghatte, pratham-mishra04, sammaji

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main Cohere streaming terminal-event fix.
Description check ✅ Passed The PR description fills the required sections and clearly explains the change, testing, impact, and related issue.
Linked Issues check ✅ Passed The code changes align with #5385 by fixing output snapshots, reasoning replay, citation item_ids, stop_reason mapping, and tool-plan completion.
Out of Scope Changes check ✅ Passed The added changelog and tests are relevant to the fix; no unrelated code changes stand out.
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

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.12.2)

level=error msg="[linters_context] typechecking error: pattern ./...: directory prefix . does not contain main module or its selected dependencies"


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.

@greptile-apps

greptile-apps Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Confidence Score: 5/5

This looks safe to merge.

  • The tool-plan index now advances the shared output counter, so later content cannot overwrite the plan item.
  • Pending tool plans are tracked before the completed response is built.
  • New pooled state is cleared between streams.
  • No blocking issues remain in the changed code.

Important Files Changed

Filename Overview
core/providers/cohere/responses.go Completes Cohere terminal payloads and allocates distinct output indexes for tool plans and later content.
core/providers/cohere/streamterminalevents_test.go Adds coverage for completed output, reasoning, citations, tool plans, replay, stop reasons, and pooled-state cleanup.
core/changelog.md Documents the Cohere streaming terminal-event fix.

Reviews (2): Last reviewed commit: "fix: allocate the tool-plan output index..." | Re-trigger Greptile

Comment thread core/providers/cohere/responses.go

@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)
core/providers/cohere/streamterminalevents_test.go (1)

162-162: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Prefer a pointer helper over the address operator (&) for variables.

Based on learnings, prefer using a pointer helper function (like schemas.Ptr, which is heavily used in this file) instead of the address operator (&) when taking the address of a local variable. This improves readability and keeps the code consistent with the repository's preferred pattern.

  • core/providers/cohere/streamterminalevents_test.go#L162-L162: Replace &document with schemas.Ptr(document).
  • core/providers/cohere/streamterminalevents_test.go#L179-L179: Replace &finishReason with schemas.Ptr(finishReason).
  • core/providers/cohere/streamterminalevents_test.go#L533-L533: Replace &finishReason with schemas.Ptr(finishReason).
🤖 Prompt for 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.

In `@core/providers/cohere/streamterminalevents_test.go` at line 162, Replace
direct address operators with schemas.Ptr for the local variables at
core/providers/cohere/streamterminalevents_test.go lines 162, 179, and 533: use
schemas.Ptr(document) and schemas.Ptr(finishReason) at the respective sites,
preserving the existing test data and behavior.

Source: Learnings

🤖 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.

Nitpick comments:
In `@core/providers/cohere/streamterminalevents_test.go`:
- Line 162: Replace direct address operators with schemas.Ptr for the local
variables at core/providers/cohere/streamterminalevents_test.go lines 162, 179,
and 533: use schemas.Ptr(document) and schemas.Ptr(finishReason) at the
respective sites, preserving the existing test data and behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 97437bd2-e1ce-4e52-a167-6a524a5f9198

📥 Commits

Reviewing files that changed from the base of the PR and between 4714e49 and 329f840.

📒 Files selected for processing (3)
  • core/changelog.md
  • core/providers/cohere/responses.go
  • core/providers/cohere/streamterminalevents_test.go

coderabbitai[bot]
coderabbitai Bot previously approved these changes Jul 20, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants