feat(openrouter): add live model sync
This commit is contained in:
@@ -2,14 +2,30 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib.resources
|
||||
import json
|
||||
import logging
|
||||
from pathlib import Path
|
||||
|
||||
from utils.env import get_env
|
||||
from utils.file_utils import read_json_file
|
||||
|
||||
from ..shared import ModelCapabilities, ProviderType
|
||||
from .base import CAPABILITY_FIELD_NAMES, CapabilityModelRegistry
|
||||
|
||||
|
||||
class OpenRouterModelRegistry(CapabilityModelRegistry):
|
||||
"""Capability registry backed by ``conf/openrouter_models.json``."""
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class OpenRouterModelRegistry(CapabilityModelRegistry):
|
||||
LIVE_ENV_VAR_NAME = "OPENROUTER_LIVE_MODELS_CONFIG_PATH"
|
||||
LIVE_DEFAULT_FILENAME = "openrouter_models_live.json"
|
||||
|
||||
def __init__(self, config_path: str | None = None, live_config_path: str | None = None) -> None:
|
||||
self._live_use_resources = False
|
||||
self._live_config_path: Path | None = None
|
||||
self._live_default_path = Path(__file__).resolve().parents[3] / "conf" / self.LIVE_DEFAULT_FILENAME
|
||||
|
||||
def __init__(self, config_path: str | None = None) -> None:
|
||||
super().__init__(
|
||||
env_var_name="OPENROUTER_MODELS_CONFIG_PATH",
|
||||
default_filename="openrouter_models.json",
|
||||
@@ -18,6 +34,79 @@ class OpenRouterModelRegistry(CapabilityModelRegistry):
|
||||
config_path=config_path,
|
||||
)
|
||||
|
||||
if live_config_path:
|
||||
self._live_config_path = Path(live_config_path)
|
||||
else:
|
||||
env_path = get_env(self.LIVE_ENV_VAR_NAME)
|
||||
if env_path:
|
||||
self._live_config_path = Path(env_path)
|
||||
else:
|
||||
try:
|
||||
resource = importlib.resources.files("conf").joinpath(self.LIVE_DEFAULT_FILENAME)
|
||||
if hasattr(resource, "read_text"):
|
||||
self._live_use_resources = True
|
||||
else:
|
||||
raise AttributeError("resource accessor not available")
|
||||
except Exception:
|
||||
self._live_config_path = self._live_default_path
|
||||
|
||||
self.reload()
|
||||
|
||||
def reload(self) -> None:
|
||||
live_data = self._load_live_config_data()
|
||||
curated_data = self._load_config_data()
|
||||
merged_data = self._merge_manifest_data(live_data, curated_data)
|
||||
|
||||
self._extras = {}
|
||||
configs = [config for config in self._parse_models(merged_data) if config is not None]
|
||||
self._build_maps(configs)
|
||||
|
||||
def _load_live_config_data(self) -> dict:
|
||||
if self._live_use_resources:
|
||||
try:
|
||||
resource = importlib.resources.files("conf").joinpath(self.LIVE_DEFAULT_FILENAME)
|
||||
if hasattr(resource, "read_text"):
|
||||
config_text = resource.read_text(encoding="utf-8")
|
||||
else:
|
||||
with resource.open("r", encoding="utf-8") as handle:
|
||||
config_text = handle.read()
|
||||
data = json.loads(config_text)
|
||||
except FileNotFoundError:
|
||||
logger.debug("Packaged %s not found", self.LIVE_DEFAULT_FILENAME)
|
||||
return {"models": []}
|
||||
except Exception as exc:
|
||||
logger.warning("Failed to read packaged %s: %s", self.LIVE_DEFAULT_FILENAME, exc)
|
||||
return {"models": []}
|
||||
return data or {"models": []}
|
||||
|
||||
if not self._live_config_path:
|
||||
return {"models": []}
|
||||
|
||||
if not self._live_config_path.exists():
|
||||
logger.debug("OpenRouter live registry config not found at %s", self._live_config_path)
|
||||
return {"models": []}
|
||||
|
||||
data = read_json_file(str(self._live_config_path))
|
||||
return data or {"models": []}
|
||||
|
||||
@staticmethod
|
||||
def _merge_manifest_data(live_data: dict, curated_data: dict) -> dict:
|
||||
merged_models: dict[str, dict] = {}
|
||||
|
||||
for source in (live_data, curated_data):
|
||||
for raw in source.get("models", []):
|
||||
if not isinstance(raw, dict):
|
||||
continue
|
||||
|
||||
model_name = raw.get("model_name")
|
||||
if not model_name:
|
||||
continue
|
||||
|
||||
existing = merged_models.get(model_name, {})
|
||||
merged_models[model_name] = {**existing, **dict(raw)}
|
||||
|
||||
return {"models": list(merged_models.values())}
|
||||
|
||||
def _finalise_entry(self, entry: dict) -> tuple[ModelCapabilities, dict]:
|
||||
provider_override = entry.get("provider")
|
||||
if isinstance(provider_override, str):
|
||||
|
||||
Reference in New Issue
Block a user