AI gateway: - Add a generic, admin-selectable `client_profile` field on gateway_providers (e.g. "opencode") so a provider needing special request headers (OpenCode Zen's client-identity spoofing) is configured like any other provider, not hardcoded by name. - Track per-(provider, model) reliability/speed/latency health in memory, seeded from the existing gateway_usage_ledger at startup - purely observational, never influences routing. - New Stats tab on /admin/gateway: request volume, latency, per-model breakdowns, and reliability weight, charted with a vendored Chart.js and devplace's own theme tokens. - Record which model a failed request actually fell back to (fallback_used_route), surfaced in the Recent Failures table. - Stop excluding context_length errors from fallback, and skip a primary attempt outright when its known context window is already too small for the estimated request size, going straight to the fallback. - gateway_usage_ledger's provider/fallback_used_route columns and indexes are ensured centrally in database/schema.py's init_db(), the single point of truth for this table's schema. - Non-OpenAI upstream routing and client-model passthrough; trust only the upstream's own X-Gateway-Model header for served-model attribution. Devii agent: - Fix a real lockup: plan/verify tools could be individually disabled via the admin tool toggles while still being required by the protocol gate, permanently bricking any task that needed tools. They can no longer be disabled, and the gate now also checks the tool is actually offered. - Fix compaction being silently calibrated for a 1M-token model while running a much smaller one: context budget is now percentage-based and the summarizer's own request is sized to fit the real model. - Give a specific, actionable retry message when plan()'s own arguments get cut off by the output limit, and tighten its schema to discourage overlong plans. Other: - Backup service: offload completed backups to a remote Hetzner Storage Box. - Container manager: fix orphan blob leaks from sync races, add a two-phase plan/execute `system prune` CLI command. - Admin gateway UI: replace the JS-rendered model/provider tables with server-rendered forms and pages. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DhmEkvutuwtzFVcLbTrhdo
147 lines
5.2 KiB
Python
147 lines
5.2 KiB
Python
# retoor <retoor@molodetz.nl>
|
|
|
|
from devplacepy.services.openai_gateway.thinking import (
|
|
apply_thinking,
|
|
client_thinking_enabled,
|
|
thinking_dialect,
|
|
upstream_capabilities,
|
|
)
|
|
|
|
|
|
def test_dialect_from_upstream_url():
|
|
assert thinking_dialect("https://api.deepseek.com/chat/completions") == "deepseek"
|
|
assert thinking_dialect("https://openrouter.ai/api/v1/chat/completions") == "openrouter"
|
|
assert thinking_dialect("http://127.0.0.1:11434/api/chat") == "ollama"
|
|
assert thinking_dialect("http://ollama.local/v1/chat/completions") == "ollama"
|
|
assert thinking_dialect("https://unknown.example/v1/chat/completions") == "deepseek"
|
|
|
|
|
|
def test_upstream_capabilities_openai_full_schema():
|
|
caps = upstream_capabilities("https://api.deepseek.com/chat/completions")
|
|
assert caps.dialect == "deepseek"
|
|
assert caps.supports_stream_options is True
|
|
assert caps.supports_stream_usage is True
|
|
|
|
|
|
def test_upstream_capabilities_ollama_lacks_stream_options():
|
|
caps = upstream_capabilities("http://127.0.0.1:11434/api/chat")
|
|
assert caps.dialect == "ollama"
|
|
assert caps.supports_stream_options is False
|
|
assert caps.supports_stream_usage is False
|
|
|
|
|
|
def test_upstream_capabilities_explicit_dialect_override():
|
|
caps = upstream_capabilities(
|
|
"https://ai.example.com/v1/chat/completions", dialect="ollama"
|
|
)
|
|
assert caps.dialect == "ollama"
|
|
assert caps.supports_stream_options is False
|
|
|
|
|
|
def test_upstream_capabilities_ollama_stream_usage_opt_in():
|
|
caps = upstream_capabilities(
|
|
"http://127.0.0.1:11434/api/chat", ollama_stream_usage=True
|
|
)
|
|
assert caps.dialect == "ollama"
|
|
assert caps.supports_stream_options is True
|
|
assert caps.supports_stream_usage is True
|
|
|
|
|
|
def test_apply_thinking_respects_explicit_dialect_override():
|
|
without = apply_thinking({}, "https://ai.example.com/v1/chat/completions")
|
|
assert without["thinking"] == {"type": "disabled"}
|
|
with_override = apply_thinking(
|
|
{}, "https://ai.example.com/v1/chat/completions", dialect="ollama"
|
|
)
|
|
assert with_override["think"] is False
|
|
assert "thinking" not in with_override
|
|
|
|
|
|
def test_client_intent_unspecified():
|
|
assert client_thinking_enabled({}) is None
|
|
assert client_thinking_enabled({"messages": []}) is None
|
|
assert client_thinking_enabled(None) is None
|
|
|
|
|
|
def test_client_intent_disable():
|
|
assert client_thinking_enabled({"think": False}) is False
|
|
assert client_thinking_enabled({"think": "false"}) is False
|
|
assert client_thinking_enabled({"thinking": {"type": "disabled"}}) is False
|
|
assert client_thinking_enabled({"reasoning": {"effort": "none"}}) is False
|
|
assert client_thinking_enabled({"reasoning": {"enabled": False}}) is False
|
|
assert client_thinking_enabled({"enable_thinking": False}) is False
|
|
assert client_thinking_enabled(
|
|
{"chat_template_kwargs": {"enable_thinking": False}}
|
|
) is False
|
|
|
|
|
|
def test_client_intent_enable():
|
|
assert client_thinking_enabled({"think": True}) is True
|
|
assert client_thinking_enabled({"think": "high"}) is True
|
|
assert client_thinking_enabled({"thinking": {"type": "enabled"}}) is True
|
|
assert client_thinking_enabled({"reasoning": {"effort": "low"}}) is True
|
|
assert client_thinking_enabled({"reasoning_effort": "max"}) is True
|
|
|
|
|
|
def test_default_disables_deepseek_thinking():
|
|
payload = apply_thinking(
|
|
{"messages": [{"role": "user", "content": "hi"}]},
|
|
"https://api.deepseek.com/chat/completions",
|
|
)
|
|
assert payload["thinking"] == {"type": "disabled"}
|
|
assert "think" not in payload
|
|
assert "reasoning" not in payload
|
|
assert "reasoning_effort" not in payload
|
|
|
|
|
|
def test_default_disables_openrouter_and_ollama():
|
|
openrouter = apply_thinking({}, "https://openrouter.ai/api/v1/chat/completions")
|
|
assert openrouter["reasoning"] == {"effort": "none"}
|
|
assert "thinking" not in openrouter
|
|
ollama = apply_thinking({}, "http://127.0.0.1:11434/api/chat")
|
|
assert ollama["think"] is False
|
|
assert ollama["reasoning_effort"] == "none"
|
|
assert "thinking" not in ollama
|
|
|
|
|
|
def test_client_can_enable_thinking():
|
|
payload = apply_thinking(
|
|
{"think": True, "messages": []},
|
|
"https://api.deepseek.com/chat/completions",
|
|
)
|
|
assert payload["thinking"] == {"type": "enabled"}
|
|
assert "think" not in payload
|
|
|
|
|
|
def test_client_disable_beats_admin_default_on():
|
|
payload = apply_thinking(
|
|
{"thinking": {"type": "disabled"}},
|
|
"https://api.deepseek.com/chat/completions",
|
|
default_enabled=True,
|
|
)
|
|
assert payload["thinking"] == {"type": "disabled"}
|
|
|
|
|
|
def test_admin_default_on_when_client_silent():
|
|
payload = apply_thinking(
|
|
{"messages": []},
|
|
"https://api.deepseek.com/chat/completions",
|
|
default_enabled=True,
|
|
)
|
|
assert payload["thinking"] == {"type": "enabled"}
|
|
|
|
|
|
def test_strips_conflicting_client_fields():
|
|
payload = apply_thinking(
|
|
{
|
|
"think": False,
|
|
"reasoning_effort": "high",
|
|
"chat_template_kwargs": {"enable_thinking": True, "other": 1},
|
|
},
|
|
"https://api.deepseek.com/chat/completions",
|
|
)
|
|
assert payload["thinking"] == {"type": "disabled"}
|
|
assert "think" not in payload
|
|
assert "reasoning_effort" not in payload
|
|
assert payload["chat_template_kwargs"] == {"other": 1}
|