From b4f031e87df15f860288daedaba49b7eebe3ccad Mon Sep 17 00:00:00 2001 From: pyr0ball Date: Tue, 14 Apr 2026 14:38:37 -0700 Subject: [PATCH] feat(kiwi): add orch_fallback field to RecipeResult --- app/cloud_session.py | 19 +++++++++++-------- app/models/schemas/recipe.py | 1 + 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/app/cloud_session.py b/app/cloud_session.py index c142c60..7649408 100644 --- a/app/cloud_session.py +++ b/app/cloud_session.py @@ -92,6 +92,7 @@ class CloudUser: has_byok: bool # True if a configured LLM backend is present in llm.yaml household_id: str | None = None is_household_owner: bool = False + license_key: str | None = None # key_display for lifetime/founders keys; None for subscription/free # ── JWT validation ───────────────────────────────────────────────────────────── @@ -132,16 +133,16 @@ def _ensure_provisioned(user_id: str) -> None: log.warning("Heimdall provision failed for user %s: %s", user_id, exc) -def _fetch_cloud_tier(user_id: str) -> tuple[str, str | None, bool]: - """Returns (tier, household_id | None, is_household_owner).""" +def _fetch_cloud_tier(user_id: str) -> tuple[str, str | None, bool, str | None]: + """Returns (tier, household_id | None, is_household_owner, license_key | None).""" now = time.monotonic() cached = _TIER_CACHE.get(user_id) if cached and (now - cached[1]) < _TIER_CACHE_TTL: entry = cached[0] - return entry["tier"], entry.get("household_id"), entry.get("is_household_owner", False) + return entry["tier"], entry.get("household_id"), entry.get("is_household_owner", False), entry.get("license_key") if not HEIMDALL_ADMIN_TOKEN: - return "free", None, False + return "free", None, False, None try: resp = requests.post( f"{HEIMDALL_URL}/admin/cloud/resolve", @@ -153,12 +154,13 @@ def _fetch_cloud_tier(user_id: str) -> tuple[str, str | None, bool]: tier = data.get("tier", "free") household_id = data.get("household_id") is_owner = data.get("is_household_owner", False) + license_key = data.get("key_display") except Exception as exc: log.warning("Heimdall tier resolve failed for user %s: %s", user_id, exc) - tier, household_id, is_owner = "free", None, False + tier, household_id, is_owner, license_key = "free", None, False, None - _TIER_CACHE[user_id] = ({"tier": tier, "household_id": household_id, "is_household_owner": is_owner}, now) - return tier, household_id, is_owner + _TIER_CACHE[user_id] = ({"tier": tier, "household_id": household_id, "is_household_owner": is_owner, "license_key": license_key}, now) + return tier, household_id, is_owner, license_key def _user_db_path(user_id: str, household_id: str | None = None) -> Path: @@ -250,7 +252,7 @@ def get_session(request: Request) -> CloudUser: user_id = validate_session_jwt(token) _ensure_provisioned(user_id) - tier, household_id, is_household_owner = _fetch_cloud_tier(user_id) + tier, household_id, is_household_owner, license_key = _fetch_cloud_tier(user_id) return CloudUser( user_id=user_id, tier=tier, @@ -258,6 +260,7 @@ def get_session(request: Request) -> CloudUser: has_byok=has_byok, household_id=household_id, is_household_owner=is_household_owner, + license_key=license_key, ) diff --git a/app/models/schemas/recipe.py b/app/models/schemas/recipe.py index 422fbb0..80d2129 100644 --- a/app/models/schemas/recipe.py +++ b/app/models/schemas/recipe.py @@ -56,6 +56,7 @@ class RecipeResult(BaseModel): grocery_links: list[GroceryLink] = Field(default_factory=list) rate_limited: bool = False rate_limit_count: int = 0 + orch_fallback: bool = False # True when orch budget exhausted; fell back to local LLM class NutritionFilters(BaseModel):