diff --git a/cortex/llm_client.py b/cortex/llm_client.py index f84660e..cc583a3 100644 --- a/cortex/llm_client.py +++ b/cortex/llm_client.py @@ -175,14 +175,17 @@ async def _local(system_prompt: str, messages: list[dict], model_cfg: dict | Non if not model: raise RuntimeError("local_model not configured — add a model at /settings/local") - logger.info("local backend: %s @ %s", model, api_url) + host_type = cfg.get("host_type", "openwebui") + # "openwebui" uses Open WebUI/Ollama path layout; "openai" uses standard OpenAI layout + chat_path = "/chat/completions" if host_type == "openai" else "/api/chat/completions" + logger.info("local backend (%s): %s @ %s", host_type, model, api_url) msgs: list[dict] = [] if system_prompt: msgs.append({"role": "system", "content": system_prompt}) msgs.extend(messages) - url = api_url.rstrip("/") + "/api/chat/completions" + url = api_url.rstrip("/") + chat_path headers: dict[str, str] = {} if api_key: headers["Authorization"] = f"Bearer {api_key}" diff --git a/cortex/model_registry.py b/cortex/model_registry.py index 16eca45..aa92a99 100644 --- a/cortex/model_registry.py +++ b/cortex/model_registry.py @@ -6,7 +6,18 @@ Stored in: home/{user}/model_registry.json Schema: { "version": 1, - "hosts": [{"id", "label", "api_url", "api_key"}, ...], + "hosts": [{"id", "label", "api_url", "api_key", + "host_type": "openwebui" | "openai"}, ...], + # + # host_type controls the API path layout: + # "openwebui" (default) — Open WebUI / Ollama: + # chat: POST {url}/api/chat/completions + # models: GET {url}/api/models + # "openai" — OpenRouter, LiteLLM, Anthropic-compatible, etc.: + # chat: POST {url}/chat/completions + # models: GET {url}/models + # Set api_url to the base path that ends just before /chat/completions, + # e.g. https://openrouter.ai/api/v1 for OpenRouter. "models": [ { "id": str, # unique within this registry @@ -212,7 +223,12 @@ def _resolve_model(registry: dict, model_id: str) -> dict | None: if not host: logger.warning("model %s references missing host_id %s", model_id, host_id) return None - return {**model, "api_url": host.get("api_url", ""), "api_key": host.get("api_key", "")} + return { + **model, + "api_url": host.get("api_url", ""), + "api_key": host.get("api_key", ""), + "host_type": host.get("host_type", "openwebui"), + } return dict(model) @@ -308,15 +324,21 @@ def get_defined_roles(username: str) -> dict[str, dict]: # ── Write API (CRUD) ────────────────────────────────────────────────────────── def save_host(username: str, host_id: str | None, - label: str, api_url: str, api_key: str) -> str: - """Create or update a host. Returns the host ID.""" + label: str, api_url: str, api_key: str, + host_type: str = "openwebui") -> str: + """Create or update a host. Returns the host ID. + + host_type: "openwebui" (default) or "openai" (OpenRouter, LiteLLM, etc.) + """ data = _load(username) + host_type = host_type if host_type in ("openwebui", "openai") else "openwebui" if host_id: for h in data["hosts"]: if h["id"] == host_id: - h["label"] = label.strip() - h["api_url"] = api_url.strip() + h["label"] = label.strip() + h["api_url"] = api_url.strip() + h["host_type"] = host_type if api_key.strip(): h["api_key"] = api_key.strip() _save(username, data) @@ -325,10 +347,11 @@ def save_host(username: str, host_id: str | None, host_id = secrets.token_hex(4) data["hosts"].append({ - "id": host_id, - "label": label.strip(), - "api_url": api_url.strip(), - "api_key": api_key.strip(), + "id": host_id, + "label": label.strip(), + "api_url": api_url.strip(), + "api_key": api_key.strip(), + "host_type": host_type, }) _save(username, data) return host_id diff --git a/cortex/routers/local_llm.py b/cortex/routers/local_llm.py index 8b9e1af..8d04aab 100644 --- a/cortex/routers/local_llm.py +++ b/cortex/routers/local_llm.py @@ -54,7 +54,10 @@ def _render(username: str, success: str = "", error: str = "") -> str: # ── Host rows ───────────────────────────────────────────────────────────── host_rows = "" for h in hosts: - key_hint = f"…{h['api_key'][-4:]}" if h.get("api_key") else "not set" + key_hint = f"…{h['api_key'][-4:]}" if h.get("api_key") else "not set" + ht = h.get("host_type", "openwebui") + ow_sel = ' selected' if ht == "openwebui" else '' + ai_sel = ' selected' if ht == "openai" else '' host_rows += f'''
Current: {key_hint}
+Current: {key_hint}
+