fix(vedastro): pin SDK at install and cap free-tier wait
Image build patches vedastro.check_for_update to a no-op after pip install so import cannot hit pypi or upgrade the pin. Free-tier queue waits are capped to VEDASTRO_TIMEOUT_SECONDS and fail fast as free_tier_rate_limited. Gateway status exposes runtime mode as booleans without secrets. BUG-719 resolved; BUG-720 stays investigating until production env is filled in.
This commit is contained in:
@@ -29,6 +29,14 @@ def _official_network_enabled() -> bool:
|
||||
return os.environ.get("VEDASTRO_ENABLE_NETWORK", "1").strip().lower() in {"1", "true", "yes", "on"}
|
||||
|
||||
|
||||
def _sdk_version() -> str | None:
|
||||
try:
|
||||
import importlib.metadata
|
||||
return importlib.metadata.version("vedastro")
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def _int_env(name: str, default: int = 0) -> int:
|
||||
raw = os.environ.get(name, "").strip()
|
||||
if not raw:
|
||||
@@ -271,7 +279,16 @@ def gateway_status() -> dict[str, Any]:
|
||||
"self_host_configured": config["self_host_endpoint_configured"],
|
||||
"official_configured": config["official_endpoint_configured"],
|
||||
"credential_configured": bool(os.environ.get("VEDASTRO_API_KEY", "").strip()),
|
||||
"endpoint_configured": bool(config["official_endpoint_configured"] or config["self_host_endpoint_configured"]),
|
||||
"fanout_enabled": os.environ.get("VEDASTRO_FULL_SNAPSHOT_FANOUT_ENABLED", "1").strip().lower() in {"1", "true", "yes", "on"},
|
||||
"range_scan_network_enabled": os.environ.get("VEDASTRO_RANGE_SCAN_NETWORK_ENABLED", "").strip().lower() in {"1", "true", "yes", "on"},
|
||||
"free_tier_queue_active": bool(
|
||||
config["official_endpoint_configured"]
|
||||
and not os.environ.get("VEDASTRO_API_KEY", "").strip()
|
||||
),
|
||||
"sdk_version": _sdk_version(),
|
||||
"cache_ttl_seconds": config["cache_ttl_seconds"],
|
||||
"official_full_snapshot_cache_ttl_seconds": _int_env("VEDASTRO_OFFICIAL_FULL_SNAPSHOT_CACHE_TTL_SECONDS", 0),
|
||||
"queue_enabled": config["queue_enabled"],
|
||||
"fail_open_local": config["fail_open_local"],
|
||||
"official_readiness": {
|
||||
|
||||
@@ -350,6 +350,7 @@ ALLOW_NETWORK_ENV = "VEDASTRO_ENABLE_NETWORK"
|
||||
CACHE_TTL_ENV = "VEDASTRO_CACHE_TTL_SECONDS"
|
||||
FREE_TIER_MAX_REQUESTS_ENV = "VEDASTRO_FREE_TIER_MAX_REQUESTS"
|
||||
FREE_TIER_WINDOW_SECONDS_ENV = "VEDASTRO_FREE_TIER_WINDOW_SECONDS"
|
||||
FREE_TIER_WAIT_BUDGET_ENV = "VEDASTRO_FREE_TIER_WAIT_BUDGET_SECONDS"
|
||||
DEFAULT_CACHE_TTL_SECONDS = 86400.0
|
||||
DEFAULT_FREE_TIER_MAX_REQUESTS = 5
|
||||
DEFAULT_FREE_TIER_WINDOW_SECONDS = 60.0
|
||||
@@ -505,6 +506,17 @@ def _free_tier_window_seconds() -> float:
|
||||
return DEFAULT_FREE_TIER_WINDOW_SECONDS
|
||||
|
||||
|
||||
def _free_tier_wait_budget_seconds() -> float:
|
||||
timeout = _timeout_seconds()
|
||||
raw = os.environ.get(FREE_TIER_WAIT_BUDGET_ENV, "").strip()
|
||||
if not raw:
|
||||
return timeout
|
||||
try:
|
||||
return max(0.0, min(float(raw), timeout))
|
||||
except ValueError:
|
||||
return timeout
|
||||
|
||||
|
||||
def schema() -> dict[str, Any]:
|
||||
request_example = {
|
||||
**PARITY_CASES["beijing_first_use_demo"],
|
||||
@@ -1903,6 +1915,7 @@ def _acquire_free_tier_slot(request_url: str) -> dict[str, Any]:
|
||||
}
|
||||
|
||||
waited_seconds = 0.0
|
||||
budget = _free_tier_wait_budget_seconds()
|
||||
with _FREE_TIER_REQUEST_LOCK:
|
||||
while True:
|
||||
now = time.monotonic()
|
||||
@@ -1913,6 +1926,16 @@ def _acquire_free_tier_slot(request_url: str) -> dict[str, Any]:
|
||||
_FREE_TIER_REQUEST_TIMESTAMPS.append(now)
|
||||
break
|
||||
sleep_seconds = max(window_seconds - (now - _FREE_TIER_REQUEST_TIMESTAMPS[0]), 0.0)
|
||||
if waited_seconds + sleep_seconds > budget:
|
||||
return {
|
||||
"mode": "free_tier_budget_exceeded",
|
||||
"queue_active": False,
|
||||
"waited_seconds": round(waited_seconds, 6),
|
||||
"window_seconds": window_seconds,
|
||||
"max_requests": max_requests,
|
||||
"wait_budget_seconds": budget,
|
||||
"degraded_reason": "free_tier_rate_limited",
|
||||
}
|
||||
waited_seconds += sleep_seconds
|
||||
if sleep_seconds > 0:
|
||||
time.sleep(sleep_seconds)
|
||||
@@ -1963,6 +1986,15 @@ def _post_json_with_retry(endpoint: str, request_preview: dict[str, Any]) -> tup
|
||||
for attempt in range(1, max_attempts + 1):
|
||||
try:
|
||||
rate_limit_metadata = _acquire_free_tier_slot(request_url)
|
||||
if rate_limit_metadata.get("mode") == "free_tier_budget_exceeded":
|
||||
return {
|
||||
"Status": "Fail",
|
||||
"Payload": {"reason": "free_tier_rate_limited"},
|
||||
"source_metadata": {
|
||||
"free_tier_rate_limit": rate_limit_metadata,
|
||||
"degraded_reason": "free_tier_rate_limited",
|
||||
},
|
||||
}, attempt, retry_error_codes
|
||||
payload = _post_json(endpoint, request_preview)
|
||||
if not isinstance(payload, dict):
|
||||
return {}, attempt, retry_error_codes
|
||||
@@ -2176,9 +2208,12 @@ def _official_snapshot_budget_exhausted_bundle(reason: str) -> dict[str, Any]:
|
||||
def _payload_status(payload: dict[str, Any]) -> str:
|
||||
if not isinstance(payload, dict):
|
||||
return "invalid"
|
||||
metadata = payload.get("source_metadata") if isinstance(payload.get("source_metadata"), dict) else {}
|
||||
if metadata.get("degraded_reason") == "free_tier_rate_limited":
|
||||
return "rate_limited"
|
||||
if str(payload.get("Status") or "").lower() == "fail":
|
||||
failure_text = json.dumps(payload.get("Payload"), ensure_ascii=False).lower()
|
||||
if "rate limit" in failure_text or "calls/minute" in failure_text or "too many requests" in failure_text:
|
||||
if "rate limit" in failure_text or "calls/minute" in failure_text or "too many requests" in failure_text or "free_tier_rate_limited" in failure_text:
|
||||
return "rate_limited"
|
||||
return "ok" if payload.get("Status") == "Pass" else "fail"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user