from typing import Literal, TypedDict import aiohttp from astrbot.core import logger from astrbot.core.utils.http_ssl import build_tls_connector class LLMModalities(TypedDict): input: list[Literal["text", "image", "audio", "video"]] output: list[Literal["text", "image", "audio", "video"]] class LLMLimit(TypedDict): context: int output: int class LLMMetadata(TypedDict): id: str reasoning: bool tool_call: bool knowledge: str release_date: str modalities: LLMModalities open_weights: bool limit: LLMLimit LLM_METADATAS: dict[str, LLMMetadata] = {} async def update_llm_metadata() -> None: url = "https://models.dev/api.json" try: async with aiohttp.ClientSession( trust_env=True, connector=build_tls_connector() ) as session: async with session.get(url) as response: data = await response.json() global LLM_METADATAS models = {} for info in data.values(): for model in info.get("models", {}).values(): model_id = model.get("id") if not model_id: continue models[model_id] = LLMMetadata( id=model_id, reasoning=model.get("reasoning", False), tool_call=model.get("tool_call", False), knowledge=model.get("knowledge", "none"), release_date=model.get("release_date", ""), modalities=model.get( "modalities", {"input": [], "output": []} ), open_weights=model.get("open_weights", False), limit=model.get("limit", {"context": 0, "output": 0}), ) # Replace the global cache in-place so references remain valid LLM_METADATAS.clear() LLM_METADATAS.update(models) logger.info(f"Successfully fetched metadata for {len(models)} LLMs.") except Exception as e: logger.error(f"Failed to fetch LLM metadata: {e}") return