ASE-GLM / server /credentials.py
Bit-Trading-Company's picture
CI deploy local
17b22d9 verified
Raw History Blame Contribute Delete
4.19 kB
"""Which token pays for a request.
The rule this file exists to enforce: **bill the viewer, not the Space owner,
whenever we possibly can.** A harness that quietly spends the owner's inference
credits on every visitor is one shared link away from an unpleasant invoice.
Precedence, highest first:
1. the signed-in viewer's Hugging Face OAuth token -> billed to the viewer
2. a key the viewer pasted into the UI this session -> billed to the viewer
3. the Space's own ``HF_TOKEN`` secret -> billed to the owner
Step 3 is off unless ``ASE_ALLOW_SPACE_CREDITS`` is set, so the fallback is a
deliberate act rather than a default. Locally it is on, because there is no
OAuth off-Space and a dev loop that cannot make a request is useless.
"""
from __future__ import annotations
import os
from dataclasses import dataclass
from pathlib import Path
@dataclass(frozen=True)
class Credential:
token: str | None
mode: str # oauth | user-key | space-secret | none
who: str | None = None
detail: str = ""
@property
def bills_viewer(self) -> bool:
return self.mode in ("oauth", "user-key")
def to_json(self) -> dict:
return {
"mode": self.mode,
"who": self.who,
"billsViewer": self.bills_viewer,
"detail": self.detail,
}
def on_space() -> bool:
return bool(os.environ.get("SPACE_ID"))
def allow_space_credits() -> bool:
"""Falling back to the owner's token is opt-in on a Space, on by default off it."""
flag = os.environ.get("ASE_ALLOW_SPACE_CREDITS")
if flag is not None:
return flag.strip().lower() in ("1", "true", "yes", "on")
return not on_space()
def resolve(oauth_token: str | None = None,
oauth_user: str | None = None,
user_key: str | None = None) -> Credential:
"""Pick the credential for one request. Never logs or returns the value."""
if oauth_token:
return Credential(oauth_token, "oauth", oauth_user,
"Signed in with Hugging Face; inference is billed to you.")
if user_key:
return Credential(user_key, "user-key", None,
"Using the key you supplied; it is held for this session only.")
secret = (os.environ.get("HF_TOKEN")
or os.environ.get("HUGGING_FACE_HUB_TOKEN")
or _dev_key_file())
if secret and allow_space_credits():
return Credential(
secret, "space-secret", None,
"Falling back to the Space's own token — this bills the Space owner.",
)
if secret:
return Credential(
None, "none", None,
"A Space token exists but owner-billed requests are disabled. "
"Sign in, or supply your own key.",
)
return Credential(None, "none", None,
"No credential available. Supply a Hugging Face token.")
def _dev_key_file() -> str | None:
"""The shared ``key.txt`` this workspace keeps for local development.
Only consulted off a Space. On a Space the token arrives as a secret in the
environment and this path does not exist, so the lookup is dead code there
rather than a second way in.
"""
if on_space():
return None
explicit = os.environ.get("ASE_KEY_FILE")
if explicit:
# Authoritative when set, including when it points at nothing. The
# search below would otherwise reach outside a test's tmp_path and
# find the developer's real key, which is a test that passes for the
# wrong reason and a suite that behaves differently on CI.
candidates = [Path(explicit)]
else:
# The workspace deliberately keeps one gitignored key above the
# projects so several of them can share it. Walk up to find it.
here = Path(__file__).resolve()
candidates = [parent / "key.txt" for parent in here.parents[:5]]
for path in candidates:
try:
if path.is_file():
token = path.read_text().strip()
if token:
return token
except OSError:
continue
return None