anuma2api / app /upstream /models.py
li2895's picture
自包含构建源: app/registrar/scripts/pyproject + 修复 COPY 上下文
fa1140b
Raw
History Blame Contribute Delete
5.64 kB
"""anuma.ai ModelRegistry:模型目录(探测自 GET portal.anuma.ai/api/v1/curated-models)。
字段:id(catalog id,即上游 curated-models 的 id 如 ``kimi/kimi-k3``)/
name / owner(provider)/ upstream_id(Fireworks 格式 ``accounts/fireworks/models/<短名>``)。
⚠️ 列表为 2026-08-01 探测快照;上游增删模型时用 scripts/probe_catalog.py 重新生成。
"""
from __future__ import annotations
import re
from typing import Any
from app.upstream.base import ModelRegistry
__all__ = [
"DEFAULT_MODEL", "MODEL_CATALOG", "IMAGE_MODEL_CATALOG",
"normalize_image_model", "DefaultModelRegistry",
]
DEFAULT_MODEL = "openai/gpt-5.6-luna" # 上游未识别 model 时的回退模型(实测所见)
# 生图模型(2026-08-08 从 chat.anuma.ai HAR 抓取):/v1/images 路由用。
# key = AnumaMediaMCP-anuma_create_image 工具 model 参数值;name = 对外展示名。
# 前缀 * 为 GPT-5.6 Luna 生图实测可用模型;tier alias(auto/anuma-*)由上游按订阅分层。
IMAGE_MODEL_CATALOG: list[dict[str, Any]] = [
{"id": "gpt-image-2", "name": "GPT Image 2"},
{"id": "flux-2-pro", "name": "Flux 2 Pro"},
{"id": "flux-2-klein-4b", "name": "Flux 2 Klein 4B"},
{"id": "grok-imagine", "name": "Grok Imagine"},
{"id": "nano-banana", "name": "Nano Banana"},
{"id": "nano-banana-pro", "name": "Nano Banana Pro"},
{"id": "nano-banana-2", "name": "Nano Banana 2"},
{"id": "anuma-flash", "name": "Anuma Flash (tier alias)"},
{"id": "anuma-pro", "name": "Anuma Pro (tier alias)"},
{"id": "anuma-flash-private", "name": "Anuma Flash Private (tier alias)"},
{"id": "anuma-pro-private", "name": "Anuma Pro Private (tier alias)"},
]
_IMAGE_BY_KEY: dict[str, dict[str, Any]] = {}
for _im in IMAGE_MODEL_CATALOG:
_IMAGE_BY_KEY[_im["id"]] = _im
_IMAGE_BY_KEY[_im["name"].lower()] = _im
def normalize_image_model(model: str | None) -> str | None:
"""客户端传入的 gen 图片模型 → 上游 tool 参数值;空/未知 → None(由客户端容错)。"""
if not model:
return None
if model in _IMAGE_BY_KEY:
return _IMAGE_BY_KEY[model]["id"]
low = model.lower()
if low in _IMAGE_BY_KEY:
return _IMAGE_BY_KEY[low]["id"]
norm = re.sub(r"[^a-z0-9]", "", low)
for m in IMAGE_MODEL_CATALOG:
if re.sub(r"[^a-z0-9]", "", m["id"]) == norm:
return m["id"]
return None
# 探测自 /api/v1/curated-models(active=true 的 text/vision 模型为主)
MODEL_CATALOG: list[dict[str, Any]] = [
{"id": "inclusionai/ling-2.6-flash", "name": "Ling 2.6 Flash", "owner": "InclusionAI", "upstream_id": "accounts/fireworks/models/ling-2.6-flash"},
{"id": "kimi/kimi-k3", "name": "Kimi K3", "owner": "Kimi", "upstream_id": "accounts/fireworks/models/kimi-k3"},
{"id": "kimi/kimi-k2.7-code", "name": "Kimi K2.7 Code", "owner": "Kimi", "upstream_id": "accounts/fireworks/models/kimi-k2.7-code"},
{"id": "kimi/kimi-k2.6", "name": "Kimi 2.6", "owner": "Kimi", "upstream_id": "accounts/fireworks/models/kimi-k2.6"},
{"id": "openai/gpt-5.6-sol", "name": "GPT 5.6 Sol", "owner": "OpenAI", "upstream_id": "accounts/fireworks/models/gpt-5.6-sol"},
{"id": "openai/gpt-5.6-terra", "name": "GPT 5.6 Terra", "owner": "OpenAI", "upstream_id": "accounts/fireworks/models/gpt-5.6-terra"},
{"id": "openai/gpt-5.6-luna", "name": "GPT 5.6 Luna", "owner": "OpenAI", "upstream_id": "accounts/fireworks/models/gpt-5.6-luna"},
{"id": "anthropic/claude-sonnet-5", "name": "Claude Sonnet 5", "owner": "Anthropic", "upstream_id": "accounts/fireworks/models/claude-sonnet-5"},
{"id": "anthropic/claude-opus-5", "name": "Claude Opus 5", "owner": "Anthropic", "upstream_id": "accounts/fireworks/models/claude-opus-5"},
{"id": "openai/gpt-5.5", "name": "GPT 5.5", "owner": "OpenAI", "upstream_id": "accounts/fireworks/models/gpt-5.5"},
{"id": "qwen/qwen-3.7-plus", "name": "Qwen 3.7 Plus", "owner": "Qwen", "upstream_id": "accounts/fireworks/models/qwen-3.7-plus"},
{"id": "qwen/qwen-3.6-plus", "name": "Qwen 3.6 Plus", "owner": "Qwen", "upstream_id": "accounts/fireworks/models/qwen-3.6-plus"},
{"id": "glm/glm-5.2", "name": "GLM 5.2", "owner": "Zhipu", "upstream_id": "accounts/fireworks/models/glm-5.2"},
{"id": "minimax/minimax-m3", "name": "MiniMax M3", "owner": "MiniMax", "upstream_id": "accounts/fireworks/models/minimax-m3"},
{"id": "minimax/minimax-m2.7", "name": "Minimax 2.7", "owner": "MiniMax", "upstream_id": "accounts/fireworks/models/minimax-m2.7"},
]
_BY_KEY: dict[str, dict[str, Any]] = {}
for _m in MODEL_CATALOG:
_BY_KEY[_m["id"]] = _m
_BY_KEY[_m["name"].lower()] = _m
class DefaultModelRegistry(ModelRegistry):
def catalog(self) -> list[dict[str, Any]]:
return list(MODEL_CATALOG)
def normalize(self, model: str | None) -> str:
"""客户端传入的 model → catalog id;空或未知 → 默认。匹配:精确 id > 显示名(小写) > 模糊。"""
if not model:
return DEFAULT_MODEL
if model in _BY_KEY:
return _BY_KEY[model]["id"]
low = model.lower()
if low in _BY_KEY:
return _BY_KEY[low]["id"]
norm = re.sub(r"[^a-z0-9]", "", low)
for m in MODEL_CATALOG:
if (re.sub(r"[^a-z0-9]", "", m["id"]) == norm
or re.sub(r"[^a-z0-9]", "", m["name"].lower()) == norm):
return m["id"]
return DEFAULT_MODEL
def upstream_id_for(self, model_id: str) -> str | None:
m = _BY_KEY.get(model_id)
return m["upstream_id"] if m else None