api-key-manager 2.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- api_key_manager-2.1.0.dist-info/METADATA +709 -0
- api_key_manager-2.1.0.dist-info/RECORD +73 -0
- api_key_manager-2.1.0.dist-info/WHEEL +5 -0
- api_key_manager-2.1.0.dist-info/entry_points.txt +2 -0
- api_key_manager-2.1.0.dist-info/top_level.txt +1 -0
- key_manager/__init__.py +16 -0
- key_manager/__main__.py +5 -0
- key_manager/api_models.py +358 -0
- key_manager/checker.py +51 -0
- key_manager/cli.py +270 -0
- key_manager/config.py +61 -0
- key_manager/core.py +205 -0
- key_manager/detector.py +335 -0
- key_manager/errors.py +179 -0
- key_manager/i18n.py +142 -0
- key_manager/logger.py +207 -0
- key_manager/model_capabilities.py +412 -0
- key_manager/parser.py +153 -0
- key_manager/providers/__init__.py +283 -0
- key_manager/providers/ai302.py +109 -0
- key_manager/providers/anthropic.py +109 -0
- key_manager/providers/baichuan.py +97 -0
- key_manager/providers/base.py +312 -0
- key_manager/providers/cerebras.py +109 -0
- key_manager/providers/cohere.py +90 -0
- key_manager/providers/cstcloud.py +122 -0
- key_manager/providers/dashscope.py +120 -0
- key_manager/providers/dashscope_coding.py +122 -0
- key_manager/providers/deepseek.py +166 -0
- key_manager/providers/dmxapi.py +109 -0
- key_manager/providers/doubao.py +109 -0
- key_manager/providers/fireworks.py +109 -0
- key_manager/providers/google.py +99 -0
- key_manager/providers/grok.py +109 -0
- key_manager/providers/groq.py +109 -0
- key_manager/providers/huggingface.py +54 -0
- key_manager/providers/hyperbolic.py +109 -0
- key_manager/providers/infini.py +135 -0
- key_manager/providers/infini_coding.py +124 -0
- key_manager/providers/kimi.py +121 -0
- key_manager/providers/kimi_coding.py +124 -0
- key_manager/providers/longcat.py +123 -0
- key_manager/providers/mimo.py +109 -0
- key_manager/providers/mimo_plan.py +140 -0
- key_manager/providers/minimax.py +97 -0
- key_manager/providers/minimax_plan.py +122 -0
- key_manager/providers/mistral.py +109 -0
- key_manager/providers/models_registry.py +2901 -0
- key_manager/providers/modelscope.py +134 -0
- key_manager/providers/nvidia.py +109 -0
- key_manager/providers/ocoolai.py +109 -0
- key_manager/providers/openai.py +140 -0
- key_manager/providers/openrouter.py +119 -0
- key_manager/providers/perplexity.py +109 -0
- key_manager/providers/poe.py +109 -0
- key_manager/providers/ppio.py +109 -0
- key_manager/providers/replicate.py +54 -0
- key_manager/providers/siliconflow.py +121 -0
- key_manager/providers/stepfun.py +132 -0
- key_manager/providers/tencent_hunyuan.py +122 -0
- key_manager/providers/together.py +134 -0
- key_manager/providers/yi.py +97 -0
- key_manager/providers/zai.py +109 -0
- key_manager/providers/zhipu.py +127 -0
- key_manager/providers/zhipu_coding.py +124 -0
- key_manager/proxy.py +70 -0
- key_manager/ssrf.py +68 -0
- key_manager/storage.py +134 -0
- key_manager/tester.py +137 -0
- key_manager/url_override.py +5 -0
- key_manager/validator.py +185 -0
- key_manager/web.py +1512 -0
- key_manager/webhook.py +257 -0
key_manager/validator.py
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import json
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
import httpx
|
|
7
|
+
|
|
8
|
+
from .providers import PROVIDERS
|
|
9
|
+
from .logger import KeyLogger
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
async def validate_keys(keys_file: str = "./data/keys.json",
|
|
13
|
+
results_file: str = "./data/check_results.json",
|
|
14
|
+
logs_dir: str = "./data/logs",
|
|
15
|
+
concurrency: int = 100,
|
|
16
|
+
timeout: int = 30,
|
|
17
|
+
proxy: str = None,
|
|
18
|
+
provider_filter: str = None,
|
|
19
|
+
status_filter: str = None,
|
|
20
|
+
single_key: str = None,
|
|
21
|
+
progress_callback=None) -> dict:
|
|
22
|
+
logger = KeyLogger(logs_dir, "check")
|
|
23
|
+
|
|
24
|
+
with open(keys_file, "r", encoding="utf-8") as f:
|
|
25
|
+
data = json.load(f)
|
|
26
|
+
|
|
27
|
+
keys_to_check = []
|
|
28
|
+
for key, info in data["keys"].items():
|
|
29
|
+
if single_key and key != single_key:
|
|
30
|
+
continue
|
|
31
|
+
if provider_filter and info["provider"].lower() != provider_filter.lower():
|
|
32
|
+
continue
|
|
33
|
+
if status_filter and info["status"] != status_filter:
|
|
34
|
+
continue
|
|
35
|
+
keys_to_check.append((key, info))
|
|
36
|
+
|
|
37
|
+
total = len(keys_to_check)
|
|
38
|
+
results = {
|
|
39
|
+
"run_at": datetime.utcnow().isoformat() + "Z",
|
|
40
|
+
"total": total,
|
|
41
|
+
"summary": {
|
|
42
|
+
"valid": {"count": 0, "keys": []},
|
|
43
|
+
"invalid": {"count": 0, "keys": []},
|
|
44
|
+
"error": {"count": 0, "keys": []}
|
|
45
|
+
},
|
|
46
|
+
"details": [],
|
|
47
|
+
"by_provider": {}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
# Initialize progress
|
|
51
|
+
if progress_callback:
|
|
52
|
+
progress_callback(0, total)
|
|
53
|
+
|
|
54
|
+
semaphore = asyncio.Semaphore(concurrency)
|
|
55
|
+
completed_count = 0
|
|
56
|
+
|
|
57
|
+
async def check_one(client: httpx.AsyncClient, key: str, info: dict):
|
|
58
|
+
nonlocal completed_count
|
|
59
|
+
async with semaphore:
|
|
60
|
+
provider_name = info["provider"].lower()
|
|
61
|
+
provider = PROVIDERS.get(provider_name)
|
|
62
|
+
if not provider:
|
|
63
|
+
from src.providers.base import CheckResult
|
|
64
|
+
result = CheckResult(valid=False, status_code=None, latency_ms=0, error=f"unknown provider: {provider_name}")
|
|
65
|
+
else:
|
|
66
|
+
result = await provider.check(client, key)
|
|
67
|
+
|
|
68
|
+
# Get balance if provider supports it and key is valid
|
|
69
|
+
balance_info = None
|
|
70
|
+
if result.valid and hasattr(provider, 'get_balance'):
|
|
71
|
+
try:
|
|
72
|
+
balance_result = await provider.get_balance(client, key)
|
|
73
|
+
if balance_result.supported and balance_result.balance is not None:
|
|
74
|
+
balance_info = {
|
|
75
|
+
"balance": balance_result.balance,
|
|
76
|
+
"currency": balance_result.currency
|
|
77
|
+
}
|
|
78
|
+
except Exception:
|
|
79
|
+
pass # Balance fetch failed, continue without it
|
|
80
|
+
|
|
81
|
+
# Determine error_type based on status code and error message
|
|
82
|
+
error_type = None
|
|
83
|
+
if not result.valid:
|
|
84
|
+
error_msg = getattr(result, 'error', None) or ""
|
|
85
|
+
status_code = result.status_code
|
|
86
|
+
if status_code in (401, 403):
|
|
87
|
+
error_type = "invalid_key"
|
|
88
|
+
elif status_code == 429:
|
|
89
|
+
error_type = "rate_limited"
|
|
90
|
+
elif status_code == 402:
|
|
91
|
+
error_type = "insufficient_balance"
|
|
92
|
+
elif status_code and status_code >= 500:
|
|
93
|
+
error_type = "server_error"
|
|
94
|
+
elif any(kw in error_msg.lower() for kw in ["balance", "quota"]):
|
|
95
|
+
error_type = "insufficient_balance"
|
|
96
|
+
else:
|
|
97
|
+
error_type = "unknown"
|
|
98
|
+
|
|
99
|
+
# Store balance and error_type in result for later use
|
|
100
|
+
result.balance = balance_info
|
|
101
|
+
result.error_type = error_type
|
|
102
|
+
|
|
103
|
+
completed_count += 1
|
|
104
|
+
if progress_callback:
|
|
105
|
+
progress_callback(completed_count, total)
|
|
106
|
+
|
|
107
|
+
return key, info, result
|
|
108
|
+
|
|
109
|
+
async with httpx.AsyncClient(timeout=timeout, proxy=proxy, follow_redirects=False) as client:
|
|
110
|
+
tasks = [check_one(client, k, i) for k, i in keys_to_check]
|
|
111
|
+
completed = await asyncio.gather(*tasks, return_exceptions=True)
|
|
112
|
+
|
|
113
|
+
for item in completed:
|
|
114
|
+
if isinstance(item, Exception):
|
|
115
|
+
# Handle exceptions from gather
|
|
116
|
+
logger.log("CHECK", "unknown", "unknown", "ERROR", str(item), 0)
|
|
117
|
+
continue
|
|
118
|
+
|
|
119
|
+
key, info, result = item
|
|
120
|
+
key_masked = info["key_masked"]
|
|
121
|
+
provider = info["provider"]
|
|
122
|
+
|
|
123
|
+
# Ensure result has error field
|
|
124
|
+
error_msg = getattr(result, 'error', None) or None
|
|
125
|
+
|
|
126
|
+
if result.valid:
|
|
127
|
+
status = "valid"
|
|
128
|
+
results["summary"]["valid"]["count"] += 1
|
|
129
|
+
results["summary"]["valid"]["keys"].append(key_masked)
|
|
130
|
+
elif result.status_code in (401, 403):
|
|
131
|
+
status = "invalid"
|
|
132
|
+
# Make sure we have an error message for invalid keys
|
|
133
|
+
if not error_msg:
|
|
134
|
+
error_msg = "invalid key"
|
|
135
|
+
results["summary"]["invalid"]["count"] += 1
|
|
136
|
+
results["summary"]["invalid"]["keys"].append(key_masked)
|
|
137
|
+
else:
|
|
138
|
+
status = "error"
|
|
139
|
+
# Make sure we have an error message for errors
|
|
140
|
+
if not error_msg:
|
|
141
|
+
if result.status_code:
|
|
142
|
+
error_msg = f"HTTP {result.status_code}"
|
|
143
|
+
else:
|
|
144
|
+
error_msg = "connection failed"
|
|
145
|
+
results["summary"]["error"]["count"] += 1
|
|
146
|
+
results["summary"]["error"]["keys"].append(key_masked)
|
|
147
|
+
|
|
148
|
+
detail = error_msg or str(result.status_code)
|
|
149
|
+
logger.log("CHECK", provider, key_masked, status.upper(), detail, result.latency_ms / 1000)
|
|
150
|
+
|
|
151
|
+
results["details"].append({
|
|
152
|
+
"key_masked": key_masked,
|
|
153
|
+
"provider": provider,
|
|
154
|
+
"status": status,
|
|
155
|
+
"code": result.status_code,
|
|
156
|
+
"latency_ms": result.latency_ms,
|
|
157
|
+
"error": error_msg,
|
|
158
|
+
"error_type": getattr(result, 'error_type', None),
|
|
159
|
+
"balance": getattr(result, 'balance', None)
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
if provider not in results["by_provider"]:
|
|
163
|
+
results["by_provider"][provider] = {"valid": 0, "invalid": 0, "error": 0}
|
|
164
|
+
results["by_provider"][provider][status] += 1
|
|
165
|
+
|
|
166
|
+
data["keys"][key]["status"] = status
|
|
167
|
+
data["keys"][key]["checks"].append({
|
|
168
|
+
"timestamp": datetime.utcnow().isoformat() + "Z",
|
|
169
|
+
"status": status,
|
|
170
|
+
"status_code": result.status_code,
|
|
171
|
+
"latency_ms": result.latency_ms,
|
|
172
|
+
"error": error_msg,
|
|
173
|
+
"error_type": getattr(result, 'error_type', None),
|
|
174
|
+
"balance": getattr(result, 'balance', None)
|
|
175
|
+
})
|
|
176
|
+
data["keys"][key]["last_checked"] = datetime.utcnow().isoformat() + "Z"
|
|
177
|
+
|
|
178
|
+
with open(keys_file, "w", encoding="utf-8") as f:
|
|
179
|
+
json.dump(data, f, indent=2, ensure_ascii=False)
|
|
180
|
+
|
|
181
|
+
with open(results_file, "w", encoding="utf-8") as f:
|
|
182
|
+
json.dump(results, f, indent=2, ensure_ascii=False)
|
|
183
|
+
|
|
184
|
+
logger.flush()
|
|
185
|
+
return results
|