symfonic.core.kimi_oauth_provider¶
kimi_oauth_provider ¶
KimiOAuthProvider — DEV/TEST USE ONLY.
Calls the Moonshot/Kimi OpenAI-compatible backend using a kimi.com
subscription OAuth token instead of a metered MOONSHOT_API_KEY.
Credential sources (two, mutually exclusive)¶
- Static token (unchanged, backward compatible): supply
KIMI_OAUTH_ACCESS_TOKEN(oraccess_token=). The provider sends it verbatim and NEVER refreshes — the caller owns the token's lifecycle. - kimi-cli store (auto-refresh): when no static token is given and a
kimi-clicredential file exists, the provider auto-loads it via :class:~symfonic.core.kimi_credentials.KimiCredentialStoreand refreshes the short-lived (~15 min) access token transparently — including mid-session, on every outgoing request. Usefrom_kimi_cli()to request this path explicitly. This DOES embed the kimi-code CLI's public device-flow client id (seekimi_credentials): a published identifier, not a secret, and refresh is impossible without it.
Side effect of the store path¶
Auto-refresh MUTATES the local kimi-cli credential file: the OAuth
refresh_token rotates on every refresh, so the new value is written back
(atomically, under a file lock) or the developer's kimi-cli login would
break. This is a deliberate, necessary side effect of the seamless-refresh
posture and applies ONLY to the store path — the static-token path touches no
files.
When to use¶
- Local development against Kimi models without provisioning a separate
metered
MOONSHOT_API_KEY. - Experimenting with the pipeline against a real LLM during development.
When NOT to use¶
- ❌ Production / multi-tenant SaaS — a subscription token doesn't bill per-tenant and its rate limits differ from API-key plans.
- ❌ Cost-tracking — subscription usage doesn't map to the per-token
pricing in :mod:
symfonic.core.observability.pricing(the Kimi model ids resolve to pricing rows, but subscription billing is flat-rate).
This provider explicitly refuses to work in production-like environments
(SYMFONIC_ENV/ENVIRONMENT/ENV/NODE_ENV set to
production/prod) to prevent accidental subscription billing under
multi-tenant load. Kimi is env-vars-only and dev/test-only, so the guard
fires regardless of how the token is supplied and allow_production=True
is the sole reviewed escape hatch. This mirrors
:class:~symfonic.core.codex_provider.CodexOAuthProvider.
ToS caveat¶
Replaying a kimi.com subscription token to the Moonshot API from a
third-party program is subscription-auth reuse. Moonshot ships a
device-auth flow (auth.kimi.com) for its own kimi-cli under a
fixed published client id — we deliberately do NOT embed that client
identity or run the handshake. The adopter runs kimi-cli login
themselves and exports the resulting token, keeping us in the same
risk class as :class:~symfonic.core.codex_provider.CodexOAuthProvider
— dev/test only.
Credentials (environment variables)¶
KIMI_OAUTH_ACCESS_TOKEN(required) subscription access token, obtained by runningkimi-clilogin yourself.KIMI_OAUTH_BASE_URL(optional) override the endpoint; defaults to the kimi.com subscription (coding) endpointhttps://api.kimi.com/coding/v1— the only endpoint a subscription OAuth token authenticates against (the meteredapi.moonshot.aiplatform endpoint rejects it with 401).KIMI_OAUTH_PLATFORM(optional) →X-Msh-Platformrequest header. Only set this if yourkimi.comsubscription token requires a platform header to be accepted. We do NOT default it to Moonshot'skimi_code_cli— supply your own value, matching the "we don't embed their client identity" principle.
You obtain the token by signing in with kimi-cli yourself and
exporting the resulting value, e.g.::
export KIMI_OAUTH_ACCESS_TOKEN=...
# optional, only if your token requires it:
export KIMI_OAUTH_PLATFORM=...
KimiCredentialError ¶
Bases: RuntimeError
Raised when a Kimi subscription token cannot be resolved or refreshed.
KimiCredentialStore ¶
KimiCredentialStore(
credentials_path: Path | None = None,
*,
client_id: str = _KIMI_CODE_PUBLIC_CLIENT_ID,
token_url: str = _DEFAULT_TOKEN_URL,
expiry_skew_seconds: int = 120,
)
Yields an always-valid kimi-cli access token, refreshing as needed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
credentials_path
|
Path | None
|
Override the credential file. Defaults to the
|
None
|
client_id
|
str
|
OAuth device-flow client id. Defaults to the kimi-code CLI's public client id. |
_KIMI_CODE_PUBLIC_CLIENT_ID
|
token_url
|
str
|
OAuth token endpoint. Defaults to |
_DEFAULT_TOKEN_URL
|
expiry_skew_seconds
|
int
|
Refresh this many seconds BEFORE the cached
token's |
120
|
Source code in src/symfonic/core/kimi_credentials.py
access_token ¶
Return a valid access token, refreshing if within the skew window.
peek_access_token ¶
Return the cached token without refreshing; None if unavailable.
For logging / fingerprinting at construction time — must never raise or trigger a network refresh.
Source code in src/symfonic/core/kimi_credentials.py
KimiOAuthProvider ¶
KimiOAuthProvider(
*,
access_token: str | None = None,
base_url: str | None = None,
platform: str | None = None,
allow_production: bool = False,
_store: KimiCredentialStore | None = None,
)
DEV/TEST provider that calls Moonshot/Kimi via a subscription token.
Mirrors :class:~symfonic.core.providers.KimiProvider's OpenAI-compatible
ChatOpenAI wiring (same Moonshot defaults, sampling parity, capability
flags); the ONLY differences are auth (a kimi.com subscription token
from an env var rather than MOONSHOT_API_KEY) and the production guard.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
access_token
|
str | None
|
Kimi subscription access token. Defaults to the
|
None
|
base_url
|
str | None
|
Moonshot backend base URL. Defaults to
|
None
|
platform
|
str | None
|
Value for the |
None
|
allow_production
|
bool
|
Skip the production-env guard. Default False. Set True only if you have an explicit, ToS-reviewed reason. |
False
|
Raises:
| Type | Description |
|---|---|
KimiCredentialError
|
No static token (arg/env) AND no kimi-cli credential file to auto-load. |
KimiProductionMisuseError
|
A production env var is set and
|
Source code in src/symfonic/core/kimi_oauth_provider.py
token_fingerprint
property
¶
Short, non-leaking identifier for the active token.
from_kimi_cli
classmethod
¶
from_kimi_cli(
*,
credentials_path: Any = None,
base_url: str | None = None,
platform: str | None = None,
allow_production: bool = False,
) -> KimiOAuthProvider
Build a provider backed by the kimi-cli credential store.
Auto-loads ~/.kimi-code/credentials/kimi-code.json (or
credentials_path / KIMI_OAUTH_CREDENTIALS_FILE) and refreshes
the short-lived access token transparently on every request — so a
developer runs kimi-cli login ONCE and never exports or refreshes a
token by hand. The production guard still fires on this path.
Source code in src/symfonic/core/kimi_oauth_provider.py
get_chat_model ¶
Return a ChatOpenAI pointed at the Moonshot/Kimi backend.
Static-token path: the token is passed as api_key — LangChain sends
it as Authorization: Bearer <token> — verbatim, no refresh.
Store-backed path: api_key is a placeholder and a per-request httpx
event hook rewrites Authorization with a FRESHLY-valid token on
every outgoing request (sync + async), so a token that expires
mid-session refreshes transparently without rebuilding the model.
Source code in src/symfonic/core/kimi_oauth_provider.py
KimiProductionMisuseError ¶
Bases: RuntimeError
Raised when KimiOAuthProvider is used in a production-like env.