# -*- coding: utf-8 -*- """算力中心模型目录:把 compute 引擎的 admin 模型(models 表,status=1)映射为桌面端可读形状。 替代原静态 pineagents_catalog:OPC 桌面端展示/使用的模型 = admin 端新增的模型(算力中心)。 """ from __future__ import annotations import json from . import compute_client def _parse_list(v): """JSON 数组字符串 → list;已 list 原样。""" if isinstance(v, list): return v if isinstance(v, str): try: a = json.loads(v) return a if isinstance(a, list) else [] except Exception: # noqa: BLE001 return [] return [] def _map(row: dict) -> dict: """Model 行 → 桌面端模型形状。""" name = row.get("name") or row.get("model_name") or "" return { "id": row.get("model_name") or row.get("name") or "", "model_name": row.get("model_name", ""), "name": name, "actual_model": row.get("actual_model") or row.get("model_name", ""), "company": "", "group": row.get("group", "default"), "billing_unit": row.get("billing_unit", "/百万tokens"), "input_price": row.get("input_price", 0) or 0, "output_price": row.get("output_price", 0) or 0, "cache_hit_price": row.get("cache_hit_price", 0) or 0, "vision_support": bool(row.get("vision_support", False)), "image_support": bool(row.get("image_support", False)), "audio_support": bool(row.get("audio_support", False)), "video_support": bool(row.get("video_support", False)), "tags": (row.get("tags") or "").split(",") if row.get("tags") else [], "vendor_id": row.get("vendor_id", 0), # 富字段(models.json 语义) "icon": row.get("icon", ""), "icon_file": row.get("icon_file", ""), "modality": row.get("modality", ""), "input_forms": _parse_list(row.get("input_forms")), "output_forms": _parse_list(row.get("output_forms")), "function_icons": _parse_list(row.get("function_icons")), "url": row.get("url", ""), "author": row.get("author", ""), "author_icon": row.get("author_icon", ""), "supplier": row.get("supplier", ""), "supplier_icon": row.get("supplier_icon", ""), "price_is_range": bool(row.get("price_is_range", False)), "input_price_min": row.get("input_price_min", 0) or 0, "input_price_max": row.get("input_price_max", 0) or 0, "output_price_min": row.get("output_price_min", 0) or 0, "output_price_max": row.get("output_price_max", 0) or 0, "context_window": row.get("context_window", 0) or 0, "context_window_unit": row.get("context_window_unit", "M"), "context_window_display": row.get("context_window_display", ""), "max_output": row.get("max_output", 0) or 0, "max_output_unit": row.get("max_output_unit", "K"), "max_output_display": row.get("max_output_display", ""), "free_trial": bool(row.get("free_trial", False)), } async def catalog() -> list[dict]: """从 compute 引擎拉取 admin 模型(status=1)并映射。空/不可达 → 返回空列表。""" try: items = await compute_client.list_models(status=1) except compute_client.ComputeError: return [] return [_map(row) for row in items if (row.get("status", 1) == 1)] async def models() -> list[dict]: return await catalog() async def prices() -> list[dict]: return [ {"id": m["id"], "name": m["name"], "input": m["input_price"], "output": m["output_price"], "cache_hit": m["cache_hit_price"], "unit": m["billing_unit"]} for m in await catalog() ] async def model_ids() -> list[str]: """供 /v1/models 使用:admin 模型的对外 id。""" return [m["id"] for m in await catalog() if m["id"]]