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.
- tamias-0.1.0a7/.gitignore +31 -0
- tamias-0.1.0a7/CHANGELOG.md +256 -0
- tamias-0.1.0a7/LICENSE +21 -0
- tamias-0.1.0a7/PKG-INFO +501 -0
- tamias-0.1.0a7/README.md +465 -0
- tamias-0.1.0a7/pyproject.toml +88 -0
- tamias-0.1.0a7/src/tamias/__init__.py +2 -0
- tamias-0.1.0a7/src/tamias/anthropic_adapter.py +650 -0
- tamias-0.1.0a7/src/tamias/budget.py +603 -0
- tamias-0.1.0a7/src/tamias/cli.py +2031 -0
- tamias-0.1.0a7/src/tamias/config.py +172 -0
- tamias-0.1.0a7/src/tamias/dashboard.py +1634 -0
- tamias-0.1.0a7/src/tamias/data/prices.default.toml +2310 -0
- tamias-0.1.0a7/src/tamias/effort.py +42 -0
- tamias-0.1.0a7/src/tamias/keys.py +400 -0
- tamias-0.1.0a7/src/tamias/manifest.py +389 -0
- tamias-0.1.0a7/src/tamias/pricing.py +482 -0
- tamias-0.1.0a7/src/tamias/pricing_fetch.py +233 -0
- tamias-0.1.0a7/src/tamias/proxy.py +1390 -0
- tamias-0.1.0a7/src/tamias/registry.py +271 -0
- tamias-0.1.0a7/src/tamias/responses_adapter.py +339 -0
- tamias-0.1.0a7/src/tamias/router.py +329 -0
- tamias-0.1.0a7/src/tamias/router_config.py +110 -0
- tamias-0.1.0a7/src/tamias/security.py +109 -0
- tamias-0.1.0a7/src/tamias/session.py +215 -0
- tamias-0.1.0a7/src/tamias/sse.py +111 -0
- tamias-0.1.0a7/src/tamias/store.py +450 -0
- tamias-0.1.0a7/src/tamias/types.py +85 -0
- tamias-0.1.0a7/src/tamias/upstream.py +77 -0
|
@@ -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.
|