feat: switch LLM to OpenWebUI with live model selection
Rebuild & Deploy / rebuild (push) Successful in 3m18s

- Point the agent at OpenWebUI's OpenAI-compatible API (LLM_BASE_URL,
  LLM_API_KEY, LLM_MODEL) with GEMMA_* env fallbacks
- Add list_available_models() fetching {base_url}/models, filtering
  non-chat ids (embedding/reranker)
- Rename GemmaLLM -> HopeLLM with update_options(model=...) for runtime
  model switching (mutates _opts.model) and instance-based compaction
- Data channel: agent publishes {type: models} after connect and on
  get_models; set_model switches live and acks model_changed
- Web UI: Brain section in the settings sheet with a model picker that
  persists the choice in localStorage
- Update tests, CI workflow env, and docs for the new endpoint
This commit is contained in:
snowspeeder committed 2026-08-29 12:09:03 -04:00
1 parent 024c42ffb1
commit 360e3276b0
14 files changed
+334 -76

No files matched your search

+21 -5
View File
@@ -3,7 +3,7 @@
Tests run on the HOST machine against the live "voice" container:
- HTTPS web UI -> https://localhost:9178 (self-signed cert, verify=False)
- Token server -> http://127.0.0.1:8091 (host network mode)
- LLM API -> GEMMA_BASE_URL (default http://192.168.86.2:8023/v1)
- LLM API -> LLM_BASE_URL (default http://192.168.86.2:3005/api)
- In-container -> docker exec voice ...
"""
@@ -18,8 +18,21 @@ import pytest
CONTAINER = "voice"
WEB_BASE_URL = os.environ.get("VOICE_WEB_URL", "https://localhost:9178")
TOKEN_URL = os.environ.get("VOICE_TOKEN_URL", "http://127.0.0.1:8091/token")
GEMMA_BASE_URL = os.environ.get("GEMMA_BASE_URL", "http://192.168.86.2:8023/v1").rstrip("/")
GEMMA_MODEL = os.environ.get("GEMMA_MODEL", "gemma-4-e4b")
LLM_BASE_URL = (
os.environ.get("LLM_BASE_URL")
or os.environ.get("GEMMA_BASE_URL")
or "http://192.168.86.2:3005/api"
).rstrip("/")
LLM_MODEL = (
os.environ.get("LLM_MODEL")
or os.environ.get("GEMMA_MODEL")
or "gemma-4-e4b"
)
LLM_API_KEY = (
os.environ.get("LLM_API_KEY")
or os.environ.get("GEMMA_API_KEY")
or "not-needed"
)
_users = json.loads((Path(__file__).parents[1] / "config" / "users.json").read_text())
_shane_memory = _users["users"]["shane"]["memory"]
MEMORY_MCP_URL = os.environ.get("MEMORY_MCP_URL", _shane_memory["url"])
@@ -88,8 +101,11 @@ def token() -> dict:
@pytest.fixture(scope="session")
def llm_client():
"""httpx client pointed at the Gemma LLM API (same one Hope uses)."""
with httpx.Client(base_url=GEMMA_BASE_URL, timeout=60) as c:
"""httpx client pointed at the LLM API (same one Hope uses)."""
headers = {}
if LLM_API_KEY and LLM_API_KEY != "not-needed":
headers["Authorization"] = f"Bearer {LLM_API_KEY}"
with httpx.Client(base_url=LLM_BASE_URL, headers=headers, timeout=60) as c:
yield c