netra-zen 1.0.9__py3-none-any.whl → 1.0.11__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.
- agent_interface/__init__.py +25 -25
- agent_interface/base_agent.py +350 -350
- {netra_zen-1.0.9.dist-info → netra_zen-1.0.11.dist-info}/METADATA +36 -15
- netra_zen-1.0.11.dist-info/RECORD +30 -0
- {netra_zen-1.0.9.dist-info → netra_zen-1.0.11.dist-info}/licenses/LICENSE.md +1 -1
- scripts/__init__.py +1 -1
- scripts/__main__.py +5 -5
- scripts/agent_cli.py +7179 -6948
- scripts/agent_logs.py +327 -327
- scripts/bump_version.py +137 -137
- scripts/demo_log_collection.py +146 -144
- scripts/embed_release_credentials.py +75 -75
- scripts/test_apex_telemetry_debug.py +221 -0
- scripts/verify_log_transmission.py +140 -140
- token_budget/budget_manager.py +199 -199
- token_budget/models.py +73 -73
- token_budget/visualization.py +21 -21
- token_transparency/__init__.py +19 -19
- token_transparency/claude_pricing_engine.py +326 -326
- zen/__init__.py +7 -7
- zen/__main__.py +11 -11
- zen/telemetry/__init__.py +14 -11
- zen/telemetry/apex_telemetry.py +259 -0
- zen/telemetry/embedded_credentials.py +59 -59
- zen/telemetry/manager.py +249 -249
- zen_orchestrator.py +3058 -3008
- netra_zen-1.0.9.dist-info/RECORD +0 -28
- {netra_zen-1.0.9.dist-info → netra_zen-1.0.11.dist-info}/WHEEL +0 -0
- {netra_zen-1.0.9.dist-info → netra_zen-1.0.11.dist-info}/entry_points.txt +0 -0
- {netra_zen-1.0.9.dist-info → netra_zen-1.0.11.dist-info}/top_level.txt +0 -0
token_budget/budget_manager.py
CHANGED
@@ -1,200 +1,200 @@
|
|
1
|
-
"""Token budget manager - enhanced implementation with cost support."""
|
2
|
-
|
3
|
-
from .models import CommandBudgetInfo, BudgetType
|
4
|
-
from typing import Dict, Optional, List, Union
|
5
|
-
import sys
|
6
|
-
from pathlib import Path
|
7
|
-
|
8
|
-
# Import ClaudePricingEngine for cost calculations
|
9
|
-
sys.path.insert(0, str(Path(__file__).parent.parent))
|
10
|
-
try:
|
11
|
-
from token_transparency import ClaudePricingEngine, TokenUsageData
|
12
|
-
except ImportError:
|
13
|
-
ClaudePricingEngine = None
|
14
|
-
TokenUsageData = None
|
15
|
-
|
16
|
-
class TokenBudgetManager:
|
17
|
-
"""Manages budgets for overall session and individual commands with support for both tokens and cost."""
|
18
|
-
|
19
|
-
def __init__(self, overall_budget: Optional[Union[int, float]] = None,
|
20
|
-
enforcement_mode: str = "warn",
|
21
|
-
budget_type: Union[str, BudgetType] = BudgetType.TOKENS,
|
22
|
-
overall_cost_budget: Optional[float] = None):
|
23
|
-
"""
|
24
|
-
Initialize the budget manager.
|
25
|
-
|
26
|
-
Args:
|
27
|
-
overall_budget: Overall token budget (backward compatibility)
|
28
|
-
enforcement_mode: Action when budget exceeded ("warn" or "block")
|
29
|
-
budget_type: Type of budget ("tokens", "cost", or "mixed")
|
30
|
-
overall_cost_budget: Overall cost budget in USD
|
31
|
-
"""
|
32
|
-
# Handle backward compatibility
|
33
|
-
if overall_cost_budget is not None:
|
34
|
-
self.overall_budget = overall_cost_budget
|
35
|
-
self.budget_type = BudgetType.COST
|
36
|
-
else:
|
37
|
-
self.overall_budget = overall_budget
|
38
|
-
if isinstance(budget_type, str):
|
39
|
-
self.budget_type = BudgetType(budget_type.lower())
|
40
|
-
else:
|
41
|
-
self.budget_type = budget_type
|
42
|
-
|
43
|
-
self.enforcement_mode = enforcement_mode
|
44
|
-
self.command_budgets: Dict[str, CommandBudgetInfo] = {}
|
45
|
-
self.total_usage: Union[int, float] = 0.0 if self.is_cost_budget else 0
|
46
|
-
|
47
|
-
# Initialize pricing engine if needed for cost calculations
|
48
|
-
self.pricing_engine = None
|
49
|
-
if ClaudePricingEngine and (self.is_cost_budget or self.is_mixed_budget):
|
50
|
-
self.pricing_engine = ClaudePricingEngine()
|
51
|
-
|
52
|
-
@property
|
53
|
-
def is_token_budget(self) -> bool:
|
54
|
-
"""Check if this is a token-based budget."""
|
55
|
-
return self.budget_type == BudgetType.TOKENS
|
56
|
-
|
57
|
-
@property
|
58
|
-
def is_cost_budget(self) -> bool:
|
59
|
-
"""Check if this is a cost-based budget."""
|
60
|
-
return self.budget_type == BudgetType.COST
|
61
|
-
|
62
|
-
@property
|
63
|
-
def is_mixed_budget(self) -> bool:
|
64
|
-
"""Check if this is a mixed budget."""
|
65
|
-
return self.budget_type == BudgetType.MIXED
|
66
|
-
|
67
|
-
def set_command_budget(self, command_name: str, limit: int):
|
68
|
-
"""Sets the token budget for a specific command (backward compatibility)."""
|
69
|
-
if command_name in self.command_budgets:
|
70
|
-
# Preserve existing usage when updating budget limit
|
71
|
-
existing_usage = self.command_budgets[command_name].used
|
72
|
-
self.command_budgets[command_name] = CommandBudgetInfo(
|
73
|
-
limit=limit, used=existing_usage, budget_type=BudgetType.TOKENS)
|
74
|
-
else:
|
75
|
-
self.command_budgets[command_name] = CommandBudgetInfo(
|
76
|
-
limit=limit, budget_type=BudgetType.TOKENS)
|
77
|
-
|
78
|
-
def set_command_cost_budget(self, command_name: str, limit: float):
|
79
|
-
"""Sets the cost budget for a specific command in USD."""
|
80
|
-
if command_name in self.command_budgets:
|
81
|
-
# Preserve existing usage when updating budget limit
|
82
|
-
existing_usage = self.command_budgets[command_name].used
|
83
|
-
self.command_budgets[command_name] = CommandBudgetInfo(
|
84
|
-
limit=limit, used=existing_usage, budget_type=BudgetType.COST)
|
85
|
-
else:
|
86
|
-
self.command_budgets[command_name] = CommandBudgetInfo(
|
87
|
-
limit=limit, budget_type=BudgetType.COST)
|
88
|
-
|
89
|
-
def record_usage(self, command_name: str, tokens: int):
|
90
|
-
"""Records token usage for a command and updates the overall total (backward compatibility)."""
|
91
|
-
if self.is_cost_budget and self.pricing_engine:
|
92
|
-
# Convert tokens to cost for cost-based budgets
|
93
|
-
cost = self.convert_tokens_to_cost(tokens)
|
94
|
-
self.record_cost_usage(command_name, cost)
|
95
|
-
else:
|
96
|
-
# Traditional token usage
|
97
|
-
self.total_usage += tokens
|
98
|
-
if command_name in self.command_budgets:
|
99
|
-
self.command_budgets[command_name].used += tokens
|
100
|
-
|
101
|
-
def record_cost_usage(self, command_name: str, cost: float):
|
102
|
-
"""Records cost usage for a command and updates the overall total."""
|
103
|
-
self.total_usage += cost
|
104
|
-
if command_name in self.command_budgets:
|
105
|
-
self.command_budgets[command_name].used += cost
|
106
|
-
|
107
|
-
def check_budget(self, command_name: str, estimated_tokens: int) -> tuple[bool, str]:
|
108
|
-
"""Checks if a command can run based on its budget and the overall budget (backward compatibility).
|
109
|
-
|
110
|
-
Returns:
|
111
|
-
tuple: (can_run: bool, reason: str) - reason explains which budget would be exceeded
|
112
|
-
"""
|
113
|
-
if self.is_cost_budget:
|
114
|
-
# Convert tokens to cost for cost budget checking
|
115
|
-
estimated_cost = self.convert_tokens_to_cost(estimated_tokens)
|
116
|
-
return self.check_cost_budget(command_name, estimated_cost)
|
117
|
-
else:
|
118
|
-
return self._check_token_budget(command_name, estimated_tokens)
|
119
|
-
|
120
|
-
def check_cost_budget(self, command_name: str, estimated_cost: float) -> tuple[bool, str]:
|
121
|
-
"""Checks if a command can run based on cost budgets.
|
122
|
-
|
123
|
-
Returns:
|
124
|
-
tuple: (can_run: bool, reason: str) - reason explains which budget would be exceeded
|
125
|
-
"""
|
126
|
-
# Check overall cost budget FIRST (takes precedence)
|
127
|
-
if self.overall_budget is not None and (self.total_usage + estimated_cost) > self.overall_budget:
|
128
|
-
projected_total = self.total_usage + estimated_cost
|
129
|
-
return False, f"Overall cost budget exceeded: ${projected_total:.4f}/${self.overall_budget:.4f}"
|
130
|
-
|
131
|
-
# Check per-command cost budget
|
132
|
-
if command_name in self.command_budgets:
|
133
|
-
command_budget = self.command_budgets[command_name]
|
134
|
-
if command_budget.is_cost_budget and (command_budget.used + estimated_cost) > command_budget.limit:
|
135
|
-
projected_command = command_budget.used + estimated_cost
|
136
|
-
return False, f"Command '{command_name}' cost budget exceeded: ${projected_command:.4f}/${command_budget.limit:.4f}"
|
137
|
-
|
138
|
-
return True, "Within cost budget limits"
|
139
|
-
|
140
|
-
def _check_token_budget(self, command_name: str, estimated_tokens: int) -> tuple[bool, str]:
|
141
|
-
"""Internal method for checking token budgets."""
|
142
|
-
# Check overall budget FIRST (takes precedence)
|
143
|
-
if self.overall_budget is not None and (self.total_usage + estimated_tokens) > self.overall_budget:
|
144
|
-
projected_total = self.total_usage + estimated_tokens
|
145
|
-
return False, f"Overall budget exceeded: {projected_total}/{self.overall_budget} tokens"
|
146
|
-
|
147
|
-
# Check per-command budget
|
148
|
-
if command_name in self.command_budgets:
|
149
|
-
command_budget = self.command_budgets[command_name]
|
150
|
-
if command_budget.is_token_budget and (command_budget.used + estimated_tokens) > command_budget.limit:
|
151
|
-
projected_command = command_budget.used + estimated_tokens
|
152
|
-
return False, f"Command '{command_name}' budget exceeded: {projected_command}/{command_budget.limit} tokens"
|
153
|
-
|
154
|
-
return True, "Within budget limits"
|
155
|
-
|
156
|
-
def convert_tokens_to_cost(self, tokens: int, model: str = "claude-3-5-sonnet") -> float:
|
157
|
-
"""Convert tokens to cost using the pricing engine."""
|
158
|
-
if not self.pricing_engine:
|
159
|
-
raise AttributeError("Pricing engine not available for cost conversion")
|
160
|
-
|
161
|
-
# Create usage data for cost calculation
|
162
|
-
# Assume equal split between input and output tokens for estimation
|
163
|
-
input_tokens = int(tokens * 0.6) # Estimate 60% input
|
164
|
-
output_tokens = tokens - input_tokens # Remaining 40% output
|
165
|
-
|
166
|
-
usage_data = TokenUsageData(
|
167
|
-
input_tokens=input_tokens,
|
168
|
-
output_tokens=output_tokens,
|
169
|
-
model=model
|
170
|
-
)
|
171
|
-
|
172
|
-
cost_breakdown = self.pricing_engine.calculate_cost(usage_data)
|
173
|
-
return cost_breakdown.total_cost
|
174
|
-
|
175
|
-
def convert_cost_to_tokens(self, cost: float, model: str = "claude-3-5-sonnet") -> int:
|
176
|
-
"""Convert cost to approximate token count using the pricing engine."""
|
177
|
-
if not self.pricing_engine:
|
178
|
-
raise AttributeError("Pricing engine not available for cost conversion")
|
179
|
-
|
180
|
-
# Get model pricing
|
181
|
-
model_pricing = self.pricing_engine.pricing_config.MODEL_PRICING.get(
|
182
|
-
model, self.pricing_engine.pricing_config.MODEL_PRICING["claude-3-5-sonnet"]
|
183
|
-
)
|
184
|
-
|
185
|
-
# Use average of input and output pricing for estimation
|
186
|
-
avg_price_per_million = (model_pricing["input"] + model_pricing["output"]) / 2
|
187
|
-
tokens = int((cost / avg_price_per_million) * 1_000_000)
|
188
|
-
|
189
|
-
return tokens
|
190
|
-
|
191
|
-
def set_budget_parameter_type(self, budget_type: Union[str, BudgetType]):
|
192
|
-
"""Set the budget parameter type (tokens, cost, or mixed)."""
|
193
|
-
if isinstance(budget_type, str):
|
194
|
-
self.budget_type = BudgetType(budget_type.lower())
|
195
|
-
else:
|
196
|
-
self.budget_type = budget_type
|
197
|
-
|
198
|
-
# Initialize pricing engine if switching to cost or mixed mode
|
199
|
-
if ClaudePricingEngine and (self.is_cost_budget or self.is_mixed_budget) and not self.pricing_engine:
|
1
|
+
"""Token budget manager - enhanced implementation with cost support."""
|
2
|
+
|
3
|
+
from .models import CommandBudgetInfo, BudgetType
|
4
|
+
from typing import Dict, Optional, List, Union
|
5
|
+
import sys
|
6
|
+
from pathlib import Path
|
7
|
+
|
8
|
+
# Import ClaudePricingEngine for cost calculations
|
9
|
+
sys.path.insert(0, str(Path(__file__).parent.parent))
|
10
|
+
try:
|
11
|
+
from token_transparency import ClaudePricingEngine, TokenUsageData
|
12
|
+
except ImportError:
|
13
|
+
ClaudePricingEngine = None
|
14
|
+
TokenUsageData = None
|
15
|
+
|
16
|
+
class TokenBudgetManager:
|
17
|
+
"""Manages budgets for overall session and individual commands with support for both tokens and cost."""
|
18
|
+
|
19
|
+
def __init__(self, overall_budget: Optional[Union[int, float]] = None,
|
20
|
+
enforcement_mode: str = "warn",
|
21
|
+
budget_type: Union[str, BudgetType] = BudgetType.TOKENS,
|
22
|
+
overall_cost_budget: Optional[float] = None):
|
23
|
+
"""
|
24
|
+
Initialize the budget manager.
|
25
|
+
|
26
|
+
Args:
|
27
|
+
overall_budget: Overall token budget (backward compatibility)
|
28
|
+
enforcement_mode: Action when budget exceeded ("warn" or "block")
|
29
|
+
budget_type: Type of budget ("tokens", "cost", or "mixed")
|
30
|
+
overall_cost_budget: Overall cost budget in USD
|
31
|
+
"""
|
32
|
+
# Handle backward compatibility
|
33
|
+
if overall_cost_budget is not None:
|
34
|
+
self.overall_budget = overall_cost_budget
|
35
|
+
self.budget_type = BudgetType.COST
|
36
|
+
else:
|
37
|
+
self.overall_budget = overall_budget
|
38
|
+
if isinstance(budget_type, str):
|
39
|
+
self.budget_type = BudgetType(budget_type.lower())
|
40
|
+
else:
|
41
|
+
self.budget_type = budget_type
|
42
|
+
|
43
|
+
self.enforcement_mode = enforcement_mode
|
44
|
+
self.command_budgets: Dict[str, CommandBudgetInfo] = {}
|
45
|
+
self.total_usage: Union[int, float] = 0.0 if self.is_cost_budget else 0
|
46
|
+
|
47
|
+
# Initialize pricing engine if needed for cost calculations
|
48
|
+
self.pricing_engine = None
|
49
|
+
if ClaudePricingEngine and (self.is_cost_budget or self.is_mixed_budget):
|
50
|
+
self.pricing_engine = ClaudePricingEngine()
|
51
|
+
|
52
|
+
@property
|
53
|
+
def is_token_budget(self) -> bool:
|
54
|
+
"""Check if this is a token-based budget."""
|
55
|
+
return self.budget_type == BudgetType.TOKENS
|
56
|
+
|
57
|
+
@property
|
58
|
+
def is_cost_budget(self) -> bool:
|
59
|
+
"""Check if this is a cost-based budget."""
|
60
|
+
return self.budget_type == BudgetType.COST
|
61
|
+
|
62
|
+
@property
|
63
|
+
def is_mixed_budget(self) -> bool:
|
64
|
+
"""Check if this is a mixed budget."""
|
65
|
+
return self.budget_type == BudgetType.MIXED
|
66
|
+
|
67
|
+
def set_command_budget(self, command_name: str, limit: int):
|
68
|
+
"""Sets the token budget for a specific command (backward compatibility)."""
|
69
|
+
if command_name in self.command_budgets:
|
70
|
+
# Preserve existing usage when updating budget limit
|
71
|
+
existing_usage = self.command_budgets[command_name].used
|
72
|
+
self.command_budgets[command_name] = CommandBudgetInfo(
|
73
|
+
limit=limit, used=existing_usage, budget_type=BudgetType.TOKENS)
|
74
|
+
else:
|
75
|
+
self.command_budgets[command_name] = CommandBudgetInfo(
|
76
|
+
limit=limit, budget_type=BudgetType.TOKENS)
|
77
|
+
|
78
|
+
def set_command_cost_budget(self, command_name: str, limit: float):
|
79
|
+
"""Sets the cost budget for a specific command in USD."""
|
80
|
+
if command_name in self.command_budgets:
|
81
|
+
# Preserve existing usage when updating budget limit
|
82
|
+
existing_usage = self.command_budgets[command_name].used
|
83
|
+
self.command_budgets[command_name] = CommandBudgetInfo(
|
84
|
+
limit=limit, used=existing_usage, budget_type=BudgetType.COST)
|
85
|
+
else:
|
86
|
+
self.command_budgets[command_name] = CommandBudgetInfo(
|
87
|
+
limit=limit, budget_type=BudgetType.COST)
|
88
|
+
|
89
|
+
def record_usage(self, command_name: str, tokens: int):
|
90
|
+
"""Records token usage for a command and updates the overall total (backward compatibility)."""
|
91
|
+
if self.is_cost_budget and self.pricing_engine:
|
92
|
+
# Convert tokens to cost for cost-based budgets
|
93
|
+
cost = self.convert_tokens_to_cost(tokens)
|
94
|
+
self.record_cost_usage(command_name, cost)
|
95
|
+
else:
|
96
|
+
# Traditional token usage
|
97
|
+
self.total_usage += tokens
|
98
|
+
if command_name in self.command_budgets:
|
99
|
+
self.command_budgets[command_name].used += tokens
|
100
|
+
|
101
|
+
def record_cost_usage(self, command_name: str, cost: float):
|
102
|
+
"""Records cost usage for a command and updates the overall total."""
|
103
|
+
self.total_usage += cost
|
104
|
+
if command_name in self.command_budgets:
|
105
|
+
self.command_budgets[command_name].used += cost
|
106
|
+
|
107
|
+
def check_budget(self, command_name: str, estimated_tokens: int) -> tuple[bool, str]:
|
108
|
+
"""Checks if a command can run based on its budget and the overall budget (backward compatibility).
|
109
|
+
|
110
|
+
Returns:
|
111
|
+
tuple: (can_run: bool, reason: str) - reason explains which budget would be exceeded
|
112
|
+
"""
|
113
|
+
if self.is_cost_budget:
|
114
|
+
# Convert tokens to cost for cost budget checking
|
115
|
+
estimated_cost = self.convert_tokens_to_cost(estimated_tokens)
|
116
|
+
return self.check_cost_budget(command_name, estimated_cost)
|
117
|
+
else:
|
118
|
+
return self._check_token_budget(command_name, estimated_tokens)
|
119
|
+
|
120
|
+
def check_cost_budget(self, command_name: str, estimated_cost: float) -> tuple[bool, str]:
|
121
|
+
"""Checks if a command can run based on cost budgets.
|
122
|
+
|
123
|
+
Returns:
|
124
|
+
tuple: (can_run: bool, reason: str) - reason explains which budget would be exceeded
|
125
|
+
"""
|
126
|
+
# Check overall cost budget FIRST (takes precedence)
|
127
|
+
if self.overall_budget is not None and (self.total_usage + estimated_cost) > self.overall_budget:
|
128
|
+
projected_total = self.total_usage + estimated_cost
|
129
|
+
return False, f"Overall cost budget exceeded: ${projected_total:.4f}/${self.overall_budget:.4f}"
|
130
|
+
|
131
|
+
# Check per-command cost budget
|
132
|
+
if command_name in self.command_budgets:
|
133
|
+
command_budget = self.command_budgets[command_name]
|
134
|
+
if command_budget.is_cost_budget and (command_budget.used + estimated_cost) > command_budget.limit:
|
135
|
+
projected_command = command_budget.used + estimated_cost
|
136
|
+
return False, f"Command '{command_name}' cost budget exceeded: ${projected_command:.4f}/${command_budget.limit:.4f}"
|
137
|
+
|
138
|
+
return True, "Within cost budget limits"
|
139
|
+
|
140
|
+
def _check_token_budget(self, command_name: str, estimated_tokens: int) -> tuple[bool, str]:
|
141
|
+
"""Internal method for checking token budgets."""
|
142
|
+
# Check overall budget FIRST (takes precedence)
|
143
|
+
if self.overall_budget is not None and (self.total_usage + estimated_tokens) > self.overall_budget:
|
144
|
+
projected_total = self.total_usage + estimated_tokens
|
145
|
+
return False, f"Overall budget exceeded: {projected_total}/{self.overall_budget} tokens"
|
146
|
+
|
147
|
+
# Check per-command budget
|
148
|
+
if command_name in self.command_budgets:
|
149
|
+
command_budget = self.command_budgets[command_name]
|
150
|
+
if command_budget.is_token_budget and (command_budget.used + estimated_tokens) > command_budget.limit:
|
151
|
+
projected_command = command_budget.used + estimated_tokens
|
152
|
+
return False, f"Command '{command_name}' budget exceeded: {projected_command}/{command_budget.limit} tokens"
|
153
|
+
|
154
|
+
return True, "Within budget limits"
|
155
|
+
|
156
|
+
def convert_tokens_to_cost(self, tokens: int, model: str = "claude-3-5-sonnet") -> float:
|
157
|
+
"""Convert tokens to cost using the pricing engine."""
|
158
|
+
if not self.pricing_engine:
|
159
|
+
raise AttributeError("Pricing engine not available for cost conversion")
|
160
|
+
|
161
|
+
# Create usage data for cost calculation
|
162
|
+
# Assume equal split between input and output tokens for estimation
|
163
|
+
input_tokens = int(tokens * 0.6) # Estimate 60% input
|
164
|
+
output_tokens = tokens - input_tokens # Remaining 40% output
|
165
|
+
|
166
|
+
usage_data = TokenUsageData(
|
167
|
+
input_tokens=input_tokens,
|
168
|
+
output_tokens=output_tokens,
|
169
|
+
model=model
|
170
|
+
)
|
171
|
+
|
172
|
+
cost_breakdown = self.pricing_engine.calculate_cost(usage_data)
|
173
|
+
return cost_breakdown.total_cost
|
174
|
+
|
175
|
+
def convert_cost_to_tokens(self, cost: float, model: str = "claude-3-5-sonnet") -> int:
|
176
|
+
"""Convert cost to approximate token count using the pricing engine."""
|
177
|
+
if not self.pricing_engine:
|
178
|
+
raise AttributeError("Pricing engine not available for cost conversion")
|
179
|
+
|
180
|
+
# Get model pricing
|
181
|
+
model_pricing = self.pricing_engine.pricing_config.MODEL_PRICING.get(
|
182
|
+
model, self.pricing_engine.pricing_config.MODEL_PRICING["claude-3-5-sonnet"]
|
183
|
+
)
|
184
|
+
|
185
|
+
# Use average of input and output pricing for estimation
|
186
|
+
avg_price_per_million = (model_pricing["input"] + model_pricing["output"]) / 2
|
187
|
+
tokens = int((cost / avg_price_per_million) * 1_000_000)
|
188
|
+
|
189
|
+
return tokens
|
190
|
+
|
191
|
+
def set_budget_parameter_type(self, budget_type: Union[str, BudgetType]):
|
192
|
+
"""Set the budget parameter type (tokens, cost, or mixed)."""
|
193
|
+
if isinstance(budget_type, str):
|
194
|
+
self.budget_type = BudgetType(budget_type.lower())
|
195
|
+
else:
|
196
|
+
self.budget_type = budget_type
|
197
|
+
|
198
|
+
# Initialize pricing engine if switching to cost or mixed mode
|
199
|
+
if ClaudePricingEngine and (self.is_cost_budget or self.is_mixed_budget) and not self.pricing_engine:
|
200
200
|
self.pricing_engine = ClaudePricingEngine()
|
token_budget/models.py
CHANGED
@@ -1,74 +1,74 @@
|
|
1
|
-
"""Token budget data models - enhanced implementation with cost support."""
|
2
|
-
|
3
|
-
from typing import Dict, Optional, Union
|
4
|
-
from enum import Enum
|
5
|
-
|
6
|
-
class BudgetType(Enum):
|
7
|
-
"""Types of budget tracking supported."""
|
8
|
-
TOKENS = "tokens"
|
9
|
-
COST = "cost"
|
10
|
-
MIXED = "mixed"
|
11
|
-
|
12
|
-
class CommandBudgetInfo:
|
13
|
-
"""Tracks the budget status for a single command with support for both tokens and cost."""
|
14
|
-
|
15
|
-
def __init__(self, limit: Union[int, float], used: Union[int, float] = 0,
|
16
|
-
budget_type: BudgetType = BudgetType.TOKENS):
|
17
|
-
"""
|
18
|
-
Initialize command budget info.
|
19
|
-
|
20
|
-
Args:
|
21
|
-
limit: Budget limit (tokens as int, cost as float)
|
22
|
-
used: Current usage (tokens as int, cost as float)
|
23
|
-
budget_type: Type of budget (tokens, cost, or mixed)
|
24
|
-
"""
|
25
|
-
self.limit = limit
|
26
|
-
self.used = used
|
27
|
-
self.budget_type = budget_type
|
28
|
-
|
29
|
-
@property
|
30
|
-
def remaining(self) -> Union[int, float]:
|
31
|
-
"""Get remaining budget amount."""
|
32
|
-
return self.limit - self.used
|
33
|
-
|
34
|
-
@property
|
35
|
-
def percentage(self) -> float:
|
36
|
-
"""Get percentage of budget used."""
|
37
|
-
return (self.used / self.limit * 100) if self.limit > 0 else 0
|
38
|
-
|
39
|
-
@property
|
40
|
-
def is_token_budget(self) -> bool:
|
41
|
-
"""Check if this is a token-based budget."""
|
42
|
-
return self.budget_type == BudgetType.TOKENS
|
43
|
-
|
44
|
-
@property
|
45
|
-
def is_cost_budget(self) -> bool:
|
46
|
-
"""Check if this is a cost-based budget."""
|
47
|
-
return self.budget_type == BudgetType.COST
|
48
|
-
|
49
|
-
@property
|
50
|
-
def is_mixed_budget(self) -> bool:
|
51
|
-
"""Check if this is a mixed budget (both tokens and cost)."""
|
52
|
-
return self.budget_type == BudgetType.MIXED
|
53
|
-
|
54
|
-
def format_limit(self) -> str:
|
55
|
-
"""Format the limit for display."""
|
56
|
-
if self.is_cost_budget:
|
57
|
-
return f"${self.limit:.4f}"
|
58
|
-
else:
|
59
|
-
return f"{int(self.limit)} tokens"
|
60
|
-
|
61
|
-
def format_used(self) -> str:
|
62
|
-
"""Format the used amount for display."""
|
63
|
-
if self.is_cost_budget:
|
64
|
-
return f"${self.used:.4f}"
|
65
|
-
else:
|
66
|
-
return f"{int(self.used)} tokens"
|
67
|
-
|
68
|
-
def format_remaining(self) -> str:
|
69
|
-
"""Format the remaining amount for display."""
|
70
|
-
remaining = self.remaining
|
71
|
-
if self.is_cost_budget:
|
72
|
-
return f"${remaining:.4f}"
|
73
|
-
else:
|
1
|
+
"""Token budget data models - enhanced implementation with cost support."""
|
2
|
+
|
3
|
+
from typing import Dict, Optional, Union
|
4
|
+
from enum import Enum
|
5
|
+
|
6
|
+
class BudgetType(Enum):
|
7
|
+
"""Types of budget tracking supported."""
|
8
|
+
TOKENS = "tokens"
|
9
|
+
COST = "cost"
|
10
|
+
MIXED = "mixed"
|
11
|
+
|
12
|
+
class CommandBudgetInfo:
|
13
|
+
"""Tracks the budget status for a single command with support for both tokens and cost."""
|
14
|
+
|
15
|
+
def __init__(self, limit: Union[int, float], used: Union[int, float] = 0,
|
16
|
+
budget_type: BudgetType = BudgetType.TOKENS):
|
17
|
+
"""
|
18
|
+
Initialize command budget info.
|
19
|
+
|
20
|
+
Args:
|
21
|
+
limit: Budget limit (tokens as int, cost as float)
|
22
|
+
used: Current usage (tokens as int, cost as float)
|
23
|
+
budget_type: Type of budget (tokens, cost, or mixed)
|
24
|
+
"""
|
25
|
+
self.limit = limit
|
26
|
+
self.used = used
|
27
|
+
self.budget_type = budget_type
|
28
|
+
|
29
|
+
@property
|
30
|
+
def remaining(self) -> Union[int, float]:
|
31
|
+
"""Get remaining budget amount."""
|
32
|
+
return self.limit - self.used
|
33
|
+
|
34
|
+
@property
|
35
|
+
def percentage(self) -> float:
|
36
|
+
"""Get percentage of budget used."""
|
37
|
+
return (self.used / self.limit * 100) if self.limit > 0 else 0
|
38
|
+
|
39
|
+
@property
|
40
|
+
def is_token_budget(self) -> bool:
|
41
|
+
"""Check if this is a token-based budget."""
|
42
|
+
return self.budget_type == BudgetType.TOKENS
|
43
|
+
|
44
|
+
@property
|
45
|
+
def is_cost_budget(self) -> bool:
|
46
|
+
"""Check if this is a cost-based budget."""
|
47
|
+
return self.budget_type == BudgetType.COST
|
48
|
+
|
49
|
+
@property
|
50
|
+
def is_mixed_budget(self) -> bool:
|
51
|
+
"""Check if this is a mixed budget (both tokens and cost)."""
|
52
|
+
return self.budget_type == BudgetType.MIXED
|
53
|
+
|
54
|
+
def format_limit(self) -> str:
|
55
|
+
"""Format the limit for display."""
|
56
|
+
if self.is_cost_budget:
|
57
|
+
return f"${self.limit:.4f}"
|
58
|
+
else:
|
59
|
+
return f"{int(self.limit)} tokens"
|
60
|
+
|
61
|
+
def format_used(self) -> str:
|
62
|
+
"""Format the used amount for display."""
|
63
|
+
if self.is_cost_budget:
|
64
|
+
return f"${self.used:.4f}"
|
65
|
+
else:
|
66
|
+
return f"{int(self.used)} tokens"
|
67
|
+
|
68
|
+
def format_remaining(self) -> str:
|
69
|
+
"""Format the remaining amount for display."""
|
70
|
+
remaining = self.remaining
|
71
|
+
if self.is_cost_budget:
|
72
|
+
return f"${remaining:.4f}"
|
73
|
+
else:
|
74
74
|
return f"{int(remaining)} tokens"
|
token_budget/visualization.py
CHANGED
@@ -1,22 +1,22 @@
|
|
1
|
-
"""Token budget visualization utilities."""
|
2
|
-
|
3
|
-
def render_progress_bar(used: int, total: int, width: int = 20) -> str:
|
4
|
-
"""Renders an ASCII progress bar."""
|
5
|
-
if total == 0:
|
6
|
-
return "[NO BUDGET SET]"
|
7
|
-
|
8
|
-
percentage = min(used / total, 1.0)
|
9
|
-
filled_width = int(percentage * width)
|
10
|
-
|
11
|
-
# Use ASCII characters for Windows compatibility
|
12
|
-
bar = '#' * filled_width + '-' * (width - filled_width)
|
13
|
-
|
14
|
-
# Color coding (ANSI escape codes)
|
15
|
-
color_start = '\033[92m' # Green
|
16
|
-
if percentage > 0.9:
|
17
|
-
color_start = '\033[91m' # Red
|
18
|
-
elif percentage > 0.7:
|
19
|
-
color_start = '\033[93m' # Yellow
|
20
|
-
color_end = '\033[0m' # Reset
|
21
|
-
|
1
|
+
"""Token budget visualization utilities."""
|
2
|
+
|
3
|
+
def render_progress_bar(used: int, total: int, width: int = 20) -> str:
|
4
|
+
"""Renders an ASCII progress bar."""
|
5
|
+
if total == 0:
|
6
|
+
return "[NO BUDGET SET]"
|
7
|
+
|
8
|
+
percentage = min(used / total, 1.0)
|
9
|
+
filled_width = int(percentage * width)
|
10
|
+
|
11
|
+
# Use ASCII characters for Windows compatibility
|
12
|
+
bar = '#' * filled_width + '-' * (width - filled_width)
|
13
|
+
|
14
|
+
# Color coding (ANSI escape codes)
|
15
|
+
color_start = '\033[92m' # Green
|
16
|
+
if percentage > 0.9:
|
17
|
+
color_start = '\033[91m' # Red
|
18
|
+
elif percentage > 0.7:
|
19
|
+
color_start = '\033[93m' # Yellow
|
20
|
+
color_end = '\033[0m' # Reset
|
21
|
+
|
22
22
|
return f"[{color_start}{bar}{color_end}] {percentage:.0%}"
|
token_transparency/__init__.py
CHANGED
@@ -1,20 +1,20 @@
|
|
1
|
-
"""
|
2
|
-
Token Transparency Module for Zen Claude Orchestrator
|
3
|
-
|
4
|
-
This module provides transparent token usage tracking and cost calculation
|
5
|
-
for Claude Code instances, ensuring compliance with official Claude pricing.
|
6
|
-
"""
|
7
|
-
|
8
|
-
from .claude_pricing_engine import (
|
9
|
-
ClaudePricingEngine,
|
10
|
-
ClaudePricingConfig,
|
11
|
-
TokenUsageData,
|
12
|
-
CostBreakdown
|
13
|
-
)
|
14
|
-
|
15
|
-
__all__ = [
|
16
|
-
'ClaudePricingEngine',
|
17
|
-
'ClaudePricingConfig',
|
18
|
-
'TokenUsageData',
|
19
|
-
'CostBreakdown'
|
1
|
+
"""
|
2
|
+
Token Transparency Module for Zen Claude Orchestrator
|
3
|
+
|
4
|
+
This module provides transparent token usage tracking and cost calculation
|
5
|
+
for Claude Code instances, ensuring compliance with official Claude pricing.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from .claude_pricing_engine import (
|
9
|
+
ClaudePricingEngine,
|
10
|
+
ClaudePricingConfig,
|
11
|
+
TokenUsageData,
|
12
|
+
CostBreakdown
|
13
|
+
)
|
14
|
+
|
15
|
+
__all__ = [
|
16
|
+
'ClaudePricingEngine',
|
17
|
+
'ClaudePricingConfig',
|
18
|
+
'TokenUsageData',
|
19
|
+
'CostBreakdown'
|
20
20
|
]
|