This commit is contained in:
team3
2026-07-03 11:55:38 +02:00
parent abcadd145d
commit 9754cbcfae
3 changed files with 99 additions and 4 deletions

View File

@@ -15,6 +15,7 @@ import os
import re
import shutil
import signal
import sqlite3
import tempfile
import time
import urllib.request
@@ -289,6 +290,9 @@ async def run_agent(
meta = {"provider": provider, "model": model, "role": role, "rc": rc}
if err_tail:
meta["stderr"] = err_tail
if PROVIDERS[provider]["cli"] == "opencode": # token accounting per agent
if (tok := await asyncio.to_thread(_session_tokens, agent_key)):
meta["tokens"] = tok
await on_event(topic=scope, kind="agent", key=agent_key, label=label,
status=status, dur_ms=int((time.monotonic() - start) * 1000),
wait_ms=wait_ms, meta=meta)
@@ -377,6 +381,31 @@ async def _run_claude_cli(agent_key: str, prompt: str, timeout: int, model: str,
return await _communicate(agent_key, cmd, prompt.encode("utf-8"), timeout, label=label)
_OPENCODE_DB = Path.home() / ".local" / "share" / "opencode" / "opencode.db"
def _session_tokens(agent_key: str) -> dict | None:
"""Token counters of the newest OpenCode session titled `agent_key` (set via run --title).
Best-effort read-only lookup — None when DB/row is missing; never fails the agent.
Timeouts count too: their sessions consumed tokens, that waste should be visible."""
try:
con = sqlite3.connect(f"file:{_OPENCODE_DB}?mode=ro", uri=True, timeout=1)
try:
row = con.execute(
"SELECT tokens_input, tokens_output, tokens_reasoning,"
" tokens_cache_read, tokens_cache_write"
" FROM session WHERE title=? ORDER BY time_created DESC LIMIT 1",
(agent_key,)).fetchone()
finally:
con.close()
except Exception:
return None
if row is None:
return None
keys = ("input", "output", "reasoning", "cache_read", "cache_write")
return {k: int(v or 0) for k, v in zip(keys, row)}
async def _run_opencode(agent_key: str, prompt: str, timeout: int, provider: str, model: str, capabilities: str, on_line=None, label: str = "") -> tuple[int, str, str]:
cfg = PROVIDERS[provider]
# Prompt via temp file instead of argv (ARG_MAX protection for large project prompts)
@@ -391,6 +420,7 @@ async def _run_opencode(agent_key: str, prompt: str, timeout: int, provider: str
"-m", model,
"--agent", _OPENCODE_AGENTS.get(capabilities, "text"),
"--dangerously-skip-permissions",
"--title", agent_key, # token accounting: joins the OpenCode session to our event
"-f", str(prompt_path),
]
if on_line is not None: