Skip to content

fix: add content when the RAG response is empty#461

Open
ewan102 wants to merge 3 commits into
linagora:mainfrom
ewan102:Fix-Adding-a-content-when-the-RAG-response-is-empty
Open

fix: add content when the RAG response is empty#461
ewan102 wants to merge 3 commits into
linagora:mainfrom
ewan102:Fix-Adding-a-content-when-the-RAG-response-is-empty

Conversation

@ewan102

@ewan102 ewan102 commented Jun 12, 2026

Copy link
Copy Markdown

Adding tests in open_ai_chat_completion() and streaming_with_source filtering that verify if the message sent by the llm has any text in it and if not, it had a generic text (I wrote something but it is just an exemple...) : I do not have the necessary documentation in order to answer your message

Summary by CodeRabbit

  • Bug Fixes
    • Added a clear client-facing fallback message when a reply becomes empty after removing source attributions, covering both streaming and non-streaming chat/completions.
    • Improved streaming end-of-response chunking so the final message reliably includes either the remaining content or the fallback, avoiding empty completions.

@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds fallback handling for empty model outputs after stripping source blocks. A shared constant EMPTY_RESPONSE_FALLBACK_MESSAGE is defined and used to ensure non-empty responses when source removal leaves no usable content. Streaming SSE finish logic is refactored with a local _sse helper and emits the fallback message when cleaned content is empty; non-streaming chat and completion endpoints apply the same fallback post-processing.

Changes

Empty Response Fallback Handling

Layer / File(s) Summary
Streaming SSE finishing refactor with fallback
openrag/components/prompts/prompts.py, openrag/components/utils.py
Defines EMPTY_RESPONSE_FALLBACK_MESSAGE constant and imports it into the streaming module. Introduces a local _sse helper that deep-copies chunk_template, sets delta and optional finish_reason, and attaches filtered sources in extra. At stream end, if the cleaned final output is empty after stripping source tags, emits a default message; otherwise emits the remaining un-emitted tail and then the finish chunk via _sse.
Non-streaming response fallback
openrag/routers/openai.py
Imports EMPTY_RESPONSE_FALLBACK_MESSAGE; adds post-processing guards for non-streaming /chat/completions and /completions: after removing any sources block, if the cleaned content/text is empty or whitespace, replaces it with the fallback message and logs a warning.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • linagora/openrag#257: Both PRs modify openrag/components/utils.py's stream_with_source_filtering streaming/SSE emission flow regarding chunk finalization and source metadata attachment.

Poem

🐰 When sources hide and answers thin,
I stitch a line to help begin.
If docs are gone and silence stays,
I offer words to guide your ways.
Hop, twitch — the stream now softly plays.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.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 directly and clearly summarizes the main change: adding fallback content when RAG responses are empty across multiple handlers.
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 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 and usage tips.

@coderabbitai coderabbitai Bot added the fix Fix issue label Jun 12, 2026

@coderabbitai coderabbitai Bot 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
openrag/routers/openai.py (1)

438-445: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Missing empty-content fallback for /completions endpoint.

The /chat/completions endpoint (lines 368-369) now returns a fallback message when clean_content is empty after source stripping. The /completions endpoint processes sources similarly (lines 438-440) but lacks the corresponding guard for empty clean_text. If the LLM response consists solely of a [Sources: ...] tag, the client would receive an empty text field.

If this asymmetry is intentional, consider adding a comment explaining why. Otherwise, apply the same fallback:

Proposed fix
     filtered = filter_sources_by_citations(sources, citations)
     complete_response["extra"] = json.dumps({"sources": filtered})
     log.debug("Returning completion response.")
+    if not clean_text.strip():
+        complete_response["choices"][0]["text"] = "I do not have the necessary documentation in order to answer your message"
     return JSONResponse(content=complete_response)
🤖 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 `@openrag/routers/openai.py` around lines 438 - 445, The /completions response
path omits the empty-content fallback used in /chat/completions: after calling
extract_and_strip_sources_block(text) you must guard against clean_text being
empty and replace it with the same fallback message used for clean_content in
the chat path; update the block handling complete_response (variables: text,
clean_text, citations, complete_response) so that if clean_text == "" you set
complete_response["choices"][0]["text"] to the fallback string before
serializing extra sources via filter_sources_by_citations and returning the
JSONResponse (keep using extract_and_strip_sources_block and
filter_sources_by_citations).
🧹 Nitpick comments (1)
openrag/components/utils.py (1)

276-281: 💤 Low value

Consider extracting the fallback message to a shared constant.

The same fallback message string appears here and in openrag/routers/openai.py:369. Defining it once (e.g., in this module or a shared constants file) would prevent drift if the wording is later updated.

🤖 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 `@openrag/components/utils.py` around lines 276 - 281, Extract the repeated
fallback string "I do not have the necessary documentation in order to answer
your message" into a shared constant (e.g., FALLBACK_NO_DOCS_MESSAGE) and
replace the inline literal in utils.py where _sse is yielded (inside the
chunk_template branch that yields the default message) and in
openrag/routers/openai.py at the location referenced (around line 369) to use
that constant; ensure the constant is exported from a shared module or the utils
module and imported where needed so both places reference the same symbol.
🤖 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.

Outside diff comments:
In `@openrag/routers/openai.py`:
- Around line 438-445: The /completions response path omits the empty-content
fallback used in /chat/completions: after calling
extract_and_strip_sources_block(text) you must guard against clean_text being
empty and replace it with the same fallback message used for clean_content in
the chat path; update the block handling complete_response (variables: text,
clean_text, citations, complete_response) so that if clean_text == "" you set
complete_response["choices"][0]["text"] to the fallback string before
serializing extra sources via filter_sources_by_citations and returning the
JSONResponse (keep using extract_and_strip_sources_block and
filter_sources_by_citations).

---

Nitpick comments:
In `@openrag/components/utils.py`:
- Around line 276-281: Extract the repeated fallback string "I do not have the
necessary documentation in order to answer your message" into a shared constant
(e.g., FALLBACK_NO_DOCS_MESSAGE) and replace the inline literal in utils.py
where _sse is yielded (inside the chunk_template branch that yields the default
message) and in openrag/routers/openai.py at the location referenced (around
line 369) to use that constant; ensure the constant is exported from a shared
module or the utils module and imported where needed so both places reference
the same symbol.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 403765fa-387a-45a8-be66-c8449c5b9089

📥 Commits

Reviewing files that changed from the base of the PR and between c9d53cc and c16efdb.

📒 Files selected for processing (2)
  • openrag/components/utils.py
  • openrag/routers/openai.py

@ewan102 ewan102 force-pushed the Fix-Adding-a-content-when-the-RAG-response-is-empty branch from c16efdb to 17a43de Compare June 12, 2026 09:20
Comment thread openrag/components/utils.py Outdated
if chunk_template and not final_clean.strip():
# Response was only a [Sources: ...] tag (or empty) — send a default
# message so the client never receives an empty completion.
yield _sse({"content": "I do not have the necessary documentation in order to answer your message"})

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This leaves internationalisation pretty tough.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Agreed — suggest making this configurable (e.g. a Hydra llm.empty_response_message / env var) defaulting to the current string, so deployments can localize. The product already targets non-English users (WEBSEARCH_LANG defaults to fr-FR).

@ewan102 ewan102 force-pushed the Fix-Adding-a-content-when-the-RAG-response-is-empty branch from 17a43de to 79aac85 Compare June 12, 2026 10:00
@ewan102 ewan102 force-pushed the Fix-Adding-a-content-when-the-RAG-response-is-empty branch from 79aac85 to ba34c4c Compare June 15, 2026 08:53
Comment thread openrag/routers/openai.py
log.debug("Returning non-streaming completion chunk.")
if not clean_content.strip():
log.warning("LLM and RAG response empty after source stripping, returning fallback message")
chunk["choices"][0]["message"]["content"] = EMPTY_RESPONSE_FALLBACK_MESSAGE

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

When the fallback fires, extra.sources is still populated (all sources if citations is None, or the cited ones if the reply was only a [Sources: ...] tag) — so the client gets "I do not have the necessary documentation" alongside a non-empty source list, which is contradictory. Consider clearing the sources here (and in the streaming / /completions paths), or softening the message.

Comment thread openrag/components/utils.py Outdated
@ewan102 ewan102 force-pushed the Fix-Adding-a-content-when-the-RAG-response-is-empty branch from ba34c4c to 1d22eaa Compare June 15, 2026 09:22

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
openrag/components/utils.py (1)

19-21: 💤 Low value

Remove orphaned comment.

This comment describing EMPTY_RESPONSE_FALLBACK_MESSAGE is disconnected from both its definition (in prompts.py) and its usage (lines 284-288, which has inline comments). It disrupts the import → globals flow.

Proposed fix
-# Sent to the client when the LLM produces no usable content (e.g. the whole
-# response was a [Sources: ...] tag), so it never receives an empty completion.
 # Global variables
🤖 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 `@openrag/components/utils.py` around lines 19 - 21, Remove the orphaned
comment block at lines 19-21 that describes EMPTY_RESPONSE_FALLBACK_MESSAGE.
This comment is disconnected from both the variable's definition in prompts.py
and its usage later in the file at lines 284-288 (which already has inline
comments), and it disrupts the import-to-globals flow in the file. Simply delete
these three lines entirely.
🤖 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.

Inline comments:
In `@openrag/components/utils.py`:
- Line 8: The import statement for EMPTY_RESPONSE_FALLBACK_MESSAGE uses a
fully-qualified path beginning with openrag.components.prompts.prompts while the
rest of the file uses bare import paths (e.g., components.indexer,
utils.logger). Change the import from the fully-qualified
openrag.components.prompts.prompts path to match the project convention by using
the bare path components.prompts.prompts instead, which will resolve the Ruff
I001 linting failure related to import style consistency.

---

Nitpick comments:
In `@openrag/components/utils.py`:
- Around line 19-21: Remove the orphaned comment block at lines 19-21 that
describes EMPTY_RESPONSE_FALLBACK_MESSAGE. This comment is disconnected from
both the variable's definition in prompts.py and its usage later in the file at
lines 284-288 (which already has inline comments), and it disrupts the
import-to-globals flow in the file. Simply delete these three lines entirely.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e5ad9bbb-0a6c-4ff1-9346-6e3fdab089df

📥 Commits

Reviewing files that changed from the base of the PR and between ba34c4c and 1d22eaa.

📒 Files selected for processing (3)
  • openrag/components/prompts/prompts.py
  • openrag/components/utils.py
  • openrag/routers/openai.py
✅ Files skipped from review due to trivial changes (1)
  • openrag/components/prompts/prompts.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • openrag/routers/openai.py

Comment thread openrag/components/utils.py Outdated
@ewan102 ewan102 force-pushed the Fix-Adding-a-content-when-the-RAG-response-is-empty branch 2 times, most recently from 231cd69 to 1977169 Compare June 15, 2026 09:45
…t-when-the-RAG-response-is-empty

# Conflicts:
#	openrag/components/utils.py
@ewan102 ewan102 force-pushed the Fix-Adding-a-content-when-the-RAG-response-is-empty branch from 1977169 to 0c03f03 Compare June 15, 2026 09:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

fix Fix issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants