Repository navigation
fix(local): fail a reply the endpoint cuts off with an in-stream error - #2170
Conversation
Once the first token has gone out the status is already 200, so llama.cpp and OpenRouter report a later failure as a `data:` chunk carrying `error`. `_delta()` read that chunk as an empty delta, so the stream closed as if the model had finished: chat stored the cut-off text as a completed reply with no error, and Studio handed it to the JSON parser. Raise `StreamedError` with the endpoint's message instead, so the turn takes the existing mid-reply failure path: an `error` frame, partial text kept.
|
@MohammadHijjawi97 is attempting to deploy a commit to the Rohan Verma's projects Team on Vercel. A member of the Team first needs to authorize it. |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info
📝 Walkthrough
Merge Risk: ⚪ Minimal · up to
|
|
Reviewed this. The fix is needed and the detection is right:
Your three tests fail against 1. It conflicts with
|
…-ends-reply # Conflicts: # docs/architecture/chat.md # surfsense_local/backend/modules/llm/providers/openai_compatible/chat.py
The Model setup chat test caught httpx errors but not the new StreamedError, so a model that failed mid-reply answered 500 instead of the 502 "chat request failed: ..." a refused request gets.
|
Merged into One thing from our earlier note is still open, for a next PR if you'd like it: an in-stream error from the local runtime is classified |
|
Thanks for the review. Merged dev into the branch. |
What
_delta()in the OpenAI-compatible chat provider now raisesStreamedError, carrying the endpoint's message, when a stream chunk carrieserror. Before, it read that chunk as an empty delta. Chat then takes its existing mid-reply failure path: anerrorframe, with the partial text kept. One line indocs/architecture/chat.mdstates this.Why
Once the first token has gone out, the status is already 200, so a later failure arrives inside the stream:
data: {"error": {"code": 500, "message": …, "type": …}}(format_oai_sseinserver-context.cpp)errorandfinish_reason: "error", and its streaming docs say to treat it as a failureThe provider skipped that chunk, so the stream closed as if the model had finished:
parse_json, which reported "model did not return valid JSON"Repro against a mocked endpoint (partial text, then OpenRouter's documented error chunk):
Before:
After:
The error reaches the chat as
unknown, which offers Retry, becauseclassify_chat_errorreads an error's text only from an HTTP error response (a 400'serror.type, and since #2179 llama.cpp's 500 message, forruntime_busy), and aStreamedErroris not one.Fixes # (no issue)
How to test
The new tests cover llama.cpp's and OpenRouter's error shapes and a bare string error. They fail without the change ("DID NOT RAISE StreamedError"). Locally
pytest -m unitpassed (3285); the chat streaming integration tests skipped without the real embedding model.High-level PR Summary
This PR fixes a bug where the OpenAI-compatible chat provider incorrectly treated mid-stream errors from endpoints like llama.cpp and OpenRouter as successful completions. The fix introduces a
StreamedErrorexception that's raised when an error chunk is detected, allowing the chat system to properly handle partial replies with an error state and offer retry options instead of treating truncated responses as complete.⏱️ Estimated Review Time: 5-15 minutes
💡 Review Order Suggestion
surfsense_local/backend/tests/unit/llm/providers/openai_compatible/test_streamed_error.pysurfsense_local/backend/modules/llm/providers/openai_compatible/chat.pydocs/architecture/chat.mdSummary by CodeRabbit