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:
parent
57be5868f9
commit
2554636ded
2 changed files with 68 additions and 5 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"),
|
||||
[
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue