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:
Scott Idem
2026-04-08 19:18:18 -04:00
parent 8ba5247ef5
commit d9a322164a
9 changed files with 538 additions and 112 deletions

View File

@@ -565,6 +565,26 @@
}
.model-tag.fallback { color: #f59e0b; }
/* Retry button — shown in error message bubbles */
.retry-btn {
display: inline-block;
margin-top: 0.6rem;
margin-left: 0.15rem;
padding: 0.25rem 0.7rem;
font-size: 0.78rem;
font-family: inherit;
background: transparent;
color: var(--error-text);
border: 1px solid var(--error-border);
border-radius: 4px;
cursor: pointer;
transition: background 0.15s, color 0.15s;
}
.retry-btn:hover {
background: var(--error-border);
color: #fff;
}
/* Note messages */
.message.note-private {
align-self: flex-end;