Skip to content

Commit 42daf02

Browse files
PabloLIONclaude
andcommitted
feat: Add foundation TypedDicts for method return types
Replace dict[str, ...] return types with specific TypedDicts: **New Types Added:** - NotificationFlags: _check_notifications return type - DisplayTimes: _format_display_times return type - ExtractedTokens: extract_tokens return type - ExtractedMetadata: _extract_metadata return type **Key Improvements:** - Eliminates vague dict[str, Any] patterns - Provides explicit field typing for return values - Enhances IDE intellisense and type safety - Foundation for remaining method typing work **Type Safety Fixes:** - Updated SessionBlock.limit_messages to use FormattedLimitInfo - Fixed import paths for JSONSerializable and BlockData - Maintained 100% mypy compliance All tests maintain compatibility. Ready for aggregate and display logic TypedDict improvements. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 8e65e9d commit 42daf02

File tree

10 files changed

+64
-16
lines changed

10 files changed

+64
-16
lines changed

src/claude_monitor/core/data_processors.py

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from datetime import datetime
88
from typing import cast
99

10-
from claude_monitor.types import ClaudeJSONEntry, JSONSerializable
10+
from claude_monitor.types import ClaudeJSONEntry, JSONSerializable, ExtractedTokens
1111
from claude_monitor.utils.time_utils import TimezoneHandler
1212

1313

@@ -69,7 +69,7 @@ class TokenExtractor:
6969
"""Unified token extraction utilities."""
7070

7171
@staticmethod
72-
def extract_tokens(data: ClaudeJSONEntry) -> dict[str, int]:
72+
def extract_tokens(data: ClaudeJSONEntry) -> ExtractedTokens:
7373
"""Extract token counts from data in standardized format.
7474
7575
Args:
@@ -118,7 +118,12 @@ def safe_get_int(value: JSONSerializable | None) -> int:
118118
logger.debug(
119119
"TokenExtractor: System/user messages have no token usage"
120120
)
121-
return tokens
121+
return {
122+
"input_tokens": 0,
123+
"output_tokens": 0,
124+
"cache_creation_tokens": 0,
125+
"cache_read_tokens": 0,
126+
}
122127
elif entry_type == "assistant":
123128
# Assistant messages have token usage - proceed with extraction
124129
pass
@@ -247,7 +252,12 @@ def safe_get_int(value: JSONSerializable | None) -> int:
247252
if tokens["total_tokens"] == 0:
248253
logger.debug("TokenExtractor: No tokens found in any source")
249254

250-
return tokens
255+
return {
256+
"input_tokens": tokens["input_tokens"],
257+
"output_tokens": tokens["output_tokens"],
258+
"cache_creation_tokens": tokens["cache_creation_tokens"],
259+
"cache_read_tokens": tokens["cache_read_tokens"],
260+
}
251261

252262

253263
class DataConverter:

src/claude_monitor/core/models.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
from datetime import datetime
99
from enum import Enum
1010

11+
from claude_monitor.types import FormattedLimitInfo
12+
1113

1214
class CostMode(Enum):
1315
"""Cost calculation modes for token usage analysis."""
@@ -86,7 +88,7 @@ class SessionBlock:
8688
models: list[str] = field(default_factory=list[str])
8789
sent_messages_count: int = 0
8890
cost_usd: float = 0.0
89-
limit_messages: list[dict[str, str]] = field(default_factory=list[dict[str, str]])
91+
limit_messages: list[FormattedLimitInfo] = field(default_factory=list[FormattedLimitInfo])
9092
projection_data: dict[str, int | float] | None = None
9193
burn_rate_snapshot: BurnRate | None = None
9294

src/claude_monitor/core/p90_calculator.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from statistics import quantiles
66
from collections.abc import Callable
77

8-
from claude_monitor.core.models import BlockData
8+
from claude_monitor.types import BlockData
99

1010

1111
@dataclass(frozen=True)

src/claude_monitor/data/reader.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
TimestampProcessor,
1515
TokenExtractor,
1616
)
17-
from claude_monitor.types import ClaudeJSONEntry, SystemEntry, UserEntry, AssistantEntry, JSONSerializable, EntryData
17+
from claude_monitor.types import ClaudeJSONEntry, SystemEntry, UserEntry, AssistantEntry, JSONSerializable, EntryData, ExtractedMetadata
1818
from claude_monitor.core.models import CostMode, UsageEntry
1919
from claude_monitor.core.pricing import PricingCalculator
2020
from claude_monitor.error_handling import report_file_error
@@ -393,7 +393,7 @@ def _extract_model(self, data: dict[str, JSONSerializable]) -> str:
393393
return DataConverter.extract_model_name(parsed_data, default="unknown")
394394
return "unknown"
395395

396-
def _extract_metadata(self, data: dict[str, JSONSerializable]) -> dict[str, str]:
396+
def _extract_metadata(self, data: dict[str, JSONSerializable]) -> ExtractedMetadata:
397397
"""Extract metadata (for test compatibility)."""
398398
message = data.get("message", {})
399399

src/claude_monitor/types/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@
4242
"ModelStatsDict",
4343
"ProgressBarStyleConfig",
4444
"ThresholdConfig",
45+
"NotificationFlags",
46+
"DisplayTimes",
4547

4648
# Config types
4749
"LastUsedParamsDict",
@@ -67,4 +69,6 @@
6769
"ProjectionData",
6870
"LimitInfo",
6971
"MonitoringData",
72+
"ExtractedTokens",
73+
"ExtractedMetadata",
7074
]

src/claude_monitor/types/common.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,4 +50,20 @@ class ProjectionData(TypedDict):
5050

5151
projected_total_tokens: int
5252
projected_total_cost: float
53-
remaining_minutes: float
53+
remaining_minutes: float
54+
55+
56+
class ExtractedTokens(TypedDict):
57+
"""Extracted token counts from Claude message data."""
58+
59+
input_tokens: int
60+
output_tokens: int
61+
cache_creation_tokens: int
62+
cache_read_tokens: int
63+
64+
65+
class ExtractedMetadata(TypedDict):
66+
"""Extracted metadata from Claude message entries."""
67+
68+
message_id: str
69+
request_id: str

src/claude_monitor/types/display.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,4 +93,20 @@ class ThresholdConfig(TypedDict):
9393

9494
low: float
9595
medium: float
96-
high: float
96+
high: float
97+
98+
99+
class NotificationFlags(TypedDict):
100+
"""Notification flags for display controller."""
101+
102+
show_switch_notification: bool
103+
show_exceed_notification: bool
104+
show_cost_will_exceed: bool
105+
106+
107+
class DisplayTimes(TypedDict):
108+
"""Formatted display times for UI."""
109+
110+
predicted_end_str: str
111+
reset_time_str: str
112+
current_time_str: str

src/claude_monitor/ui/display_controller.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
import pytz
1313
from rich.console import Console, Group, RenderableType
1414

15-
from claude_monitor.types import JSONSerializable, TimeData, CostPredictions, ExtractedSessionData, ProcessedDisplayData, BlockDict, AnalysisResult, BlockData
15+
from claude_monitor.types import JSONSerializable, TimeData, CostPredictions, ExtractedSessionData, ProcessedDisplayData, BlockDict, AnalysisResult, BlockData, NotificationFlags, DisplayTimes
1616
from rich.live import Live
1717
from rich.text import Text
1818

@@ -107,7 +107,7 @@ def _check_notifications(
107107
cost_limit: float,
108108
predicted_end_time: datetime,
109109
reset_time: datetime,
110-
) -> dict[str, bool]:
110+
) -> NotificationFlags:
111111
"""Check and update notification states."""
112112
notifications = {}
113113

@@ -150,15 +150,15 @@ def _check_notifications(
150150
and self.notification_manager.is_notification_active("cost_will_exceed")
151151
)
152152

153-
return notifications
153+
return cast(NotificationFlags, notifications)
154154

155155
def _format_display_times(
156156
self,
157157
args: argparse.Namespace,
158158
current_time: datetime,
159159
predicted_end_time: datetime,
160160
reset_time: datetime,
161-
) -> dict[str, str]:
161+
) -> DisplayTimes:
162162
"""Format times for display."""
163163
tz_handler = TimezoneHandler(default_tz="Europe/Warsaw")
164164
timezone_to_use = (

src/claude_monitor/ui/progress_bars.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from abc import ABC, abstractmethod
99
from typing import Final, Protocol, TypedDict
1010

11-
from claude_monitor.core.models import JSONSerializable
11+
from claude_monitor.types import JSONSerializable
1212
from claude_monitor.utils.time_utils import percentage
1313

1414

src/tests/test_display_controller.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import pytest
77

8-
from claude_monitor.core.models import JSONSerializable
8+
from claude_monitor.types import JSONSerializable
99
from claude_monitor.ui.display_controller import (
1010
DisplayController,
1111
LiveDisplayManager,

0 commit comments

Comments
 (0)