zwarm 0.1.0__py3-none-any.whl → 1.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -33,10 +33,46 @@ class ClaudeCodeAdapter(ExecutorAdapter):
33
33
  """
34
34
 
35
35
  name = "claude_code"
36
+ DEFAULT_MODEL = "claude-sonnet-4-5-20250514" # Best balance of speed and capability
36
37
 
37
38
  def __init__(self, model: str | None = None):
38
- self._model = model
39
+ self._model = model or self.DEFAULT_MODEL
39
40
  self._sessions: dict[str, str] = {} # session_id -> claude session_id
41
+ # Cumulative token usage for cost tracking
42
+ self._total_usage: dict[str, int] = {
43
+ "input_tokens": 0,
44
+ "output_tokens": 0,
45
+ "cache_creation_input_tokens": 0,
46
+ "cache_read_input_tokens": 0,
47
+ "total_tokens": 0,
48
+ }
49
+
50
+ def _accumulate_usage(self, usage: dict[str, Any]) -> None:
51
+ """Add usage to cumulative totals."""
52
+ if not usage:
53
+ return
54
+ for key in self._total_usage:
55
+ self._total_usage[key] += usage.get(key, 0)
56
+
57
+ def _extract_usage(self, output: dict) -> dict[str, int]:
58
+ """Extract token usage from claude CLI JSON output."""
59
+ usage = {}
60
+ # Claude CLI may include usage in various formats
61
+ if "usage" in output:
62
+ usage = output["usage"]
63
+ elif "cost_usd" in output:
64
+ # Alternative: estimate from cost if available
65
+ usage["cost_usd"] = output["cost_usd"]
66
+ # Also check for token counts in the output
67
+ for key in ["input_tokens", "output_tokens", "total_tokens"]:
68
+ if key in output:
69
+ usage[key] = output[key]
70
+ return usage
71
+
72
+ @property
73
+ def total_usage(self) -> dict[str, int]:
74
+ """Get cumulative token usage across all calls."""
75
+ return self._total_usage.copy()
40
76
 
41
77
  @weave.op()
42
78
  async def _call_claude(
@@ -75,11 +111,14 @@ class ClaudeCodeAdapter(ExecutorAdapter):
75
111
 
76
112
  response_text = self._extract_response(result.stdout, result.stderr)
77
113
 
78
- # Try to get session ID from JSON output
114
+ # Try to get session ID and usage from JSON output
79
115
  session_id = None
116
+ usage = {}
80
117
  try:
81
118
  output = json.loads(result.stdout)
82
119
  session_id = output.get("session_id")
120
+ usage = self._extract_usage(output)
121
+ self._accumulate_usage(usage)
83
122
  except (json.JSONDecodeError, TypeError):
84
123
  pass
85
124
 
@@ -87,6 +126,8 @@ class ClaudeCodeAdapter(ExecutorAdapter):
87
126
  "response": response_text,
88
127
  "session_id": session_id,
89
128
  "exit_code": result.returncode,
129
+ "usage": usage,
130
+ "total_usage": self.total_usage,
90
131
  }
91
132
 
92
133
  @weave.op()
@@ -126,16 +167,21 @@ class ClaudeCodeAdapter(ExecutorAdapter):
126
167
 
127
168
  response_text = self._extract_response(result.stdout, result.stderr)
128
169
 
129
- # Try to get session ID from JSON output
170
+ # Try to get session ID and usage from JSON output
130
171
  new_session_id = None
172
+ usage = {}
131
173
  try:
132
174
  output = json.loads(result.stdout)
133
175
  new_session_id = output.get("session_id")
176
+ usage = self._extract_usage(output)
177
+ self._accumulate_usage(usage)
134
178
  except (json.JSONDecodeError, TypeError):
135
179
  pass
136
180
 
137
181
  return {
138
182
  "response": response_text,
183
+ "usage": usage,
184
+ "total_usage": self.total_usage,
139
185
  "session_id": new_session_id or session_id,
140
186
  "exit_code": result.returncode,
141
187
  }
@@ -175,6 +221,9 @@ class ClaudeCodeAdapter(ExecutorAdapter):
175
221
  session.add_message("user", task)
176
222
  session.add_message("assistant", result["response"])
177
223
 
224
+ # Track token usage on the session
225
+ session.add_usage(result.get("usage", {}))
226
+
178
227
  else:
179
228
  # Async mode: run in background
180
229
  cmd = ["claude", "-p", "--output-format", "json"]
@@ -223,6 +272,9 @@ class ClaudeCodeAdapter(ExecutorAdapter):
223
272
  session.add_message("user", message)
224
273
  session.add_message("assistant", response_text)
225
274
 
275
+ # Track token usage on the session
276
+ session.add_usage(result.get("usage", {}))
277
+
226
278
  return response_text
227
279
 
228
280
  async def check_status(