mirror of
https://github.com/Routstr/routstr-core.git
synced 2026-10-05 12:28:22 +00:00
fix: drop blank system text blocks before litellm dispatch
This commit is contained in:
@@ -84,6 +84,34 @@ ALLOWED_MESSAGES_REQUEST_FIELDS: frozenset[str] = frozenset(
|
||||
)
|
||||
|
||||
|
||||
def prune_blank_system_blocks(body: dict) -> None:
|
||||
"""Drop whitespace-only ``system`` text.
|
||||
|
||||
Anthropic accepts a blank system prompt; OpenAI-compatible upstreams
|
||||
reject it with ``text content blocks must contain non-whitespace text``.
|
||||
"""
|
||||
system = body.get("system")
|
||||
if isinstance(system, str):
|
||||
if not system.strip():
|
||||
body.pop("system", None)
|
||||
return
|
||||
if not isinstance(system, list):
|
||||
return
|
||||
kept = [
|
||||
block
|
||||
for block in system
|
||||
if not (
|
||||
isinstance(block, dict)
|
||||
and block.get("type") == "text"
|
||||
and not str(block.get("text") or "").strip()
|
||||
)
|
||||
]
|
||||
if kept:
|
||||
body["system"] = kept
|
||||
else:
|
||||
body.pop("system", None)
|
||||
|
||||
|
||||
def coerce_litellm_payload(payload: object) -> dict:
|
||||
"""Convert a litellm event into a plain dict.
|
||||
|
||||
@@ -511,6 +539,8 @@ async def dispatch_anthropic_messages(
|
||||
)
|
||||
body = {k: v for k, v in body.items() if k in ALLOWED_MESSAGES_REQUEST_FIELDS}
|
||||
|
||||
prune_blank_system_blocks(body)
|
||||
|
||||
model_suffix = adapt_request(body) if adapt_request else ""
|
||||
|
||||
# LiteLLM turns Anthropic's server-side web_search tool into the OpenAI
|
||||
|
||||
@@ -23,6 +23,9 @@ from routstr.core.db import ApiKey # noqa: E402
|
||||
from routstr.payment.cost_calculation import CostData # noqa: E402
|
||||
from routstr.payment.models import Architecture, Model, Pricing # noqa: E402
|
||||
from routstr.upstream.base import BaseUpstreamProvider # noqa: E402
|
||||
from routstr.upstream.messages_dispatch import ( # noqa: E402
|
||||
prune_blank_system_blocks,
|
||||
)
|
||||
from routstr.wallet import MintConnectionError, TokenConsumedError # noqa: E402
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -113,6 +116,39 @@ def _make_request(request_id: str | None = "req-test") -> Any:
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_prune_blank_system_blocks_drops_blank_blocks() -> None:
|
||||
body = {
|
||||
"system": [
|
||||
{"type": "text", "text": " \n"},
|
||||
{"type": "text", "text": "real prompt"},
|
||||
]
|
||||
}
|
||||
prune_blank_system_blocks(body)
|
||||
assert body["system"] == [{"type": "text", "text": "real prompt"}]
|
||||
|
||||
|
||||
def test_prune_blank_system_blocks_drops_key_when_all_blank() -> None:
|
||||
body = {"system": [{"type": "text", "text": "\n"}], "max_tokens": 8}
|
||||
prune_blank_system_blocks(body)
|
||||
assert body == {"max_tokens": 8}
|
||||
|
||||
|
||||
def test_prune_blank_system_blocks_handles_string_system() -> None:
|
||||
blank = {"system": " "}
|
||||
prune_blank_system_blocks(blank)
|
||||
assert blank == {}
|
||||
|
||||
kept = {"system": "be brief"}
|
||||
prune_blank_system_blocks(kept)
|
||||
assert kept == {"system": "be brief"}
|
||||
|
||||
|
||||
def test_prune_blank_system_blocks_keeps_non_text_blocks() -> None:
|
||||
body = {"system": [{"type": "image", "source": {}}]}
|
||||
prune_blank_system_blocks(body)
|
||||
assert body["system"] == [{"type": "image", "source": {}}]
|
||||
|
||||
|
||||
def test_coerce_litellm_payload_handles_dict() -> None:
|
||||
out = BaseUpstreamProvider._coerce_litellm_payload({"a": 1})
|
||||
assert out == {"a": 1}
|
||||
|
||||
Reference in New Issue
Block a user