Add LLM-WP-0007: Kimi K3 default and EUR spend reporting
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s

Propose workplan for moonshotai/kimi-k3 as OpenRouter basemodel/default,
CLI token+EUR cost reporting, and Mon-based weekly spend views.
This commit is contained in:
tegwick 2026-08-03 21:52:08 +02:00
parent ef2231b2cc
commit 7d4a8a51c2

View file

@ -0,0 +1,291 @@
---
id: LLM-WP-0007
type: workplan
title: "Kimi K3 default via OpenRouter + EUR spend reporting"
domain: agents
repo: llm-connect
status: ready
owner: codex
topic_slug: kimi-k3-default-spend-reporting
planning_priority: high
planning_order: 7
created: "2026-08-03"
updated: "2026-08-03"
depends_on_workplans:
- LLM-WP-0005
related_workplans:
- LLM-WP-0006
---
# LLM-WP-0007 — Kimi K3 default via OpenRouter + EUR spend reporting
**status:** ready
**owner:** codex
## Purpose
1. Make **MoonshotAI Kimi K3** (`moonshotai/kimi-k3` on OpenRouter) a first-class
basemodel option, and **promote it to the library default** once a live smoke
call succeeds (rate table, adapter default, config default, docs).
2. Ensure every path that uses **llm-connect from the command line** reports
**token usage and cost in euros** for the work it handled.
3. Make weekly spend easy to inspect:
- **Last week** Monday 00:00 → Sunday 24:00 (local operator timezone, default
Europe/Berlin)
- **Current week** Monday 00:00 → now
This workplan does **not** change activity-core runtime profiles or K8s deploy
defaults unless those profiles currently inherit the global OpenRouter default
without an explicit model — those stay explicit where they already are.
## Demand signal
Operator request (2026-08-03): no open workplans; next priority is evaluating
Kimi K3 as basemodel/default via OpenRouter, plus CLI cost transparency in
tokens and €, plus monsun / monnow weekly spend views.
## Current repo state (as of 2026-08-03)
| Area | State |
|---|---|
| Active workplans | **None** — WP-0001…0006 + ADHOC-2026-06-02 all finished/completed |
| OpenRouter default model | `anthropic/claude-sonnet-4` (`openrouter.py`, `config.LLMConfig`) |
| Rate table | USD-only seed of nine models; **no** `moonshotai/kimi-k3` entry |
| Cost model | `estimate_cost``CostEstimate` with `cost_usd` only (`costs.py`) |
| CLI (`llm-connect`) | Registry tools only: `rates show`, `classes show`, `classes fit`**no** prompt execute, **no** spend report |
| Usage persistence | Server optional audit JSON under `LLM_CONNECT_AUDIT_DIR`; quality ledger is for adaptive routing, not operator spend |
| BudgetTracker | Token-cap only; no EUR accounting |
OpenRouter live listing (checked 2026-08-03):
- Model id: `moonshotai/kimi-k3`
- Context: 1_048_576
- List price (USD per token → per 1k): **prompt $0.003 / 1k**, **completion $0.015 / 1k**
## Architecture sketch
```
llm_connect/
rates.py # + moonshotai/kimi-k3 rate; currency remains list-price USD
costs.py # CostEstimate gains EUR fields via FxRate
fx.py # NEW: USD→EUR conversion (config/env/snapshot; never invent FX)
usage.py # NEW: UsageEvent + UsageLedger (append-only JSONL)
openrouter.py # default model → moonshotai/kimi-k3 after T01 smoke
config.py # LLMConfig.model default aligned
cli.py # run | cost report | spend week
```
### Defaults (after smoke gate)
| Surface | New default |
|---|---|
| `OpenRouterAdapter` `_DEFAULT_MODEL` | `moonshotai/kimi-k3` |
| `LLMConfig.model` | `moonshotai/kimi-k3` |
| `create_adapter("openrouter")` with no model | inherits adapter default |
| Explicit profiles / activity-core triage model | **unchanged** (already pin a model) |
If T01 smoke **fails** (auth, model unavailable, empty content), keep Claude Sonnet
as default, ship Kimi as an **opt-in** model id in rates + docs, and mark T02
`wait` with a short note — do not force a broken default.
### Cost + EUR
- Keep rate table denominated in **USD** (OpenRouter list prices).
- Add `FxRate(usd_per_eur or eur_per_usd, source, captured_at)` with resolution:
1. Explicit arg / config
2. Env `LLM_CONNECT_EUR_PER_USD` (float: euros per one USD)
3. Bundled snapshot rate with `captured_at` (operator can refresh)
- Extend `CostEstimate` with optional `cost_eur`, `prompt_cost_eur`,
`completion_cost_eur`, `fx_source` — missing FX yields `cost_eur=None` with
explicit source (same philosophy as unknown models).
- Never treat missing rates or missing FX as zero cost.
### Usage ledger (spend history)
Append-only JSONL, separate from `QualityLedger` (quality is adaptive-routing
signal; spend is operator accounting).
```python
@dataclass(frozen=True)
class UsageEvent:
recorded_at: datetime # timezone-aware UTC
provider: str
model_id: str
prompt_tokens: int
completion_tokens: int
total_tokens: int
cost_usd: float | None
cost_eur: float | None
cost_source: str
fx_source: str | None
source: str # "cli" | "server" | "library"
tags: dict[str, Any] # optional request id, profile, etc.
```
Default ledger path resolution:
1. `--ledger` / explicit path
2. `LLM_CONNECT_USAGE_LEDGER` env
3. `~/.local/share/llm-connect/usage.jsonl` (XDG-style fallback)
Week windows use **Monday 00:00 local** → exclusive end. Local timezone default
`Europe/Berlin` (override: `--tz` / `LLM_CONNECT_TZ`).
### CLI surface
```bash
# One-shot prompt (records usage when ledger available)
llm-connect run "Summarise X" [--provider openrouter] [--model moonshotai/kimi-k3]
# stderr or footer: tokens in/out/total · € cost (and USD if useful)
# Weekly spend
llm-connect spend week # current week Mon → now
llm-connect spend week --last # previous Mon → Sun
llm-connect spend week --json
# Optional: show cost for a hypothetical token count
llm-connect cost estimate --model moonshotai/kimi-k3 --prompt-tokens N --completion-tokens M
```
Per-call reporting for `run`: always print tokens; print € when FX + rate known;
print clear `unknown` when not.
## Scope guardrails
**In scope**
- Kimi K3 rate entry + default flip after smoke
- EUR conversion on top of existing USD rate table
- Usage ledger + CLI run/spend/cost commands
- Unit tests with fixed FX and fake clock; no live network in default CI
- Contract doc updates under `contracts/functional/`
**Out of scope**
- Multi-currency rate tables beyond USD list + EUR display
- Billing reconciliation against OpenRouter invoices (estimate-based only)
- Changing activity-core K8s model pins or triage profile model ids
- Full TUI / web dashboard
- Automatic live FX fetch in CI (optional later; snapshot + env is enough)
## Tasks
```task
id: LLM-WP-0007-T01
status: todo
priority: high
```
**Validate Kimi K3 via OpenRouter.** Confirm `moonshotai/kimi-k3` is callable
with a real key when available (manual/operator smoke; CI uses mock). Add rate
table entry: prompt `$0.003` / completion `$0.015` per 1k USD (captured from
OpenRouter 2026-08-03). Document model id and pricing source in rates contract
or README providers table. Gate for T02.
```task
id: LLM-WP-0007-T02
status: todo
priority: high
```
**Promote Kimi K3 to OpenRouter basemodel default** after T01 smoke passes.
Update `openrouter._DEFAULT_MODEL`, `LLMConfig.model`, any factory/docs that
advertise the default, and tests that hard-code `anthropic/claude-sonnet-4` as
the implicit default. Leave explicit profile models untouched. If smoke failed,
leave default unchanged and record the blocker in the task notes.
```task
id: LLM-WP-0007-T03
status: todo
priority: high
```
**USD→EUR cost conversion.** Add `fx` helper + extend `CostEstimate` with EUR
fields and `fx_source`. Resolution order: explicit → env → bundled snapshot.
Update `contracts/functional/costs.md`. Tests: known FX, missing FX, unknown
model still yields `cost_usd=None` / `cost_eur=None`.
```task
id: LLM-WP-0007-T04
status: todo
priority: high
```
**Usage ledger.** Implement `UsageEvent` + append-only JSONL `UsageLedger`
(path locks patterned after `QualityLedger`). Helpers: `record(...)`,
`iter_events()`, `sum_range(start, end)` aggregating tokens + cost_usd/eur.
Default path via env / XDG. Tests for append, concurrent-safe append (best
effort flock), and range aggregation.
```task
id: LLM-WP-0007-T05
status: todo
priority: high
```
**CLI `run` with per-call cost footer.** Extend `llm-connect` with a `run`
subcommand that creates an adapter, executes a prompt, prints content, and
reports tokens + € (and USD). Record a `UsageEvent` to the ledger. Wire cost
estimation from response usage + rate table + FX. Tests with mock adapter.
```task
id: LLM-WP-0007-T06
status: todo
priority: high
```
**CLI weekly spend views.** `llm-connect spend week` and `spend week --last`
using MonSun / Monnow windows in `Europe/Berlin` (overridable). Human table +
`--json`. Empty ledger → zero totals, exit 0. Tests with synthetic events and
fixed timezone.
```task
id: LLM-WP-0007-T07
status: todo
priority: medium
```
**Optional library/server recording.** When `LLM_CONNECT_USAGE_LEDGER` is set,
record usage from adapter post-call path and/or server `/execute` (source
`library` / `server`) so non-CLI traffic contributes to weekly spend. Keep
opt-in so library consumers are not surprised by home-dir writes.
```task
id: LLM-WP-0007-T08
status: todo
priority: medium
```
**Docs + acceptance polish.** README: new default model, CLI examples for
`run` / `spend week`, env vars (`LLM_CONNECT_USAGE_LEDGER`,
`LLM_CONNECT_EUR_PER_USD`, `LLM_CONNECT_TZ`). Update `contracts/functional/`
as needed. Smoke script or Makefile target for optional live Kimi check.
## Acceptance
- [ ] `moonshotai/kimi-k3` is in the default rate registry with documented USD rates
- [ ] After successful smoke, OpenRouter/library default model is `moonshotai/kimi-k3`
- [ ] `llm-connect run "..."` prints token counts and € cost (or explicit unknown)
- [ ] Successful CLI runs append to the usage ledger
- [ ] `llm-connect spend week` and `spend week --last` show tokens + € for the
correct Mon-based windows
- [ ] Default CI tests pass offline (mock adapter, fixed FX, no live OpenRouter)
- [ ] Missing rate or FX never reported as €0.00 without an `unknown` marker
## Risks / notes
- **Model id spelling:** operator said "kimi-k3"; OpenRouter id is
`moonshotai/kimi-k3` (not K2 variants). Stick to that id unless operator
renames the requirement.
- **FX staleness:** bundled EUR rate will drift; env override is the operator
escape hatch. Do not scrape FX APIs in the hot path for v1.
- **Default flip blast radius:** any consumer relying on implicit Sonnet will
change behaviour — document in README and release notes; profiles that pin
models are safe.
- **Estimate vs invoice:** ledger costs are list-price estimates, not OpenRouter
billed totals (discounts, cache reads, provider routing may differ).
## Implementation order
T01 → T02 (gated) in parallel with T03 → T04 → T05 → T06 → T07 → T08.
T03/T04 can start before T01; T02 must wait for T01 smoke outcome.