Files
server-core/app/services/compute_catalog.py
T

98 lines
3.8 KiB
Python
Raw Normal View History

# -*- coding: utf-8 -*-
"""算力中心模型目录:把 compute 引擎的 admin 模型(models 表,status=1)映射为桌面端可读形状。
替代原静态 pineagents_catalogOPC 桌面端展示/使用的模型 = 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"]]