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:
@@ -22,7 +22,9 @@ from auth_utils import get_user_gemini_key
|
||||
from config import settings
|
||||
from context_loader import load_context
|
||||
from persona import set_context, validate as validate_persona
|
||||
import model_registry
|
||||
import orchestrator_engine
|
||||
import openai_orchestrator
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
router = APIRouter(prefix="/orchestrate", tags=["orchestrator"])
|
||||
@@ -157,13 +159,25 @@ async def _run_job(job_id: str, req: OrchestrateRequest, user: str) -> None:
|
||||
history = load_session(session_id)
|
||||
session_messages = history or None
|
||||
|
||||
result = await orchestrator_engine.run(
|
||||
task=req.task,
|
||||
system_prompt=system_prompt,
|
||||
session_messages=session_messages,
|
||||
respond_with_claude=req.respond_with_claude,
|
||||
gemini_api_key=get_user_gemini_key(user),
|
||||
)
|
||||
# Choose engine based on the orchestrator role in the model registry
|
||||
orch_model = model_registry.get_model_for_role(user, "orchestrator")
|
||||
|
||||
if orch_model and orch_model.get("type") == "local_openai":
|
||||
result = await openai_orchestrator.run(
|
||||
task=req.task,
|
||||
system_prompt=system_prompt,
|
||||
session_messages=session_messages,
|
||||
model_cfg=orch_model,
|
||||
respond_with_final=req.respond_with_claude,
|
||||
)
|
||||
else:
|
||||
result = await orchestrator_engine.run(
|
||||
task=req.task,
|
||||
system_prompt=system_prompt,
|
||||
session_messages=session_messages,
|
||||
respond_with_claude=req.respond_with_claude,
|
||||
gemini_api_key=get_user_gemini_key(user),
|
||||
)
|
||||
|
||||
# Save the turn to the session store so it survives a page refresh
|
||||
history.append({"role": "user", "content": req.task})
|
||||
|
||||
Reference in New Issue
Block a user