RayMelius Claude Sonnet 4.6 commited on
Commit
f2fc19a
·
1 Parent(s): 440fe7b

Fix Generate Now: HF 503 retry + 120s countdown timer in UI

Browse files

ai_analyst.py:
- HF API: retry up to 3x on 503 model-loading, waiting estimated_time
- Increase HF timeout 45s → 60s

dashboard template:
- Button shows live elapsed-seconds counter while waiting
- Hard timeout 120s (was 10s): shows "no response — try again"
- SSE ai_insight event clears the countdown via stored interval handle

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

ai_analyst/ai_analyst.py CHANGED
@@ -49,30 +49,38 @@ def call_llm(prompt: str) -> str | None:
49
  except Exception as e:
50
  print(f"[AI-Analyst] Ollama unreachable: {e}")
51
 
52
- # 2. HuggingFace Inference API
53
  if HF_TOKEN:
54
- try:
55
- url = f"https://api-inference.huggingface.co/models/{HF_MODEL}/v1/chat/completions"
56
- resp = requests.post(
57
- url,
58
- headers={"Authorization": f"Bearer {HF_TOKEN}"},
59
- json={
60
- "model": HF_MODEL,
61
- "messages": [{"role": "user", "content": prompt}],
62
- "max_tokens": 220,
63
- "temperature": 0.7,
64
- },
65
- timeout=45,
66
- )
67
- if resp.status_code == 200:
68
- text = resp.json()["choices"][0]["message"]["content"].strip()
69
- if text:
70
- print(f"[AI-Analyst] Insight via HuggingFace ({HF_MODEL})")
71
- return text
72
- else:
73
- print(f"[AI-Analyst] HF HTTP {resp.status_code}: {resp.text[:300]}")
74
- except Exception as e:
75
- print(f"[AI-Analyst] HF API error: {e}")
 
 
 
 
 
 
 
 
76
 
77
  return None
78
 
 
49
  except Exception as e:
50
  print(f"[AI-Analyst] Ollama unreachable: {e}")
51
 
52
+ # 2. HuggingFace Inference API (with retry on 503 model-loading)
53
  if HF_TOKEN:
54
+ url = f"https://api-inference.huggingface.co/models/{HF_MODEL}/v1/chat/completions"
55
+ for attempt in range(3):
56
+ try:
57
+ resp = requests.post(
58
+ url,
59
+ headers={"Authorization": f"Bearer {HF_TOKEN}"},
60
+ json={
61
+ "model": HF_MODEL,
62
+ "messages": [{"role": "user", "content": prompt}],
63
+ "max_tokens": 220,
64
+ "temperature": 0.7,
65
+ },
66
+ timeout=60,
67
+ )
68
+ if resp.status_code == 200:
69
+ text = resp.json()["choices"][0]["message"]["content"].strip()
70
+ if text:
71
+ print(f"[AI-Analyst] Insight via HuggingFace ({HF_MODEL})")
72
+ return text
73
+ elif resp.status_code == 503:
74
+ body = resp.json() if resp.content else {}
75
+ wait = body.get("estimated_time", 20)
76
+ print(f"[AI-Analyst] HF model loading, waiting {wait:.0f}s (attempt {attempt+1}/3)")
77
+ time.sleep(min(float(wait), 30))
78
+ else:
79
+ print(f"[AI-Analyst] HF HTTP {resp.status_code}: {resp.text[:300]}")
80
+ break
81
+ except Exception as e:
82
+ print(f"[AI-Analyst] HF API error (attempt {attempt+1}/3): {e}")
83
+ break
84
 
85
  return None
86
 
dashboard/templates/index.html CHANGED
@@ -434,18 +434,32 @@
434
  let selectedOrder = null;
435
 
436
  async function triggerAIInsight() {
437
- const btn = document.getElementById("ai-generate-btn");
 
438
  btn.disabled = true;
439
  btn.textContent = "Generating…";
440
- document.getElementById("ai-status").textContent = "generating insight…";
441
  try {
442
  await fetch("/session/ai_insight", { method: "POST" });
443
  } catch(e) {}
444
- // Re-enable after 10s (LLM needs time)
 
 
 
 
 
 
 
 
445
  setTimeout(() => {
 
446
  btn.disabled = false;
447
- btn.textContent = "Generate Now";
448
- }, 10000);
 
 
 
 
449
  }
450
 
451
  function addInsight(insight) {
@@ -1117,8 +1131,9 @@
1117
  document.getElementById("ai-status").textContent =
1118
  "Last update: " + new Date().toLocaleTimeString();
1119
  const btn = document.getElementById("ai-generate-btn");
 
1120
  btn.disabled = false;
1121
- btn.textContent = "Generate Now";
1122
  });
1123
 
1124
  eventSource.onerror = () => {
 
434
  let selectedOrder = null;
435
 
436
  async function triggerAIInsight() {
437
+ const btn = document.getElementById("ai-generate-btn");
438
+ const status = document.getElementById("ai-status");
439
  btn.disabled = true;
440
  btn.textContent = "Generating…";
441
+ status.textContent = "contacting LLM…";
442
  try {
443
  await fetch("/session/ai_insight", { method: "POST" });
444
  } catch(e) {}
445
+
446
+ // Countdown timer — LLM (especially HF cold-start) can take up to 2 min
447
+ let elapsed = 0;
448
+ const tick = setInterval(() => {
449
+ elapsed += 1;
450
+ status.textContent = `generating… ${elapsed}s`;
451
+ }, 1000);
452
+
453
+ // Hard cap: re-enable after 120s with failure message
454
  setTimeout(() => {
455
+ clearInterval(tick);
456
  btn.disabled = false;
457
+ btn.textContent = "Generate Now";
458
+ status.textContent = "no response — check logs or try again";
459
+ }, 120000);
460
+
461
+ // Store tick handle so SSE success can clear it
462
+ btn._tick = tick;
463
  }
464
 
465
  function addInsight(insight) {
 
1131
  document.getElementById("ai-status").textContent =
1132
  "Last update: " + new Date().toLocaleTimeString();
1133
  const btn = document.getElementById("ai-generate-btn");
1134
+ if (btn._tick) { clearInterval(btn._tick); btn._tick = null; }
1135
  btn.disabled = false;
1136
+ btn.textContent = "Generate Now";
1137
  });
1138
 
1139
  eventSource.onerror = () => {