6 Commits

Author SHA1 Message Date
iomgaa e06cd8e8b7 feat: record which tier a call actually ran at
Twenty-five columns and not one of them answered "which tier was this?",
so the question the whole issue exists to settle - does a higher tier buy
anything - had no way to group its data.

The three emit entry points deliberately disagree, the way sampling
already does. A successful attempt records what the transport actually
sent: with EFFORT_FALLBACK=nearest a request for medium goes out as low,
and recomputing here would file the row under a tier that never left the
process. A failed attempt has no response to read, so it falls back to
the requested tier - which is exactly right for the tier errors that are
rejected before any HTTP happens, because the rejected tier is the
signal. Cache hits and terminal failures have no chosen source at all,
so a source-level tier is not a thing they could report.

emit_attempt now demands to be told whether the path reasons at all.
Embedding and OCR share the emitter but never send reasoning parameters;
without the flag a source that mistakenly carries ENABLE_THINKING would
hang a tier on a call that could not possibly have run at one.

The value lands as a plain str. StrEnum is a str subclass and asyncpg
promises nothing about encoding subclasses, and a telemetry write that
fails is only a warning - Postgres would just quietly lose the column.
NULL means nobody declared a tier, which is not the same statement as
'none', and the two must never be folded together.
2026-09-05 05:57:29 -04:00
iomgaa 80a8013642 feat: carry the per-call tier down to the transport that must send it
The Transport port took the request apart into five arguments, so a tier
placed on ChatRequest could never reach _build_payload: the field was set,
read by nobody, and silently ignored - the exact shape of failure that sent
downstream to extra_body in the first place.

complete() now takes reasoning_effort with no default, matching the
TelemetryRecorder convention: a default would turn a missing hand-off into
a silent 'no opinion'. All four fakes move with it, since @runtime_checkable
checks method names and not signatures.

EmbeddingTransport and OcrTransport are deliberately left alone - they have
no reasoning semantics - and a test now holds that line.

_build_payload drops its inline sugar conversion for effective_effort(), so
the guard and the hot path share one judgement, and passes the source's
effort_fallback for the same reason.
2026-09-05 02:28:24 -04:00
iomgaa 33ed7ecdfc feat: cap telemetry bodies at a configurable length
Chat rows stored full message and response text with no upper bound, so
downstream contracts and tenders lived in llm_calls indefinitely. Add
_cap_text/_cap_messages in the single telemetry exit (_record), applied
after digest_messages and before json.dumps, plus to response/thinking.

Capping is per text, not over the serialized JSON: cutting the whole
string would emit invalid JSON into an unvalidated TEXT column. The cap
builds new dicts and never mutates in place — digest_messages passes
non-list content straight through as the same object, so an in-place cut
would silently poison the caller's messages and the cache key.

text_cap is required on TelemetryEmitter (internal class, three known
construction sites) and defaults to None on the three public clients, so
the default behaviour stays byte-for-byte identical. Settings wiring
lands separately.
2026-08-19 13:39:17 -04:00
iomgaa cd8bebba00 refactor: drop the now-unused source parameter from usage resolvers
三态兜底不再读源配置,_resolve_usage / _resolve_stream_usage /
_resolve_embedding_usage 的 source 形参已成死参数;保留它等于在签名上继续
宣称用量口径依赖源配置,与本次改动切断该依赖的意图相悖。同步三个调用点
与测试的直接调用;SourceConfig 仍被文件内错误翻译等函数使用,import 保留。
2026-07-30 10:46:52 -04:00
iomgaa 195454d2e3 fix: stop passing est_tokens off as measured usage
usage 帧缺失/非法时不再拿 est_tokens(最坏情形上界)当实测值,chat 与
embedding 两处兜底改记 0 并标 unavailable;打捞覆盖加 measured 前置条件,
避免 0/0 被洗成 estimated 而算出假的 0.0。embedding 全批合并扩三态(任一批
不可得 → 整体不可得),_total_cost 遇不可得批整体记 NULL。
2026-07-30 10:39:32 -04:00
iomgaa e5dbcf5d33 feat: derive TPM reservation and pin the usage_source domain
Task 1 of the est_tokens decoupling: capability only, no call site
touched, so library behaviour is unchanged word for word.

SourceConfig.effective_est_tokens() returns the explicit est_tokens when
set, otherwise tpm // 60 floored at 1, otherwise 0 when the TPM gate is
off. The divisor is scale free: any quota size yields the same in-flight
ceiling of roughly sixty calls, which is what makes the default
explainable where a fixed constant was not.

USAGE_SOURCES lands with the two assertions the design asks for, not as
a dead constant. test_usage_source_domain.py drives every production
point -- _resolve_usage, _resolve_embedding_usage, _merge and the three
TelemetryEmitter.emit_* helpers -- and asserts the output stays inside
the domain; it is a separate file because the assertion spans
transports, embedding and telemetry, and the innermost kernel test
should not depend on implementations. The second assertion pins the
opposite ruling: constructing LLMResponse with an out-of-domain value
must not raise, since a bare ValueError at a runtime construction point
falls outside the four error categories and would escape chat().

tpm > 0 with est_tokens = 0 is still rejected until Task 4, so the
derivation tests build the future-legal shape through a helper that
bypasses the constraint; the helper collapses back to _make_source once
the constraint is gone.
2026-07-30 10:05:11 -04:00