mirror of
https://github.com/hanzoai/chat.git
synced 2026-09-30 20:51:17 +00:00
/v1/audio/* resolves its caller through IAM and meters the account that caller
names, so speech is billed exactly like a completion — because to the wallet it
IS one. The completion path already knew this: `{{CHAT_OPENID_TOKEN}}` in the
config means "forward the signed-in user own IAM bearer", resolveTenantBearer
produces it, X-Org-Id rides beside it, and a guest gets the capped guest key.
STT and TTS did not. Both read their key with extractEnvVariable, so whatever
single credential the config named paid for every user in the system. Under
prepay that is not a rounding error: one wallet funds everyone, or nobody speaks.
This adds no second rule. resolveSpeechCredential defers to the same sentinel and
the same resolver, and both services now call it — one function, so a transcript
and the reply read back cannot come to disagree about who pays.
Signed out, or a bearer aged out, yields NO credential rather than a fallback.
Spending someone else money to avoid an error message is the worse failure, and
@hanzo/voice 0.1.6 reports the refusal and hands back to the browser recogniser
instead of absorbing it.
req had to be threaded to reach the providers. In TTS it goes AFTER stream:
strategy.call already passes stream in that position, so adding req before it
would have handed the provider `true` as a request object.
Mutation-proven: making the sentinel never match — the pre-fix behaviour, valid
JS — fails 4 of the 5 new tests; the one that survives is the literal-key case,
which is unchanged by design. The two neighbouring suites fail identically on
pristine main (a jest moduleNameMapper gap), and these are the only passing tests
in the directory.
Co-authored-by: Hanzo Dev <[email protected]>