netra-zen 1.0.9__py3-none-any.whl → 1.0.10__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.
@@ -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"
@@ -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%}"
@@ -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
  ]