mirror of
https://github.com/myronblair/jarvis
synced 2026-07-28 00:34:35 -05:00
Fix silent compose failures: poll actual job status in the compose modal instead of a fire-and-forget dispatch toast; add Groq 429 retry-with-backoff using its own rate-limit-reset header (12k tokens/min tier gets exhausted by gmail_triage alone)
This commit is contained in:
+30
-7
@@ -173,17 +173,40 @@ async def _claude_call(messages: list, system: str = "") -> str:
|
||||
raise RuntimeError(f"Claude API error {resp.status}: {data.get('error',{}).get('message','')}")
|
||||
return data["content"][0]["text"]
|
||||
|
||||
def _parse_groq_reset(val: str) -> float:
|
||||
"""Parse Groq's Go-style duration strings ('229ms', '2.5s', '2m52.8s') into seconds."""
|
||||
import re as _re
|
||||
if not val:
|
||||
return 0.0
|
||||
total = 0.0
|
||||
for num, unit in _re.findall(r'([\d.]+)(ms|s|m|h)', val):
|
||||
n = float(num)
|
||||
total += n/1000 if unit == 'ms' else n*60 if unit == 'm' else n*3600 if unit == 'h' else n
|
||||
return total
|
||||
|
||||
async def _groq_call(messages: list, system: str = "") -> str:
|
||||
# Added 2026-07-07: retry once on 429 (rate limit) using Groq's own reset-time
|
||||
# header, capped short — this account's tier has a low 12k-tokens/min ceiling that
|
||||
# gmail_triage alone can exhaust, so transient contention here is common and often
|
||||
# clears in well under a second. Capped so a genuinely long reset just falls
|
||||
# through to Ollama instead of blocking the whole compose flow.
|
||||
all_msgs = ([{"role": "system", "content": system}] if system else []) + messages
|
||||
payload = {"model": GROQ_MODEL, "messages": all_msgs, "max_tokens": 4096}
|
||||
headers = {"Authorization": f"Bearer {GROQ_API_KEY}", "Content-Type": "application/json"}
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.post("https://api.groq.com/openai/v1/chat/completions", json=payload,
|
||||
headers=headers, timeout=aiohttp.ClientTimeout(total=45)) as resp:
|
||||
data = await resp.json()
|
||||
if resp.status != 200:
|
||||
raise RuntimeError(f"Groq error {resp.status}")
|
||||
return data["choices"][0]["message"]["content"]
|
||||
for attempt in range(2):
|
||||
async with aiohttp.ClientSession() as session:
|
||||
async with session.post("https://api.groq.com/openai/v1/chat/completions", json=payload,
|
||||
headers=headers, timeout=aiohttp.ClientTimeout(total=45)) as resp:
|
||||
if resp.status == 429 and attempt == 0:
|
||||
wait = min(_parse_groq_reset(resp.headers.get("x-ratelimit-reset-tokens", "")) or
|
||||
_parse_groq_reset(resp.headers.get("x-ratelimit-reset-requests", "")) or 2.0, 8.0)
|
||||
log.info(f"[LLM] Groq 429 — retrying in {wait:.1f}s")
|
||||
await asyncio.sleep(wait)
|
||||
continue
|
||||
data = await resp.json()
|
||||
if resp.status != 200:
|
||||
raise RuntimeError(f"Groq error {resp.status}")
|
||||
return data["choices"][0]["message"]["content"]
|
||||
|
||||
async def _ollama_call(messages: list, system: str = "") -> str:
|
||||
prompt = (system + "\n\n" if system else "") + "\n".join(f"{m['role'].upper()}: {m['content']}" for m in messages)
|
||||
|
||||
Reference in New Issue
Block a user