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
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
key_manager/__init__.py,sha256=x4LlmQ4xZJVNwK-01OQdwdwc2H5PYz4VRLJ51gf3V4I,421
|
|
2
|
+
key_manager/__main__.py,sha256=GzkP1E6JO-GmOaq3z30Op1DQMS0i7YopewS5k7F2iAE,124
|
|
3
|
+
key_manager/api_models.py,sha256=EHMyl7NUefPihEEUBQWcG7bhKoalitq-cPn0jNlUNIQ,15946
|
|
4
|
+
key_manager/checker.py,sha256=srQUMT_StEDphSTYr3rYyKZPI2g4Je22RidzV5oz5DE,1675
|
|
5
|
+
key_manager/cli.py,sha256=KRouKOkRaMHhlk4d27Qv1IsgEsY8fYSqxQotM9UqXX4,9387
|
|
6
|
+
key_manager/config.py,sha256=bAKPmpdQXOwcKtfSqFV4-Vu3h83a1dQq0KQDB_I4Vvk,1814
|
|
7
|
+
key_manager/core.py,sha256=IoU7Ic4OnTaI4Dg6tZ0ILRmC5KgoZgloGubHc7izVzE,6419
|
|
8
|
+
key_manager/detector.py,sha256=8vHZkMMrmy_acGLceN4lqUt4b9nQfktr65LlF2EqRkk,13809
|
|
9
|
+
key_manager/errors.py,sha256=cB_z8qoYXvj3hlh86SCAjgNJBejR6w6YRdcmHNG-5hE,6088
|
|
10
|
+
key_manager/i18n.py,sha256=hqqFWanD6ybs8or5qg9FcOg9a7T61zLVdGVkUYRhevQ,4077
|
|
11
|
+
key_manager/logger.py,sha256=rzISVc4SBSJ06BV0houobmLpDYAqcTwQLXh7kpxjSRM,7922
|
|
12
|
+
key_manager/model_capabilities.py,sha256=o_Qb7gl9fB5HOxsGE2zRRq7Sj9Dpmnm-qazPezkgXtc,12758
|
|
13
|
+
key_manager/parser.py,sha256=oanTRB0h8hFLfn6oY-gU418KBcU5lT7A1ALrwERBwnQ,5429
|
|
14
|
+
key_manager/proxy.py,sha256=jrpm8-XnUxGhqJrQQh4Kh0SllpFB9RgdyXh927BYjwo,1894
|
|
15
|
+
key_manager/ssrf.py,sha256=xh4RexLgIudjWZH-2-Uroj_4V3yU5WkiNzdWb26oDcA,1992
|
|
16
|
+
key_manager/storage.py,sha256=qrwKKxbc-5684mNGStM11HjVU1YYlZdUvjsllFbYTic,4514
|
|
17
|
+
key_manager/tester.py,sha256=_jMZSmH9U5tPtFnd8ZZTEf0lLpWam2-UfjP2CYmn-lY,5107
|
|
18
|
+
key_manager/url_override.py,sha256=EQYre1FDcPd1oC9PLxeUoSiLwQ_vn--96Xhy_3LLiE0,252
|
|
19
|
+
key_manager/validator.py,sha256=zNHomlXNL6y8ro9OTMVPvk_sOdlF2Xn_Z84OWPVBENQ,7291
|
|
20
|
+
key_manager/web.py,sha256=trMpdtb383j70Z0_yTAWQ_r65akl7-Jr7JaohvE82SA,66778
|
|
21
|
+
key_manager/webhook.py,sha256=ke4-7kNyaDu6ZQqg_YeWeHPVe2kTM6ansQ4im93goYE,8055
|
|
22
|
+
key_manager/providers/__init__.py,sha256=UEPyPh4FN5nSijsNSxzENMDdwu0-GsHR4Pgk5MVy2AE,13968
|
|
23
|
+
key_manager/providers/ai302.py,sha256=-Rw6eC6wxwjVdze4LFs_iTq-7RlP84uEOVa7EOq8ccA,4421
|
|
24
|
+
key_manager/providers/anthropic.py,sha256=wUG9bwRAFFyWmmfoQhgvimyZWA5HdNUYcfcHSnQus_g,4382
|
|
25
|
+
key_manager/providers/baichuan.py,sha256=mzjSseNslrI1O9iRASRRlPEr5GQn8WGDKdVxo6O7sXM,4156
|
|
26
|
+
key_manager/providers/base.py,sha256=zmwPMoQk3-4o8s2N83HnH-KX836fFSzL24K9yJpQ_js,11819
|
|
27
|
+
key_manager/providers/cerebras.py,sha256=cbqTpKr6siDGHl_OovSP_5G6adpNj-rCJYUeU4C7dlA,4432
|
|
28
|
+
key_manager/providers/cohere.py,sha256=ccNRzCLdIumqIDPkPbml1tfvc02dsg-1us0Y1Iom3b0,3645
|
|
29
|
+
key_manager/providers/cstcloud.py,sha256=UwcNrltUl1p8pSALHwFB-D_-8vqgq6Rh5CdzmMF0Z9s,5346
|
|
30
|
+
key_manager/providers/dashscope.py,sha256=6Gf3slaXXLI1Pw0R-ocF2iGWWOeOEyvx9n4yPS6WhtI,5206
|
|
31
|
+
key_manager/providers/dashscope_coding.py,sha256=bIEt9-O4ZoDwZbp5geAkbnBv0CVGD1ksmT7zgl8QpUc,5404
|
|
32
|
+
key_manager/providers/deepseek.py,sha256=RHrk0pOPAxZP1JA8DXjGP5tFu-S39wyJxkDGIyp_W_Q,7074
|
|
33
|
+
key_manager/providers/dmxapi.py,sha256=uPUG6-dyWFRAHhin80PX7q1yczBHExiTD6E4KKbC-1Y,4426
|
|
34
|
+
key_manager/providers/doubao.py,sha256=h3l2h_6MvtTBrd79qBAaGxlfrfUPKme-IWwQKYjYRUc,4448
|
|
35
|
+
key_manager/providers/fireworks.py,sha256=ciecI3plQGsUL582YoIwGSCWmhV2Q6W-d9UiGRVNTO4,4545
|
|
36
|
+
key_manager/providers/google.py,sha256=Td1OhcIRJFPJEQ4VaUVTOBJUCDkBa4Bj1-0QdwqMFUs,4132
|
|
37
|
+
key_manager/providers/grok.py,sha256=R-zSjaLBzmaIg_E22_57jR555g_IHlMRIaY228K1bPc,4418
|
|
38
|
+
key_manager/providers/groq.py,sha256=Bht4XUApmUA3JZjuDnh_IUZp6p_PfBoblCrXdAi1uAE,4454
|
|
39
|
+
key_manager/providers/huggingface.py,sha256=1IjsoQNh3cdtf38u5Bh3mZQNFeHN069AFr7lFfExhrs,2352
|
|
40
|
+
key_manager/providers/hyperbolic.py,sha256=i13BASJn2jBCEFbaji3GPCovTZSJNJag6wUh9C9VLKg,4481
|
|
41
|
+
key_manager/providers/infini.py,sha256=PmrazMa7YcCEwXyjg5f4jqPrsLl56jtQ4MprmQ3Knh8,5597
|
|
42
|
+
key_manager/providers/infini_coding.py,sha256=yLCzH8CI5u5w4hyciGqgTn0aBJLrBgddIprBYnHEOLw,5408
|
|
43
|
+
key_manager/providers/kimi.py,sha256=T1M9k1cC-v0QGdIIYP4ddRhkkgpBwXThi_Ws6W9FyWw,5201
|
|
44
|
+
key_manager/providers/kimi_coding.py,sha256=THjpljhqjZirkGz8GOXxwq0GbFNAwXgobO3ax2gkUPI,5360
|
|
45
|
+
key_manager/providers/longcat.py,sha256=1EzQZ0FehXWA8IO7VZmNZpU4B10je9Uy4EaegHnj7_8,5035
|
|
46
|
+
key_manager/providers/mimo.py,sha256=OxSJu9-E0ZkniyIpv6AHZ43_gH733lQinyncYvMZ5o4,4419
|
|
47
|
+
key_manager/providers/mimo_plan.py,sha256=vdRwqOC9vK0FDKKM5goyIYYLrHVwYzf2xV8NWbFE9vk,5904
|
|
48
|
+
key_manager/providers/minimax.py,sha256=WrxtjpLOg3vE9ZYTl6enPvkOKKoEZFoJlj4z96Mo4vg,4142
|
|
49
|
+
key_manager/providers/minimax_plan.py,sha256=FOTLFENsKaoBf7XRRs3DOxTh7nqCqP8MHPkcOHpSowY,5407
|
|
50
|
+
key_manager/providers/mistral.py,sha256=loySnKHvxz4j-ZWfCyWCT-RtwtCJiHAtr41oR4jmiTQ,4437
|
|
51
|
+
key_manager/providers/models_registry.py,sha256=XSegzg6L-pesh-s4Onb4OBXuuJl6LPfGQrZpYP-lVHk,84818
|
|
52
|
+
key_manager/providers/modelscope.py,sha256=3DPtPEZ1cyMlWOc_T_B_9JDMndMGAM643TBsPs7My8Y,5603
|
|
53
|
+
key_manager/providers/nvidia.py,sha256=OzDFTBcRerJniXn69MbKfykmagEifwfo8Mlv40IOy4M,4461
|
|
54
|
+
key_manager/providers/ocoolai.py,sha256=kdha8BPLVWphuTOezwkRG4keywqlAMhsnLIR6FTmmHM,4430
|
|
55
|
+
key_manager/providers/openai.py,sha256=ky0ykK0hbqljTWK13Jni9XeltWlFEmb9X3t_FooiPns,5652
|
|
56
|
+
key_manager/providers/openrouter.py,sha256=xiCw7H1stbzjQSixEak4ELkYLWv1LeDD__QcHZ5Z4RI,5262
|
|
57
|
+
key_manager/providers/perplexity.py,sha256=ZUtVQSwYwlVnGBwH3yImJapIkmnC5T82Eb0xs_ZHlhs,4454
|
|
58
|
+
key_manager/providers/poe.py,sha256=rUr_jzt3dH_FODh0qoca3WzKjfa8Dz2zyc0oRGak1AA,4418
|
|
59
|
+
key_manager/providers/ppio.py,sha256=1nq5JHffdWYD0K3sxWfaTd1mGkkxcqVSfbCkjX5NdYc,4453
|
|
60
|
+
key_manager/providers/replicate.py,sha256=TwoiGGS-bIaKPYG_KW9Z92QsTp_xCaUuhNK5rcxGyus,2348
|
|
61
|
+
key_manager/providers/siliconflow.py,sha256=SdlWuyEGYiRFBJavyUBF3SMi2bsSSjOioLd2NGLNxpY,5234
|
|
62
|
+
key_manager/providers/stepfun.py,sha256=JkuZv26G-imVXOGiGLGgbouIWoiWFNvFJUsRUlbklzA,5416
|
|
63
|
+
key_manager/providers/tencent_hunyuan.py,sha256=q5fV8af98qNAovadLe_rDTS11LVcF4FTnE-3Yt8c4oI,5416
|
|
64
|
+
key_manager/providers/together.py,sha256=wD8Z3T_si-9ttMSxYNFxY7t588O1Ok7dPcKmzCPvvxg,5566
|
|
65
|
+
key_manager/providers/yi.py,sha256=krOLqoEV6ytwLV4JqCFMjgI28s6B11NA-TtyiZlfiB0,4135
|
|
66
|
+
key_manager/providers/zai.py,sha256=RkLlhEz9LEs8VoHVhQUhh9o-p9q4lZsSmF1lK1saXyY,4424
|
|
67
|
+
key_manager/providers/zhipu.py,sha256=gUMSyJSzUvLopNHKEM1bn4FBSzRaiSAQPs3itwOrnhY,5665
|
|
68
|
+
key_manager/providers/zhipu_coding.py,sha256=yy4SuKXXpLCAHqZfTInlvZ5tYCOQ1u55EPbZfKVYcNw,5381
|
|
69
|
+
api_key_manager-2.1.0.dist-info/METADATA,sha256=KxMBaY8mYAh4U4UOxmfmTtje7-smlMimsnO1NXYkvGo,21747
|
|
70
|
+
api_key_manager-2.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
71
|
+
api_key_manager-2.1.0.dist-info/entry_points.txt,sha256=Md42USNyHdYOXdPsW9NYvcZyTl_5rP7aySf8gqQu9tA,57
|
|
72
|
+
api_key_manager-2.1.0.dist-info/top_level.txt,sha256=tCMUyU-fS25OwJkzgCLR5ewI0FzSiO2k6Ui1eqITqAc,12
|
|
73
|
+
api_key_manager-2.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
key_manager
|
key_manager/__init__.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""Key Manager - Batch API key management for 44+ AI providers."""
|
|
2
|
+
from key_manager.core import KeyManager
|
|
3
|
+
from key_manager.providers import PROVIDERS, get_display_name
|
|
4
|
+
from key_manager.errors import KeyManagerError, ErrorCode
|
|
5
|
+
from key_manager.storage import KeyStore
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"KeyManager",
|
|
9
|
+
"PROVIDERS",
|
|
10
|
+
"get_display_name",
|
|
11
|
+
"KeyManagerError",
|
|
12
|
+
"ErrorCode",
|
|
13
|
+
"KeyStore",
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
__version__ = "2.1.0"
|
key_manager/__main__.py
ADDED
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from pydantic import BaseModel, Field
|
|
7
|
+
|
|
8
|
+
from key_manager.errors import ErrorResponse
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"ErrorResponse",
|
|
12
|
+
"KeyInfo",
|
|
13
|
+
"KeyListResponse",
|
|
14
|
+
"KeyExportItem",
|
|
15
|
+
"KeyExportResponse",
|
|
16
|
+
"ImportRequest",
|
|
17
|
+
"ImportResponse",
|
|
18
|
+
"CheckSingleRequest",
|
|
19
|
+
"CheckSingleResponse",
|
|
20
|
+
"CheckBatchItem",
|
|
21
|
+
"CheckBatchRequest",
|
|
22
|
+
"CheckBatchResult",
|
|
23
|
+
"CheckBatchSummary",
|
|
24
|
+
"CheckBatchResponse",
|
|
25
|
+
"TestSingleRequest",
|
|
26
|
+
"TestSingleResponse",
|
|
27
|
+
"BalanceRequest",
|
|
28
|
+
"BalanceResponse",
|
|
29
|
+
"ModelInfo",
|
|
30
|
+
"ModelsResponse",
|
|
31
|
+
"ProviderInfo",
|
|
32
|
+
"ProvidersResponse",
|
|
33
|
+
"ProviderDetail",
|
|
34
|
+
"ProviderDetailResponse",
|
|
35
|
+
"StatsProviderEntry",
|
|
36
|
+
"StatsResponse",
|
|
37
|
+
"StatsChartProviderEntry",
|
|
38
|
+
"StatsChartStatuses",
|
|
39
|
+
"StatsChartResponse",
|
|
40
|
+
"LogEntry",
|
|
41
|
+
"LogsResponse",
|
|
42
|
+
"OperationEntry",
|
|
43
|
+
"OperationsResponse",
|
|
44
|
+
"ProgressResponse",
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
# ── Keys ──────────────────────────────────────────────────────────────────────
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class KeyInfo(BaseModel):
|
|
52
|
+
key_masked: str = Field(..., description="Masked key for display")
|
|
53
|
+
provider: str = Field(..., description="Provider identifier")
|
|
54
|
+
status: str = Field(..., description="Key status: valid / invalid / error / unknown")
|
|
55
|
+
last_checked: str | None = Field(None, description="ISO-8601 timestamp of last check")
|
|
56
|
+
last_error: str | None = Field(None, description="Most recent error message")
|
|
57
|
+
error_type: str | None = Field(None, description="Categorised error type")
|
|
58
|
+
tests: dict[str, Any] = Field(default_factory=dict, description="Test results (max_tokens, max_concurrency)")
|
|
59
|
+
models: list[str] = Field(default_factory=list, description="Available models")
|
|
60
|
+
sources_count: int = Field(0, description="Number of import sources")
|
|
61
|
+
balance: float | None = Field(None, description="Account balance if available")
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class KeyListResponse(BaseModel):
|
|
65
|
+
keys: list[KeyInfo] = Field(default_factory=list, description="Paginated key list")
|
|
66
|
+
total: int = Field(0, description="Total number of keys matching filters")
|
|
67
|
+
page: int = Field(1, description="Current page number")
|
|
68
|
+
total_pages: int = Field(0, description="Total number of pages")
|
|
69
|
+
page_size: int = Field(50, description="Page size")
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class KeyExportItem(BaseModel):
|
|
73
|
+
provider: str = Field(..., description="Provider identifier")
|
|
74
|
+
max_tokens: int | None = Field(None, description="Maximum token limit")
|
|
75
|
+
max_concurrency: int | None = Field(None, description="Maximum concurrency limit")
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class KeyExportResponse(BaseModel):
|
|
79
|
+
keys: list[KeyExportItem] = Field(default_factory=list, description="Exported valid keys")
|
|
80
|
+
total: int = Field(0, description="Total exported key count")
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
# ── Import ────────────────────────────────────────────────────────────────────
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class ImportRequest(BaseModel):
|
|
87
|
+
file: str | None = Field(None, description="Path to a JSON file containing keys")
|
|
88
|
+
directory: str | None = Field(None, description="Directory to scan for key files")
|
|
89
|
+
batch: list[str] | None = Field(None, description="Inline list of keys to import")
|
|
90
|
+
|
|
91
|
+
model_config = {
|
|
92
|
+
"json_schema_extra": {
|
|
93
|
+
"examples": [
|
|
94
|
+
{"file": "./data/input/keys.json"},
|
|
95
|
+
{"batch": ["sk-abc123", "sk-def456"]},
|
|
96
|
+
]
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class ImportResponse(BaseModel):
|
|
102
|
+
new: int = Field(0, description="Number of newly imported keys")
|
|
103
|
+
duplicates: int = Field(0, description="Number of duplicate keys skipped")
|
|
104
|
+
errors: list[str] = Field(default_factory=list, description="Import error messages")
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
# ── Check ─────────────────────────────────────────────────────────────────────
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class CheckSingleRequest(BaseModel):
|
|
111
|
+
key: str = Field(..., min_length=1, description="API key to check")
|
|
112
|
+
provider: str = Field("", description="Provider name (auto-detected if empty)")
|
|
113
|
+
custom_base_url: str | None = Field(None, description="Override provider base URL")
|
|
114
|
+
|
|
115
|
+
model_config = {
|
|
116
|
+
"json_schema_extra": {
|
|
117
|
+
"examples": [{"key": "sk-abc123", "provider": "openai"}]
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class CheckSingleResponse(BaseModel):
|
|
123
|
+
key_masked: str = Field(..., description="Masked key for display")
|
|
124
|
+
provider: str = Field(..., description="Provider identifier")
|
|
125
|
+
display_name: str | None = Field(None, description="Human-readable provider name")
|
|
126
|
+
status: str = Field(..., description="Check result: valid / invalid / error / unknown")
|
|
127
|
+
status_code: int | None = Field(None, description="HTTP status code from provider")
|
|
128
|
+
latency_ms: float = Field(0, description="Response latency in milliseconds")
|
|
129
|
+
error: str | None = Field(None, description="Error message if check failed")
|
|
130
|
+
error_type: str | None = Field(None, description="Categorised error type")
|
|
131
|
+
balance: dict[str, Any] | None = Field(None, description="Balance information")
|
|
132
|
+
models: list[str] = Field(default_factory=list, description="Available models")
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
class CheckBatchItem(BaseModel):
|
|
136
|
+
key: str = Field(..., min_length=1, description="API key to check")
|
|
137
|
+
provider: str = Field("", description="Provider name (auto-detected if empty)")
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
class CheckBatchRequest(BaseModel):
|
|
141
|
+
keys: list[CheckBatchItem] = Field(..., min_length=1, description="Keys to check")
|
|
142
|
+
timeout: int = Field(10, description="Per-key timeout in seconds")
|
|
143
|
+
concurrency: int = Field(50, description="Maximum concurrent checks")
|
|
144
|
+
custom_base_url: str | None = Field(None, description="Override provider base URL for all keys")
|
|
145
|
+
|
|
146
|
+
model_config = {
|
|
147
|
+
"json_schema_extra": {
|
|
148
|
+
"examples": [
|
|
149
|
+
{
|
|
150
|
+
"keys": [
|
|
151
|
+
{"key": "sk-abc123", "provider": "openai"},
|
|
152
|
+
{"key": "sk-def456", "provider": "deepseek"},
|
|
153
|
+
],
|
|
154
|
+
"concurrency": 20,
|
|
155
|
+
}
|
|
156
|
+
]
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
class CheckBatchResult(BaseModel):
|
|
162
|
+
key_masked: str = Field(..., description="Masked key for display")
|
|
163
|
+
provider: str = Field(..., description="Provider identifier")
|
|
164
|
+
status: str = Field(..., description="Check result: valid / invalid / error / unknown")
|
|
165
|
+
status_code: int | None = Field(None, description="HTTP status code from provider")
|
|
166
|
+
latency_ms: float = Field(0, description="Response latency in milliseconds")
|
|
167
|
+
error: str | None = Field(None, description="Error message if check failed")
|
|
168
|
+
error_type: str | None = Field(None, description="Categorised error type")
|
|
169
|
+
balance: dict[str, Any] | None = Field(None, description="Balance information")
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
class CheckBatchSummary(BaseModel):
|
|
173
|
+
total: int = Field(0, description="Total keys checked")
|
|
174
|
+
valid: int = Field(0, description="Number of valid keys")
|
|
175
|
+
invalid: int = Field(0, description="Number of invalid keys")
|
|
176
|
+
error: int = Field(0, description="Number of errored keys")
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
class CheckBatchResponse(BaseModel):
|
|
180
|
+
results: list[CheckBatchResult] = Field(default_factory=list, description="Per-key results")
|
|
181
|
+
summary: CheckBatchSummary = Field(default_factory=CheckBatchSummary, description="Aggregate summary")
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
# ── Test ──────────────────────────────────────────────────────────────────────
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
class TestSingleRequest(BaseModel):
|
|
188
|
+
key: str = Field(..., min_length=1, description="API key to test")
|
|
189
|
+
provider: str = Field("", description="Provider name (auto-detected if empty)")
|
|
190
|
+
|
|
191
|
+
model_config = {
|
|
192
|
+
"json_schema_extra": {
|
|
193
|
+
"examples": [{"key": "sk-abc123", "provider": "openai"}]
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
class TestSingleResponse(BaseModel):
|
|
199
|
+
provider: str = Field(..., description="Provider identifier")
|
|
200
|
+
key_masked: str = Field(..., description="Masked key for display")
|
|
201
|
+
max_tokens: int | None = Field(None, description="Detected maximum token limit")
|
|
202
|
+
max_concurrency: int | None = Field(None, description="Detected maximum concurrency")
|
|
203
|
+
models: list[str] = Field(default_factory=list, description="Available models")
|
|
204
|
+
error: str | None = Field(None, description="Error message if test failed")
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
# ── Balance ───────────────────────────────────────────────────────────────────
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
class BalanceRequest(BaseModel):
|
|
211
|
+
key: str = Field(..., min_length=1, description="API key to query balance for")
|
|
212
|
+
provider: str = Field("", description="Provider name (auto-detected if empty)")
|
|
213
|
+
custom_base_url: str | None = Field(None, description="Override provider base URL")
|
|
214
|
+
|
|
215
|
+
model_config = {
|
|
216
|
+
"json_schema_extra": {
|
|
217
|
+
"examples": [{"key": "sk-abc123", "provider": "openai"}]
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
class BalanceResponse(BaseModel):
|
|
223
|
+
provider: str = Field(..., description="Provider identifier")
|
|
224
|
+
supported: bool = Field(False, description="Whether provider supports balance queries")
|
|
225
|
+
balance: float | None = Field(None, description="Account balance")
|
|
226
|
+
currency: str | None = Field(None, description="Balance currency code")
|
|
227
|
+
key_masked: str | None = Field(None, description="Masked key for display")
|
|
228
|
+
error: str | None = Field(None, description="Error message if query failed")
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
# ── Models ────────────────────────────────────────────────────────────────────
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
class ModelInfo(BaseModel):
|
|
235
|
+
model: str = Field(..., description="Model identifier")
|
|
236
|
+
available: bool | None = Field(None, description="Whether the model is available (if checked)")
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
class ModelsResponse(BaseModel):
|
|
240
|
+
provider: str = Field(..., description="Provider identifier")
|
|
241
|
+
models: list[str] = Field(default_factory=list, description="Available model identifiers")
|
|
242
|
+
total: int = Field(0, description="Total model count")
|
|
243
|
+
type_filter: str = Field("all", description="Applied type filter")
|
|
244
|
+
source: str | None = Field(None, description="Data source: api / static")
|
|
245
|
+
hint: str | None = Field(None, description="Hint message for user")
|
|
246
|
+
error: str | None = Field(None, description="Error message if fetch failed")
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
# ── Providers ─────────────────────────────────────────────────────────────────
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
class ProviderInfo(BaseModel):
|
|
253
|
+
name: str = Field(..., description="Provider identifier")
|
|
254
|
+
display_name: str = Field(..., description="Human-readable provider name")
|
|
255
|
+
prefix: str = Field("-", description="Key prefix pattern")
|
|
256
|
+
base_url: str = Field("", description="Provider API base URL")
|
|
257
|
+
type: str = Field("ai", description="Provider type")
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
class ProvidersResponse(BaseModel):
|
|
261
|
+
providers: list[ProviderInfo] = Field(default_factory=list, description="Registered providers")
|
|
262
|
+
total: int = Field(0, description="Total provider count")
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
class ProviderDetail(BaseModel):
|
|
266
|
+
name: str = Field(..., description="Provider identifier")
|
|
267
|
+
display_name: str = Field(..., description="Human-readable provider name")
|
|
268
|
+
prefix: str = Field("-", description="Key prefix pattern")
|
|
269
|
+
base_url: str = Field("", description="Provider API base URL")
|
|
270
|
+
website_url: str = Field("", description="Provider website URL")
|
|
271
|
+
docs_url: str = Field("", description="Provider documentation URL")
|
|
272
|
+
website_name: str = Field("", description="Provider website display name")
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
class ProviderDetailResponse(BaseModel):
|
|
276
|
+
providers: list[ProviderDetail] = Field(default_factory=list, description="Provider details")
|
|
277
|
+
total: int = Field(0, description="Total provider count")
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
# ── Stats ─────────────────────────────────────────────────────────────────────
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
class StatsProviderEntry(BaseModel):
|
|
284
|
+
total: int = Field(0, description="Total keys for this provider")
|
|
285
|
+
valid: int = Field(0, description="Valid keys")
|
|
286
|
+
invalid: int = Field(0, description="Invalid keys")
|
|
287
|
+
error: int = Field(0, description="Errored keys")
|
|
288
|
+
display_name: str = Field("", description="Human-readable provider name")
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
class StatsResponse(BaseModel):
|
|
292
|
+
providers: dict[str, StatsProviderEntry] = Field(
|
|
293
|
+
default_factory=dict, description="Per-provider statistics"
|
|
294
|
+
)
|
|
295
|
+
total: int = Field(0, description="Total key count")
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
class StatsChartProviderEntry(BaseModel):
|
|
299
|
+
total: int = Field(0, description="Total keys for this provider")
|
|
300
|
+
valid: int = Field(0, description="Valid keys")
|
|
301
|
+
invalid: int = Field(0, description="Invalid keys")
|
|
302
|
+
error: int = Field(0, description="Errored keys")
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
class StatsChartStatuses(BaseModel):
|
|
306
|
+
valid: int = Field(0, description="Total valid keys")
|
|
307
|
+
invalid: int = Field(0, description="Total invalid keys")
|
|
308
|
+
error: int = Field(0, description="Total errored keys")
|
|
309
|
+
unknown: int = Field(0, description="Total unknown-status keys")
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
class StatsChartResponse(BaseModel):
|
|
313
|
+
providers: dict[str, StatsChartProviderEntry] = Field(
|
|
314
|
+
default_factory=dict, description="Per-provider breakdown for charts"
|
|
315
|
+
)
|
|
316
|
+
statuses: StatsChartStatuses = Field(
|
|
317
|
+
default_factory=StatsChartStatuses, description="Global status counts"
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
# ── Logs ──────────────────────────────────────────────────────────────────────
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
class LogEntry(BaseModel):
|
|
325
|
+
timestamp: str | None = Field(None, description="Log entry timestamp")
|
|
326
|
+
level: str | None = Field(None, description="Log level (INFO, WARNING, ERROR)")
|
|
327
|
+
message: str = Field("", description="Log message content")
|
|
328
|
+
extra: dict[str, Any] | None = Field(None, description="Additional structured data")
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
class LogsResponse(BaseModel):
|
|
332
|
+
logs: list[LogEntry | str] = Field(default_factory=list, description="Recent log entries")
|
|
333
|
+
total: int = Field(0, description="Number of entries returned")
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
class OperationEntry(BaseModel):
|
|
337
|
+
timestamp: str | None = Field(None, description="Operation timestamp")
|
|
338
|
+
action: str = Field("", description="Operation type (import, check, test, etc.)")
|
|
339
|
+
detail: str = Field("", description="Operation detail")
|
|
340
|
+
extra: dict[str, Any] | None = Field(None, description="Additional structured data")
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
class OperationsResponse(BaseModel):
|
|
344
|
+
operations: list[OperationEntry | dict[str, Any]] = Field(
|
|
345
|
+
default_factory=list, description="Structured operations log"
|
|
346
|
+
)
|
|
347
|
+
total: int = Field(0, description="Number of entries returned")
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
# ── Progress ──────────────────────────────────────────────────────────────────
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
class ProgressResponse(BaseModel):
|
|
354
|
+
active: bool = Field(False, description="Whether a long-running task is in progress")
|
|
355
|
+
current: int = Field(0, description="Current progress counter")
|
|
356
|
+
total: int = Field(0, description="Total items to process")
|
|
357
|
+
status: str = Field("", description="Task status: loading / done / error")
|
|
358
|
+
results: dict[str, Any] | None = Field(None, description="Final results when complete")
|
key_manager/checker.py
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
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 .validator import validate_keys
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
async def run_check(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
|
+
retry_failed: bool = True,
|
|
19
|
+
retry_count: int = 2) -> dict:
|
|
20
|
+
results = await validate_keys(
|
|
21
|
+
keys_file=keys_file,
|
|
22
|
+
results_file=results_file,
|
|
23
|
+
logs_dir=logs_dir,
|
|
24
|
+
concurrency=concurrency,
|
|
25
|
+
timeout=timeout,
|
|
26
|
+
proxy=proxy
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
# Retry failed keys
|
|
30
|
+
if retry_failed and results["summary"]["error"]["count"] > 0:
|
|
31
|
+
for i in range(retry_count):
|
|
32
|
+
error_keys = results["summary"]["error"]["keys"]
|
|
33
|
+
if not error_keys:
|
|
34
|
+
break
|
|
35
|
+
|
|
36
|
+
retry_results = await validate_keys(
|
|
37
|
+
keys_file=keys_file,
|
|
38
|
+
results_file=results_file,
|
|
39
|
+
logs_dir=logs_dir,
|
|
40
|
+
concurrency=concurrency,
|
|
41
|
+
timeout=timeout,
|
|
42
|
+
proxy=proxy,
|
|
43
|
+
status_filter="error"
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
# Merge results
|
|
47
|
+
results["summary"]["valid"]["count"] += retry_results["summary"]["valid"]["count"]
|
|
48
|
+
results["summary"]["invalid"]["count"] += retry_results["summary"]["invalid"]["count"]
|
|
49
|
+
results["summary"]["error"]["count"] = retry_results["summary"]["error"]["count"]
|
|
50
|
+
|
|
51
|
+
return results
|