This commit is contained in:
team3
2026-07-12 16:13:50 +02:00
parent a284e03225
commit 31a42bbf1d
48 changed files with 3625 additions and 253 deletions

View File

@@ -159,7 +159,10 @@ async def run_agent(key: str, prompt: str, timeout: int, *, provider: str,
_active[key] = time.time()
try:
if use_api:
await _api_slot(key)
await _api_slot(key) # belegt _api_inflight — bei Abbruch freigeben
if _ist_abgebrochen(key):
_api_inflight -= 1
return AgentErgebnis(1, "", "abgebrochen")
wait_s = time.time() - warte_start
try:
res = await _text_api(key, prompt, timeout, model, role)
@@ -171,6 +174,8 @@ async def run_agent(key: str, prompt: str, timeout: int, *, provider: str,
res.model = model
return res
await _drossel_warten(key)
if _ist_abgebrochen(key): # Nachzügler nach dem Cooldown nicht mehr feuern
return AgentErgebnis(1, "", "abgebrochen")
wait_s = time.time() - warte_start
if PROVIDERS[provider]["cli"] == "opencode":
res = await _opencode(key, prompt, timeout, model, capabilities)
@@ -255,11 +260,13 @@ async def _spawn(key: str, cmd: list[str], stdin_data: bytes | None, timeout: in
try:
try:
stdout, stderr = await asyncio.wait_for(process.communicate(input=stdin_data), timeout=timeout)
except asyncio.TimeoutError:
except (asyncio.TimeoutError, asyncio.CancelledError):
# Timeout ODER Cancel (Hedge-Verlierer, LaufPause) → Prozess killen,
# sonst rechnet ein verwaister CLI-Prozess unsichtbar weiter
_kill(process)
try:
await asyncio.wait_for(process.wait(), timeout=5)
except asyncio.TimeoutError:
except (asyncio.TimeoutError, asyncio.CancelledError):
pass
raise
log.info("agent %s: exit %s nach %.1fs", key, process.returncode, time.monotonic() - start)
@@ -308,7 +315,14 @@ async def _opencode(key: str, prompt: str, timeout: int, model: str, capabilitie
"-m", model, "--agent", _OPENCODE_AGENTS.get(capabilities, "text"),
"--dangerously-skip-permissions", "--title", key, "-f", str(pfad)]
try:
res = await _spawn(key, cmd, None, timeout)
try:
res = await _spawn(key, cmd, None, timeout)
except asyncio.TimeoutError:
# Timeout heißt nicht 0 Tokens — die Session hat oft schon zigtausend
# erzeugt. Als infra (err="timeout") mit echten Tokens zurückgeben,
# damit Budget/Ledger stimmen. CancelledError NICHT fangen (Hedge).
tokens = await asyncio.to_thread(_opencode_tokens, key)
return AgentErgebnis(1, "", "timeout", tokens or {})
res.text = _clean_opencode(res.text)
res.tokens = await asyncio.to_thread(_opencode_tokens, key) or {}
return res