Files
clawdmeter/tools/probe_zai.py
T
wenilandClaude Opus 4.8 ac0742d92c v3 M3: z.ai (GLM) provider — Anthropic-compatible poll from a pasted API key
ZaiProvider replaces the zai stub. It reaches z.ai's Anthropic-compatible
endpoint (default https://api.z.ai/api/anthropic) exactly like Claude Code with
ANTHROPIC_BASE_URL pointed at z.ai: a /v1/messages POST with a Bearer key. The
key + base URL come from the config the control panel's z.ai field writes
(providers.zai.base_url/.api_key) — no OAuth, no local files.

Two z.ai-specific cares: (1) static API key, no self-refresh; (2) the GLM Coding
Plan meters by prompts, so a 60s poll would drain quota — the provider
self-throttles to one network call per poll_interval_s (default 15 min) and
serves a cached status in between. Rate-limit parsing assumes z.ai proxies
Anthropic's unified headers (5h->s/sr, 7d->w/wr); if a real response lacks them
it still reports connected and logs the limit-ish headers it did return, so the
mapping can be finalized against a live key.

tools/probe_zai.py dumps a real z.ai response's headers (key via env, never
printed) to pin the exact header names. Graceful without a key: selecting z.ai
shows the blue theme + "no key", never crashes the loop. Registry: all three
providers now real; unknown ids fall back to StubProvider. +7 z.ai tests (mocked
transport: header mapping, auth, throttle, fallback); 120 passed, 2 Linux-only.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 07:27:34 +03:00

55 lines
1.9 KiB
Python

#!/usr/bin/env python3
"""One-off diagnostic: see exactly what z.ai's Anthropic-compatible endpoint
returns, so the ZaiProvider's rate-limit header mapping can be finalized against
a real response (the header names aren't documented).
Usage (key via env so it never lands in shell history):
ZAI_API_KEY=... python tools/probe_zai.py [base_url] [model]
Defaults: base = https://api.z.ai/api/anthropic , model = glm-4.6
Prints the HTTP status and the FULL response header set (highlighting any header
mentioning limit/rate/quota/usage/reset), plus a short body preview. Sends a
1-token "hi" — on a prompt-metered plan that is one prompt, so run it sparingly.
The API key is read from the environment and never printed.
"""
import json
import os
import sys
import httpx
BASE = (sys.argv[1] if len(sys.argv) > 1 else "https://api.z.ai/api/anthropic").rstrip("/")
MODEL = sys.argv[2] if len(sys.argv) > 2 else "glm-4.6"
KEY = os.environ.get("ZAI_API_KEY") or os.environ.get("ANTHROPIC_AUTH_TOKEN")
if not KEY:
sys.exit("Set ZAI_API_KEY in the environment first (it is not printed).")
url = f"{BASE}/v1/messages"
headers = {
"Authorization": f"Bearer {KEY}",
"anthropic-version": "2023-06-01",
"Content-Type": "application/json",
}
body = {"model": MODEL, "max_tokens": 1, "messages": [{"role": "user", "content": "hi"}]}
print(f"POST {url} (model={MODEL})")
try:
resp = httpx.post(url, headers=headers, json=body, timeout=30.0)
except httpx.HTTPError as e:
sys.exit(f"request failed: {e}")
print(f"HTTP {resp.status_code}\n")
INTEREST = ("limit", "rate", "quota", "usage", "reset", "remaining")
print("--- response headers ---")
for k in sorted(resp.headers.keys()):
mark = " <<<" if any(w in k.lower() for w in INTEREST) else ""
print(f" {k}: {resp.headers[k]}{mark}")
print("\n--- body preview ---")
try:
print(json.dumps(resp.json(), ensure_ascii=False)[:600])
except ValueError:
print(resp.text[:600])