Fix dispatcher: pipe via stdin, use real cost from CLI JSON

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Symbiont 2026-03-19 19:33:09 +00:00
parent 49f73e5b46
commit 71041b92b1

View File

@ -113,39 +113,43 @@ def dispatch(
"--print", # non-interactive, just print the response "--print", # non-interactive, just print the response
"--model", model_flag, "--model", model_flag,
"--output-format", "json", "--output-format", "json",
"--dangerously-skip-permissions", # full access, no prompts
] ]
if system_prompt: if system_prompt:
cmd.extend(["--system-prompt", system_prompt]) cmd.extend(["--system-prompt", system_prompt])
# Prompt is a positional argument in Claude Code CLI
cmd.append(prompt)
start = time.monotonic() start = time.monotonic()
try: try:
# Pipe the prompt via stdin to avoid shell escaping issues
result = subprocess.run( result = subprocess.run(
cmd, cmd,
input=prompt,
capture_output=True, capture_output=True,
text=True, text=True,
timeout=timeout_seconds, timeout=timeout_seconds,
) )
elapsed = time.monotonic() - start elapsed = time.monotonic() - start
# Try to parse JSON output for token counts # Parse the rich JSON output from Claude Code CLI
output_text = result.stdout.strip() output_text = result.stdout.strip()
input_tokens = 0 input_tokens = 0
output_tokens = 0 output_tokens = 0
actual_cost_usd = 0.0
try: try:
parsed = json.loads(output_text) parsed = json.loads(output_text)
# Claude Code JSON output includes result and usage
if isinstance(parsed, dict): if isinstance(parsed, dict):
output_text = parsed.get("result", output_text) output_text = parsed.get("result", output_text)
# Claude Code gives us exact usage data
usage = parsed.get("usage", {}) usage = parsed.get("usage", {})
input_tokens = usage.get("input_tokens", 0) input_tokens = usage.get("input_tokens", 0)
output_tokens = usage.get("output_tokens", 0) output_tokens = usage.get("output_tokens", 0)
# Real cost from the CLI (even on Pro, this tracks the equivalent)
actual_cost_usd = parsed.get("total_cost_usd", 0.0)
# Also capture cache stats for efficiency tracking
cache_read = usage.get("cache_read_input_tokens", 0)
cache_create = usage.get("cache_creation_input_tokens", 0)
except json.JSONDecodeError: except json.JSONDecodeError:
pass # Plain text output, that's fine pass # Plain text output, that's fine
@ -171,7 +175,10 @@ def dispatch(
error=f"Exit code {result.returncode}: {stderr}", error=f"Exit code {result.returncode}: {stderr}",
) )
# Estimate cost for the ledger # Use real cost from CLI if available, otherwise estimate
if actual_cost_usd > 0:
estimated_cost = actual_cost_usd
else:
costs = MODEL_COSTS[tier] costs = MODEL_COSTS[tier]
estimated_cost = ( estimated_cost = (
(input_tokens / 1_000_000) * costs["input"] (input_tokens / 1_000_000) * costs["input"]