agentshim 0.21.0__tar.gz → 0.22.0__tar.gz
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.
- {agentshim-0.21.0 → agentshim-0.22.0}/CHANGELOG.md +29 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/PKG-INFO +1 -1
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/__init__.py +3 -1
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/core/__init__.py +2 -1
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/core/usage.py +16 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/pricing.py +1 -1
- agentshim-0.22.0/agentshim/providers/claude/cost.py +140 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/claude/events.py +4 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/claude/model_data.py +22 -2
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/claude/parser.py +41 -5
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/claude/stream_transport.py +9 -2
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/codex/app_server/transport.py +6 -1
- {agentshim-0.21.0 → agentshim-0.22.0}/pyproject.toml +1 -1
- {agentshim-0.21.0 → agentshim-0.22.0}/.gitignore +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/LICENSE +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/README.md +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/agent.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/core/_files.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/core/auth.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/core/checkpoints.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/core/clock.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/core/conversation.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/core/env.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/core/errors.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/core/events.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/core/home.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/core/ids.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/core/mcp.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/core/models.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/core/permissions.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/core/plugin.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/core/pricing.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/core/profile.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/core/provider.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/core/schema.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/core/session_policy.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/core/skills.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/core/status.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/core/stream.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/core/turn.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/execution/__init__.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/execution/confinement.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/execution/docker.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/execution/executor.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/execution/host.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/execution/process.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/execution/readiness.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/execution/transform.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/models.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/oneshot.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/probe.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/__init__.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/_testing.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/catalog.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/claude/__init__.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/claude/failures.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/claude/hooks/__init__.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/claude/hooks/confine_reads.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/claude/plugin.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/claude/probe.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/claude/provider.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/claude/rate_limits.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/claude/sandbox.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/claude/scripted.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/claude/stream_argv.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/claude/testing.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/claude/tools.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/claude/user_hooks.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/codex/__init__.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/codex/_toml.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/codex/app_server/__init__.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/codex/app_server/_wire.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/codex/app_server/approvals.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/codex/app_server/channel.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/codex/app_server/errors.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/codex/app_server/items.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/codex/app_server/mcp.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/codex/app_server/permissions.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/codex/app_server/profile.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/codex/app_server/protocol.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/codex/app_server/rate_limits.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/codex/app_server/usage.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/codex/events.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/codex/failures.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/codex/mcp_scope.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/codex/model_data.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/codex/parser.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/codex/plugin.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/codex/probe.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/codex/provider.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/codex/rules.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/codex/sandbox.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/codex/scripted.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/codex/skills.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/codex/testing.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/copilot/__init__.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/copilot/events.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/copilot/parser.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/copilot/plugin.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/copilot/probe.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/copilot/provider.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/copilot/scripted.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/copilot/testing.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/gemini/__init__.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/gemini/events.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/gemini/parser.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/gemini/plugin.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/gemini/probe.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/gemini/provider.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/gemini/scripted.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/gemini/testing.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/opencode/__init__.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/opencode/events.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/opencode/model_data.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/opencode/parser.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/opencode/plugin.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/opencode/probe.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/opencode/provider.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/opencode/scripted.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/opencode/testing.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/providers/static_plugin.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/py.typed +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/runtime.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/session.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/testing/__init__.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/testing/claude_stream.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/testing/clock.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/testing/codex_app_server.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/testing/confinement.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/testing/contracts.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/testing/conversation.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/testing/probe.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/agentshim/testing/process.py +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/tests/fixtures/claude_stream/README.md +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/tests/fixtures/codex/README.md +0 -0
- {agentshim-0.21.0 → agentshim-0.22.0}/tests/fixtures/codex_app_server/README.md +0 -0
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.22.0 (2026-10-19)
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- A Claude turn's cost is priced from the per-model token counts in the CLI's `modelUsage` and
|
|
8
|
+
agentshim's own price table, not taken from the CLI's `total_cost_usd`. A Claude Code build older
|
|
9
|
+
than the model it runs does not know the model and bills it at a fallback rate (Claude Code
|
|
10
|
+
2.1.268 priced `claude-sonnet-5-5` about 2.6 times too high). `modelUsage` is cumulative over the
|
|
11
|
+
process, so a long-lived `ClaudeStreamTransport` prices each turn by the difference. A turn that
|
|
12
|
+
used a model with no table price has `cost_usd=None`; no price is guessed. Only a result with no
|
|
13
|
+
`modelUsage` keeps the CLI's figure.
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
|
|
17
|
+
- `CostSource` (`PRICE_TABLE`, `PROVIDER_REPORTED`, `UNKNOWN_MODEL`) and
|
|
18
|
+
`ProviderUsage.cost_source` say where `total_cost_usd` came from; `None` when no cost was
|
|
19
|
+
reported. The Codex app-server transport reports `PRICE_TABLE`. `to_dict()` gains `cost_source`.
|
|
20
|
+
- `ClaudeStreamParser` takes `model_usage_baseline` and `pricing`, and exposes
|
|
21
|
+
`model_usage_total` for the next turn's baseline.
|
|
22
|
+
|
|
23
|
+
## 0.21.1 (2026-10-19)
|
|
24
|
+
|
|
25
|
+
### Fixed
|
|
26
|
+
|
|
27
|
+
- Claude Sonnet 5.5, Claude Haiku 5.5, Claude Opus 4.7 and Claude Opus 4.6 have prices, so
|
|
28
|
+
`price_for` and cost lookups no longer come back empty for them. Haiku 5.5 carries its
|
|
29
|
+
prompts-up-to-100,000-tokens prices; its higher over-100,000-token prices are in the entry
|
|
30
|
+
`note`. Every Claude model in `default_models()` now has a price (tested).
|
|
31
|
+
|
|
3
32
|
## 0.21.0 (2026-10-18)
|
|
4
33
|
|
|
5
34
|
### Added
|
|
@@ -36,6 +36,7 @@ from .core import (
|
|
|
36
36
|
ContinuityError,
|
|
37
37
|
Conversation,
|
|
38
38
|
ConversationSpec,
|
|
39
|
+
CostSource,
|
|
39
40
|
CredentialKind,
|
|
40
41
|
CredentialSource,
|
|
41
42
|
EndpointSpec,
|
|
@@ -185,7 +186,7 @@ from .providers.opencode import OpencodeProvider
|
|
|
185
186
|
from .runtime import Agent
|
|
186
187
|
from .session import Session, Turn, TurnTicket, map_mcp_servers
|
|
187
188
|
|
|
188
|
-
__version__ = "0.
|
|
189
|
+
__version__ = "0.22.0"
|
|
189
190
|
|
|
190
191
|
__all__ = [ # noqa: RUF022 - public API test uses Python's case-sensitive order
|
|
191
192
|
"Agent",
|
|
@@ -234,6 +235,7 @@ __all__ = [ # noqa: RUF022 - public API test uses Python's case-sensitive order
|
|
|
234
235
|
"Conversation",
|
|
235
236
|
"ConversationSpec",
|
|
236
237
|
"CopilotProvider",
|
|
238
|
+
"CostSource",
|
|
237
239
|
"CredentialKind",
|
|
238
240
|
"CredentialSource",
|
|
239
241
|
"DEFAULT_CATALOG",
|
|
@@ -126,7 +126,7 @@ from .skills import SkillSummary, SkillTracker
|
|
|
126
126
|
from .status import AuthState, ProviderStatus
|
|
127
127
|
from .stream import ToolTracker, parse_json_object
|
|
128
128
|
from .turn import OutputSchema, TurnRequest, TurnResult
|
|
129
|
-
from .usage import ProviderUsage, TokenUsage, TokenWeights, normalized_usage
|
|
129
|
+
from .usage import CostSource, ProviderUsage, TokenUsage, TokenWeights, normalized_usage
|
|
130
130
|
|
|
131
131
|
__all__ = [
|
|
132
132
|
"AgentEvent",
|
|
@@ -157,6 +157,7 @@ __all__ = [
|
|
|
157
157
|
"ContinuityError",
|
|
158
158
|
"Conversation",
|
|
159
159
|
"ConversationSpec",
|
|
160
|
+
"CostSource",
|
|
160
161
|
"CredentialKind",
|
|
161
162
|
"CredentialSource",
|
|
162
163
|
"EndpointSpec",
|
|
@@ -20,6 +20,7 @@ from __future__ import annotations
|
|
|
20
20
|
|
|
21
21
|
import math
|
|
22
22
|
from dataclasses import dataclass, field
|
|
23
|
+
from enum import Enum
|
|
23
24
|
from typing import TYPE_CHECKING, Any
|
|
24
25
|
|
|
25
26
|
if TYPE_CHECKING:
|
|
@@ -261,6 +262,17 @@ def normalized_usage( # noqa: PLR0913 - one keyword per normalized count
|
|
|
261
262
|
)
|
|
262
263
|
|
|
263
264
|
|
|
265
|
+
class CostSource(str, Enum):
|
|
266
|
+
"""Where a reported cost came from."""
|
|
267
|
+
|
|
268
|
+
#: Computed from the turn's token counts and agentshim's price table.
|
|
269
|
+
PRICE_TABLE = "price_table"
|
|
270
|
+
#: The CLI's own figure, used only when it did not say which model ran.
|
|
271
|
+
PROVIDER_REPORTED = "provider_reported"
|
|
272
|
+
#: The turn used a model agentshim has no price for, so there is no cost.
|
|
273
|
+
UNKNOWN_MODEL = "unknown_model"
|
|
274
|
+
|
|
275
|
+
|
|
264
276
|
@dataclass(frozen=True)
|
|
265
277
|
class ProviderUsage:
|
|
266
278
|
"""Normalized token counts plus the raw provider mapping.
|
|
@@ -278,6 +290,9 @@ class ProviderUsage:
|
|
|
278
290
|
provider: str = ""
|
|
279
291
|
raw: Mapping[str, Any] | None = None
|
|
280
292
|
increment_known: bool = True
|
|
293
|
+
#: Where ``total_cost_usd`` came from; ``None`` when no cost was reported.
|
|
294
|
+
#: ``UNKNOWN_MODEL`` goes with a ``None`` cost: no price is guessed.
|
|
295
|
+
cost_source: CostSource | None = None
|
|
281
296
|
|
|
282
297
|
def to_dict(self) -> dict[str, Any]:
|
|
283
298
|
"""Flatten the normalized counts, cost, and provider name into one mapping.
|
|
@@ -290,4 +305,5 @@ class ProviderUsage:
|
|
|
290
305
|
"total_cost_usd": self.total_cost_usd,
|
|
291
306
|
"provider": self.provider,
|
|
292
307
|
"increment_known": self.increment_known,
|
|
308
|
+
"cost_source": None if self.cost_source is None else self.cost_source.value,
|
|
293
309
|
}
|
|
@@ -106,4 +106,4 @@ def default_pricing(*, catalog: ProviderCatalog | None = None) -> PricingTable:
|
|
|
106
106
|
by_key[entry.key] = entry
|
|
107
107
|
entries = tuple(by_key.values())
|
|
108
108
|
updated = max((entry.checked for entry in entries), default=dt.date(2026, 9, 27))
|
|
109
|
-
return PricingTable(version="2026-
|
|
109
|
+
return PricingTable(version="2026-10-19", last_updated=updated, entries=entries)
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"""Price a Claude turn from the CLI's per-model token counts.
|
|
2
|
+
|
|
3
|
+
The CLI's own ``total_cost_usd`` is priced by the CLI's built-in table. A CLI
|
|
4
|
+
older than the model it runs does not know the model and bills it at a
|
|
5
|
+
fallback rate, so that figure can be several times too high. The ``modelUsage``
|
|
6
|
+
mapping of a ``result`` frame carries the token counts per model, which
|
|
7
|
+
agentshim prices with its own table instead.
|
|
8
|
+
|
|
9
|
+
``modelUsage`` is cumulative over the CLI process, like ``total_cost_usd``, so
|
|
10
|
+
a turn's tokens are the difference from the previous result's counts. It does
|
|
11
|
+
not split cache writes by TTL; the turn's ``usage.cache_creation`` does, and
|
|
12
|
+
its share of one-hour writes is applied to every model's writes.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from dataclasses import dataclass
|
|
18
|
+
from typing import TYPE_CHECKING, Any, cast
|
|
19
|
+
|
|
20
|
+
from agentshim.core.pricing import cost_usd
|
|
21
|
+
from agentshim.core.usage import CostSource, TokenUsage, normalized_usage
|
|
22
|
+
from agentshim.pricing import price_for
|
|
23
|
+
|
|
24
|
+
if TYPE_CHECKING:
|
|
25
|
+
from collections.abc import Mapping
|
|
26
|
+
|
|
27
|
+
from agentshim.core.pricing import PricingTable
|
|
28
|
+
|
|
29
|
+
PROVIDER_NAME = "claude"
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass(frozen=True)
|
|
33
|
+
class ModelCounts:
|
|
34
|
+
"""One model's cumulative token counts as ``modelUsage`` reports them.
|
|
35
|
+
|
|
36
|
+
``input_tokens`` is uncached input, disjoint from the cache counts, as in
|
|
37
|
+
the CLI's own report.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
input_tokens: int = 0
|
|
41
|
+
output_tokens: int = 0
|
|
42
|
+
cache_read_tokens: int = 0
|
|
43
|
+
cache_write_tokens: int = 0
|
|
44
|
+
#: The id the CLI says the model canonically is, when it says so.
|
|
45
|
+
canonical: str | None = None
|
|
46
|
+
|
|
47
|
+
def since(self, baseline: ModelCounts | None) -> ModelCounts:
|
|
48
|
+
"""The counts accrued after *baseline*; a count that went down reads as 0."""
|
|
49
|
+
if baseline is None:
|
|
50
|
+
return self
|
|
51
|
+
return ModelCounts(
|
|
52
|
+
input_tokens=max(self.input_tokens - baseline.input_tokens, 0),
|
|
53
|
+
output_tokens=max(self.output_tokens - baseline.output_tokens, 0),
|
|
54
|
+
cache_read_tokens=max(self.cache_read_tokens - baseline.cache_read_tokens, 0),
|
|
55
|
+
cache_write_tokens=max(self.cache_write_tokens - baseline.cache_write_tokens, 0),
|
|
56
|
+
canonical=self.canonical,
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
@property
|
|
60
|
+
def empty(self) -> bool:
|
|
61
|
+
"""Whether no token was counted."""
|
|
62
|
+
return not (
|
|
63
|
+
self.input_tokens
|
|
64
|
+
or self.output_tokens
|
|
65
|
+
or self.cache_read_tokens
|
|
66
|
+
or self.cache_write_tokens
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@dataclass(frozen=True)
|
|
71
|
+
class TurnCost:
|
|
72
|
+
"""A turn's cost and where it came from; ``usd`` is ``None`` for an unknown model."""
|
|
73
|
+
|
|
74
|
+
usd: float | None
|
|
75
|
+
source: CostSource
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def parse_model_usage(raw: object) -> dict[str, ModelCounts] | None:
|
|
79
|
+
"""Read a ``result`` frame's ``modelUsage``; ``None`` when it is absent or malformed."""
|
|
80
|
+
if not isinstance(raw, dict):
|
|
81
|
+
return None
|
|
82
|
+
parsed: dict[str, ModelCounts] = {}
|
|
83
|
+
for model, counts in cast("dict[object, object]", raw).items():
|
|
84
|
+
if not isinstance(model, str) or not isinstance(counts, dict):
|
|
85
|
+
return None
|
|
86
|
+
fields = cast("Mapping[str, Any]", counts)
|
|
87
|
+
canonical = fields.get("canonicalModel")
|
|
88
|
+
parsed[model] = ModelCounts(
|
|
89
|
+
input_tokens=_count(fields, "inputTokens"),
|
|
90
|
+
output_tokens=_count(fields, "outputTokens"),
|
|
91
|
+
cache_read_tokens=_count(fields, "cacheReadInputTokens"),
|
|
92
|
+
cache_write_tokens=_count(fields, "cacheCreationInputTokens"),
|
|
93
|
+
canonical=canonical if isinstance(canonical, str) else None,
|
|
94
|
+
)
|
|
95
|
+
return parsed
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def priced_turn(
|
|
99
|
+
model_usage: Mapping[str, ModelCounts],
|
|
100
|
+
baseline: Mapping[str, ModelCounts],
|
|
101
|
+
*,
|
|
102
|
+
one_hour_share: float,
|
|
103
|
+
table: PricingTable | None = None,
|
|
104
|
+
) -> TurnCost:
|
|
105
|
+
"""Price one turn's tokens, each model at its own table price.
|
|
106
|
+
|
|
107
|
+
*one_hour_share* is the fraction of cache writes billed at the one-hour
|
|
108
|
+
rate. A model with tokens in the turn and no price makes the whole turn
|
|
109
|
+
``UNKNOWN_MODEL`` with no cost: a partial sum would read as complete.
|
|
110
|
+
"""
|
|
111
|
+
total = 0.0
|
|
112
|
+
for model, counts in model_usage.items():
|
|
113
|
+
delta = counts.since(baseline.get(model))
|
|
114
|
+
if delta.empty:
|
|
115
|
+
continue
|
|
116
|
+
pricing = price_for(PROVIDER_NAME, model, table)
|
|
117
|
+
if pricing is None and delta.canonical is not None:
|
|
118
|
+
pricing = price_for(PROVIDER_NAME, delta.canonical, table)
|
|
119
|
+
if pricing is None:
|
|
120
|
+
return TurnCost(None, CostSource.UNKNOWN_MODEL)
|
|
121
|
+
total += cost_usd(_tokens(delta, one_hour_share), pricing)
|
|
122
|
+
return TurnCost(total, CostSource.PRICE_TABLE)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _tokens(counts: ModelCounts, one_hour_share: float) -> TokenUsage:
|
|
126
|
+
share = min(max(one_hour_share, 0.0), 1.0)
|
|
127
|
+
return normalized_usage(
|
|
128
|
+
input_tokens=counts.input_tokens + counts.cache_read_tokens + counts.cache_write_tokens,
|
|
129
|
+
output_tokens=counts.output_tokens,
|
|
130
|
+
cache_read_input_tokens=counts.cache_read_tokens,
|
|
131
|
+
cache_write_input_tokens=counts.cache_write_tokens,
|
|
132
|
+
cache_write_1h_input_tokens=round(counts.cache_write_tokens * share),
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def _count(counts: Mapping[str, Any], key: str) -> int:
|
|
137
|
+
value = counts.get(key)
|
|
138
|
+
if isinstance(value, bool) or not isinstance(value, (int, float)):
|
|
139
|
+
return 0
|
|
140
|
+
return max(int(value), 0)
|
|
@@ -10,6 +10,7 @@ import json
|
|
|
10
10
|
from dataclasses import dataclass
|
|
11
11
|
from typing import TYPE_CHECKING, Any, cast
|
|
12
12
|
|
|
13
|
+
from .cost import ModelCounts, parse_model_usage
|
|
13
14
|
from .rate_limits import RateLimitFrame, parse_rate_limit
|
|
14
15
|
|
|
15
16
|
if TYPE_CHECKING:
|
|
@@ -86,6 +87,8 @@ class ResultFrame:
|
|
|
86
87
|
num_turns: int | None = None
|
|
87
88
|
usage: Mapping[str, Any] | None = None
|
|
88
89
|
total_cost_usd: float | None = None
|
|
90
|
+
#: Per-model cumulative token counts (the CLI's ``modelUsage``); ``None`` when absent.
|
|
91
|
+
model_usage: Mapping[str, ModelCounts] | None = None
|
|
89
92
|
duration_ms: int | None = None
|
|
90
93
|
is_error: bool = False
|
|
91
94
|
subtype: str | None = None
|
|
@@ -126,6 +129,7 @@ def parse_frame(data: Mapping[str, Any]) -> ClaudeFrame | None:
|
|
|
126
129
|
num_turns=_int_or_none(data.get("num_turns")),
|
|
127
130
|
usage=_mapping_or_none(data.get("usage")),
|
|
128
131
|
total_cost_usd=_float_or_none(data.get("total_cost_usd")),
|
|
132
|
+
model_usage=parse_model_usage(data.get("modelUsage")),
|
|
129
133
|
duration_ms=_int_or_none(data.get("duration_ms")),
|
|
130
134
|
is_error=bool(data.get("is_error")),
|
|
131
135
|
subtype=_str_or_none(data.get("subtype")),
|
|
@@ -10,7 +10,7 @@ ANTHROPIC_CONTEXT_WINDOWS_URL = (
|
|
|
10
10
|
)
|
|
11
11
|
ANTHROPIC_PRICING_URL = "https://platform.claude.com/docs/en/about-claude/pricing"
|
|
12
12
|
_MODEL_CHECKED = dt.date(2026, 10, 17)
|
|
13
|
-
_PRICE_CHECKED = dt.date(2026,
|
|
13
|
+
_PRICE_CHECKED = dt.date(2026, 10, 19)
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
def _anthropic_model(
|
|
@@ -61,6 +61,9 @@ MODEL_ENTRIES = (
|
|
|
61
61
|
)
|
|
62
62
|
|
|
63
63
|
|
|
64
|
+
_NOTE = "Claude API first-party, global inference; thinking is billed as output."
|
|
65
|
+
|
|
66
|
+
|
|
64
67
|
def _anthropic( # noqa: PLR0913 - one positional per price column
|
|
65
68
|
model: str,
|
|
66
69
|
input_usd: float,
|
|
@@ -68,6 +71,7 @@ def _anthropic( # noqa: PLR0913 - one positional per price column
|
|
|
68
71
|
cache_write_1h_usd: float,
|
|
69
72
|
cache_read_usd: float,
|
|
70
73
|
output_usd: float,
|
|
74
|
+
note: str = "",
|
|
71
75
|
) -> ModelPricing:
|
|
72
76
|
return ModelPricing(
|
|
73
77
|
vendor="anthropic",
|
|
@@ -79,7 +83,7 @@ def _anthropic( # noqa: PLR0913 - one positional per price column
|
|
|
79
83
|
output_usd=output_usd,
|
|
80
84
|
source=ANTHROPIC_PRICING_URL,
|
|
81
85
|
checked=_PRICE_CHECKED,
|
|
82
|
-
note="
|
|
86
|
+
note=f"{_NOTE} {note}".strip(),
|
|
83
87
|
)
|
|
84
88
|
|
|
85
89
|
|
|
@@ -89,7 +93,23 @@ PRICING_ENTRIES = (
|
|
|
89
93
|
_anthropic("claude-opus-5-5", 4.00, 5.00, 8.00, 0.20, 20.00),
|
|
90
94
|
_anthropic("claude-opus-5", 5.00, 6.25, 10.00, 0.50, 25.00),
|
|
91
95
|
_anthropic("claude-opus-4-8", 5.00, 6.25, 10.00, 0.50, 25.00),
|
|
96
|
+
_anthropic("claude-opus-4-7", 5.00, 6.25, 10.00, 0.50, 25.00),
|
|
97
|
+
_anthropic("claude-opus-4-6", 5.00, 6.25, 10.00, 0.50, 25.00),
|
|
98
|
+
_anthropic("claude-sonnet-5-5", 2.00, 2.50, 4.00, 0.10, 10.00),
|
|
92
99
|
_anthropic("claude-sonnet-5", 2.00, 2.50, 4.00, 0.20, 10.00),
|
|
93
100
|
_anthropic("claude-sonnet-4-6", 3.00, 3.75, 6.00, 0.30, 15.00),
|
|
101
|
+
_anthropic(
|
|
102
|
+
"claude-haiku-5-5",
|
|
103
|
+
0.10,
|
|
104
|
+
0.125,
|
|
105
|
+
0.20,
|
|
106
|
+
0.01,
|
|
107
|
+
0.50,
|
|
108
|
+
note=(
|
|
109
|
+
"Prices for prompts up to 100,000 tokens; a request whose prompt is over "
|
|
110
|
+
"100,000 tokens pays 0.50 input, 0.625 cache write, 1.00 1h cache write, "
|
|
111
|
+
"0.05 cache read and 2.50 output, which this table leaves out."
|
|
112
|
+
),
|
|
113
|
+
),
|
|
94
114
|
_anthropic("claude-haiku-4-5", 1.00, 1.25, 2.00, 0.10, 5.00),
|
|
95
115
|
)
|
|
@@ -21,8 +21,9 @@ from agentshim.core.events import (
|
|
|
21
21
|
)
|
|
22
22
|
from agentshim.core.provider import ParsedTurn
|
|
23
23
|
from agentshim.core.stream import ToolTracker, parse_json_object
|
|
24
|
-
from agentshim.core.usage import ProviderUsage, TokenUsage, normalized_usage
|
|
24
|
+
from agentshim.core.usage import CostSource, ProviderUsage, TokenUsage, normalized_usage
|
|
25
25
|
|
|
26
|
+
from .cost import ModelCounts, priced_turn
|
|
26
27
|
from .events import (
|
|
27
28
|
AssistantMessage,
|
|
28
29
|
ResultFrame,
|
|
@@ -40,6 +41,7 @@ if TYPE_CHECKING:
|
|
|
40
41
|
|
|
41
42
|
from agentshim.core.errors import FailureKind
|
|
42
43
|
from agentshim.core.events import AgentEvent
|
|
44
|
+
from agentshim.core.pricing import PricingTable
|
|
43
45
|
|
|
44
46
|
PROVIDER_NAME = "claude"
|
|
45
47
|
|
|
@@ -128,6 +130,8 @@ class ClaudeStreamParser:
|
|
|
128
130
|
*,
|
|
129
131
|
expect_structured: bool = False,
|
|
130
132
|
cost_baseline_usd: float = 0.0,
|
|
133
|
+
model_usage_baseline: Mapping[str, ModelCounts] | None = None,
|
|
134
|
+
pricing: PricingTable | None = None,
|
|
131
135
|
) -> None:
|
|
132
136
|
"""Start a parser for one run.
|
|
133
137
|
|
|
@@ -137,9 +141,20 @@ class ClaudeStreamParser:
|
|
|
137
141
|
total its previous turn ended at as *cost_baseline_usd* and this
|
|
138
142
|
parser reports the turn's own cost (``total_cost_usd`` then still
|
|
139
143
|
holds the cumulative figure).
|
|
144
|
+
|
|
145
|
+
The turn's cost is priced from the result's ``modelUsage`` with the
|
|
146
|
+
agentshim price table (*pricing*, or the default one), never from the
|
|
147
|
+
CLI's ``total_cost_usd`` when the models are known. ``modelUsage`` is
|
|
148
|
+
cumulative too: pass the previous turn's ``model_usage_total`` as
|
|
149
|
+
*model_usage_baseline*. Only a result without ``modelUsage`` falls
|
|
150
|
+
back to the CLI's figure.
|
|
140
151
|
"""
|
|
141
152
|
self._cost_baseline = cost_baseline_usd
|
|
153
|
+
self._model_usage_baseline: Mapping[str, ModelCounts] = model_usage_baseline or {}
|
|
154
|
+
self._pricing = pricing
|
|
142
155
|
self.total_cost_usd: float | None = None
|
|
156
|
+
#: The cumulative per-model counts the last result reported.
|
|
157
|
+
self.model_usage_total: Mapping[str, ModelCounts] | None = None
|
|
143
158
|
self._emit = emit
|
|
144
159
|
self._expect_structured = expect_structured
|
|
145
160
|
self._tools = ToolTracker()
|
|
@@ -150,6 +165,7 @@ class ClaudeStreamParser:
|
|
|
150
165
|
self._usage = ProviderUsage(provider=PROVIDER_NAME)
|
|
151
166
|
self._results = 0
|
|
152
167
|
self._cost_usd: float | None = None
|
|
168
|
+
self._cost_source: CostSource | None = None
|
|
153
169
|
self._error: str | None = None
|
|
154
170
|
# What the stream said about a failed API request, for classifying it.
|
|
155
171
|
self._api_error: str | None = None
|
|
@@ -278,11 +294,9 @@ class ClaudeStreamParser:
|
|
|
278
294
|
def _result(self, frame: ResultFrame) -> None:
|
|
279
295
|
self._final_text = frame.text
|
|
280
296
|
self.total_cost_usd = frame.total_cost_usd
|
|
281
|
-
self._cost_usd = (
|
|
282
|
-
None if frame.total_cost_usd is None else frame.total_cost_usd - self._cost_baseline
|
|
283
|
-
)
|
|
284
|
-
self._structured = self._structured_payload(frame)
|
|
285
297
|
tokens = fold_usage(frame.usage, turns=frame.num_turns or 0)
|
|
298
|
+
self._price(frame, tokens)
|
|
299
|
+
self._structured = self._structured_payload(frame)
|
|
286
300
|
if self._results:
|
|
287
301
|
# A steered message can start a follow-on turn inside this run; its
|
|
288
302
|
# ``result`` reports only its own tokens, so the run sums them.
|
|
@@ -293,6 +307,7 @@ class ClaudeStreamParser:
|
|
|
293
307
|
total_cost_usd=self._cost_usd,
|
|
294
308
|
provider=PROVIDER_NAME,
|
|
295
309
|
raw=frame.usage,
|
|
310
|
+
cost_source=self._cost_source,
|
|
296
311
|
)
|
|
297
312
|
self._emit(UsageReport(self._usage, self._cost_usd))
|
|
298
313
|
if frame.is_error:
|
|
@@ -301,6 +316,27 @@ class ClaudeStreamParser:
|
|
|
301
316
|
self._error = self._error_text(frame)
|
|
302
317
|
self._emit(ProviderError(self._error))
|
|
303
318
|
|
|
319
|
+
def _price(self, frame: ResultFrame, tokens: TokenUsage) -> None:
|
|
320
|
+
"""Set the turn's cost from its per-model tokens, or from the CLI's when it names none."""
|
|
321
|
+
if frame.model_usage is not None:
|
|
322
|
+
self.model_usage_total = frame.model_usage
|
|
323
|
+
writes = tokens.cache_write_input_tokens
|
|
324
|
+
share = tokens.cache_write_1h_input_tokens / writes if writes else 0.0
|
|
325
|
+
priced = priced_turn(
|
|
326
|
+
frame.model_usage,
|
|
327
|
+
self._model_usage_baseline,
|
|
328
|
+
one_hour_share=share,
|
|
329
|
+
table=self._pricing,
|
|
330
|
+
)
|
|
331
|
+
self._cost_usd = priced.usd
|
|
332
|
+
self._cost_source = priced.source
|
|
333
|
+
elif frame.total_cost_usd is not None:
|
|
334
|
+
self._cost_usd = frame.total_cost_usd - self._cost_baseline
|
|
335
|
+
self._cost_source = CostSource.PROVIDER_REPORTED
|
|
336
|
+
else:
|
|
337
|
+
self._cost_usd = None
|
|
338
|
+
self._cost_source = None
|
|
339
|
+
|
|
304
340
|
def _structured_payload(self, frame: ResultFrame) -> object | None:
|
|
305
341
|
# Only a turn that asked for a schema gets a payload: an unrequested
|
|
306
342
|
# ``structured_output`` field must never replace the prose answer.
|
|
@@ -10,8 +10,9 @@ in ``tests/fixtures/claude_stream``):
|
|
|
10
10
|
|
|
11
11
|
* ``system/init`` is repeated at the start of every turn, so readiness is the
|
|
12
12
|
reply to the ``initialize`` request, not ``init``.
|
|
13
|
-
* ``total_cost_usd``
|
|
14
|
-
so the cost of a turn is the difference from the
|
|
13
|
+
* ``total_cost_usd`` and ``modelUsage`` are cumulative over the process while
|
|
14
|
+
``usage`` is per turn, so the cost of a turn is the difference from the
|
|
15
|
+
previous result. The cost is priced from ``modelUsage`` (see ``cost.py``).
|
|
15
16
|
* A failed API request arrives as a ``result`` with subtype ``success`` and
|
|
16
17
|
``is_error`` true; a failure is classified on ``is_error`` first.
|
|
17
18
|
* ``--json-schema``, MCP servers, effort and extra arguments are flags of the
|
|
@@ -99,6 +100,7 @@ if TYPE_CHECKING:
|
|
|
99
100
|
from agentshim.execution.executor import CommandExecutor
|
|
100
101
|
from agentshim.execution.process import Process, ProcessOutput
|
|
101
102
|
|
|
103
|
+
from .cost import ModelCounts
|
|
102
104
|
from .user_hooks import ClaudeHook
|
|
103
105
|
|
|
104
106
|
#: How often an idle pull loop looks at the clock again.
|
|
@@ -288,6 +290,7 @@ class _StreamConversation:
|
|
|
288
290
|
self._argv: tuple[str, ...] = ()
|
|
289
291
|
self._config = ProcessConfig()
|
|
290
292
|
self._cost_baseline = 0.0
|
|
293
|
+
self._model_usage_baseline: Mapping[str, ModelCounts] = {}
|
|
291
294
|
self._stderr_tail: deque[str] = _lines()
|
|
292
295
|
self._closed = False
|
|
293
296
|
self._running = False
|
|
@@ -414,6 +417,7 @@ class _StreamConversation:
|
|
|
414
417
|
relay,
|
|
415
418
|
expect_structured=request.output_schema is not None,
|
|
416
419
|
cost_baseline_usd=self._cost_baseline,
|
|
420
|
+
model_usage_baseline=self._model_usage_baseline,
|
|
417
421
|
)
|
|
418
422
|
deadline = None if request.timeout is None else started + request.timeout
|
|
419
423
|
run = _TurnRun(parser, relay, request.timeout, deadline)
|
|
@@ -502,6 +506,7 @@ class _StreamConversation:
|
|
|
502
506
|
self._argv = tuple(argv)
|
|
503
507
|
self._config = config
|
|
504
508
|
self._cost_baseline = 0.0
|
|
509
|
+
self._model_usage_baseline = {}
|
|
505
510
|
if closed:
|
|
506
511
|
self._stop(process) # outside the lock: a stop waits, and interrupts need the lock
|
|
507
512
|
msg = "conversation closed while its process started"
|
|
@@ -863,6 +868,8 @@ class _StreamConversation:
|
|
|
863
868
|
parsed = parser.finish()
|
|
864
869
|
if parser.total_cost_usd is not None:
|
|
865
870
|
self._cost_baseline = parser.total_cost_usd
|
|
871
|
+
if parser.model_usage_total is not None:
|
|
872
|
+
self._model_usage_baseline = parser.model_usage_total
|
|
866
873
|
self._learn_id(parsed.session_id)
|
|
867
874
|
self._report_denials(run, frame)
|
|
868
875
|
if run.expired:
|
|
@@ -51,6 +51,7 @@ from agentshim.core.permissions import ApprovalPolicy
|
|
|
51
51
|
from agentshim.core.schema import dialect_problems
|
|
52
52
|
from agentshim.core.skills import SkillTracker
|
|
53
53
|
from agentshim.core.turn import TurnResult
|
|
54
|
+
from agentshim.core.usage import CostSource
|
|
54
55
|
from agentshim.execution.host import HostCommandExecutor
|
|
55
56
|
from agentshim.execution.process import SpawnRequest
|
|
56
57
|
from agentshim.pricing import cost_usd, price_for
|
|
@@ -927,7 +928,11 @@ class _Conversation:
|
|
|
927
928
|
usage = turn_usage(turn.total, self._baseline)
|
|
928
929
|
pricing = self._pricing
|
|
929
930
|
if usage.increment_known and pricing is not None:
|
|
930
|
-
usage = replace(
|
|
931
|
+
usage = replace(
|
|
932
|
+
usage,
|
|
933
|
+
total_cost_usd=cost_usd(usage.tokens, pricing),
|
|
934
|
+
cost_source=CostSource.PRICE_TABLE,
|
|
935
|
+
)
|
|
931
936
|
if turn.total is not None:
|
|
932
937
|
self._baseline = turn.total
|
|
933
938
|
return usage
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|