Gateway: Deutsch-Direktive fuer IDE-Traffic + web_search-Tool im Web-MCP

1) /v1-Gateway haengt an jede Chat-Anfrage (ausser hermes/Lucy — SOUL.md
   regelt das selbst) eine System-Direktive an: Antworten auf Deutsch,
   Code/Bezeichner unveraendert. Abschaltbar via MC_GATEWAY_LANG_DIRECTIVE="".
2) mcp_web.py: neues Tool web_search (ddgs/DuckDuckGo, kein Key), Ergebnis
   injection-geschuetzt via wrap_untrusted. Damit koennen IDE-Agents (Zed
   via context_servers) und Hermes im Web suchen; Debug-Log geht auf stderr,
   stdout bleibt MCP-sauber (verifiziert).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-07-03 08:55:22 +02:00
parent a2091b4f5f
commit 50867b2781
2 changed files with 47 additions and 0 deletions
+29
View File
@@ -1,3 +1,5 @@
import os
import httpx
from fastapi import APIRouter, Request
from fastapi.responses import JSONResponse, StreamingResponse
@@ -9,6 +11,32 @@ from services.routing_policy import load_policy
router = APIRouter(prefix="/v1")
# Antwortsprache für IDE-/Lane-Traffic: die Coding-Modelle antworten sonst englisch
# (User-Anforderung 03.07.2026). Leerer String (MC_GATEWAY_LANG_DIRECTIVE="") schaltet ab.
_LANG_DIRECTIVE = os.environ.get(
"MC_GATEWAY_LANG_DIRECTIVE",
"Antworte dem Nutzer grundsätzlich auf Deutsch (Erklärungen, Pläne, Rückfragen, "
"Zusammenfassungen) — auch wenn die Frage oder Tool-Anweisungen englisch sind. "
"Quellcode, Bezeichner und Shell-Befehle bleiben unverändert.")
def _inject_language(body: dict, alias: str) -> None:
"""Deutsch-Direktive anhängen. An die ERSTE System-Message (viele Chat-Templates
erwarten nur eine), sonst als neue System-Message. `hermes` ausgenommen — Lucys
Persona (SOUL.md) regelt die Sprache selbst."""
if not _LANG_DIRECTIVE or alias == "hermes":
return
msgs = body.get("messages")
if not isinstance(msgs, list):
return
first_sys = next((m for m in msgs if isinstance(m, dict) and m.get("role") == "system"), None)
if first_sys is None:
msgs.insert(0, {"role": "system", "content": _LANG_DIRECTIVE})
elif isinstance(first_sys.get("content"), str):
first_sys["content"] = first_sys["content"].rstrip() + "\n\n" + _LANG_DIRECTIVE
elif isinstance(first_sys.get("content"), list):
first_sys["content"].append({"type": "text", "text": _LANG_DIRECTIVE})
# Virtuelle Lanes, die der Gateway zusätzlich zu den echten Modellen als „Modell" anbietet.
_LANE_LABELS = {"coding": "Coding (Router → coder/heavy/fast)", "chat": "Chat (Router → fast/heavy)"}
@@ -66,6 +94,7 @@ async def _proxy(path: str, request: Request):
pol = load_policy()
if pol["fast_no_think"] and alias == pol["fast"] and "chat_template_kwargs" not in body:
body["chat_template_kwargs"] = {"enable_thinking": False}
_inject_language(body, alias)
url = f"{LLAMA_SWAP_URL}{path}"
if body.get("stream"):