Skip to content

LLM: native Anthropic caching adapter + measurement stack - #661

Merged
devswha merged 63 commits into
mainfrom
dev
Jul 29, 2026
Merged

LLM: native Anthropic caching adapter + measurement stack#661
devswha merged 63 commits into
mainfrom
dev

Conversation

@devswha

@devswha devswha commented Jul 24, 2026

Copy link
Copy Markdown
Owner

The paid-path cost work, measured end to end: native /v1/messages adapter with prompt caching (opt-in flag, 90% input-cost cut verified live), thinking control defaulting to provider-on (quality A/B showed thinking-off amputates content), G002 usage adapter v2 for the 2026 first-party usage shape, cached measurement bundle checked in (warm per-request $0.0892 vs $0.164 uncached). Production behavior unchanged until PATINA_ANTHROPIC_NATIVE_CACHE is set in Vercel. Gate: full suite + 10 adapter tests green.

devswha added 7 commits July 24, 2026 18:30
OpenAI-compatible streams omit the usage frame unless stream_options
include_usage is sent, so every successful streamed attempt recorded
usage: null — blinding cost observability for the production pro
rewrite stage and making PAY-B-COST billing evidence unassemblable
(found assembling the first G002 bundle). The SSE parser already
captures a usage chunk when present; this sends the request flag,
verified against the Anthropic compat endpoint. The collector's billing
classification also corrects to usage-presence (an errored attempt with
usage was still metered).
…designed

Three probes measured on the real pipeline (uncached claude-sonnet-5,
standard $3/$15 pricing): upper COGS $878/1M chars, dominated by the
fixed ~17k-token catalog prompt per call — the char-scaled worst case
collapses at short inputs. The 60% gate refuses the current shape
($9.99, 1M chars/mo) at -10,243% margin. Raw bundle persisted so cap,
price, and model scenarios recompute offline with zero further spend.
derivePayBCostFinancial exported for refusal reporting and scenario
analysis; the collector persists the bundle before issuing.
The OpenAI-compat endpoint ignores prompt caching in every form
(verified empirically: content-block and top-level cache_control both
bill full price on repeat). With ~34k static catalog tokens per paid
call, caching is the difference between $0.164 and ~$0.07 per pro
rewrite — so the paid path gets a first-party /v1/messages branch:

- Opt-in via PATINA_ANTHROPIC_NATIVE_CACHE + first-party host check;
  zero caller-surface change (api.js / streaming-api.js branch
  internally), compat behavior byte-identical when off.
- Zero prompt-semantics change: the prompt stays one user message,
  split at the first input fence into a cache_control prefix block
  (the static catalog) and the dynamic tail. Refine prompts fall under
  the cache minimum and skip caching — no write-only cache churn.
- Native SSE parser maps message_start/content_block_delta/
  message_delta into the existing delta/attempt plumbing; usage keeps
  Anthropic field names (cache-token extractor and the G002 usage
  adapter already accept them).
- sonnet-5 rejects temperature natively too ('deprecated for this
  model'): the native body consults the shared rejection memo and the
  temperature_schema retry now covers both paths.

Live verification: first call cache_creation 34,254 tokens; second call
cache_read 34,254 at the 0.1x rate — 90% input-cost cut confirmed.
Suite: 8 new adapter tests, full gate green.
…ched bundle

- Thinking stays at the provider default: the A/B showed thinking-off
  rewrites amputate content (fidelity 50, judged by a thinking-on
  scorer), and quality is what pro sells. PATINA_ANTHROPIC_THINKING=0
  remains as an experiment-only opt-out. Measured context: thinking is
  76% of pro-path output tokens.
- G002 usage adapter v2 accepts the first-party 2026 usage shape
  (cache_creation breakdown, thinking details, routing metadata) and
  fail-closes on 1-hour cache writes that would break single-rate
  pricing.
- Cached measurement bundle checked in: warm-cache per-request cost
  $0.0892 (output tokens 80% of it), vs $0.164 uncached.
@vercel

vercel Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
patina Ready Ready Preview, Comment Jul 29, 2026 8:52am

Request Review

devswha added 3 commits July 25, 2026 03:52
Self-judging (candidate model grades its own rewrite) made cross-model
comparisons incomparable and noisy run-to-run — measured on the Token Plan
screening (2026-07-25) where glm-5.2 verdicts flipped between identical runs.

- PATINA_LIVE_JUDGE_{MODEL,PROVIDER,API_BASE,API_KEY,TIMEOUT_MS} env vars
  and --judge-* flags pin scoreText/scoreMPS/scoreFidelity to one judge
- judge on a different host never inherits the primary credential (fails
  closed with a clear error instead)
- report records settings.judge; markdown header prints judge: <model>
- default behavior unchanged (self-judging) when no judge is configured
devswha added 2 commits July 25, 2026 03:58
…ccounting

Judge selection needs measured speed and quota cost, not estimates, and
cost is easily distorted by hidden reasoning tokens, cache reads, and
schema-retry doubling. Every live call now records wall ms, paid attempt
count, and per-attempt normalized usage (OpenAI-compat + native Anthropic
shapes): prompt/completion/reasoning/cached-read/cache-write tokens.
Results carry usage.candidate / usage.judge; the report sums summary.usage.
Failed paid retries stay billed via per-attempt usage.
devswha added 2 commits July 25, 2026 04:15
gpt-5.5 — the best measured judge (AUC 1.00, 2026-07-13 calibration) — and
claude are reachable through logged-in subscription CLI seats without any
API key. PATINA_LIVE_JUDGE_BACKEND / --judge-backend routes the scoring
calls (scoreText/scoreMPS/scoreFidelity) through the local backend chain
(codex-cli, claude-cli, gemini-cli, kimi-cli); the key fail-closed gate is
skipped for backend judges since the seat is the credential. CLI backends
report no token usage, so cost accounting records calls and wall time only.

Verified live: claude-cli judge scored a Token Plan candidate end-to-end
(zero marginal cost); codex-cli routing works but the seat's refresh token
is revoked upstream (re-login is an operator action).
devswha added 3 commits July 25, 2026 11:59
The migrated Kimi Code CLI (0.28.x) removed --print/--input-format/
--final-message-only/--no-thinking/--max-steps-per-turn, which left the
kimi-cli backend fully broken (every invoke exited 1 with 'unknown option').

- modern invocation: --prompt <text> (argv; the CLI no longer reads stdin)
  with --output-format stream-json, recovering the final assistant message
  from NDJSON events (same semantics as the retired --final-message-only)
- legacy CLIs keep working via an 'unknown option' fallback to the old
  stdin --print invocation
- security stance unchanged: never --yolo/--auto, tools stay unapprovable
  in non-interactive prompt mode
- isAuthenticated now also checks ~/.kimi-code (kimi-code data dir) so
  auth detection survives a legacy ~/.kimi cleanup after 'kimi migrate'
Local CLI backends serialize on a concurrency cap of 1, so the parallel
Promise.all scoring pass only burned each call's budget inside the slot
queue (observed: 4 x 300s all-timeout with a kimi judge), and the single
shared absolute deadline expired before later calls could start. Backend
judges now score sequentially with a per-call timeout and no shared
deadline; the HTTP judge path is unchanged. Verified live with codex-cli
(gpt-5.5, 4 calls / 55.6s) and kimi-cli (stream-json verdicts returned).
devswha added 2 commits July 25, 2026 13:03
…rovider-key reference

.env.example led with 35 lines of the retired ops harness (DISCORD_*/
MARKETING_*/PATINA_RUNTIME_CLI) that nothing in this snapshot reads —
AGENTS.md already declares that harness absent. Meanwhile the keys the
CLI actually resolves (src/auth.js HTTP_KEY_ENV_VARS, src/providers.js
presets) were undocumented. Lead with the provider-key reference and a
pointer to the subscription-CLI and fixed-judge (PATINA_LIVE_JUDGE_*)
docs; the hosted-service section is unchanged.
devswha added 2 commits July 25, 2026 13:39
Provider docs review + live probes confirmed every reasoning-default
provider exposes a body-level off-switch the harness could not send:
DeepSeek v4 thinking:{type:disabled} (193s -> 1.6s single-call probe),
Gemini OpenAI-compat reasoning_effort (accepted on 3.6-flash), Alibaba
enable_thinking:false (4.5x). Reasoning is the dominant judge cost
distortion (measured 93-95% of output tokens).

- src/api.js callLLM accepts extraBody, spread into the OpenAI-compat
  body before protocol fields so model/messages can never be clobbered;
  ignored on the native Anthropic path
- live-quality: PATINA_LIVE_EXTRA_BODY / PATINA_LIVE_JUDGE_EXTRA_BODY
  (+ --extra-body / --judge-extra-body) parse a JSON object and ride
  every candidate/judge call; junk input fails fast
…soning judges fail

Admitted three HTTP challengers to the 2026-07-13 calibration under the same
pre-registered criteria (44-doc KO corpus, same prompt, PASS = AUC >= 0.75 &
median repeat SD <= 12). 192/192 challenger calls parsed, 0 lost.

- gemini-3.6-flash: accuracy 0.91, AUC 0.96 [0.91, 1.00], repeat SD 2.2,
  4.6s/call on a near-free tier -> PASS (second seat; gpt-5.5 keeps the 1.00)
- grok-4.20-non-reasoning: AUC 0.71, called 23 of 24 AI docs "human" -> WATCH
- deepseek-v4-flash thinking-off: AUC 0.70, 20 of 24 missed, SD 12.5 -> WATCH

Speed bought nothing for the non-reasoning judges: their human/AI mean gap is
4-9 points versus 53-65 for every PASS judge, and their low repeat SD is the
consistency of always answering ~20, not stability.

Harness: HTTP judge transport (records per-call latency + usage next to the
verdict), --judges filter for bounded resumable passes, kimi invocation moved
to the Kimi Code >= 0.28 argv/NDJSON shape. The pre-registered pooled panel
stays defined as the original 3-judge 2-of-3 mean.
devswha added 2 commits July 28, 2026 03:16
v6.2 made the persona the sole voice owner and both shipping surfaces moved
with it — src/cli/run.js and src/web-rewrite.js each call resolvePersonaForRun
before building the prompt. The live-quality harness never did, so it measured
a third prompt that nothing serves.

For Korean the missing piece is the persona directive itself: '원문의
주장·사실·수치·인용·논지 순서를 100% 보존하고 어투·리듬·어휘·문장구조만
페르소나에 맞춘다'. Every meaning-preservation number this harness produced was
taken from a rewrite that was never told to preserve meaning, so MPS failures
here were biased against the product — including the five fixtures recorded
this week as an open meaning-loss problem.

With the fix the harness prompt is byte-identical to the hosted rewrite prompt
(41,775 chars, diff 0), pinned by a parity test so the two cannot drift again.
Every measurement in docs/operations from 2026-07-25/26/27 predates this and
should be re-run before it is cited.
devswha added 2 commits July 28, 2026 06:05
Rerun with the harness prompt now byte-identical to the hosted one, on
subscription seats (gemini-3.6-flash rewriting, gpt-5.5 judging, zero cost):

| | broken rubric | fidelity fixed | + prompt parity |
|---|---|---|---|
| pass | 9/22 (41%) | 17/22 (77%) | 20/22 (91%) |
| MPS mean | 76.3 | 80.5 | 90.4 |
| ko | 2/11 | 5/11 | 11/11 |

Every register recorded this week as systematically broken now passes:
ko-instructional MPS 20 -> 100, ko-marketing 50 -> 100, ko-social 40 -> 100,
ko-blog 45 -> 70, ko-product 50 -> 80. Neither defect was in the rewriting
engine; both were in the apparatus measuring it.

Two English fixtures remain and fail in opposite directions: en-marketing-01
strips hype thoroughly (AI 35.6 -> 5.7) but drops anchors (MPS 60), while
en-public-docs-01 holds meaning and barely moves the AI score (15.6 -> 16.5).
Both are single fixtures; the handoff now says to add a second fixture per
register before reading either as a pattern.
devswha added 2 commits July 28, 2026 06:53
The comparison that moved both tiers to gemini-3.6-flash was taken under a
broken fidelity rubric and a harness prompt missing the persona, so it was
void. Rerun on the same 22 fixtures with both fixed, on subscription seats
(candidates via their own seats, judge gpt-5.5 via codex, zero API spend):

| | gemini-3.6-flash | claude-sonnet-5 |
|---|---|---|
| pass | 20/22 (ko 11/11, en 9/11) | 20/22 (ko 11/11, en 9/11) |
| MPS mean | 90.4 | 91.5 |
| fidelity mean | 92.4 | 94.7 |
| $/rewrite | $0.030 | $0.156 |
| s/rewrite | 8.3 | 27.7 |

The decision holds but its justification does not: the two engines are level
on quality, and sonnet-5's 1.1-point MPS edge is inside one fixture of noise
at n=22. The case for gemini is cost and latency, not rewrite quality. Docs
and .env.example now say that instead of the void 'AI improvement 13.0 vs
11.8' figures; the .env.example freeze hash is revised in both ledgers.

Still void and not rerun: deepseek-v4-flash, grok-4.3, gpt-5.3-chat-latest,
gpt-5.4-mini, gpt-5.6-luna, and the gpt-4.1-mini result that motivated moving
the free tier. en-marketing-01 fails on both engines, which points at that
fixture or the prompt rather than at either model.
devswha added 2 commits July 28, 2026 07:45
Reran the cheap candidates on the fixed apparatus to see whether the shipped
engine could be undercut. It cannot, and the two failure modes are mirror
images of each other:

| engine | pass | MPS worst | not-improved | $/rewrite |
|---|---|---|---|---|
| gemini-3.6-flash | 20/22 | 60 | 1 | $0.030 |
| claude-sonnet-5 | 20/22 | 50 | 0 | $0.156 |
| deepseek-v4-flash | 18/22 | 24 | 1 | $0.003 |
| gemini-3.5-flash-lite | 10/22 | 40 | 8 | $0.007 |

deepseek-v4-flash is 10x cheaper and still rewrites hard, but gutted
ko-news-01 to MPS 24. gemini-3.5-flash-lite fails the opposite way: four
Korean fixtures score MPS 100 while the AI score barely moves, i.e. it returns
the input nearly unchanged — the same evasion that made gpt-4.1-mini look like
a leader under the broken apparatus.

Holding both ends at once is the real difficulty here, so $0.030 per rewrite
is recorded as the floor rather than a number to shave. The OpenAI-hosted
candidates stay unmeasured on an exhausted balance; none is in production and,
given both cheaper models failed in opposite directions, none looks promising.
devswha added 3 commits July 28, 2026 11:04
patina rewrote marketing copy and added 'No hidden commitments—cancel anytime'
plus 'see how much time you save every day'. Neither appears in the original.
Inserting contractual promises into a user's published copy is a liability, not
a style choice, and nothing in the default prompt forbade it: the only 'never
invent' rule was about passive-voice actors, and the fact/number prohibition
lived exclusively inside the --transform block that most runs never emit.

Meanwhile the marketing profile actively demands specificity ('replace a vague
future-promise ending with a concrete CTA', 'who, where, in what context?'). On
hype-only source text there is no specific fact to supply, so the model
invented one.

Added, unconditionally: Phase 2 now forbids adding any claim, fact, number,
guarantee, or commitment absent from the source, and instructs cutting the
vague sentence instead of inventing a replacement; Phase 3 self-audit gains a
check that every claim, number, and promise traces back to the input. Also
corrected the length rule, which still cited the pre-2026-07-27 70-130% band
while lengthRatioPoints now scores 50-130% — the model was self-censoring
against a threshold that no longer existed.

Verified qualitatively on en-marketing-01: both fabricated commitments are gone
and the three real anchors (7-day trial, $9/month, all features) survive.

Honest about the aggregate: 22 fixtures scored 20/22 before and after, with MPS
mean 90.4 -> 88.2. Per-fixture scores swing +-20 MPS between identical runs, so
this change is not measurable at one run per fixture. It is kept on correctness
grounds, and the variance is itself the finding — single-run sweeps cannot
validate anything at this effect size.
Every conclusion drawn this week rested on one sample per fixture, and that is
not enough: identical configurations swing +-20 MPS per fixture. ko-blog-01
scored 45 in one sweep and 100 in three consecutive reruns, and the
no-fabrication prompt change measured as a 2-point regression in aggregate
while its intended effect was plainly visible in the rewrite text. Three
separate conclusions this week were revised after resampling.

--repeat N samples each fixture N times. Reported scores are medians, the
status is the worst sample so repeating can only expose instability rather than
average it away, and result.repeat carries every value with its spread for
inspection. Paired with the subscription seats the extra samples cost nothing,
so there is no reason to run a decision-grade comparison at N=1 again.
devswha added 2 commits July 28, 2026 18:54
Production returned provider 429s to free-tier users three times on
2026-07-27 and nothing alerted. The monitor exists and runs every 15 minutes,
but it evaluates tier 'pro' — which has no users while checkout is disabled —
so its aggregate is permanently zero, its one live signal (monitor_blind)
fires constantly as background noise, and its synthetic probe is pro-only,
hourly, and needs three consecutive failures. The free counters were being
written the whole time under patina:mon:v1:<channel>:free:*; only the reader
was missing.

evaluateFreeTierHealth reads them, in two layers because either alone has a
blind spot:
- aggregate ratio: alerts when >50% of at least 5 requests in the window did
  not complete. Costs nothing and runs every tick, but is silent at zero
  traffic. quota_denied is excluded — that is the product working.
- HTTP canary: a real free rewrite, for when traffic is zero and the aggregate
  cannot tell idle from down. It consumes the free IP quota (20/day), so a
  lease budgets it to one probe per two hours; probing every tick would
  exhaust the quota and manufacture its own alerts.

Wired into the existing cron, so no new endpoint and no vercel.json change
(that file is frozen). It runs after the paid evaluation and can never fail
the cron: a canary problem must not mask the pro evidence run, and the status
code still speaks for the paid path alone.

Detection goes from never to at most two hours at zero traffic, and to one
cron tick once anyone is actually using the service.
devswha added 2 commits July 29, 2026 17:31
…g pastes

Pasting any text containing HH:MM clock times (chat logs, meeting notes,
timelines like '16:47 - 16:50') always failed the web rewrite with a
generic '리라이트 실패' — digit:digit hit NUMERIC_OPERATOR_RE, so
addClaims fail-closed on the ORIGINAL text alone and every rewrite,
including an identity rewrite, emitted number_safety_failed before
scoring. Retry could never succeed and the 'check the mode/key' copy
was wrong.

- meaning-proxy: claim HH:MM(:SS) as exact time: claims before the
  operator check. Two-digit minutes keep ratios/scores (1:2, 3:1) and
  invalid times (25:30, 16:75) fail-closed exactly as before; time drift
  or dropped times fail as numeric_claim_changed. Safety is not
  weakened: reject-always becomes must-match-exactly.
- playground: number_safety_failed now classifies to a dedicated
  NUMBER_SAFETY kind with honest localized copy (en/ko/zh/ja) instead
  of the generic mode/key failNote.

Verified: unit suite (1481 pass), full test run, lint, plus a live
byok e2e of the originally failing paste through runWebRewriteStream
(gemini-3.6-flash): start -> 16 deltas -> done, MPS 100 / fidelity 100,
all timestamps preserved verbatim.
devswha added 2 commits July 29, 2026 17:39
…screen readers

The .output-status line under every patina message showed
'Unapproved — checks have not passed' from the moment streaming
started, which reads as an alarming warning during a perfectly normal
in-flight rewrite (10-60s with a real model), then flipped to
'Approved' on done. Sighted users already get the flagged border,
floorWarn, and error notes for real failures, so the visible line is
redundant noise.

Clip the element out visually (standard sr-only pattern) while keeping
the role=status aria-live region, the localized copy, and the
unapproved/approved dataset markers untouched — the a11y contract
pinned by playground-pro.test.js is unchanged and all 9 assertions
still pass.
devswha added 2 commits July 29, 2026 17:51
Bump 6.3.1 -> 6.3.2 across every version-bearing surface (package.json,
lockfile, README x4 badges + config examples, SKILL.md,
.patina.default.yaml, playground ver-badges, patina-humanizer alias,
Claude plugin manifests, release-metadata test pin) and add the 6.3.2
CHANGELOG entry.

Re-freeze the v6.4 preflight hold SHA-256 manifest (script + JSON, 12
files) for the version-bump mutations only — every blocker, decision,
prohibition, and provider semantic is byte-identical, matching the
precedent of 8ffb920. The 6.4 tag/publish prohibitions remain in force;
this release is authorized because the guard scopes them to 6.4.x.

Refresh the checked-in benchmark reports (content unchanged; timestamp
and node-version metadata only).

Gates: release:check OK for 6.3.2, npm test 1620 pass (hold validator
green on the new freeze), lint clean, benchmark 100%, dogfood under 30.
@devswha
devswha merged commit b98b3b0 into main Jul 29, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant