Studio: follow-up fix for GGUF developer prompts (#6115)

* Studio: merge developer prompts for GGUF chat

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
Wasim Yousef Said 2026-06-09 18:11:38 +02:00 committed by GitHub
commit 2554636ded
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 68 additions and 5 deletions

View file

@ -2050,8 +2050,8 @@ def _extract_content_parts(messages: list) -> tuple[str, list[dict], "Optional[s
first_image_b64: Optional[str] = None
for msg in messages:
# ── System messages → extract as system_prompt ────────
if msg.role == "system":
# ── System / developer messages → extract as system_prompt ────────
if msg.role in ("system", "developer"):
if isinstance(msg.content, str):
system_parts.append(msg.content)
elif isinstance(msg.content, list):
@ -3053,6 +3053,7 @@ async def openai_chat_completions(
payload,
llama_backend.is_vision,
)
gguf_messages = _set_or_prepend_system_message(gguf_messages, system_prompt)
image_b64 = None
cancel_event = threading.Event()
@ -5190,9 +5191,9 @@ def _set_or_prepend_system_message(
if not system_prompt:
return safe_messages
# Drop existing system turns so the backend never sees duplicate or
# conflicting system instructions, then prepend the resolved prompt.
others = [dict(msg) for msg in safe_messages if msg.get("role") != "system"]
# Drop existing system/developer turns so the backend never sees duplicate
# or conflicting system instructions, then prepend the resolved prompt.
others = [dict(msg) for msg in safe_messages if msg.get("role") not in ("system", "developer")]
return [{"role": "system", "content": system_prompt}, *others]
@ -6415,6 +6416,8 @@ def _build_openai_passthrough_body(payload, backend_ctx = None) -> dict:
leak to the backend.
"""
messages = _openai_messages_for_passthrough(payload)
system_prompt, _, _ = _extract_content_parts(payload.messages)
messages = _set_or_prepend_system_message(messages, system_prompt)
tool_choice = payload.tool_choice if payload.tool_choice is not None else "auto"
# When the caller asked for a specific reasoning mode, forward it via
# chat_template_kwargs so the Jinja template renders with (or without) the

View file

@ -33,6 +33,7 @@ from core.inference.anthropic_compat import (
anthropic_tool_choice_to_openai,
)
from routes.inference import (
_build_openai_passthrough_body,
_build_passthrough_payload,
_clamp_finish_reason,
_effective_max_tokens,
@ -634,6 +635,24 @@ class TestBuildPassthroughPayloadToolChoice:
assert body.get("repeat_penalty") == 1.1
assert "repetition_penalty" not in body
def test_passthrough_body_merges_system_and_developer_messages(self):
payload = ChatCompletionRequest(
model = "default",
messages = [
{"role": "system", "content": "original system"},
{"role": "developer", "content": "developer rules"},
{"role": "user", "content": "hi"},
],
tools = self._args()["openai_tools"],
)
body = _build_openai_passthrough_body(payload, backend_ctx = 4096)
assert body["messages"] == [
{"role": "system", "content": "original system\n\ndeveloper rules"},
{"role": "user", "content": "hi"},
]
# =====================================================================
# OpenAI API compatibility helpers — verified spec edge cases
@ -1092,6 +1111,47 @@ class TestGgufVisionToolRouting:
assert captured["kwargs"]["disable_parallel_tool_use"] is True
def test_standard_gguf_merges_system_and_developer_messages(self, monkeypatch):
import routes.inference as inf_mod
captured = {}
def _generate(**kwargs):
captured["messages"] = kwargs["messages"]
yield "done"
yield {
"type": "metadata",
"usage": {"prompt_tokens": 3, "completion_tokens": 1, "total_tokens": 4},
"finish_reason": "stop",
}
backend = SimpleNamespace(
is_loaded = True,
is_vision = False,
supports_tools = False,
model_identifier = "test-gguf",
generate_chat_completion = _generate,
)
monkeypatch.setattr(inf_mod, "get_llama_cpp_backend", lambda: backend)
payload = ChatCompletionRequest(
model = "default",
messages = [
{"role": "system", "content": "original system"},
{"role": "developer", "content": "developer rules"},
{"role": "user", "content": "hi"},
],
)
self._drive(
openai_chat_completions(payload, request = self._Request(), current_subject = "test")
)
assert captured["messages"] == [
{"role": "system", "content": "original system\n\ndeveloper rules"},
{"role": "user", "content": "hi"},
]
@pytest.mark.parametrize(
("seed", "expected"),
[