Fix AI Analyst: align model default, actionable provider-not-enabled error
Browse files- dashboard.py: change HF_MODEL default from Qwen2.5-3B to Qwen2.5-7B-Instruct-1M
(matches docker-compose ai_analyst service)
- Detect 400 model_not_supported / provider errors and return a clear message
with link to huggingface.co/settings/inference-providers
- /ai/debug: add "fix" field with provider setup instructions when 400 occurs
- docker-compose: add HF_TOKEN + HF_MODEL env vars to dashboard service
- index.html: error insights shown in red; status header turns red on error
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- dashboard/dashboard.py +15 -2
- dashboard/templates/index.html +8 -2
- docker-compose.yml +2 -0
dashboard/dashboard.py
CHANGED
|
@@ -28,7 +28,7 @@ FRONTEND_URL = os.getenv("FRONTEND_URL", "")
|
|
| 28 |
|
| 29 |
# ββ AI Analyst (inline LLM for on-demand generation) βββββββββββββββββββββββββββ
|
| 30 |
HF_TOKEN = os.getenv("HF_TOKEN", "")
|
| 31 |
-
HF_MODEL = os.getenv("HF_MODEL", "Qwen/Qwen2.5-
|
| 32 |
HF_URL = "https://router.huggingface.co/v1/chat/completions"
|
| 33 |
OLLAMA_HOST = os.getenv("OLLAMA_HOST", "")
|
| 34 |
OLLAMA_MODEL = os.getenv("OLLAMA_MODEL", "llama3.1:8b")
|
|
@@ -119,6 +119,13 @@ def _call_llm(prompt):
|
|
| 119 |
time.sleep(wait)
|
| 120 |
else:
|
| 121 |
print(f"[Dashboard/LLM] HF error body: {r.text[:400]}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 122 |
return None, f"HF HTTP {r.status_code}: {r.text[:120]}"
|
| 123 |
except requests.exceptions.Timeout:
|
| 124 |
print(f"[Dashboard/LLM] HF timeout (attempt {attempt+1})")
|
|
@@ -607,7 +614,13 @@ def ai_debug():
|
|
| 607 |
result["http_status"] = r.status_code
|
| 608 |
result["response_body"] = r.text[:500]
|
| 609 |
try:
|
| 610 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 611 |
except Exception:
|
| 612 |
pass
|
| 613 |
except Exception as e:
|
|
|
|
| 28 |
|
| 29 |
# ββ AI Analyst (inline LLM for on-demand generation) βββββββββββββββββββββββββββ
|
| 30 |
HF_TOKEN = os.getenv("HF_TOKEN", "")
|
| 31 |
+
HF_MODEL = os.getenv("HF_MODEL", "Qwen/Qwen2.5-7B-Instruct-1M")
|
| 32 |
HF_URL = "https://router.huggingface.co/v1/chat/completions"
|
| 33 |
OLLAMA_HOST = os.getenv("OLLAMA_HOST", "")
|
| 34 |
OLLAMA_MODEL = os.getenv("OLLAMA_MODEL", "llama3.1:8b")
|
|
|
|
| 119 |
time.sleep(wait)
|
| 120 |
else:
|
| 121 |
print(f"[Dashboard/LLM] HF error body: {r.text[:400]}")
|
| 122 |
+
try:
|
| 123 |
+
err_code = r.json().get("error", {}).get("code", "")
|
| 124 |
+
except Exception:
|
| 125 |
+
err_code = ""
|
| 126 |
+
if err_code == "model_not_supported" or "provider" in r.text.lower():
|
| 127 |
+
return None, ("HF providers not enabled. Go to huggingface.co/settings/inference-providers "
|
| 128 |
+
"and enable at least one provider (e.g. Cerebras, Groq, or HF Inference).")
|
| 129 |
return None, f"HF HTTP {r.status_code}: {r.text[:120]}"
|
| 130 |
except requests.exceptions.Timeout:
|
| 131 |
print(f"[Dashboard/LLM] HF timeout (attempt {attempt+1})")
|
|
|
|
| 614 |
result["http_status"] = r.status_code
|
| 615 |
result["response_body"] = r.text[:500]
|
| 616 |
try:
|
| 617 |
+
rj = r.json()
|
| 618 |
+
result["response_json"] = rj
|
| 619 |
+
err_code = rj.get("error", {}).get("code", "")
|
| 620 |
+
if err_code == "model_not_supported" or "provider" in r.text.lower():
|
| 621 |
+
result["fix"] = ("Enable inference providers at: "
|
| 622 |
+
"https://huggingface.co/settings/inference-providers "
|
| 623 |
+
"Enable at least one free provider: Cerebras, Groq, SambaNova, or HF Inference.")
|
| 624 |
except Exception:
|
| 625 |
pass
|
| 626 |
except Exception as e:
|
dashboard/templates/index.html
CHANGED
|
@@ -467,7 +467,10 @@
|
|
| 467 |
const ph = document.getElementById("ai-placeholder");
|
| 468 |
if (ph) ph.remove();
|
| 469 |
const div = document.createElement("div");
|
|
|
|
|
|
|
| 470 |
div.className = "insight-card insight-new";
|
|
|
|
| 471 |
const t = new Date(insight.timestamp * 1000).toLocaleTimeString();
|
| 472 |
div.innerHTML = `<div class="insight-time">${t}</div><div>${insight.text}</div>`;
|
| 473 |
list.prepend(div);
|
|
@@ -1128,8 +1131,11 @@
|
|
| 1128 |
eventSource.addEventListener("ai_insight", (e) => {
|
| 1129 |
const insight = JSON.parse(e.data);
|
| 1130 |
addInsight(insight);
|
| 1131 |
-
|
| 1132 |
-
|
|
|
|
|
|
|
|
|
|
| 1133 |
const btn = document.getElementById("ai-generate-btn");
|
| 1134 |
if (btn._tick) { clearInterval(btn._tick); btn._tick = null; }
|
| 1135 |
btn.disabled = false;
|
|
|
|
| 467 |
const ph = document.getElementById("ai-placeholder");
|
| 468 |
if (ph) ph.remove();
|
| 469 |
const div = document.createElement("div");
|
| 470 |
+
const isErr = insight.source === "error" || insight.source === "config"
|
| 471 |
+
|| (insight.text || "").startsWith("β οΈ");
|
| 472 |
div.className = "insight-card insight-new";
|
| 473 |
+
if (isErr) div.style.cssText = "border-left-color:#e53935; background:#fff5f5;";
|
| 474 |
const t = new Date(insight.timestamp * 1000).toLocaleTimeString();
|
| 475 |
div.innerHTML = `<div class="insight-time">${t}</div><div>${insight.text}</div>`;
|
| 476 |
list.prepend(div);
|
|
|
|
| 1131 |
eventSource.addEventListener("ai_insight", (e) => {
|
| 1132 |
const insight = JSON.parse(e.data);
|
| 1133 |
addInsight(insight);
|
| 1134 |
+
const isErr = insight.source === "error" || insight.source === "config"
|
| 1135 |
+
|| (insight.text || "").startsWith("β οΈ");
|
| 1136 |
+
const statusEl = document.getElementById("ai-status");
|
| 1137 |
+
statusEl.textContent = isErr ? "error β see below" : "Last update: " + new Date().toLocaleTimeString();
|
| 1138 |
+
statusEl.style.color = isErr ? "#e53935" : "#999";
|
| 1139 |
const btn = document.getElementById("ai-generate-btn");
|
| 1140 |
if (btn._tick) { clearInterval(btn._tick); btn._tick = null; }
|
| 1141 |
btn.disabled = false;
|
docker-compose.yml
CHANGED
|
@@ -179,6 +179,8 @@ services:
|
|
| 179 |
environment:
|
| 180 |
- MATCHER_URL=http://matcher:6000
|
| 181 |
- FRONTEND_URL=http://localhost:5000
|
|
|
|
|
|
|
| 182 |
|
| 183 |
volumes:
|
| 184 |
matcher_data: # Persists SQLite database across container restarts
|
|
|
|
| 179 |
environment:
|
| 180 |
- MATCHER_URL=http://matcher:6000
|
| 181 |
- FRONTEND_URL=http://localhost:5000
|
| 182 |
+
- HF_TOKEN=${HF_TOKEN:-}
|
| 183 |
+
- HF_MODEL=${HF_MODEL:-Qwen/Qwen2.5-7B-Instruct-1M}
|
| 184 |
|
| 185 |
volumes:
|
| 186 |
matcher_data: # Persists SQLite database across container restarts
|