tamias 0.1.0a7__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.
@@ -0,0 +1,31 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ .pytest_cache/
4
+ .ruff_cache/
5
+ .venv/
6
+ venv/
7
+ build/
8
+ dist/
9
+ *.egg-info/
10
+ live_check.db
11
+ sbom.json
12
+ licenses.csv
13
+
14
+ # Request logs are local artifacts. `demo/runs/` holds the committed dry-run
15
+ # evidence that DEMO.md and demo/RUNBOOK.md read back, so those specific files
16
+ # are negated below and stay tracked; anything else a run produces is ignored.
17
+ *.db
18
+ *.db-journal
19
+ demo/runs/
20
+ !demo/runs/baseline-*.db
21
+ !demo/runs/baseline-*.json
22
+ !demo/runs/routed-*.db
23
+ !demo/runs/routed-*.json
24
+ demo/runs/*.log
25
+
26
+ # demo/config.env is a machine-local copy of the committed template; the
27
+ # synthetic history is regenerated by demo/make_history.py, so neither is
28
+ # tracked.
29
+ demo/config.env
30
+ demo/history/agent-3weeks.db
31
+ demo/history/agent-3weeks.json
@@ -0,0 +1,256 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.1.0a7] - 2026-10-20
9
+
10
+ ### Changed
11
+
12
+ - **The non-loopback bind rule is documented as what it is: two keys.** A
13
+ wider `serve` bind needs *both* authentication (`--auth-token` /
14
+ `$TAMIAS_AUTH_TOKEN` / `--keys-db`) *and* the explicit
15
+ `--allow-non-loopback` acknowledgement. `SECURITY.md` and `README.md`
16
+ previously said "either", which was wrong; `security.py` also printed "this
17
+ service has no authentication" even when a token or keys were configured
18
+ (council run 20261020-1500). The refusal message is now conditional and names
19
+ only what is actually missing.
20
+ - `dashboard.py` no longer emits a `SyntaxWarning` at import: the CSV
21
+ formula-injection guard's JS regex had an invalid `\-` escape in a non-raw
22
+ string, which becomes a `SyntaxError` in a future Python.
23
+
24
+ ### Added
25
+
26
+ - A real OpenRouter price sheet is bundled inside the package. When neither
27
+ `./prices.toml` nor `~/.tamias/prices.toml` exists, `serve` and `run` price
28
+ against it and print a provenance warning on stderr, so a fresh install
29
+ prices a request above `$0.00` instead of looking like a broken proxy (F-20).
30
+ The sheet is a dated snapshot; run `tamias prices fetch` for current rates.
31
+ - The catch-all passthrough now writes one **count-only** row per request
32
+ (`route="passthrough"`, token counts and cost NULL, reason `passthrough
33
+ route is not metered`), and `tamias report` prints
34
+ `passthrough requests (count-only, never priced): N`. Off-route traffic is
35
+ still never priced or budget-gated, but it can no longer leave the ledger
36
+ invisible (F-12). The request log gains a `route` column
37
+ (`chat`/`messages`/`responses`/`passthrough`); existing logs get it via the
38
+ usual append-only migration.
39
+ - `tests/test_compile_clean.py` compiles every source module with
40
+ `SyntaxWarning` promoted to error, so the invalid-escape class cannot recur.
41
+ - Bind-matrix tests pin the two-key rule (authentication and acknowledgement
42
+ are independent) and assert the refusal message never claims "no
43
+ authentication" when authentication is configured.
44
+
45
+ ### Changed
46
+
47
+ - The saving arithmetic is now **cache-aware and can be negative** (F-1). The
48
+ dashboard's `saved` and both `tamias report` saving lines price the stay model
49
+ with a warm cache and the switch model on the row's real counts, so a switch
50
+ that re-ingests more context than it saves reports a loss instead of being
51
+ clamped to `$0.00`. New primitive `pricing.assume_cache_warm`. The old
52
+ non-negative same-tokens difference is kept as an explicit upper bound
53
+ (`saved_upper_bound` on a row, `saving_upper_total` in the summary). The
54
+ estimate label is now
55
+ `"[estimate; cache-aware, can be negative; not measured]"`, and the dashboard
56
+ Saving card says how many priced rows have no cache-aware saving. A losing
57
+ switch is asserted by
58
+ `tests/test_cli.py::test_report_shows_a_negative_saving_when_a_switch_would_have_lost`
59
+ and
60
+ `tests/test_dashboard.py::test_a_cache_miss_replay_can_show_a_negative_saving`.
61
+
62
+ ## [0.1.0a6] - 2026-10-15
63
+
64
+ Supersedes the mis-versioned `0.1.0a5` tag, which was cut from `0.1.0a4` content and never uploaded to TestPyPI; that tag was deleted on 2026-10-20 (its only content, `tamias-sql-db.html`, was removed in `a68f7e9`).
65
+
66
+ ### Added
67
+
68
+ - A capability registry for models (`src/tamias/registry.py`): a strict TOML
69
+ catalogue the router may never choose outside, carrying capabilities only.
70
+ - A real reasoning-effort axis, decoupled from model routing: `--effort-mode`
71
+ `off` | `meter` | `apply`, applied only for a model the registry vouches for.
72
+ - Company keys (`tamias admin key`, `serve --keys-db`): per-caller identity with
73
+ a model allowlist enforced before forwarding.
74
+ - Per-key limits enforced by the gateway itself: a USD budget over a period,
75
+ per-model sub-caps, and per-minute request and token rates. Money uses
76
+ reserve-then-settle; refusals are 402 for money and 429 for rate. See
77
+ `docs/BUDGET.md`.
78
+ - Signed project manifests (`tamias admin manifest keygen|sign|verify`,
79
+ `serve --manifest`): a company policy TOML becomes a JSON document with an
80
+ Ed25519 signature, verified at startup and served at `GET /policy`. A missing,
81
+ expired, altered or mis-pinned manifest is refused; the private key never
82
+ reaches the gateway. Needs the optional `cryptography` extra:
83
+ `pip install 'tamias[manifest]'`.
84
+
85
+ ### Changed
86
+
87
+ - Active request rewriting (`--router-mode active`) is available, opt-in and
88
+ fail-closed: the process refuses to start unless the target is named, listed
89
+ in `--registry`, tool-capable and priced, and a switch the caller's key does
90
+ not allow is suppressed rather than refused. `tamias report` prints
91
+ `requests routed to the cheap model` and names the switches it declined to
92
+ apply. See `docs/ROUTING.md`.
93
+
94
+ ## [0.1.0a4] - 2026-10-10
95
+
96
+ ### Added
97
+
98
+ - Persistent per-database session salts and safe migration of legacy session ids.
99
+ - Shared SSE parser fixtures covering CRLF, byte boundaries, comments, and
100
+ multi-line data events.
101
+ - CI checks for tests, Ruff, package builds, metadata, and wheel contents.
102
+
103
+ ### Changed
104
+
105
+ - Public alpha routing accepts only `shadow` and `off`; active request rewriting
106
+ is disabled.
107
+ - Ambiguous legacy `cost_usd` values remain UNKNOWN and are excluded from
108
+ computed-cost and billed-cost reconciliation.
109
+ - Dashboard defaults to port 8010 and forwards `--compare-db`.
110
+
111
+ ### Security
112
+
113
+ - Failure-ledger fields accept only the closed coded vocabulary; raw exception
114
+ text is discarded at the storage boundary.
115
+
116
+ ## [0.1.0a3] - 2026-10-07
117
+
118
+ ### Added
119
+
120
+ - Optional reasoning-effort routing: `tamias serve --effort-policy easy-low`
121
+ adds high effort to planning and tool-error turns and low effort to easy tool
122
+ turns when active routing is selected. `--effort-style` chooses OpenRouter's
123
+ `reasoning.effort` or OpenAI's `reasoning_effort` request field.
124
+ - The request log now records requested effort, forwarded effort, and the
125
+ router's target effort. Existing logs migrate those nullable columns in
126
+ place, and the dashboard displays the request and forwarded values.
127
+
128
+ ### Known limitations
129
+
130
+ - Effort behavior is verified with fixtures only; whether a provider honours a
131
+ requested effort remains unverified.
132
+
133
+ [0.1.0a3]: https://github.com/kr1shnac/tamias/releases/tag/v0.1.0a3
134
+
135
+ ## [0.1.0a2] - 2026-10-06
136
+
137
+ Pre-alpha, prepared for TestPyPI. Merges four branches that had been developed
138
+ apart: `feat/cli`, `feat/pricing`, `feat/router` and `ui/restyle`.
139
+
140
+ ### Added
141
+
142
+ - `tamias prices fetch --out FILE`: pull an OpenRouter model catalogue and render
143
+ it as a TOML sheet that carries its own provenance, with free models marked
144
+ `0` rather than omitted.
145
+ - `tamias run -- COMMAND`: run one agent command through a short-lived proxy.
146
+ `--prices` falls back to `./prices.toml` then `~/.tamias/prices.toml`, and
147
+ `--upstream` to `$TAMIAS_UPSTREAM` then `https://openrouter.ai/api`; a missing
148
+ sheet stops with exit code 2 and the command that would fix it.
149
+ - `tamias doctor`: offline readiness checks — venv, price sheet, ports, log
150
+ writability — with `--online` for one short timed upstream probe. It warns for
151
+ a missing `OPENROUTER_API_KEY` without ever printing the value.
152
+ - `tamias dashboard --db … --prices …`: local dashboard over a request log, with
153
+ optional `--compare-db`, serving `/api/nav`, `/api/summary` and `/api/rows`.
154
+ - `tamias agent-config`: print a ready-to-paste agent configuration snippet.
155
+ - Project scoping: `--project` on `serve`, `run` and `agent-config`, a `project`
156
+ column in the request log, and project/session grouping in the dashboard.
157
+ - Router configuration: `--router-profile generic|legacy`, `--router-config FILE`
158
+ reading a flat TOML file with strict unknown-key, type and range validation,
159
+ `--cheap-model` / `--strong-model` / `--min-gap`, generic classification of
160
+ read/edit/shell tool names, shell error markers, and a big-output cap so long
161
+ shell results are never called easy.
162
+ - Billed-cost accounting: `provider_cost_usd`, `generation_id`, and price
163
+ provenance columns in the log; `tamias report` qualifies every dollar figure
164
+ with its sheet, its simulated flag, or `UNKNOWN`.
165
+ - `demo/`: task prompt, two-arm runner, model picker and RUNBOOK, plus three
166
+ weeks of synthetic agent history for an offline demo.
167
+ - `docs/ARCHITECTURE.md` (the frozen v1.2 spec, previously untracked),
168
+ `docs/ROUTING.md`, `docs/PRICING.md`, and `DEMO.md`.
169
+
170
+ ### Changed
171
+
172
+ - `tamias report` prints `estimated saving` and `realised saving` separately; the
173
+ second covers only rows the proxy actually rewrote.
174
+ - `load_router_config` accepts a `profile` argument; the profile stays rejected
175
+ as a TOML key, so a config file cannot pick the rules used to judge its own
176
+ results.
177
+ - The dashboard was restyled.
178
+
179
+ ### Fixed
180
+
181
+ - `realised saving` now always prints its "not priced" caveat. It was suppressed
182
+ whenever unpriced rows were not a strict minority, so a log that priced 1 of 10
183
+ rewritten rows reported a dollar figure over 1 row with nothing to say the
184
+ other 9 went unmeasured.
185
+ - Anthropic cache costs stay `UNKNOWN` rather than being priced without a rate.
186
+ - OpenAI Responses usage is metered, and only once it has completed.
187
+ - Upstream proxy requests are bounded, and upstream rate-limit headers pass
188
+ through.
189
+ - The `SIMULATED PRICES, NOT REAL SAVINGS` label can no longer be dropped from a
190
+ saving line when the price bases differ.
191
+
192
+ ### Known limitations
193
+
194
+ - Tested on fixtures only. Live provider behaviour and real Codex and Claude
195
+ Code traffic are unverified.
196
+ - The two demo arms wrote different row counts, so their cost totals are not
197
+ comparable; the comparable evidence is `decision_action` and `model_used` vs
198
+ `model_requested`.
199
+ - Reasoning-effort switching is not in this release.
200
+ - No budgets, caps, alerts or spend controls.
201
+
202
+ [0.1.0a2]: https://github.com/kr1shnac/tamias/releases/tag/v0.1.0a2
203
+
204
+ ## [0.1.0a1] - 2026-10-04
205
+
206
+ First pre-alpha. Not published to PyPI. Expect breakage.
207
+
208
+ ### Added
209
+
210
+ - `tamias serve`: local HTTP proxy in front of an OpenAI-compatible
211
+ `/v1/chat/completions` upstream. Request bodies and headers are forwarded
212
+ byte for byte; streaming responses are relayed chunk by chunk; upstream status
213
+ codes pass through.
214
+ - `tamias report`: read-only summary of a request log — request count, cost
215
+ total, how many requests shadow mode would have switched, and a labelled
216
+ saving estimate.
217
+ - SQLite request log, one row per request: timestamp, session id, model
218
+ requested, model used, token counts, cost, price sheet date, latency, status,
219
+ and the routing decision. No prompt or response text is stored, and the schema
220
+ has no column that could hold any.
221
+ - TOML price sheets: USD per 1,000,000 tokens, with a required top-level `date`.
222
+ A rate of `0` means the model really is free; a rate omitted means UNKNOWN.
223
+ - Cost arithmetic that records the formula used, and never substitutes zero for
224
+ an unknown token count or rate. Unknown propagates to an unknown cost.
225
+ - Rule-based shadow router: records the routing decision it *would* make —
226
+ `STAY` or `SWITCH` to a cheap model for mechanical tool work — and forwards
227
+ the request unchanged.
228
+ - `--router-mode` with `shadow` (default), `active` and `off`.
229
+ - Anthropic Messages API adapter (`src/tamias/anthropic_adapter.py`) covering
230
+ `x-api-key` passthrough, Anthropic's split `usage` fields, and usage spread
231
+ across `message_start` / `message_delta` stream events. **Experimental.**
232
+ - `docs/OPENCODE.md`: pointing OpenCode at the proxy, including the verified
233
+ `opencode.json` provider shape and which Zen models are reachable through
234
+ `/v1/chat/completions`.
235
+
236
+ ### Known limitations
237
+
238
+ - Cost is UNKNOWN (never guessed as zero) when a field needed to price the
239
+ request is missing; OpenAI-style APIs report no cache writes, so give those
240
+ models `cache_write = 0` in the price sheet and their requests are priced.
241
+ - No savings have been measured. The `estimated saving` line in `tamias report`
242
+ is arithmetic over logged token counts, ignores cache-rebuild cost, and is not
243
+ a measurement.
244
+ - `--router-mode active` is implemented and unit-tested but has never been run
245
+ against a real provider. Use `shadow`.
246
+ - Only `/v1/chat/completions` and `/v1/messages` produce log rows.
247
+ `/v1/responses` and all other paths are relayed by a catch-all route without
248
+ being logged.
249
+ - The Anthropic adapter has never been run against the real Anthropic API; it is
250
+ wired into `tamias serve` and unit-tested against a mock only.
251
+ - Sessions are grouped by an `x-tamias-session` header. Clients that do not send
252
+ one — OpenCode, as far as the documentation shows — land in a single
253
+ `default` session.
254
+ - No budgets, caps, alerts or spend controls.
255
+
256
+ [0.1.0a1]: https://github.com/kr1shnac/tamias/releases/tag/v0.1.0a1
tamias-0.1.0a7/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 tamias contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.