Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a219c68aa0 | |||
| 89570b3837 | |||
| 91c22163f7 | |||
| c3507f25c9 |
+37
-36
@@ -1,38 +1,39 @@
|
||||
{
|
||||
"version": "v1.10",
|
||||
"api_keys": {},
|
||||
"auth_login": "",
|
||||
"auth_password": "",
|
||||
"root_password": "",
|
||||
"agent_profile": "agent0",
|
||||
"agent_knowledge_subdir": "custom",
|
||||
"workdir_path": "/a0/usr/workdir",
|
||||
"workdir_show": true,
|
||||
"workdir_max_depth": 5,
|
||||
"workdir_max_files": 20,
|
||||
"workdir_max_folders": 20,
|
||||
"workdir_max_lines": 250,
|
||||
"workdir_gitignore": "# Python environments & cache\nvenv/**\n**/__pycache__/**\n\n# Node.js dependencies\n**/node_modules/**\n**/.npm/**\n\n# Version control metadata\n**/.git/**",
|
||||
"rfc_auto_docker": true,
|
||||
"rfc_url": "localhost",
|
||||
"rfc_password": "",
|
||||
"rfc_port_http": 55080,
|
||||
"websocket_server_restart_enabled": true,
|
||||
"uvicorn_access_logs_enabled": false,
|
||||
"stt_model_size": "base",
|
||||
"stt_language": "en",
|
||||
"stt_silence_threshold": 0.3,
|
||||
"stt_silence_duration": 1000,
|
||||
"stt_waiting_timeout": 2000,
|
||||
"tts_kokoro": true,
|
||||
"mcp_servers": "{\n \"mcpServers\": {}\n}",
|
||||
"mcp_client_init_timeout": 10,
|
||||
"mcp_client_tool_timeout": 120,
|
||||
"mcp_server_enabled": true,
|
||||
"a2a_server_enabled": false,
|
||||
"variables": "",
|
||||
"secrets": "",
|
||||
"litellm_global_kwargs": {},
|
||||
"update_check_enabled": true,
|
||||
"chat_inherit_project": true
|
||||
"version": "v1.10",
|
||||
"api_keys": {"gnommoweb": "RVWUl_-I1W3EjDpM"},
|
||||
"auth_login": "",
|
||||
"auth_password": "",
|
||||
"root_password": "",
|
||||
"agent_profile": "agent0",
|
||||
"agent_knowledge_subdir": "custom",
|
||||
"workdir_path": "/a0/usr/workdir",
|
||||
"workdir_show": true,
|
||||
"workdir_max_depth": 5,
|
||||
"workdir_max_files": 20,
|
||||
"workdir_max_folders": 20,
|
||||
"workdir_max_lines": 250,
|
||||
"workdir_gitignore": "# Python environments & cache\nvenv/**\n**/__pycache__/**\n\n# Node.js dependencies\n**/node_modules/**\n**/.npm/**\n\n# Version control metadata\n**/.git/**",
|
||||
"rfc_auto_docker": true,
|
||||
"rfc_url": "localhost",
|
||||
"rfc_password": "",
|
||||
"rfc_port_http": 55080,
|
||||
"websocket_server_restart_enabled": true,
|
||||
"uvicorn_access_logs_enabled": false,
|
||||
"stt_model_size": "base",
|
||||
"stt_language": "en",
|
||||
"stt_silence_threshold": 0.3,
|
||||
"stt_silence_duration": 1000,
|
||||
"stt_waiting_timeout": 2000,
|
||||
"tts_kokoro": true,
|
||||
"mcp_servers": "{\n \"mcpServers\": {}\n}",
|
||||
"mcp_client_init_timeout": 10,
|
||||
"mcp_client_tool_timeout": 120,
|
||||
"mcp_server_enabled": false,
|
||||
"mcp_server_token": "",
|
||||
"a2a_server_enabled": false,
|
||||
"variables": "",
|
||||
"secrets": "",
|
||||
"litellm_global_kwargs": {},
|
||||
"update_check_enabled": true,
|
||||
"chat_inherit_project": true
|
||||
}
|
||||
@@ -2162,52 +2162,52 @@ async def update_config(request: Request) -> dict:
|
||||
|
||||
@app.get("/test-chat/models")
|
||||
async def test_chat_models(request: Request) -> dict:
|
||||
"""Return the list of models currently available in upstream_openai (Ollama)."""
|
||||
"""Return the list of lm-studio/openai models configured in the DB."""
|
||||
pool = request.app.state.pool
|
||||
cfg = request.app.state.yaml_config
|
||||
upstream = cfg.get("upstream_openai", "")
|
||||
if not upstream:
|
||||
return {"ok": False, "error": "upstream_openai not configured", "models": []}
|
||||
url = f"{upstream.rstrip('/')}/v1/models"
|
||||
try:
|
||||
async with httpx.AsyncClient(timeout=8.0) as client:
|
||||
r = await client.get(url)
|
||||
if not r.is_success:
|
||||
return {"ok": False, "error": f"HTTP {r.status_code} from {url}", "models": []}
|
||||
ids = sorted(m.get("id", "") for m in r.json().get("data", []))
|
||||
return {"ok": True, "models": ids, "upstream": upstream}
|
||||
except httpx.ConnectError:
|
||||
return {"ok": False, "error": f"Connection refused at {url} — is Ollama running?", "models": []}
|
||||
except Exception as exc:
|
||||
return {"ok": False, "error": str(exc), "models": []}
|
||||
async with pool.acquire() as conn:
|
||||
rows = await conn.fetch(
|
||||
"SELECT model_name, base_url FROM models WHERE provider IN ('lm-studio', 'openai') ORDER BY id"
|
||||
)
|
||||
if rows:
|
||||
models = [r["model_name"] for r in rows]
|
||||
return {"ok": True, "models": models, "upstream": upstream}
|
||||
return {"ok": False, "error": "No lm-studio or openai models configured — add one via Model Manager.", "models": []}
|
||||
|
||||
|
||||
@app.post("/test-chat")
|
||||
async def test_chat(request: Request) -> dict:
|
||||
cfg = request.app.state.yaml_config
|
||||
pool = request.app.state.pool
|
||||
data = await request.json()
|
||||
message = (data.get("message") or "Say hello in one sentence.").strip()
|
||||
model = (data.get("model") or "").strip()
|
||||
message = (data.get("message") or "Say hello in one sentence.").strip()
|
||||
model = (data.get("model") or "").strip()
|
||||
upstream = cfg.get("upstream_openai", "")
|
||||
|
||||
if not upstream:
|
||||
return {"ok": False, "error": "upstream_openai not configured"}
|
||||
|
||||
# Always fetch the available model list so the UI can show it
|
||||
available: list[str] = []
|
||||
try:
|
||||
async with httpx.AsyncClient(timeout=8.0) as client:
|
||||
mr = await client.get(f"{upstream.rstrip('/')}/v1/models")
|
||||
if mr.is_success:
|
||||
available = sorted(m.get("id", "") for m in mr.json().get("data", []))
|
||||
except Exception:
|
||||
pass
|
||||
# Load configured models from DB so the UI can refresh the dropdown
|
||||
async with pool.acquire() as conn:
|
||||
db_rows = await conn.fetch(
|
||||
"SELECT model_name, base_url FROM models WHERE provider IN ('lm-studio', 'openai') ORDER BY id"
|
||||
)
|
||||
available = [r["model_name"] for r in db_rows]
|
||||
db_model_map = {r["model_name"]: r["base_url"] or "" for r in db_rows}
|
||||
|
||||
if not model:
|
||||
if not available:
|
||||
return {"ok": False, "error": f"No model specified and none found at {upstream}/v1/models", "available": []}
|
||||
return {"ok": False, "error": "No lm-studio/openai models configured. Add one via Model Manager first.", "available": []}
|
||||
model = available[0]
|
||||
|
||||
url = f"{upstream.rstrip('/')}/v1/chat/completions"
|
||||
# Resolve upstream: prefer the model's own base_url from DB, fall back to upstream_openai config
|
||||
raw_base = db_model_map.get(model, "") or upstream
|
||||
if not raw_base:
|
||||
return {"ok": False, "error": "upstream_openai not configured and model has no base_url", "available": available}
|
||||
# Strip /v1 suffix — we append /v1/chat/completions ourselves
|
||||
effective_upstream = raw_base.rstrip("/")
|
||||
if effective_upstream.endswith("/v1"):
|
||||
effective_upstream = effective_upstream[:-3]
|
||||
|
||||
url = f"{effective_upstream}/v1/chat/completions"
|
||||
body = {
|
||||
"model": model,
|
||||
"stream": False,
|
||||
@@ -2222,11 +2222,12 @@ async def test_chat(request: Request) -> dict:
|
||||
if not r.is_success:
|
||||
return {"ok": False, "error": f"HTTP {r.status_code}: {r.text[:400]}", "url": url, "model": model, "ms": ms, "available": available}
|
||||
resp = r.json()
|
||||
reply = resp.get("choices", [{}])[0].get("message", {}).get("content", "(empty)")
|
||||
choices = resp.get("choices") or []
|
||||
reply = choices[0].get("message", {}).get("content", "(empty)") if choices else "(empty response)"
|
||||
return {"ok": True, "reply": reply, "model": model, "url": url, "ms": ms, "available": available}
|
||||
except httpx.ConnectError as exc:
|
||||
ms = int((time.perf_counter() - t0) * 1000)
|
||||
return {"ok": False, "error": f"Connection refused — is Ollama running at {upstream}? ({exc})", "url": url, "ms": ms, "available": available}
|
||||
return {"ok": False, "error": f"Connection refused at {effective_upstream} ({exc})", "url": url, "ms": ms, "available": available}
|
||||
except httpx.TimeoutException:
|
||||
ms = int((time.perf_counter() - t0) * 1000)
|
||||
return {"ok": False, "error": f"Request timed out after {ms}ms", "url": url, "ms": ms, "available": available}
|
||||
@@ -3705,7 +3706,7 @@ ADMIN_HTML = """<!DOCTYPE html>
|
||||
|
||||
<h2>Test Chat</h2>
|
||||
<p style="font-size:0.83em;color:#666;margin-bottom:0.8em">
|
||||
Send a message directly to <code>upstream_openai</code> (Ollama) to verify the connection end-to-end.
|
||||
Send a test message via a configured model (lm-studio / openai) to verify the connection end-to-end.
|
||||
</p>
|
||||
<div id="tc-models-row" style="font-size:0.82em;color:#555;margin-bottom:0.6em">
|
||||
<span id="tc-models-label">Loading available models…</span>
|
||||
|
||||
Reference in New Issue
Block a user