feat: OpenAI-compatible orchestrator + backend auto-routing
- openai_orchestrator.py — new ReAct tool loop engine for any OpenAI-compatible endpoint (OpenRouter, Open WebUI, Ollama, LiteLLM); model handles both tool loop and final response, no Claude handoff needed - tools/__init__.py — auto-derive OpenAI JSON Schema from existing Gemini FunctionDeclarations so tool definitions have a single source of truth - routers/orchestrator.py — route to openai_orchestrator when model registry "orchestrator" role resolves to a local_openai type host - routers/chat.py — pass role to _backend_label(); fix fallback_used logic (only meaningful for explicit backend overrides, not auto-routing) - static/app.js — add null/"auto" to backend cycle; fetch local model hint without overriding the auto default on page load - model_registry.py — _normalize() back-fills host_type on old registry files - requirements.txt — add openai>=1.0.0 - ARCH__BACKENDS.md — document OpenAI-compat backend and routing logic Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -18,14 +18,14 @@ import event_bus
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
def _backend_label(backend: str, username: str) -> str:
|
||||
def _backend_label(backend: str, username: str, role: str = "chat") -> str:
|
||||
"""Human-readable label for the model that handled a request."""
|
||||
if backend == "claude":
|
||||
return "Claude"
|
||||
if backend == "gemini":
|
||||
return "Gemini"
|
||||
if backend == "local":
|
||||
cfg = model_registry.get_best_local_model(username)
|
||||
cfg = model_registry.get_best_local_model(username, role)
|
||||
if cfg:
|
||||
return cfg.get("label") or cfg.get("model_name") or "Local"
|
||||
return "Local"
|
||||
@@ -113,14 +113,16 @@ async def _stream_chat(req: ChatRequest):
|
||||
if not req.off_record:
|
||||
log_turn(session_id, req.message, response_text)
|
||||
|
||||
requested = req.model or settings.primary_backend
|
||||
# fallback_used only makes sense for explicit backend selections.
|
||||
# In auto mode (req.model is None), just report what responded.
|
||||
fallback_used = bool(req.model and actual_backend != req.model)
|
||||
payload = {
|
||||
"type": "response",
|
||||
"response": response_text,
|
||||
"session_id": session_id,
|
||||
"backend": actual_backend,
|
||||
"backend_label": _backend_label(actual_backend, user),
|
||||
"fallback_used": actual_backend != requested,
|
||||
"backend_label": _backend_label(actual_backend, user, role="chat"),
|
||||
"fallback_used": fallback_used,
|
||||
}
|
||||
yield f"data: {json.dumps(payload)}\n\n"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user