Files
hack/tools/scripts/import_cn_batch2.py
T

133 lines
4.0 KiB
Python

#!/usr/bin/env python3
"""Import the 2026-08-03 CN batch2 usable keys into NewAPI.
All four upstreams (iflytek_maas, dmxapi, aihubmix) are standard
OpenAI-compatible /v1 endpoints, so each channel uses type=1
(TYPE_OPENAI) and the base_url has the trailing /v1 stripped —
NewAPI appends /v1/chat/completions itself for type 1.
Each key gets its own channel. The full upstream model list (fetched
live via GET /v1/models) is synced into the channel's `models` field.
"""
import json
import sys
import time
from pathlib import Path
sys.path.insert(0, str(Path(__file__).resolve().parent))
from newapi_client import (
add_channel, list_channels, get_channel, update_channel,
test_channel, TYPE_OPENAI,
)
HUNTER = Path(__file__).resolve().parent / "llm-key-hunter"
VAULT = HUNTER / "usable_keys"
# (provider label, vault json, base_url WITHOUT /v1)
# All are standard /v1 OpenAI-compatible -> type=1, no /v1 suffix.
SOURCES = [
("IFlyTekMaaS", VAULT / "iflytek_maas" / "keys.json",
"https://maas-api.cn-huabei-1.xf-yun.com"),
("DMXAPI", VAULT / "dmxapi" / "keys.json",
"https://www.dmxapi.cn"),
("AIHubMix", VAULT / "aihubmix" / "keys.json",
"https://aihubmix.com"),
]
def load_usable():
"""Yield (label, key, models, base_url) for each USABLE vault entry."""
for label, path, base_url in SOURCES:
data = json.loads(path.read_text())
for entry in data:
if entry.get("status") != "USABLE":
continue
models = entry.get("models") or []
if not models:
continue
yield label, entry["key"], models, base_url
def existing_keys():
"""Return set of API keys already configured across all channels."""
out = set()
for ch in list_channels():
try:
full = get_channel(ch["id"]) or {}
k = full.get("key", "")
if k:
out.add(k)
except Exception:
pass
return out
def main():
entries = list(load_usable())
print(f"Usable vault entries to import: {len(entries)}")
have = existing_keys()
print(f"Existing channel keys: {len(have)}\n")
added, skipped, failed = [], 0, 0
for label, key, models, base_url in entries:
if key in have:
print(f" SKIP {label}_{key[-6:]} (key already in NewAPI)")
skipped += 1
continue
name = f"{label}_{key[-6:]}"
models_csv = ",".join(models)
channel = {
"name": name,
"type": TYPE_OPENAI, # standard /v1 -> OpenAI type
"key": key,
"base_url": base_url, # NO /v1 suffix per LO's rule
"models": models_csv,
"groups": ["default"],
"model_mapping": "",
"priority": 0,
"weight": 0,
"auto_ban": 1,
}
ok, msg = add_channel(channel)
if not ok:
print(f" ADD FAIL {name}: {msg}")
failed += 1
continue
# locate the new channel id
new_id = None
for ch in list_channels():
if ch["name"] == name:
new_id = ch["id"]
break
if new_id is None:
print(f" {name} added but id not found")
failed += 1
continue
t_ok, t_msg, _ = test_channel(new_id)
if t_ok:
print(f" ✓ {name} ID={new_id} models={len(models)} [{t_msg[:60]}]")
added.append((new_id, name, len(models)))
else:
update_channel(new_id, status=2) # auto-disable on test failure
print(f" ✗ {name} ID={new_id} DISABLED: {t_msg[:80]}")
failed += 1
time.sleep(0.3)
print("\n=== Summary ===")
print(f" Added & test-passed: {len(added)}")
print(f" Failed/auto-disabled: {failed}")
print(f" Skipped (duplicate): {skipped}")
for cid, name, n in added:
print(f" ID={cid} {name} ({n} models)")
if __name__ == "__main__":
main()