Files
chenlin f3a8a38acd
web-platform-ci / Standalone decision service (no cloud credentials) (push) Has been cancelled
web-platform-ci / TypeScript, lint, unit, build (push) Has been cancelled
web-platform-ci / Playwright E2E (push) Has been cancelled
lekiwi-compatibility / cpu-compatibility (push) Has been cancelled
web-platform-ci / Standalone decision service (no cloud credentials) (pull_request) Has been cancelled
web-platform-ci / TypeScript, lint, unit, build (pull_request) Has been cancelled
web-platform-ci / Playwright E2E (pull_request) Has been cancelled
lekiwi-compatibility / cpu-compatibility (pull_request) Has been cancelled
feat: release v1.0.1 CADWorld 网站与 LeKiwi 智能抓放
集成同源 BYOK 会话隔离、精简模型设置、官方订阅入口和 HTTPS 发布运维;保留本地训练/调参与控制能力。同步 npm 版本及 CHANGELOG,记录公网真实 API 验收仍待用户凭据。
2026-09-24 09:57:41 +08:00

71 lines
2.7 KiB
Python

"""Bounded public model metadata, never an arbitrary URL proxy or paid request."""
import asyncio
import re
import time
import aiohttp
from .protocol import loads
from .web_config import DEEPSEEK_MODELS
class ModelCatalog:
def __init__(self):
self.models = {}
self.checked_at = 0
self.available = False
self.lock = asyncio.Lock()
async def refresh(self, session):
if self.lock.locked():
return # Don't accumulate an unbounded queue while the upstream is unavailable.
async with self.lock:
if time.monotonic() - self.checked_at < 300:
return
self.checked_at = time.monotonic()
try:
async with session.get(
"https://openrouter.ai/api/v1/models",
allow_redirects=False,
timeout=aiohttp.ClientTimeout(total=10),
) as response:
if response.status != 200:
raise ValueError("catalog_unavailable")
raw = bytearray()
async for chunk in response.content.iter_chunked(65536):
raw.extend(chunk)
if len(raw) > 8 * 1024 * 1024:
raise ValueError("catalog_too_large")
result = loads(raw.decode("utf-8"))
models = {}
for item in result.get("data", []):
ident = item.get("id")
if (
isinstance(ident, str)
and re.fullmatch(r"[A-Za-z0-9_./:-]{1,128}", ident)
and "structured_outputs" in item.get("supported_parameters", [])
):
models[ident] = str(item.get("name", ident))[:160]
if (
len(models) >= 256
): # Keep the public response within the browser byte limit.
break
if not models:
raise ValueError("no_structured_models")
self.models, self.available = models, True
except Exception:
# Never use upstream text in a response; keep only a bounded known-good catalog.
self.available = False
def public(self):
return {
"models": [{"provider": "deepseek", "id": m, "name": m} for m in DEEPSEEK_MODELS]
+ [
{"provider": "openrouter", "id": m, "name": n}
for m, n in sorted(self.models.items())
],
"openrouterAvailable": self.available,
"cached": bool(self.models) and not self.available,
}