textmachine/eval/role_topology/list_models.py

68 lines
3.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""Ф1 шаг 0 — ЖИВОЙ листинг моделей по каждому ключу. $0 (GET /models не биллится).
Зачем отдельным скриптом и почему первым. Гардрейл CLAUDE.md: «слаги моделей не менять без live-
фактчека `/models`», и quirks 00 несёт отдельный урок календаря — «слаг живой ≠ модель та же»
(DeepSeek 31.07 сменил веса под неизменным слагом, D39.61). Пре-рег обязан назвать кандидатов
слагами, снятыми В ДЕНЬ ЗАПУСКА, иначе смета и вердикты Ф1 повисают на памяти сессии.
Ключи не печатаются никогда — скрипт выводит только имена моделей и HTTP-код.
`.env` читается через python-dotenv; для Claude файл закрыт deny-правилом, и это правильно.
"""
from __future__ import annotations
import json
import os
from pathlib import Path
import requests
from dotenv import load_dotenv
REPO = Path("/home/ubuntu/projects/textmachine")
load_dotenv(REPO / "eval" / ".env")
OUT = Path.home() / "books" / "role-topology"
OUT.mkdir(parents=True, exist_ok=True)
# (провайдер, base_url, env-ключ) — источник: quirks 00 «Эндпоинты и модели»; семь ключей,
# перечисленных промтом эксп-21 Ф1.
PROVIDERS = [
("deepseek", "https://api.deepseek.com/v1", "DEEPSEEK_API_KEY"),
("zai", "https://api.z.ai/api/paas/v4", "ZAI_API_KEY"),
("kimi", "https://api.moonshot.ai/v1", "KIMI_API_KEY"),
("openai", "https://api.openai.com/v1", "OPENAI_API_KEY"),
("gemini", "https://generativelanguage.googleapis.com/v1beta/openai", "GEMINI_API_KEY"),
("xai", "https://api.x.ai/v1", "XAI_API_KEY"),
("mistral", "https://api.mistral.ai/v1", "MISTRAL_API_KEY"),
]
def main() -> None:
snap = {}
for name, base, env in PROVIDERS:
key = os.getenv(env)
if not key:
print(f"{name:10s} КЛЮЧА НЕТ ({env}) — провайдер вне скрина, объявить в пре-реге")
snap[name] = {"error": "no key"}
continue
try:
r = requests.get(f"{base}/models", headers={"Authorization": f"Bearer {key}"},
timeout=30)
except Exception as e: # noqa: BLE001
print(f"{name:10s} СЕТЬ: {type(e).__name__}")
snap[name] = {"error": type(e).__name__}
continue
if r.status_code != 200:
print(f"{name:10s} HTTP {r.status_code}")
snap[name] = {"error": f"http {r.status_code}", "body": r.text[:200]}
continue
ids = sorted(m["id"] for m in r.json().get("data", []))
snap[name] = {"models": ids}
print(f"{name:10s} {len(ids):3d}: {', '.join(ids[:40])}")
if len(ids) > 40:
print(f"{'':10s} …ещё {len(ids) - 40}")
(OUT / "models-live.json").write_text(json.dumps(snap, ensure_ascii=False, indent=1),
encoding="utf-8")
print(f"\nснимок → {OUT / 'models-live.json'}")
if __name__ == "__main__":
main()