fix: resolve review budget feedback

This commit is contained in:
Claude
2026-09-01 12:14:18 +00:00
parent 4d51e14a1a
commit 592747d98d
3 changed files with 49 additions and 13 deletions
+2 -2
View File
@@ -163,6 +163,8 @@ def equivalent_cost(usage: dict, model: str, price_target: str = "") -> float:
"""Estimate comparison cost for one completed iteration.""" """Estimate comparison cost for one completed iteration."""
try: try:
from cost_model import PRICES, Usage, cost from cost_model import PRICES, Usage, cost
except (ImportError, ModuleNotFoundError):
return 0.0
target = price_target or os.environ.get("PRAGENT_PRICE_TARGET", "claude-sonnet-5") target = price_target or os.environ.get("PRAGENT_PRICE_TARGET", "claude-sonnet-5")
price = PRICES.get(target) price = PRICES.get(target)
if price is None: if price is None:
@@ -173,5 +175,3 @@ def equivalent_cost(usage: dict, model: str, price_target: str = "") -> float:
cache_writes=int(usage.get("cache_write") or 0), cache_writes=int(usage.get("cache_write") or 0),
output=int(usage.get("output") or 0), output=int(usage.get("output") or 0),
), price) ), price)
except Exception:
return 0.0
+23 -1
View File
@@ -1,6 +1,7 @@
"""Isolated opencode process runtime.""" """Isolated opencode process runtime."""
import os import os
import selectors
import subprocess import subprocess
import time import time
@@ -130,6 +131,8 @@ def _run_process(
cmd, cwd=cwd, env=env, capture_output=True, text=True, cmd, cwd=cwd, env=env, capture_output=True, text=True,
stdin=subprocess.DEVNULL, timeout=timeout, stdin=subprocess.DEVNULL, timeout=timeout,
) )
if runner not in (None, subprocess.run):
raise ValueError("custom runners are unsupported for budgeted streaming")
existing_reason = budget_state.reason() existing_reason = budget_state.reason()
if existing_reason: if existing_reason:
budget_state.cap_reason = existing_reason budget_state.cap_reason = existing_reason
@@ -142,9 +145,27 @@ def _run_process(
previous = {"steps": 0, "total": 0, "output": 0, "cost": 0.0} previous = {"steps": 0, "total": 0, "output": 0, "cost": 0.0}
cap_reason = "" cap_reason = ""
started = time.monotonic() started = time.monotonic()
selector = selectors.DefaultSelector()
try: try:
assert proc.stdout is not None assert proc.stdout is not None
for line in proc.stdout: selector.register(proc.stdout, selectors.EVENT_READ)
while selector.get_map():
remaining = budget.max_duration_seconds - (time.monotonic() - started)
if remaining <= 0:
cap_reason = "max_duration_seconds"
budget_state.cap_reason = cap_reason
proc.terminate()
break
events = selector.select(timeout=remaining)
if not events:
cap_reason = "max_duration_seconds"
budget_state.cap_reason = cap_reason
proc.terminate()
break
line = proc.stdout.readline()
if not line:
selector.unregister(proc.stdout)
break
output.append(line) output.append(line)
_, usage = parse_events("".join(output)) _, usage = parse_events("".join(output))
if usage: if usage:
@@ -180,6 +201,7 @@ def _run_process(
proc.kill() proc.kill()
proc.wait() proc.wait()
finally: finally:
selector.close()
if proc.stdout: if proc.stdout:
proc.stdout.close() proc.stdout.close()
stderr = proc.stderr.read() if proc.stderr else "" stderr = proc.stderr.read() if proc.stderr else ""
+15 -1
View File
@@ -3,6 +3,7 @@
import os import os
import sys import sys
import json import json
import subprocess
ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", "..")) ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "..", ".."))
sys.path.insert(0, os.path.join(ROOT, "pilot")) sys.path.insert(0, os.path.join(ROOT, "pilot"))
@@ -56,7 +57,20 @@ def test_process_terminates_after_step_budget():
proc = opencode_runtime._run_process( proc = opencode_runtime._run_process(
[sys.executable, "-u", "-c", code], cwd=".", env=os.environ.copy(), [sys.executable, "-u", "-c", code], cwd=".", env=os.environ.copy(),
timeout=10, parse_events=parse_opencode_events, budget=budget, timeout=10, parse_events=parse_opencode_events, budget=budget,
budget_state=state, model="glm-5.2:cloud", runner=None, budget_state=state, model="glm-5.2:cloud", runner=subprocess.run,
) )
assert state.snapshot()["cap_reason"] == "max_steps" assert state.snapshot()["cap_reason"] == "max_steps"
assert proc.stdout.count("step_finish") == 1 assert proc.stdout.count("step_finish") == 1
def test_process_terminates_silent_child_at_duration_budget():
code = "import time; time.sleep(30)"
budget = Budget(max_steps=20, max_duration_seconds=1)
state = BudgetState(budget)
proc = opencode_runtime._run_process(
[sys.executable, "-u", "-c", code], cwd=".", env=os.environ.copy(),
timeout=10, parse_events=parse_opencode_events, budget=budget,
budget_state=state, model="glm-5.2:cloud", runner=subprocess.run,
)
assert state.snapshot()["cap_reason"] == "max_duration_seconds"
assert proc.stdout == ""