#!/usr/bin/env python3 """Re-verify the 32 old VolcanoArk channels directly, delete, and recreate live ones with correct type=8 full-path config (PAYGO/CODING/AGENT separated).""" import sys, json, time, re from pathlib import Path from concurrent.futures import ThreadPoolExecutor, as_completed sys.path.insert(0, str(Path(__file__).resolve().parent)) import newapi_client as n import importlib.util spec = importlib.util.spec_from_file_location( "r", str(Path(__file__).resolve().parent / "reconcile_newapi.py")) R = importlib.util.module_from_spec(spec); spec.loader.exec_module(R) chans = {c["id"]: c for c in json.load(open("/tmp/chans.json"))} volc = [c for c in chans.values() if "VolcanoArk_" in c.get("name", "") and "CodingPlan" not in c["name"]] all_keys = R.load_all_keys() def findkey(name): suf = name.rsplit("_", 1)[-1].lower() for k in all_keys: if k["prov"] == "volcanoark" and (k["key"].lower()[-4:] == suf or k["key"].lower()[-6:].endswith(suf)): return k["key"] return None def mkch(name, ctype, base, key, models, test_model): return dict(name=name, type=ctype, key=key, base_url=base, models=models, test_model=test_model, model_mapping="", groups=["default"], group="default", priority=0, weight=0, auto_ban=1) print(f"re-verifying {len(volc)} old VolcanoArk channels direct...") def work(c): key = findkey(c["name"]) if not key: return c["id"], c["name"], None, "NO_KEY" v, detail, info = R.verify_volcano(key) return c["id"], c["name"], key, (v, detail, info) results = [] with ThreadPoolExecutor(max_workers=12) as ex: futs = [ex.submit(work, c) for c in volc] for f in as_completed(futs): results.append(f.result()) if len(results) % 10 == 0: print(f" {len(results)}/{len(volc)}") live, dead, other = [], [], [] for cid, name, key, res in results: if key is None: other.append((cid, name, res)); continue v, detail, info = res if v == "USABLE": live.append((cid, name, key, info, detail)) elif v == "DEAD": dead.append((cid, name, detail)) else: other.append((cid, name, detail)) print(f"\nUSABLE={len(live)} DEAD={len(dead)} OTHER={len(other)}") for cid, name, key, info, detail in live: print(f" + {cid} {name}: coding={info.get('coding')} agent={info.get('agent')} paygo={info.get('paygo')}") for cid, name, d in dead: print(f" x DEAD {cid} {name}: {d[:60]}") for cid, name, d in other: print(f" ? {cid} {name}: {str(d)[:60]}") # delete all old, recreate live print("\nrecreating live channels...") created = 0 for cid, name, key, info, detail in live: n.delete_channel(cid); time.sleep(0.15) suf = name.rsplit("_", 1)[-1] newids = [] if info.get("coding"): ch = mkch(f"VolcanoCoding_{suf}", n.TYPE_ANTHROPIC, "https://ark.cn-beijing.volces.com/api/coding", key, "ark-code-latest", "ark-code-latest") ok, msg = n.add_channel(ch) if ok: created += 1; print(f" VolcanoCoding_{suf} OK") if info.get("agent"): ch = mkch(f"VolcanoAgent_{suf}", n.TYPE_CUSTOM, "https://ark.cn-beijing.volces.com/api/plan/v3/chat/completions", key, "ark-code-latest", "ark-code-latest") ok, msg = n.add_channel(ch) if ok: created += 1; print(f" VolcanoAgent_{suf} OK") for model in info.get("paygo", []): tag = model.split("-")[-1][:6] ch = mkch(f"VolcanoPAYGO_{suf}_{tag}", n.TYPE_CUSTOM, "https://ark.cn-beijing.volces.com/api/v3/chat/completions", key, model, model) ok, msg = n.add_channel(ch) if ok: created += 1; print(f" VolcanoPAYGO_{suf}_{tag} ({model}) OK") time.sleep(0.2) # delete dead too for cid, name, d in dead: n.delete_channel(cid); print(f" deleted dead {cid} {name}") time.sleep(0.1) print(f"\n=== created {created} channels from {len(live)} live keys, removed {len(dead)} dead ===")