Skip to content

Feat/custom system prompt - #774

Open
ewan102 wants to merge 4 commits into
linagora:developfrom
ewan102:feat/custom-system-prompt
Open

Feat/custom system prompt#774
ewan102 wants to merge 4 commits into
linagora:developfrom
ewan102:feat/custom-system-prompt

Conversation

@ewan102

@ewan102 ewan102 commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

A leading role="system" message in a chat request is now treated as a
pinned instruction rather than a chat turn: it survives
chat_history_depth truncation, never leaks into query-generation as a
fake conversation turn, and is spliced into the RAG system prompt right
after the intro paragraph rather than just prepended raw.

Security: the custom content is wrapped in an <unsafe_custom_prompt>
tag to flag it as untrusted input, and both prompt templates gained a
Security rule instructing the model to keep following its core rules
regardless of what that block says.

Summary by CodeRabbit

  • New Features
    • Added {custom_prompt} support across both system and spoken-style templates, injected into the context area.
    • Custom prompt text is wrapped in a dedicated unsafe-content block at the {custom_prompt} placeholder.
    • Preserves and merges any leading pinned client system messages into the generated context, including with history truncation.
  • Bug Fixes
    • Prevented system-message loss/duplication when assembling or truncating chat prompts.
    • Hardened custom-prompt handling by escaping attempts to break out of the custom prompt wrapper.
  • Tests
    • Expanded unit coverage for {custom_prompt} wrapping/sanitization and system-message merge/truncation scenarios.

@coderabbitai

coderabbitai Bot commented Jul 24, 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

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: dce0126d-5ad3-4390-a048-a60d47789301

📥 Commits

Reviewing files that changed from the base of the PR and between 518a48d and 4910ebd.

📒 Files selected for processing (2)
  • openrag/prompts/templates/spoken_style_answer_tmpl.txt
  • tests/unit/core/prompts/test_chat_prompt_builder.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • openrag/prompts/templates/spoken_style_answer_tmpl.txt
  • tests/unit/core/prompts/test_chat_prompt_builder.py

📝 Walkthrough

Walkthrough

prepend_system_prompt now injects optional client prompt content through a {custom_prompt} placeholder. QueryService preserves leading system messages across truncation, merges them into the generated system prompt, and retains mid-conversation system messages. Templates add security instructions and tests cover these behaviors.

Changes

Custom system prompt handling

Layer / File(s) Summary
Prompt injection contract and templates
openrag/core/prompts/chat_prompt_builder.py, openrag/prompts/templates/*, tests/unit/core/prompts/test_chat_prompt_builder.py
prepend_system_prompt accepts an optional custom prompt, escapes closing tags, wraps it in <unsafe_custom_prompt>, and supplies an empty placeholder when absent. Both templates add the placeholder and security rules, with unit coverage for these cases.
Leading system message assembly
openrag/services/orchestrators/query_service.py, tests/unit/services/orchestrators/test_query_service.py
QueryService extracts contiguous leading system messages before truncation, removes aligned entries from history, injects their merged content into the generated system prompt, and tests preservation, merging, truncation, and mid-conversation behavior.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant QueryService
  participant prepend_system_prompt
  Client->>QueryService: chat messages with leading system prompts
  QueryService->>QueryService: split pinned prompts from truncated history
  QueryService->>prepend_system_prompt: pass custom prompt, template, context, and date
  prepend_system_prompt-->>QueryService: formatted system message and history
  QueryService-->>Client: prepared chat messages
Loading

Suggested labels: enhancement

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and matches the main change: adding support for custom system prompts.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
✨ 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.

@coderabbitai coderabbitai Bot added the enhancement New feature or request label Jul 24, 2026
@ewan102
ewan102 changed the base branch from develop to feat/chat_w_specified_files July 24, 2026 15:55

@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: 2

🤖 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/core/prompts/chat_prompt_builder.py`:
- Around line 148-150: Update the custom prompt construction in the chat prompt
builder to sanitize or encode any `</unsafe_custom_prompt>` sequence in `prefix`
before interpolating it into the `<unsafe_custom_prompt>` wrapper, while
preserving normal custom content. Add a regression test covering a prefix that
contains the closing delimiter and verify subsequent client text remains inside
the intended untrusted boundary.

In `@openrag/services/orchestrators/query_service.py`:
- Around line 393-394: Update the query flow around _split_leading_system_prompt
and generate_query so an empty messages collection after removing leading system
prompts is handled before generate_query runs. Reject the system-only request
through the existing client-validation path, or apply the established no-query
fallback, while preserving normal generation for histories containing a
non-system message.
🪄 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 Plus

Run ID: 3e558893-89a3-423b-9892-6aa39ad227f2

📥 Commits

Reviewing files that changed from the base of the PR and between b2ab6b1 and 8bc82ba.

📒 Files selected for processing (7)
  • openrag/core/prompts/chat_prompt_builder.py
  • openrag/core/utils/source_filtering.py
  • openrag/prompts/templates/spoken_style_answer_tmpl.txt
  • openrag/prompts/templates/sys_prompt_tmpl.txt
  • openrag/services/orchestrators/query_service.py
  • tests/unit/core/prompts/test_chat_prompt_builder.py
  • tests/unit/services/orchestrators/test_query_service.py

Comment thread openrag/core/prompts/chat_prompt_builder.py
Comment on lines +393 to +394
custom_prompt, messages = _split_leading_system_prompt(payload["messages"], messages)
queries = await self.generate_query(messages, llm=llm)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Handle system-only histories before generating a query.

After extracting all leading system messages, messages can be empty. Line 394 then calls generate_query(), which accesses messages[-1] and raises IndexError. Reject this request with the existing client-validation path or define a no-query fallback before calling it.

🤖 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/services/orchestrators/query_service.py` around lines 393 - 394,
Update the query flow around _split_leading_system_prompt and generate_query so
an empty messages collection after removing leading system prompts is handled
before generate_query runs. Reject the system-only request through the existing
client-validation path, or apply the established no-query fallback, while
preserving normal generation for histories containing a non-system message.

@ewan102
ewan102 changed the base branch from feat/chat_w_specified_files to develop July 24, 2026 16:00
A leading role=system message in a chat request is now treated as a
pinned instruction rather than a chat turn: it survives
chat_history_depth truncation, never leaks into query-generation as a
fake conversation turn, and is spliced into the RAG system prompt right
after the intro paragraph rather than just prepended raw.

Security: the custom content is wrapped in an <unsafe_custom_prompt>
tag to flag it as untrusted input, and both prompt templates gained a
Security rule instructing the model to keep following its core rules
regardless of what that block says.
@ewan102
ewan102 force-pushed the feat/custom-system-prompt branch from 8bc82ba to a659675 Compare July 27, 2026 14:12

@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

🤖 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/services/orchestrators/query_service.py`:
- Around line 662-668: Update the truncation/forwarding logic around strip so it
accounts for the raw suffix offset where truncated begins, rather than comparing
truncated entries against raw_messages from index zero. Advance strip past
pinned entries that overlap the truncated window, ensuring entries such as SYS2
are not both injected into parts and returned in truncated[strip:].
🪄 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 Plus

Run ID: c1f5906c-0a25-4eca-af71-905ec39a70ed

📥 Commits

Reviewing files that changed from the base of the PR and between 8bc82ba and a659675.

📒 Files selected for processing (6)
  • openrag/core/prompts/chat_prompt_builder.py
  • openrag/prompts/templates/spoken_style_answer_tmpl.txt
  • openrag/prompts/templates/sys_prompt_tmpl.txt
  • openrag/services/orchestrators/query_service.py
  • tests/unit/core/prompts/test_chat_prompt_builder.py
  • tests/unit/services/orchestrators/test_query_service.py
🚧 Files skipped from review as they are similar to previous changes (4)
  • openrag/prompts/templates/spoken_style_answer_tmpl.txt
  • openrag/prompts/templates/sys_prompt_tmpl.txt
  • openrag/core/prompts/chat_prompt_builder.py
  • tests/unit/core/prompts/test_chat_prompt_builder.py

Comment thread openrag/services/orchestrators/query_service.py Outdated
ewan102 added 2 commits July 27, 2026 18:57
…closing tag

- _split_leading_system_prompt compared truncated[j] against raw_messages[j]
  from index 0, which only worked when no truncation occurred. When
  chat_history_depth split a multi-message leading system run in half, the
  still-in-window remainder was left in messages instead of being stripped,
  duplicating it as an unwrapped, untrusted-marker-free system message.
  Now computed directly from the tail offset instead of object identity.
- escape a client-supplied </unsafe_custom_prompt> sequence in prefix before
  interpolating it, so a crafted prefix can't forge the wrapper's closing tag
  and make trailing attacker text read as outside the untrusted boundary.

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

🧹 Nitpick comments (1)
tests/unit/core/prompts/test_chat_prompt_builder.py (1)

121-135: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Cover mixed-case closing tags.

_UNSAFE_PROMPT_CLOSE_TAG_RE is case-insensitive, but this test only exercises the lowercase spelling. Add a mixed-case delimiter case to preserve that security invariant during future changes.

🤖 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 `@tests/unit/core/prompts/test_chat_prompt_builder.py` around lines 121 - 135,
Add a mixed-case closing-tag variant to
test_prepend_system_prompt_escapes_closing_tag_in_custom_prompt, while
preserving the existing assertions that only the builder’s genuine delimiter
remains unescaped and the injected delimiter is escaped.
🤖 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 `@tests/unit/core/prompts/test_chat_prompt_builder.py`:
- Around line 121-135: Add a mixed-case closing-tag variant to
test_prepend_system_prompt_escapes_closing_tag_in_custom_prompt, while
preserving the existing assertions that only the builder’s genuine delimiter
remains unescaped and the injected delimiter is escaped.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 3e213e3c-7346-4321-9619-d7b3215e81f8

📥 Commits

Reviewing files that changed from the base of the PR and between 827ea30 and 518a48d.

📒 Files selected for processing (5)
  • openrag/core/prompts/chat_prompt_builder.py
  • openrag/prompts/templates/sys_prompt_tmpl.txt
  • openrag/services/orchestrators/query_service.py
  • tests/unit/core/prompts/test_chat_prompt_builder.py
  • tests/unit/services/orchestrators/test_query_service.py
🚧 Files skipped from review as they are similar to previous changes (3)
  • openrag/prompts/templates/sys_prompt_tmpl.txt
  • openrag/services/orchestrators/query_service.py
  • tests/unit/services/orchestrators/test_query_service.py

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant