interlatch 0.22.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- chronicle/__init__.py +17 -0
- chronicle/__main__.py +3 -0
- chronicle/access.py +93 -0
- chronicle/agents.py +23 -0
- chronicle/analyze.py +498 -0
- chronicle/antigravity_parser.py +311 -0
- chronicle/artifacts.py +621 -0
- chronicle/bob_parser.py +259 -0
- chronicle/cases.py +75 -0
- chronicle/chat_import.py +325 -0
- chronicle/chatgpt_export.py +127 -0
- chronicle/claude_export.py +129 -0
- chronicle/cli.py +2980 -0
- chronicle/codex_cloud.py +138 -0
- chronicle/codex_parser.py +753 -0
- chronicle/config.py +627 -0
- chronicle/connectors.py +666 -0
- chronicle/container.py +202 -0
- chronicle/copilot_parser.py +494 -0
- chronicle/db.py +598 -0
- chronicle/desktop.py +543 -0
- chronicle/diagram.py +488 -0
- chronicle/digest.py +194 -0
- chronicle/export_md.py +210 -0
- chronicle/friction.py +841 -0
- chronicle/glossary.py +522 -0
- chronicle/groups.py +181 -0
- chronicle/hooks.py +169 -0
- chronicle/hub.py +2291 -0
- chronicle/i18n.py +37 -0
- chronicle/i18n_ja.py +421 -0
- chronicle/ingest.py +1190 -0
- chronicle/install.py +590 -0
- chronicle/instructions.py +699 -0
- chronicle/ladder.py +244 -0
- chronicle/learning.py +170 -0
- chronicle/llm.py +570 -0
- chronicle/mcp_server.py +403 -0
- chronicle/menubar.py +771 -0
- chronicle/migrate.py +580 -0
- chronicle/mirror.py +453 -0
- chronicle/news.py +110 -0
- chronicle/notify.py +92 -0
- chronicle/parser.py +908 -0
- chronicle/people.py +536 -0
- chronicle/pg.py +122 -0
- chronicle/pricing.py +168 -0
- chronicle/providers.py +591 -0
- chronicle/redact.py +50 -0
- chronicle/reviews.py +251 -0
- chronicle/screen.py +308 -0
- chronicle/search.py +238 -0
- chronicle/server.py +3009 -0
- chronicle/session_export.py +178 -0
- chronicle/statusline.py +258 -0
- chronicle/suggest.py +643 -0
- chronicle/synthesize.py +365 -0
- chronicle/systems.py +1265 -0
- chronicle/tailnet.py +97 -0
- chronicle/team_store.py +450 -0
- chronicle/update.py +404 -0
- chronicle/util.py +217 -0
- chronicle/views.py +249 -0
- chronicle/web/PLEX-OFL.txt +93 -0
- chronicle/web/app.css +2350 -0
- chronicle/web/app.js +7587 -0
- chronicle/web/art-book.webp +0 -0
- chronicle/web/art-camera.webp +0 -0
- chronicle/web/art-chip.webp +0 -0
- chronicle/web/art-clock.webp +0 -0
- chronicle/web/art-cloud.webp +0 -0
- chronicle/web/art-coins.webp +0 -0
- chronicle/web/art-container.webp +0 -0
- chronicle/web/art-dashboard.webp +0 -0
- chronicle/web/art-dex.webp +0 -0
- chronicle/web/art-document.webp +0 -0
- chronicle/web/art-flag.webp +0 -0
- chronicle/web/art-folder.webp +0 -0
- chronicle/web/art-gateway.webp +0 -0
- chronicle/web/art-gears.webp +0 -0
- chronicle/web/art-glitch.webp +0 -0
- chronicle/web/art-globe.webp +0 -0
- chronicle/web/art-laptop.webp +0 -0
- chronicle/web/art-log.webp +0 -0
- chronicle/web/art-lumi.webp +0 -0
- chronicle/web/art-magnifier.webp +0 -0
- chronicle/web/art-pip.webp +0 -0
- chronicle/web/art-plug.webp +0 -0
- chronicle/web/art-rack.webp +0 -0
- chronicle/web/art-tink.webp +0 -0
- chronicle/web/boot.js +25 -0
- chronicle/web/favicon.png +0 -0
- chronicle/web/i18n.js +50 -0
- chronicle/web/icon-180.png +0 -0
- chronicle/web/icon-192.png +0 -0
- chronicle/web/icon-512.png +0 -0
- chronicle/web/icon.png +0 -0
- chronicle/web/index.html +92 -0
- chronicle/web/ja.js +1880 -0
- chronicle/web/learning.js +438 -0
- chronicle/web/manifest.webmanifest +15 -0
- chronicle/web/panel.css +175 -0
- chronicle/web/panel.html +14 -0
- chronicle/web/panel.js +241 -0
- chronicle/web/plex-mono-400.woff2 +0 -0
- chronicle/web/plex-mono-500.woff2 +0 -0
- chronicle/web/plex-mono-600.woff2 +0 -0
- chronicle/web/plex-sans-400.woff2 +0 -0
- chronicle/web/plex-sans-500.woff2 +0 -0
- chronicle/web/plex-sans-600.woff2 +0 -0
- chronicle/web/plex-sans-700.woff2 +0 -0
- chronicle/web/rough.js +2 -0
- chronicle/worker.py +390 -0
- interlatch-0.22.0.dist-info/METADATA +221 -0
- interlatch-0.22.0.dist-info/RECORD +118 -0
- interlatch-0.22.0.dist-info/WHEEL +4 -0
- interlatch-0.22.0.dist-info/entry_points.txt +3 -0
- interlatch-0.22.0.dist-info/licenses/LICENSE +21 -0
chronicle/pricing.py
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
"""API-equivalent cost estimation from transcript usage blocks.
|
|
2
|
+
|
|
3
|
+
Prices are USD per million tokens (first-party list prices). Cache writes cost 1.25x input for
|
|
4
|
+
the 5-minute TTL and 2x for the 1-hour TTL; cache reads are 0.1x input except where noted.
|
|
5
|
+
Subscription users do not pay these amounts; they are a consistent yardstick for usage.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import re
|
|
11
|
+
|
|
12
|
+
# model id -> (input, output, cache_read)
|
|
13
|
+
PRICES: dict[str, tuple[float, float, float]] = {
|
|
14
|
+
"claude-fable-5-1": (10.0, 50.0, 0.25),
|
|
15
|
+
"claude-mythos-5-1": (10.0, 50.0, 0.25),
|
|
16
|
+
"claude-fable-5": (10.0, 50.0, 1.0),
|
|
17
|
+
"claude-mythos-5": (10.0, 50.0, 1.0),
|
|
18
|
+
"claude-mythos-preview": (10.0, 50.0, 1.0),
|
|
19
|
+
"claude-opus-5-5": (4.0, 20.0, 0.20),
|
|
20
|
+
"claude-opus-5": (5.0, 25.0, 0.50),
|
|
21
|
+
"claude-opus-4-8": (5.0, 25.0, 0.50),
|
|
22
|
+
"claude-opus-4-7": (5.0, 25.0, 0.50),
|
|
23
|
+
"claude-opus-4-6": (5.0, 25.0, 0.50),
|
|
24
|
+
"claude-opus-4-5": (5.0, 25.0, 0.50),
|
|
25
|
+
"claude-opus-4-1": (15.0, 75.0, 1.50),
|
|
26
|
+
"claude-opus-4-0": (15.0, 75.0, 1.50),
|
|
27
|
+
"claude-opus-4": (15.0, 75.0, 1.50),
|
|
28
|
+
"claude-sonnet-5": (2.0, 10.0, 0.20),
|
|
29
|
+
"claude-sonnet-4-6": (3.0, 15.0, 0.30),
|
|
30
|
+
"claude-sonnet-4-5": (3.0, 15.0, 0.30),
|
|
31
|
+
"claude-sonnet-4-0": (3.0, 15.0, 0.30),
|
|
32
|
+
"claude-sonnet-4": (3.0, 15.0, 0.30),
|
|
33
|
+
"claude-3-7-sonnet": (3.0, 15.0, 0.30),
|
|
34
|
+
"claude-3-5-sonnet": (3.0, 15.0, 0.30),
|
|
35
|
+
"claude-haiku-4-5": (1.0, 5.0, 0.10),
|
|
36
|
+
"claude-3-5-haiku": (0.80, 4.0, 0.08),
|
|
37
|
+
"claude-3-haiku": (0.25, 1.25, 0.03),
|
|
38
|
+
"claude-3-opus": (15.0, 75.0, 1.50),
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
# Fallback by family when an unknown (newer) model shows up.
|
|
42
|
+
FAMILY_FALLBACK = {
|
|
43
|
+
"fable": "claude-fable-5-1",
|
|
44
|
+
"mythos": "claude-mythos-5-1",
|
|
45
|
+
"opus": "claude-opus-5",
|
|
46
|
+
"sonnet": "claude-sonnet-5",
|
|
47
|
+
"haiku": "claude-haiku-4-5",
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
WEB_SEARCH_PER_REQUEST = 10.0 / 1000 # $10 per 1,000 searches
|
|
51
|
+
FAST_MODE_MULTIPLIER = 2.0
|
|
52
|
+
|
|
53
|
+
_DATE_SUFFIX = re.compile(r"-\d{8}$")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def normalize_model(model: str | None) -> str | None:
|
|
57
|
+
"""'claude-opus-5-5[1m]' -> 'claude-opus-5-5'; 'claude-haiku-4-5-20251001' -> 'claude-haiku-4-5'."""
|
|
58
|
+
if not model or model.startswith("<"):
|
|
59
|
+
return None
|
|
60
|
+
m = model.strip().lower()
|
|
61
|
+
m = re.sub(r"\[.*?\]$", "", m)
|
|
62
|
+
m = m.split("@")[0]
|
|
63
|
+
m = re.sub(r"^(us|eu|apac|global)\.anthropic\.", "", m)
|
|
64
|
+
m = re.sub(r"^anthropic\.", "", m)
|
|
65
|
+
m = re.sub(r"-v\d+(:\d+)?$", "", m)
|
|
66
|
+
m = _DATE_SUFFIX.sub("", m)
|
|
67
|
+
return m
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def price_for(model: str | None) -> tuple[tuple[float, float, float] | None, bool]:
|
|
71
|
+
"""Return ((input, output, cache_read), exact_match)."""
|
|
72
|
+
m = normalize_model(model)
|
|
73
|
+
if not m:
|
|
74
|
+
return None, True
|
|
75
|
+
if m in PRICES:
|
|
76
|
+
return PRICES[m], True
|
|
77
|
+
# longest known prefix (e.g. a dated or suffixed variant)
|
|
78
|
+
for known in sorted(PRICES, key=len, reverse=True):
|
|
79
|
+
if m.startswith(known):
|
|
80
|
+
return PRICES[known], True
|
|
81
|
+
for family, fallback in FAMILY_FALLBACK.items():
|
|
82
|
+
if family in m:
|
|
83
|
+
return PRICES[fallback], False
|
|
84
|
+
return None, False
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def usage_cost(model: str | None, usage: dict | None, speed: str | None = None) -> float:
|
|
88
|
+
"""Estimate the USD cost of one API response from its usage block."""
|
|
89
|
+
if not usage:
|
|
90
|
+
return 0.0
|
|
91
|
+
prices, _ = price_for(model)
|
|
92
|
+
if not prices:
|
|
93
|
+
return 0.0
|
|
94
|
+
p_in, p_out, p_cache_read = prices
|
|
95
|
+
input_tokens = usage.get("input_tokens") or 0
|
|
96
|
+
output_tokens = usage.get("output_tokens") or 0
|
|
97
|
+
cache_read = usage.get("cache_read_input_tokens") or 0
|
|
98
|
+
cache_creation = usage.get("cache_creation_input_tokens") or 0
|
|
99
|
+
breakdown = usage.get("cache_creation") or {}
|
|
100
|
+
write_1h = breakdown.get("ephemeral_1h_input_tokens") or 0
|
|
101
|
+
write_5m = breakdown.get("ephemeral_5m_input_tokens")
|
|
102
|
+
if write_5m is None:
|
|
103
|
+
write_5m = max(cache_creation - write_1h, 0)
|
|
104
|
+
cost = (
|
|
105
|
+
input_tokens * p_in
|
|
106
|
+
+ write_5m * p_in * 1.25
|
|
107
|
+
+ write_1h * p_in * 2.0
|
|
108
|
+
+ cache_read * p_cache_read
|
|
109
|
+
+ output_tokens * p_out
|
|
110
|
+
) / 1_000_000
|
|
111
|
+
if (speed or usage.get("speed")) == "fast":
|
|
112
|
+
cost *= FAST_MODE_MULTIPLIER
|
|
113
|
+
server = usage.get("server_tool_use") or {}
|
|
114
|
+
cost += (server.get("web_search_requests") or 0) * WEB_SEARCH_PER_REQUEST
|
|
115
|
+
return cost
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
# ------------------------------------------------------------------ OpenAI (Codex sessions)
|
|
119
|
+
# model -> (input, output, cached input) USD per million tokens; reasoning tokens bill as output.
|
|
120
|
+
OPENAI_PRICES: dict[str, tuple[float, float, float]] = {
|
|
121
|
+
"gpt-5": (1.25, 10.0, 0.125),
|
|
122
|
+
"gpt-5-codex": (1.25, 10.0, 0.125),
|
|
123
|
+
"gpt-5-mini": (0.25, 2.0, 0.025),
|
|
124
|
+
"gpt-5-nano": (0.05, 0.40, 0.005),
|
|
125
|
+
"gpt-4.1": (2.0, 8.0, 0.5),
|
|
126
|
+
"gpt-4.1-mini": (0.4, 1.6, 0.1),
|
|
127
|
+
"gpt-4.1-nano": (0.1, 0.4, 0.025),
|
|
128
|
+
"gpt-4o": (2.5, 10.0, 1.25),
|
|
129
|
+
"gpt-4o-mini": (0.15, 0.6, 0.075),
|
|
130
|
+
"o3": (2.0, 8.0, 0.5),
|
|
131
|
+
"o3-pro": (20.0, 80.0, 20.0),
|
|
132
|
+
"o4-mini": (1.1, 4.4, 0.275),
|
|
133
|
+
"codex-mini-latest": (1.5, 6.0, 0.375),
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def is_openai_model(model: str | None) -> bool:
|
|
138
|
+
m = normalize_model(model) or ""
|
|
139
|
+
return m.startswith(("gpt-", "o1", "o3", "o4", "codex-"))
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def openai_price_for(model: str | None) -> tuple[tuple[float, float, float] | None, bool]:
|
|
143
|
+
"""((input, output, cached), exact). Newer GPT-5.x/6 variants fall back to GPT-5 rates as an estimate."""
|
|
144
|
+
m = normalize_model(model)
|
|
145
|
+
if not m:
|
|
146
|
+
return None, True
|
|
147
|
+
if m in OPENAI_PRICES:
|
|
148
|
+
return OPENAI_PRICES[m], True
|
|
149
|
+
for known in sorted(OPENAI_PRICES, key=len, reverse=True):
|
|
150
|
+
if m.startswith(known + "-") or m.startswith(known + "."):
|
|
151
|
+
return OPENAI_PRICES[known], True
|
|
152
|
+
if "nano" in m:
|
|
153
|
+
return OPENAI_PRICES["gpt-5-nano"], False
|
|
154
|
+
if "mini" in m:
|
|
155
|
+
return OPENAI_PRICES["gpt-5-mini"], False
|
|
156
|
+
if m.startswith(("gpt-5", "gpt-6", "gpt-", "codex")):
|
|
157
|
+
return OPENAI_PRICES["gpt-5"], False
|
|
158
|
+
if m.startswith(("o1", "o3", "o4")):
|
|
159
|
+
return OPENAI_PRICES["o3"], False
|
|
160
|
+
return None, False
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def openai_cost(model: str | None, uncached_input: int, cached_input: int, output: int) -> float:
|
|
164
|
+
prices, _ = openai_price_for(model)
|
|
165
|
+
if not prices:
|
|
166
|
+
return 0.0
|
|
167
|
+
p_in, p_out, p_cached = prices
|
|
168
|
+
return (uncached_input * p_in + cached_input * p_cached + output * p_out) / 1_000_000
|