api-key-manager 3.0.0__tar.gz → 4.0.0__tar.gz

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.
Files changed (165) hide show
  1. api_key_manager-4.0.0/PKG-INFO +1087 -0
  2. api_key_manager-4.0.0/README.md +1053 -0
  3. api_key_manager-4.0.0/api_key_manager.egg-info/PKG-INFO +1087 -0
  4. {api_key_manager-3.0.0 → api_key_manager-4.0.0}/api_key_manager.egg-info/SOURCES.txt +18 -6
  5. {api_key_manager-3.0.0 → api_key_manager-4.0.0}/api_key_manager.egg-info/requires.txt +1 -0
  6. {api_key_manager-3.0.0 → api_key_manager-4.0.0}/key_manager/__init__.py +2 -2
  7. {api_key_manager-3.0.0 → api_key_manager-4.0.0}/key_manager/api_models.py +0 -1
  8. {api_key_manager-3.0.0 → api_key_manager-4.0.0}/key_manager/checker.py +2 -5
  9. {api_key_manager-3.0.0 → api_key_manager-4.0.0}/key_manager/cli.py +9 -11
  10. {api_key_manager-3.0.0 → api_key_manager-4.0.0}/key_manager/config.py +3 -2
  11. {api_key_manager-3.0.0 → api_key_manager-4.0.0}/key_manager/core.py +43 -43
  12. {api_key_manager-3.0.0 → api_key_manager-4.0.0}/key_manager/detector.py +126 -144
  13. {api_key_manager-3.0.0 → api_key_manager-4.0.0}/key_manager/errors.py +14 -14
  14. {api_key_manager-3.0.0 → api_key_manager-4.0.0}/key_manager/i18n.py +3 -3
  15. {api_key_manager-3.0.0 → api_key_manager-4.0.0}/key_manager/logger.py +6 -7
  16. {api_key_manager-3.0.0 → api_key_manager-4.0.0}/key_manager/model_capabilities.py +70 -70
  17. {api_key_manager-3.0.0 → api_key_manager-4.0.0}/key_manager/parser.py +5 -6
  18. {api_key_manager-3.0.0 → api_key_manager-4.0.0}/key_manager/providers/__init__.py +38 -36
  19. api_key_manager-4.0.0/key_manager/providers/ai302.py +7 -0
  20. {api_key_manager-3.0.0 → api_key_manager-4.0.0}/key_manager/providers/anthropic.py +2 -1
  21. api_key_manager-4.0.0/key_manager/providers/baichuan.py +7 -0
  22. {api_key_manager-3.0.0 → api_key_manager-4.0.0}/key_manager/providers/base.py +144 -61
  23. api_key_manager-4.0.0/key_manager/providers/cerebras.py +7 -0
  24. {api_key_manager-3.0.0 → api_key_manager-4.0.0}/key_manager/providers/cohere.py +2 -1
  25. api_key_manager-4.0.0/key_manager/providers/config_providers.py +134 -0
  26. api_key_manager-4.0.0/key_manager/providers/cstcloud.py +8 -0
  27. api_key_manager-4.0.0/key_manager/providers/dashscope.py +30 -0
  28. api_key_manager-4.0.0/key_manager/providers/dashscope_coding.py +8 -0
  29. {api_key_manager-3.0.0 → api_key_manager-4.0.0}/key_manager/providers/deepseek.py +52 -24
  30. api_key_manager-4.0.0/key_manager/providers/dmxapi.py +7 -0
  31. api_key_manager-4.0.0/key_manager/providers/doubao.py +7 -0
  32. api_key_manager-4.0.0/key_manager/providers/fireworks.py +7 -0
  33. {api_key_manager-3.0.0 → api_key_manager-4.0.0}/key_manager/providers/google.py +2 -1
  34. api_key_manager-4.0.0/key_manager/providers/grok.py +7 -0
  35. api_key_manager-4.0.0/key_manager/providers/groq.py +7 -0
  36. {api_key_manager-3.0.0 → api_key_manager-4.0.0}/key_manager/providers/huggingface.py +2 -2
  37. api_key_manager-4.0.0/key_manager/providers/hyperbolic.py +7 -0
  38. api_key_manager-4.0.0/key_manager/providers/infini.py +33 -0
  39. api_key_manager-4.0.0/key_manager/providers/infini_coding.py +8 -0
  40. api_key_manager-4.0.0/key_manager/providers/kimi.py +31 -0
  41. api_key_manager-4.0.0/key_manager/providers/kimi_coding.py +8 -0
  42. api_key_manager-4.0.0/key_manager/providers/longcat.py +7 -0
  43. api_key_manager-4.0.0/key_manager/providers/mimo.py +7 -0
  44. api_key_manager-4.0.0/key_manager/providers/mimo_plan.py +8 -0
  45. api_key_manager-4.0.0/key_manager/providers/minimax.py +7 -0
  46. api_key_manager-4.0.0/key_manager/providers/minimax_plan.py +8 -0
  47. api_key_manager-4.0.0/key_manager/providers/mistral.py +7 -0
  48. api_key_manager-4.0.0/key_manager/providers/modelscope.py +21 -0
  49. api_key_manager-4.0.0/key_manager/providers/nvidia.py +7 -0
  50. api_key_manager-4.0.0/key_manager/providers/ocoolai.py +7 -0
  51. api_key_manager-4.0.0/key_manager/providers/openai.py +56 -0
  52. {api_key_manager-3.0.0 → api_key_manager-4.0.0}/key_manager/providers/opencode.py +38 -20
  53. api_key_manager-4.0.0/key_manager/providers/opencode_zen.py +8 -0
  54. api_key_manager-4.0.0/key_manager/providers/openrouter.py +29 -0
  55. api_key_manager-4.0.0/key_manager/providers/perplexity.py +7 -0
  56. api_key_manager-4.0.0/key_manager/providers/poe.py +7 -0
  57. api_key_manager-4.0.0/key_manager/providers/ppio.py +7 -0
  58. {api_key_manager-3.0.0 → api_key_manager-4.0.0}/key_manager/providers/replicate.py +2 -2
  59. api_key_manager-4.0.0/key_manager/providers/siliconflow.py +31 -0
  60. api_key_manager-4.0.0/key_manager/providers/stepfun.py +30 -0
  61. api_key_manager-4.0.0/key_manager/providers/tencent_hunyuan.py +8 -0
  62. api_key_manager-4.0.0/key_manager/providers/together.py +32 -0
  63. api_key_manager-4.0.0/key_manager/providers/yi.py +7 -0
  64. api_key_manager-4.0.0/key_manager/providers/zai.py +7 -0
  65. api_key_manager-4.0.0/key_manager/providers/zhipu.py +37 -0
  66. api_key_manager-4.0.0/key_manager/providers/zhipu_coding.py +8 -0
  67. {api_key_manager-3.0.0 → api_key_manager-4.0.0}/key_manager/proxy.py +2 -2
  68. {api_key_manager-3.0.0 → api_key_manager-4.0.0}/key_manager/ssrf.py +4 -5
  69. {api_key_manager-3.0.0 → api_key_manager-4.0.0}/key_manager/storage.py +43 -6
  70. {api_key_manager-3.0.0 → api_key_manager-4.0.0}/key_manager/tester.py +3 -3
  71. {api_key_manager-3.0.0 → api_key_manager-4.0.0}/key_manager/validator.py +3 -2
  72. api_key_manager-4.0.0/key_manager/web/__init__.py +35 -0
  73. api_key_manager-4.0.0/key_manager/web/_app.py +168 -0
  74. api_key_manager-4.0.0/key_manager/web/middleware.py +197 -0
  75. api_key_manager-4.0.0/key_manager/web/progress.py +78 -0
  76. api_key_manager-4.0.0/key_manager/web/routes/__init__.py +19 -0
  77. api_key_manager-4.0.0/key_manager/web/routes/balance.py +93 -0
  78. api_key_manager-4.0.0/key_manager/web/routes/check.py +322 -0
  79. api_key_manager-4.0.0/key_manager/web/routes/keys.py +242 -0
  80. api_key_manager-4.0.0/key_manager/web/routes/misc.py +254 -0
  81. api_key_manager-4.0.0/key_manager/web/routes/models.py +279 -0
  82. api_key_manager-4.0.0/key_manager/web/routes/providers.py +229 -0
  83. api_key_manager-4.0.0/key_manager/web/routes/stats.py +91 -0
  84. api_key_manager-4.0.0/key_manager/web/routes/test.py +441 -0
  85. {api_key_manager-3.0.0 → api_key_manager-4.0.0}/key_manager/webhook.py +9 -11
  86. {api_key_manager-3.0.0 → api_key_manager-4.0.0}/pyproject.toml +2 -2
  87. {api_key_manager-3.0.0 → api_key_manager-4.0.0}/tests/test_api_endpoints.py +24 -80
  88. {api_key_manager-3.0.0 → api_key_manager-4.0.0}/tests/test_base_check.py +4 -27
  89. {api_key_manager-3.0.0 → api_key_manager-4.0.0}/tests/test_bug_fixes.py +34 -35
  90. {api_key_manager-3.0.0 → api_key_manager-4.0.0}/tests/test_checker.py +6 -14
  91. {api_key_manager-3.0.0 → api_key_manager-4.0.0}/tests/test_core.py +0 -5
  92. api_key_manager-4.0.0/tests/test_deepseek_balance.py +203 -0
  93. {api_key_manager-3.0.0 → api_key_manager-4.0.0}/tests/test_e2e.py +20 -72
  94. api_key_manager-3.0.0/tests/test_parser_supplement.py → api_key_manager-4.0.0/tests/test_parser.py +158 -19
  95. {api_key_manager-3.0.0 → api_key_manager-4.0.0}/tests/test_provider_detection.py +19 -88
  96. api_key_manager-4.0.0/tests/test_provider_refactoring.py +262 -0
  97. {api_key_manager-3.0.0 → api_key_manager-4.0.0}/tests/test_security.py +79 -3
  98. {api_key_manager-3.0.0 → api_key_manager-4.0.0}/tests/test_storage.py +19 -3
  99. {api_key_manager-3.0.0 → api_key_manager-4.0.0}/tests/test_tester.py +0 -11
  100. api_key_manager-4.0.0/tests/test_token_limit_fast.py +161 -0
  101. api_key_manager-4.0.0/tests/test_v320_changes.py +193 -0
  102. {api_key_manager-3.0.0 → api_key_manager-4.0.0}/tests/test_validator.py +190 -8
  103. {api_key_manager-3.0.0 → api_key_manager-4.0.0}/tests/test_webhook.py +0 -10
  104. api_key_manager-3.0.0/PKG-INFO +0 -785
  105. api_key_manager-3.0.0/README.md +0 -752
  106. api_key_manager-3.0.0/api_key_manager.egg-info/PKG-INFO +0 -785
  107. api_key_manager-3.0.0/key_manager/providers/ai302.py +0 -76
  108. api_key_manager-3.0.0/key_manager/providers/baichuan.py +0 -69
  109. api_key_manager-3.0.0/key_manager/providers/cerebras.py +0 -76
  110. api_key_manager-3.0.0/key_manager/providers/cstcloud.py +0 -94
  111. api_key_manager-3.0.0/key_manager/providers/dashscope.py +0 -92
  112. api_key_manager-3.0.0/key_manager/providers/dashscope_coding.py +0 -94
  113. api_key_manager-3.0.0/key_manager/providers/dmxapi.py +0 -76
  114. api_key_manager-3.0.0/key_manager/providers/doubao.py +0 -76
  115. api_key_manager-3.0.0/key_manager/providers/fireworks.py +0 -76
  116. api_key_manager-3.0.0/key_manager/providers/grok.py +0 -75
  117. api_key_manager-3.0.0/key_manager/providers/groq.py +0 -76
  118. api_key_manager-3.0.0/key_manager/providers/hyperbolic.py +0 -76
  119. api_key_manager-3.0.0/key_manager/providers/infini.py +0 -102
  120. api_key_manager-3.0.0/key_manager/providers/infini_coding.py +0 -96
  121. api_key_manager-3.0.0/key_manager/providers/kimi.py +0 -93
  122. api_key_manager-3.0.0/key_manager/providers/kimi_coding.py +0 -96
  123. api_key_manager-3.0.0/key_manager/providers/longcat.py +0 -76
  124. api_key_manager-3.0.0/key_manager/providers/mimo.py +0 -76
  125. api_key_manager-3.0.0/key_manager/providers/mimo_plan.py +0 -107
  126. api_key_manager-3.0.0/key_manager/providers/minimax.py +0 -69
  127. api_key_manager-3.0.0/key_manager/providers/minimax_plan.py +0 -94
  128. api_key_manager-3.0.0/key_manager/providers/mistral.py +0 -76
  129. api_key_manager-3.0.0/key_manager/providers/modelscope.py +0 -120
  130. api_key_manager-3.0.0/key_manager/providers/nvidia.py +0 -76
  131. api_key_manager-3.0.0/key_manager/providers/ocoolai.py +0 -76
  132. api_key_manager-3.0.0/key_manager/providers/openai.py +0 -101
  133. api_key_manager-3.0.0/key_manager/providers/opencode_zen.py +0 -89
  134. api_key_manager-3.0.0/key_manager/providers/openrouter.py +0 -91
  135. api_key_manager-3.0.0/key_manager/providers/perplexity.py +0 -75
  136. api_key_manager-3.0.0/key_manager/providers/poe.py +0 -76
  137. api_key_manager-3.0.0/key_manager/providers/ppio.py +0 -76
  138. api_key_manager-3.0.0/key_manager/providers/siliconflow.py +0 -93
  139. api_key_manager-3.0.0/key_manager/providers/stepfun.py +0 -99
  140. api_key_manager-3.0.0/key_manager/providers/tencent_hunyuan.py +0 -94
  141. api_key_manager-3.0.0/key_manager/providers/together.py +0 -101
  142. api_key_manager-3.0.0/key_manager/providers/yi.py +0 -69
  143. api_key_manager-3.0.0/key_manager/providers/zai.py +0 -76
  144. api_key_manager-3.0.0/key_manager/providers/zhipu.py +0 -99
  145. api_key_manager-3.0.0/key_manager/providers/zhipu_coding.py +0 -96
  146. api_key_manager-3.0.0/key_manager/web.py +0 -1676
  147. api_key_manager-3.0.0/tests/test_detector.py +0 -317
  148. api_key_manager-3.0.0/tests/test_parser.py +0 -149
  149. api_key_manager-3.0.0/tests/test_ssrf_supplement.py +0 -81
  150. api_key_manager-3.0.0/tests/test_validator_supplement.py +0 -196
  151. api_key_manager-3.0.0/tests/test_web_fixes.py +0 -161
  152. {api_key_manager-3.0.0 → api_key_manager-4.0.0}/api_key_manager.egg-info/dependency_links.txt +0 -0
  153. {api_key_manager-3.0.0 → api_key_manager-4.0.0}/api_key_manager.egg-info/entry_points.txt +0 -0
  154. {api_key_manager-3.0.0 → api_key_manager-4.0.0}/api_key_manager.egg-info/top_level.txt +0 -0
  155. {api_key_manager-3.0.0 → api_key_manager-4.0.0}/key_manager/__main__.py +0 -0
  156. {api_key_manager-3.0.0 → api_key_manager-4.0.0}/key_manager/providers/models_registry.py +0 -0
  157. {api_key_manager-3.0.0 → api_key_manager-4.0.0}/key_manager/py.typed +0 -0
  158. {api_key_manager-3.0.0 → api_key_manager-4.0.0}/key_manager/url_override.py +0 -0
  159. {api_key_manager-3.0.0 → api_key_manager-4.0.0}/setup.cfg +0 -0
  160. {api_key_manager-3.0.0 → api_key_manager-4.0.0}/tests/test_errors.py +0 -0
  161. {api_key_manager-3.0.0 → api_key_manager-4.0.0}/tests/test_i18n.py +0 -0
  162. {api_key_manager-3.0.0 → api_key_manager-4.0.0}/tests/test_logger.py +0 -0
  163. {api_key_manager-3.0.0 → api_key_manager-4.0.0}/tests/test_openapi.py +0 -0
  164. {api_key_manager-3.0.0 → api_key_manager-4.0.0}/tests/test_providers.py +0 -0
  165. {api_key_manager-3.0.0 → api_key_manager-4.0.0}/tests/test_proxy.py +0 -0
@@ -0,0 +1,1087 @@
1
+ Metadata-Version: 2.4
2
+ Name: api-key-manager
3
+ Version: 4.0.0
4
+ Summary: Batch manage API keys for 45+ AI providers with CLI and Web interfaces
5
+ Author: Townrain
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/Townrain/API-Key-Manager
8
+ Project-URL: Documentation, https://github.com/Townrain/API-Key-Manager#readme
9
+ Project-URL: Repository, https://github.com/Townrain/API-Key-Manager
10
+ Keywords: api-key,ai,openai,anthropic,google,deepseek
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
19
+ Requires-Python: >=3.10
20
+ Description-Content-Type: text/markdown
21
+ Requires-Dist: httpx>=0.27.2
22
+ Requires-Dist: pyyaml>=6.0.2
23
+ Requires-Dist: rich>=13.9.0
24
+ Requires-Dist: fastapi>=0.115.0
25
+ Requires-Dist: uvicorn>=0.32.0
26
+ Requires-Dist: python-multipart>=0.0.9
27
+ Requires-Dist: cryptography>=42.0.0
28
+ Requires-Dist: jinja2>=3.0.0
29
+ Provides-Extra: dev
30
+ Requires-Dist: pytest>=8.0.0; extra == "dev"
31
+ Requires-Dist: pytest-asyncio>=0.23.0; extra == "dev"
32
+ Requires-Dist: pytest-cov>=5.0.0; extra == "dev"
33
+ Requires-Dist: ruff>=0.4.0; extra == "dev"
34
+
35
+ # API Key Manager
36
+
37
+ 批量管理 45+ AI 服务商 API 密钥的 Python 工具,支持 CLI 和 Web 两种界面。
38
+
39
+ ## 功能特性
40
+
41
+ - **批量导入** - 从 JSON 文件导入 API 密钥,自动去重
42
+ - **密钥验证** - 并发验证密钥有效性,支持 45+ AI 服务商
43
+ - **能力测试** - 测试 Token 上限和并发能力
44
+ - **模型筛选** - 按类型筛选模型(推理/视觉/联网/免费/嵌入/重排/工具)
45
+ - **智能检测** - 前缀匹配 + 模式匹配 + 错误签名匹配,三级提供商自动识别
46
+ - **自动发现** - 新增服务商只需创建 Python 文件,自动注册
47
+ - **可扩展架构** - 支持 YAML 配置自定义服务商,Web API 管理
48
+ - **Web 界面** - 赛博朋克风格的管理界面
49
+ - **代理支持** - 支持 HTTP/SOCKS 代理
50
+ - **加密存储** - AES-256-GCM 加密存储 API 密钥,随机盐值
51
+ - **安全防护** - 路径遍历防护、SSRF 防护、时序安全认证
52
+ - **API 文档** - Swagger UI 和 Redoc 自动文档
53
+ - **国际化** - 支持中英文错误信息
54
+ - **SDK 支持** - Python 和 TypeScript 客户端库
55
+ - **Webhook 通知** - 事件驱动的 Webhook 通知系统
56
+
57
+ ## 系统架构
58
+
59
+ ![系统架构流程图](docs/images/flowchart.png)
60
+
61
+
62
+ ## 支持的 AI 服务商
63
+
64
+ ### 国际
65
+
66
+ | 服务商 | 前缀 |
67
+ |--------|------|
68
+ | OpenAI | `sk-proj-` |
69
+ | Anthropic | `sk-ant-api03-` |
70
+ | Google Gemini | `AIza` |
71
+ | DeepSeek | `sk-` |
72
+ | Groq | `gsk_` |
73
+ | Mistral | `sk-` |
74
+ | Cohere | `sk-` |
75
+ | Perplexity | `pplx-` |
76
+ | Together AI | `sk-` |
77
+ | Replicate | `r8_` |
78
+ | Hugging Face | `hf_` |
79
+ | Fireworks | `fw_` |
80
+ | OpenRouter | `sk-or-v1-` |
81
+ | Grok (xAI) | `xai-` |
82
+ | Cerebras | `sk-` |
83
+ | NVIDIA | `sk-` |
84
+ | Hyperbolic | `sk-` |
85
+ | Poe | `sk-` |
86
+
87
+ ### 中国
88
+
89
+ | 服务商 | 前缀 | 显示名 |
90
+ |--------|------|--------|
91
+ | 阿里百炼 | `sk-ws-` / `sk-` |
92
+ | 阿里百炼编程 | `sk-sp-` |
93
+ | ModelScope | `ms-` | 魔搭 |
94
+ | Zhipu GLM | `sk-` | 智谱 |
95
+ | Kimi | `sk-` | 月之暗面 |
96
+ | MiniMax | `sk-` | MiniMax |
97
+ | SiliconFlow | `sk-` | 硅基流动 |
98
+ | Baichuan | `sk-` | 百川 |
99
+ | Yi | `sk-` | 零一万物 |
100
+ | StepFun | `sk-` | 阶跃星辰 |
101
+ | Doubao | `sk-` | 豆包 |
102
+ | Infini | `sk-` | 无问芯穹 |
103
+ | MiMo | `sk-` | 小米 |
104
+ | Tencent Hunyuan | `sk-` | 腾讯混元 |
105
+ | CSTCloud | `sk-` | 中算云 |
106
+
107
+ ### 新增服务商
108
+
109
+ | 服务商 | 说明 |
110
+ |--------|------|
111
+ | LongCat | 新增 |
112
+ | AI302 | 新增 |
113
+ | PPIO | 新增 |
114
+ | DMXAPI | 新增 |
115
+ | OCoolAI | 新增 |
116
+ | ZAI | 新增 |
117
+ | MiMo Plan | 计划版 |
118
+ | MiniMax Plan | 计划版 |
119
+ | DashScope Coding | 编程版 |
120
+ | Zhipu Coding | 编程版 |
121
+ | Kimi Coding | 编程版 |
122
+ | Infini Coding | 编程版 |
123
+
124
+ ## 添加自定义服务商
125
+
126
+ ### 方式一:Python 文件(推荐)
127
+
128
+ 创建 `key_manager/providers/my_llm.py`:
129
+
130
+ ```python
131
+ from .base import ProviderBase
132
+
133
+
134
+ class MyLlmProvider(ProviderBase):
135
+ name = "my-llm"
136
+ base_url = "https://api.my-llm.com/v1"
137
+ check_endpoint = "/models"
138
+ check_model = "my-model"
139
+ display_name = "My LLM"
140
+ key_prefixes = ["myllm-"]
141
+ error_signatures = ["my-llm", "invalid api key"]
142
+ website_url = "https://my-llm.com"
143
+ docs_url = "https://docs.my-llm.com"
144
+
145
+ def build_headers(self, key: str) -> dict:
146
+ return {"Authorization": f"Bearer {key}"}
147
+ ```
148
+
149
+ 重启服务即可自动发现。
150
+
151
+ ### 方式二:YAML 配置
152
+
153
+ 在 `config.yaml` 中添加:
154
+
155
+ ```yaml
156
+ providers:
157
+ custom:
158
+ - name: "my-llm"
159
+ base_url: "https://api.my-llm.com/v1"
160
+ check_endpoint: "/models"
161
+ check_model: "my-model"
162
+ display_name: "My LLM"
163
+ key_prefixes: ["myllm-"]
164
+ auth_type: bearer
165
+ ```
166
+
167
+ ### 方式三:Web API
168
+
169
+ ```bash
170
+ # 添加服务商
171
+ curl -X POST http://localhost:18001/api/providers \
172
+ -H "Content-Type: application/json" \
173
+ -d '{"name":"my-llm","base_url":"https://api.my-llm.com/v1","check_endpoint":"/models"}'
174
+
175
+ # 列出所有服务商
176
+ curl http://localhost:18001/api/providers
177
+
178
+ # 删除服务商
179
+ curl -X DELETE http://localhost:18001/api/providers/my-llm
180
+ ```
181
+
182
+ ## 快速开始
183
+
184
+ ### 安装
185
+
186
+ ```bash
187
+ # 克隆项目
188
+ git clone https://github.com/Townrain/API-Key-Manager.git
189
+ cd key
190
+
191
+ # 安装依赖
192
+ pip install -r requirements.txt
193
+
194
+ # 安装开发依赖(含测试工具)
195
+ pip install -e ".[dev]"
196
+ ```
197
+
198
+ ### CLI 使用
199
+
200
+ ```bash
201
+ # 导入密钥
202
+ python main.py import --file data/input/example.json
203
+ python main.py import --dir ./data/input
204
+
205
+ # 验证密钥
206
+ python main.py check
207
+ python main.py check --provider openai
208
+ python main.py check --key sk-xxx
209
+
210
+ # 测试密钥
211
+ python main.py test
212
+ python main.py test --skip-token
213
+ python main.py test --skip-concurrency
214
+
215
+ # 列出密钥
216
+ python main.py list --provider anthropic --status valid
217
+ python main.py list --status invalid
218
+
219
+ # 生成报告
220
+ python main.py report --days 7
221
+ ```
222
+
223
+ ### Web 界面
224
+
225
+ ```bash
226
+ # 启动 Web 服务器
227
+ python web.py
228
+
229
+ # 访问以下地址:
230
+ # 主界面:http://localhost:18001
231
+ # API 文档:http://localhost:18001/docs
232
+ # Redoc:http://localhost:18001/redoc
233
+ ```
234
+
235
+ ## 安全特性
236
+
237
+ ### 加密存储
238
+
239
+ API 密钥默认使用 AES-256-GCM 加密存储,每次加密使用随机盐值:
240
+
241
+ ```bash
242
+ # 设置加密密钥(环境变量)
243
+ set KEY_MANAGER_SECRET=your-secret-key
244
+
245
+ # 启动服务
246
+ python web.py
247
+ ```
248
+
249
+ 加密后的 `keys.json` 格式:
250
+ ```json
251
+ {
252
+ "encrypted": true,
253
+ "salt": "base64-encoded-random-salt",
254
+ "nonce": "base64-encoded-nonce",
255
+ "data": "base64-encoded-ciphertext"
256
+ }
257
+ ```
258
+
259
+ ### 安全防护
260
+
261
+ - **路径遍历防护** - 导入端点验证路径在允许目录内
262
+ - **SSRF 防护** - `custom_base_url` 验证域名白名单,阻止私有 IP,已接入 `check/single` 和 `balance` 端点
263
+ - **时序安全认证** - 使用 `hmac.compare_digest()` 防止时序攻击
264
+ - **认证警告** - 未配置 API Key 时启动警告
265
+ - **密钥掩码** - API 响应中只返回 `key_masked`,不暴露完整密钥
266
+ - **Webhook 安全** - Webhook 端点使用正确的 API 方法,防止运行时错误
267
+
268
+ ### API 认证
269
+
270
+ ```bash
271
+ # 设置 API Key(环境变量)
272
+ set KEY_MANAGER_API_KEY=your-api-key
273
+
274
+ # 或在 config.yaml 中配置
275
+ # auth:
276
+ # api_key: "your-api-key"
277
+ ```
278
+
279
+ ## 提供商智能检测
280
+
281
+ ### 检测策略
282
+
283
+ 系统采用**全并发探测**策略,自动识别 45+ 个 AI 服务商的 API 密钥。
284
+
285
+ ### 检测流程(重要!)
286
+
287
+ ```
288
+ 1. 前缀匹配 - 检查唯一前缀(如 sk-proj- → OpenAI,AIza → Google)
289
+ 2. 格式匹配 - 检查特殊格式(如智谱的 {id}.{secret} 格式)
290
+ 3. 全并发探测 - 同时向所有服务商发送请求
291
+ 4. 签名匹配 - 如果无200响应,通过错误响应体签名识别服务商
292
+ ```
293
+
294
+ ### ⚠️ 关键概念:/v1/models vs /chat/completions
295
+
296
+ **这两个端点的作用完全不同,不能混用!**
297
+
298
+ | 端点 | 作用 | 返回200的含义 |
299
+ |------|------|--------------|
300
+ | `/v1/models` | 获取模型列表 | 只表示可以获取模型列表,**不能**判断密钥是否有效 |
301
+ | `/chat/completions` | 调用模型 | 表示密钥对该提供商有效,**这才是判断提供商的依据** |
302
+
303
+ **常见错误:**
304
+ - ❌ 用 `/v1/models` 返回200来判断提供商 → 错误!
305
+ - ✅ 用 `/chat/completions` 返回200来判断提供商 → 正确!
306
+
307
+ ### 检测流程详解
308
+
309
+ #### Step 1: 前缀匹配
310
+
311
+ 检查密钥是否匹配唯一前缀:
312
+
313
+ ```python
314
+ # 唯一前缀 → 直接返回
315
+ "sk-proj-" → OpenAI
316
+ "sk-ant-api03-" → Anthropic
317
+ "AIza" → Google
318
+ "ms-" → ModelScope
319
+
320
+ # 共享前缀 → 需要进一步探测
321
+ "sk-" → 20+ 提供商(DeepSeek, OpenAI, 等)
322
+ ```
323
+
324
+ #### Step 2: 格式匹配
325
+
326
+ 检查密钥是否匹配特殊格式:
327
+
328
+ ```python
329
+ # 智谱/Z.AI 格式:{id}.{secret}
330
+ 50bcde33b8774aa8a2cc1bd6d39444ae.ifriyNWRLStzpLEs
331
+ → 返回 ["zhipu", "zai"]
332
+ ```
333
+
334
+ #### Step 3: 全并发探测(核心逻辑)
335
+
336
+ **重要:这一步用 `/chat/completions` 验证,不是 `/v1/models`!**
337
+
338
+ ```python
339
+ async def detect_provider(client, key):
340
+ # Step 3.1: 获取所有提供商的模型列表(/v1/models)
341
+ # 这一步只是为了获取模型列表,不能判断提供商
342
+ models = {}
343
+ for name, provider in PROVIDERS.items():
344
+ resp = await client.get(f"{provider.base_url}/v1/models")
345
+ if resp.status_code == 200:
346
+ models[name] = extract_models(resp.json())
347
+
348
+ # Step 3.2: 并发测试所有(提供商,模型)对的 /chat/completions
349
+ # 这一步才是判断提供商的依据!
350
+ tasks = []
351
+ for name, model_list in models.items():
352
+ for model in model_list:
353
+ tasks.append(try_chat_completion(name, model))
354
+
355
+ # 第一个返回200的提供商胜出
356
+ for coro in asyncio.as_completed(tasks):
357
+ name, valid, status_code = await coro
358
+ if valid: # /chat/completions 返回200
359
+ return name
360
+ ```
361
+
362
+ #### Step 4: 签名匹配
363
+
364
+ 如果所有 `/chat/completions` 都失败,通过错误响应体识别提供商:
365
+
366
+ ```python
367
+ # 错误签名匹配
368
+ UNIQUE_SIGNATURES = {
369
+ "dashscope": ["model-studio", "modelstudio", "apikey-error"],
370
+ "anthropic": ["request not allowed", "anthropic", "x-api-key"],
371
+ "openai": ["platform.openai.com"],
372
+ # ... 更多签名
373
+ }
374
+
375
+ # 匹配阈值:至少2个签名匹配(200分)才返回结果
376
+ if best_score >= 200:
377
+ return best_name
378
+ ```
379
+
380
+ ### 免费模型的重要性
381
+
382
+ **某些提供商(如 OpenCode Zen)提供免费模型,这些模型对检测非常重要!**
383
+
384
+ ```python
385
+ # OpenCode Zen 的模型列表:
386
+ # - claude-fable-5 (付费)
387
+ # - deepseek-v4-flash-free (免费) ✅
388
+ # - mimo-v2.5-free (免费) ✅
389
+ # - ...
390
+
391
+ # 如果密钥只对免费模型有效:
392
+ # - /chat/completions + claude-fable-5 → 401 (付费模型,无权限)
393
+ # - /chat/completions + deepseek-v4-flash-free → 200 (免费模型,有权限) ✅
394
+ ```
395
+
396
+ **检测逻辑会并发测试所有模型,包括免费模型。只要有一个模型返回200,提供商就会被正确识别。**
397
+
398
+ ### 检测优先级
399
+
400
+ | 优先级 | 方法 | 端点 | 说明 |
401
+ |--------|------|------|------|
402
+ | 1 | 前缀匹配 | - | 唯一前缀,如 `sk-proj-` → OpenAI |
403
+ | 2 | 格式匹配 | - | 特殊格式,如 `{id}.{secret}` → 智谱 |
404
+ | 3 | 全并发探测 | `/chat/completions` | 第一个返回200的提供商胜出 |
405
+ | 4 | 签名匹配 | - | 通过错误响应体识别,需至少2个签名匹配 |
406
+
407
+ ### 常见问题
408
+
409
+ #### Q: 为什么我的密钥被错误识别为其他提供商?
410
+
411
+ **A: 可能是因为:**
412
+ 1. 密钥使用共享前缀(如 `sk-`),需要通过 `/chat/completions` 验证
413
+ 2. 密钥只对免费模型有效,但检测逻辑没有测试免费模型
414
+ 3. 提供商的 `/v1/models` 返回200,但 `/chat/completions` 返回401
415
+
416
+ #### Q: 为什么 `/v1/models` 返回200,但检测失败?
417
+
418
+ **A: 因为 `/v1/models` 返回200不能判断提供商!**
419
+ - `/v1/models` 只表示可以获取模型列表
420
+ - `/chat/completions` 返回200才能判断提供商
421
+
422
+ #### Q: 如何确保检测正确?
423
+
424
+ **A: 确保以下几点:**
425
+ 1. 密钥对至少一个模型有调用权限(包括免费模型)
426
+ 2. 提供商的 `/chat/completions` 端点正常工作
427
+ 3. 密钥没有过期或被撤销
428
+
429
+ ### 检测流程图
430
+
431
+ ```
432
+ 输入: API Key
433
+
434
+
435
+ ┌─────────────────────────────────────────────────────────────┐
436
+ │ Step 1: 前缀匹配 │
437
+ │ - 唯一前缀 → 直接返回(如 sk-proj- → OpenAI) │
438
+ │ - 共享前缀 → 继续下一步 │
439
+ └─────────────────────────────────────────────────────────────┘
440
+
441
+
442
+ ┌─────────────────────────────────────────────────────────────┐
443
+ │ Step 2: 格式匹配 │
444
+ │ - 智谱格式 {id}.{secret} → 返回 ["zhipu", "zai"] │
445
+ └─────────────────────────────────────────────────────────────┘
446
+
447
+
448
+ ┌─────────────────────────────────────────────────────────────┐
449
+ │ Step 3: 全并发探测(用 /chat/completions 验证) │
450
+ │ │
451
+ │ 3.1 获取所有提供商的模型列表(/v1/models) │
452
+ │ - 这一步只是为了获取模型,不能判断提供商 │
453
+ │ │
454
+ │ 3.2 并发测试所有(提供商,模型)对的 /chat/completions │
455
+ │ - 第一个返回200的提供商胜出 │
456
+ │ - 包括免费模型和付费模型 │
457
+ └─────────────────────────────────────────────────────────────┘
458
+
459
+
460
+ ┌─────────────────────────────────────────────────────────────┐
461
+ │ Step 4: 签名匹配(如果 /chat/completions 都失败) │
462
+ │ - 通过错误响应体中的关键词识别提供商 │
463
+ │ - 需要至少2个签名匹配(200分)才返回结果 │
464
+ └─────────────────────────────────────────────────────────────┘
465
+ ```
466
+
467
+ ### 代码实现
468
+
469
+ 检测逻辑在 `key_manager/detector.py` 中实现:
470
+
471
+ ```python
472
+ async def detect_provider(client, key: str, suspected_provider: str = None) -> str:
473
+ """Detect provider by concurrently probing ALL providers with multiple models.
474
+
475
+ Strategy:
476
+ 1. If suspected_provider given, try it first
477
+ 2. If key matches unique pattern, try that provider
478
+ 3. Otherwise, concurrently probe ALL providers with their top 5 models
479
+ 4. First provider returning 200 wins
480
+ """
481
+ # Step 1: If suspected provider, try it first
482
+ if suspected_provider:
483
+ provider_name = suspected_provider.lower()
484
+ if provider_name in PROVIDERS:
485
+ return provider_name
486
+
487
+ # Step 2: Try format matching (e.g., Zhipu's {id}.{secret})
488
+ format_candidates = detect_by_format(key)
489
+ if format_candidates:
490
+ for name in format_candidates:
491
+ if name in PROVIDERS:
492
+ return name
493
+
494
+ # Step 3: Try prefix matching
495
+ prefix_candidates = detect_by_prefix(key)
496
+ if prefix_candidates:
497
+ if len(prefix_candidates) == 1:
498
+ return prefix_candidates[0]
499
+ # If multiple candidates, continue to Step 4
500
+
501
+ # Step 4: Concurrently probe ALL providers
502
+ # First, get models from all providers concurrently
503
+ model_tasks = [get_provider_models(name, provider) for name, provider in PROVIDERS.items()]
504
+ model_results = await asyncio.gather(*model_tasks)
505
+
506
+ # Build tasks: (provider_name, model) pairs
507
+ tasks = []
508
+ for name, models, is_valid in model_results:
509
+ if models:
510
+ for model in models:
511
+ tasks.append((name, model))
512
+
513
+ # Concurrently check all (provider, model) pairs
514
+ all_tasks = [try_model(name, model) for name, model in tasks]
515
+
516
+ # First provider returning 200 wins
517
+ for coro in asyncio.as_completed(all_tasks):
518
+ name, valid, body, status_code = await coro
519
+ if valid:
520
+ return name
521
+
522
+ # Step 5: Signature matching
523
+ return match_by_signature(error_bodies)
524
+ ```
525
+
526
+ ## 错误信息简化
527
+
528
+ 系统会自动将服务商返回的原始错误信息简化为用户友好的提示:
529
+
530
+ | 原始错误信息 | 简化后 |
531
+ |-------------|--------|
532
+ | `Access denied, please make sure your account is in good standing...` | 余额不足 |
533
+ | `Invalid API Key` | Key 无效 |
534
+ | `Authentication fails` | 认证失败 |
535
+ | `Token expired` | Key 已过期 |
536
+ | `Rate limit exceeded` | 请求过于频繁 |
537
+ | `Account suspended` | 账号被封禁 |
538
+ | `Access denied` | 无权限访问 |
539
+ | `Model does not exist` | 模型不存在 |
540
+
541
+ 错误信息简化在 `base.py` 的 `simplify_error()` 函数中实现,支持:
542
+
543
+ - 基于状态码的简化(401 → Key 无效,402 → 余额不足,429 → 请求过于频繁)
544
+ - 基于关键词的模式匹配(authentication、expired、rate limit 等)
545
+ - 长错误信息截断(超过100字符时截断并添加省略号)
546
+
547
+ ## 模型检测
548
+
549
+ ### 模型列表来源
550
+
551
+ 系统从 Cherry Studio 同步模型数据,生成 `models_registry.py` 文件,包含每个服务商的静态模型列表:
552
+
553
+ ```python
554
+ PROVIDER_MODELS = {
555
+ "openai": ["gpt-4o", "gpt-4o-mini", "gpt-3.5-turbo", ...],
556
+ "anthropic": ["claude-3-opus", "claude-3-sonnet", ...],
557
+ "dashscope": ["qwen-turbo", "qwen-plus", "qwen-max", ...],
558
+ "siliconflow": ["Qwen/Qwen2.5-7B-Instruct", ...],
559
+ # ... 45+ 服务商
560
+ }
561
+ ```
562
+
563
+ ### 模型检测流程
564
+
565
+ 当用户点击「检测可用模型」时:
566
+
567
+ 1. 获取服务商的模型列表(优先使用 API 返回,回退到静态列表)
568
+ 2. 并发检测每个模型的可用性(batch_size 动态调整)
569
+ 3. 每个模型发送一个最小请求(`POST /chat/completions`,`max_tokens: 5`)
570
+ 4. 返回200的模型标记为可用,其他标记为失败
571
+ 5. 失败的模型会串行重试(最多2次)
572
+
573
+ ```python
574
+ # 模型检测逻辑
575
+ async def check_model(http, model):
576
+ resp = await client.post(
577
+ f"{provider.get_base_url()}/chat/completions",
578
+ json={"model": model, "messages": [...], "max_tokens": 5}
579
+ )
580
+ return model, 200 if resp.status_code == 200 else resp.status_code
581
+
582
+ # 动态并发控制
583
+ batch_size = 5 # 初始并发数
584
+ for i in range(0, len(models), batch_size):
585
+ batch = models[i:i+batch_size]
586
+ results = await asyncio.gather(*[check_model(http, m) for m in batch])
587
+
588
+ # 全部成功 → 并发数 +1
589
+ if all_success:
590
+ batch_size += 1
591
+ # 有失败 → 保持当前并发数
592
+
593
+ # 失败模型串行重试
594
+ for model in failed_models:
595
+ _, code = await check_model(http, model)
596
+ if code == 200:
597
+ # 重试成功
598
+ ```
599
+
600
+ ### 模型能力检测
601
+
602
+ 系统支持按类型筛选模型:
603
+
604
+ | 类型 | 说明 | 筛选方法 |
605
+ |------|------|----------|
606
+ | 视觉模型 | 支持图像输入 | `is_vision_model()` |
607
+ | 工具模型 | 支持函数调用 | `is_tool_model()` |
608
+ | 推理模型 | 支持思维链 | `is_reasoning_model()` |
609
+ | 联网模型 | 支持网络搜索 | `is_websearch_model()` |
610
+ | 免费模型 | 免费额度 | `is_free_model()` |
611
+ | 嵌入模型 | 文本嵌入 | `is_embedding_model()` |
612
+ | 重排模型 | 搜索重排 | `is_rerank_model()` |
613
+
614
+ 能力数据从 Cherry Studio 同步,存储在 `data/model_capabilities.json` 中。
615
+
616
+ ## 项目结构
617
+ ```
618
+ key/
619
+ ├── key_manager/ # 核心包
620
+ │ ├── __init__.py # 包导出
621
+ │ ├── cli.py # CLI 入口
622
+ │ ├── web/ # Web 模块 (v4.0.0 重构)
623
+ │ │ ├── __init__.py # 包导出
624
+ │ │ ├── _app.py # FastAPI 应用入口
625
+ │ │ ├── middleware.py # 中间件和错误处理器
626
+ │ │ ├── progress.py # ProgressTracker和SSE辅助
627
+ │ │ └── routes/ # 路由模块
628
+ │ │ ├── keys.py # 密钥管理路由
629
+ │ │ ├── check.py # 验证路由
630
+ │ │ ├── test.py # 测试路由
631
+ │ │ ├── balance.py # 余额查询路由
632
+ │ │ ├── models.py # 模型路由
633
+ │ │ ├── providers.py # 提供商路由
634
+ │ │ ├── stats.py # 统计路由
635
+ │ │ └── misc.py # 杂项路由
636
+ │ ├── config.py # 配置加载
637
+ │ ├── storage.py # AES-256-GCM 加密存储
638
+ │ ├── errors.py # 结构化错误码
639
+ │ ├── api_models.py # Pydantic 模型
640
+ │ ├── parser.py # JSON 导入 + 路径验证
641
+ │ ├── detector.py # 智能提供商检测
642
+ │ ├── validator.py # 并发验证引擎
643
+ │ ├── checker.py # 重试包装器
644
+ │ ├── tester.py # 能力测试
645
+ │ ├── ssrf.py # SSRF 防护
646
+ │ ├── logger.py # 日志系统
647
+ │ ├── proxy.py # 代理检测
648
+ │ ├── webhook.py # Webhook 通知
649
+ │ ├── i18n.py # 国际化
650
+ │ ├── model_capabilities.py # 模型能力检测
651
+ │ └── providers/ # 45+ 提供商实现
652
+ │ ├── __init__.py # 注册表
653
+ │ ├── base.py # ABC 接口
654
+ │ ├── openai.py # OpenAI
655
+ │ ├── anthropic.py # Anthropic
656
+ │ └── ... # 更多提供商
657
+ ├── static/ # 前端静态资源 (v4.0.0 重构)
658
+ │ ├── css/
659
+ │ │ ├── tokens.css # CSS 变量
660
+ │ │ ├── base.css # 基础样式
661
+ │ │ ├── components.css # 组件入口 (@import)
662
+ │ │ ├── components/ # 独立组件样式
663
+ │ │ │ ├── button.css # 按钮
664
+ │ │ │ ├── form.css # 表单控件
665
+ │ │ │ ├── card.css # 卡片、操作区
666
+ │ │ │ ├── table.css # 表格、徽章、分页
667
+ │ │ │ ├── stat.css # 统计卡片
668
+ │ │ │ ├── nav.css # 导航标签
669
+ │ │ │ └── overlay.css # 进度、Toast、日志
670
+ │ │ ├── modals.css # 模态框样式
671
+ │ │ └── animations.css # 动画
672
+ │ └── js/
673
+ │ ├── state.js # 全局状态
674
+ │ ├── utils.js # 工具函数
675
+ │ ├── api/ # API 模块
676
+ │ │ ├── client.js # 通用 fetch 逻辑
677
+ │ │ ├── index.js # 重新导出
678
+ │ │ ├── stats.js # 统计 API
679
+ │ │ ├── keys.js # 密钥 API
680
+ │ │ ├── check.js # 验证 API
681
+ │ │ ├── test.js # 测试 API
682
+ │ │ ├── balance.js # 余额 API
683
+ │ │ ├── models.js # 模型 API
684
+ │ │ ├── providers.js # 提供商 API
685
+ │ │ └── misc.js # 杂项 API
686
+ │ ├── toast.js # Toast 通知
687
+ │ ├── progress.js # 进度覆盖层
688
+ │ ├── confirm.js # 确认模态框
689
+ │ ├── keys-table.js # 表格渲染
690
+ │ ├── providers.js # 提供商网格
691
+ │ ├── batch.js # 批量结果
692
+ │ ├── modals.js # 模态框
693
+ │ ├── model-detect.js # 模型检测
694
+ │ └── init.js # 入口点
695
+ ├── templates/
696
+ │ └── index.html # Web UI 入口 (467行)
697
+ ├── tests/ # 测试套件
698
+ │ ├── conftest.py # 共享 fixtures 和帮助函数
699
+ │ ├── test_detector.py # 提供商检测测试
700
+ │ ├── test_parser.py # 解析器测试
701
+ │ ├── test_validator.py # 验证器测试
702
+ │ ├── test_checker.py # 检查器测试
703
+ │ ├── test_providers.py # 提供商合约测试
704
+ │ ├── test_security.py # 安全回归测试
705
+ │ ├── test_storage.py # 加密存储测试
706
+ │ ├── test_errors.py # 错误系统测试
707
+ │ ├── test_i18n.py # 国际化测试
708
+ │ ├── test_e2e.py # 端到端测试
709
+ │ ├── test_webhook.py # Webhook 测试
710
+ │ ├── test_bug_fixes.py # Bug 修复回归测试
711
+ │ └── test_provider_refactoring.py # 提供商重构测试
712
+ ├── sdk/ # SDK
713
+ │ ├── python/ # Python SDK
714
+ │ └── typescript/ # TypeScript SDK
715
+ ├── config.yaml # 配置文件
716
+ ├── pyproject.toml # 项目配置
717
+ ├── main.py # CLI 入口
718
+ └── web.py # Web 入口
719
+ ```
720
+
721
+ ## 配置说明
722
+
723
+ 编辑 `config.yaml`:
724
+
725
+ ```yaml
726
+ # 代理设置
727
+ proxy: "http://127.0.0.1:7890" # 或 socks5://127.0.0.1:7890
728
+
729
+ # 验证设置
730
+ check:
731
+ concurrency: 100 # 并发数
732
+ timeout_seconds: 30 # 超时时间
733
+ retry_failed: true # 失败重试
734
+ retry_count: 2 # 重试次数
735
+
736
+ # 测试设置
737
+ test:
738
+ token_steps:
739
+ - 1024
740
+ - 4096
741
+ - 16384
742
+ - 65536
743
+ concurrency_steps:
744
+ - 1
745
+ - 5
746
+ - 10
747
+ - 20
748
+
749
+ # 认证设置
750
+ auth:
751
+ api_key: "your-secret-api-key" # API 认证
752
+
753
+ # 速率限制
754
+ rate_limit:
755
+ requests_per_minute: 60
756
+ ```
757
+
758
+ ## API 端点
759
+
760
+ | 方法 | 端点 | 说明 |
761
+ |------|------|------|
762
+ | GET | `/api/keys` | 获取密钥列表 |
763
+ | GET | `/api/keys/export` | 导出有效密钥 |
764
+ | POST | `/api/import` | 导入密钥 |
765
+ | POST | `/api/import/upload` | 上传 JSON 文件导入 |
766
+ | POST | `/api/check/single` | 验证单个密钥 |
767
+ | POST | `/api/check/batch` | 批量验证 |
768
+ | POST | `/api/test/single` | 测试单个密钥 |
769
+ | POST | `/api/test/token` | 测试 Token 上限 |
770
+ | POST | `/api/test/concurrency` | 测试并发能力 |
771
+ | GET | `/api/models` | 获取模型列表 |
772
+ | POST | `/api/models/check` | 检测可用模型(SSE 流) |
773
+ | GET | `/api/providers` | 获取服务商列表 |
774
+ | GET | `/api/stats` | 获取统计信息 |
775
+ | GET | `/api/logs` | 获取日志 |
776
+ | POST | `/api/webhooks` | 创建 Webhook |
777
+ | GET | `/docs` | Swagger UI 文档 |
778
+ | GET | `/redoc` | Redoc 文档 |
779
+
780
+ ## Webhook 使用
781
+
782
+ ### 支持的事件类型
783
+
784
+ | 事件 | 说明 |
785
+ |------|------|
786
+ | `key.imported` | 密钥导入完成 |
787
+ | `key.checked` | 密钥验证完成 |
788
+ | `key.tested` | 密钥测试完成 |
789
+ | `key.deleted` | 密钥删除 |
790
+ | `batch.check.completed` | 批量验证完成 |
791
+ | `batch.test.completed` | 批量测试完成 |
792
+ | `error.occurred` | 发生错误 |
793
+
794
+ ### 配置 Webhook
795
+
796
+ ```yaml
797
+ webhooks:
798
+ - url: "https://example.com/webhook"
799
+ events:
800
+ - "key.imported"
801
+ - "key.checked"
802
+ secret: "your-webhook-secret" # HMAC-SHA256 签名
803
+ active: true
804
+ max_retries: 3
805
+ ```
806
+
807
+ ### 签名验证
808
+
809
+ ```python
810
+ import hmac
811
+ import hashlib
812
+ import json
813
+
814
+ def verify_signature(payload, secret, signature):
815
+ body = json.dumps(payload, separators=(",", ":"), sort_keys=True)
816
+ expected = hmac.new(
817
+ secret.encode("utf-8"),
818
+ body.encode("utf-8"),
819
+ hashlib.sha256,
820
+ ).hexdigest()
821
+ return signature == f"sha256={expected}"
822
+ ```
823
+
824
+ ## 测试
825
+
826
+ ```bash
827
+ # 运行所有测试
828
+ python -m pytest tests/ -v
829
+
830
+ # 运行特定测试
831
+ python -m pytest tests/test_security.py -v
832
+ python -m pytest tests/test_detector.py -v
833
+
834
+ # 运行测试并查看覆盖率
835
+ python -m pytest tests/ --cov=key_manager --cov-report=term-missing
836
+ ```
837
+
838
+ ### 测试覆盖
839
+
840
+ | 模块 | 测试文件 | 测试数 |
841
+ |------|---------|--------|
842
+ | 提供商检测 | `test_detector.py` | 44 |
843
+ | 密钥解析 | `test_parser.py` | 24 |
844
+ | 验证器 | `test_validator.py` | 13 |
845
+ | 检查器 | `test_checker.py` | 4 |
846
+ | 提供商合约 | `test_providers.py` | 220 |
847
+ | 安全回归 | `test_security.py` | 21 |
848
+ | 加密存储 | `test_storage.py` | 26 |
849
+ | 错误系统 | `test_errors.py` | 28 |
850
+ | 国际化 | `test_i18n.py` | 37 |
851
+ | 端到端 | `test_e2e.py` | 18 |
852
+ | Webhook | `test_webhook.py` | 35 |
853
+ | OpenAPI | `test_openapi.py` | 26 |
854
+ | 代理检测 | `test_proxy.py` | 19 |
855
+ | 日志系统 | `test_logger.py` | 21 |
856
+ | 能力测试 | `test_tester.py` | 11 |
857
+ | 核心门面 | `test_core.py` | 22 |
858
+ | Bug 修复 | `test_bug_fixes.py` | 15 |
859
+ | 提供商重构 | `test_provider_refactoring.py` | 32 |
860
+
861
+ **总测试数**: 728+ | **覆盖率**: 80%
862
+ ## SDK 使用
863
+
864
+ ### Python SDK
865
+
866
+ ```bash
867
+ cd sdk/python
868
+ pip install -e .
869
+ ```
870
+
871
+ ```python
872
+ from key_manager_sdk import KeyManagerClient
873
+
874
+ client = KeyManagerClient(base_url="http://localhost:18001")
875
+
876
+ # 获取密钥列表
877
+ keys = client.keys()
878
+
879
+ # 验证单个密钥
880
+ result = client.check_single(key="sk-xxx", provider="openai")
881
+ ```
882
+
883
+ ### TypeScript SDK
884
+
885
+ ```bash
886
+ cd sdk/typescript
887
+ npm install
888
+ ```
889
+
890
+ ```typescript
891
+ npm install @api-key-manager/sdk
892
+
893
+ const client = new KeyManagerClient({ baseUrl: 'http://localhost:18001' });
894
+
895
+ // 获取密钥列表
896
+ const keys = await client.keys();
897
+
898
+ // 验证单个密钥
899
+ const result = await client.checkSingle({ key: 'sk-xxx', provider: 'openai' });
900
+ ```
901
+
902
+ ## 依赖
903
+
904
+ - Python 3.10+
905
+ - httpx - 异步 HTTP 客户端
906
+ - FastAPI - Web 框架
907
+ - uvicorn - ASGI 服务器
908
+ - PyYAML - 配置解析
909
+ - Rich - 终端美化
910
+ - cryptography - 加密存储
911
+ - pydantic - 数据验证
912
+
913
+ ## 已知问题和限制
914
+
915
+ ### 1. 中转站服务商误识别
916
+
917
+ 由于某些中转站服务商(如 Z.AI、DMXAPI、OCoolAI 等)使用与原厂相同的 API 端点和模型,当密钥无效时,错误响应可能包含原厂的签名关键词,导致误识别。
918
+
919
+ 例如:一个硅基流动的密钥,如果被发送到阿里百炼的端点,阿里百炼会返回包含 "model-studio" 和 "apikey-error" 的错误响应,这可能导致系统误认为该密钥是阿里百炼的。
920
+
921
+ **解决方案**:系统要求至少2个签名匹配(200分)才返回识别结果,以减少误报。
922
+
923
+ ### 2. 签名匹配的局限性
924
+
925
+ 签名匹配依赖于服务商返回的错误响应体中的关键词。如果服务商更改了错误消息格式,签名可能失效。
926
+
927
+ **建议**:定期运行 `verify_signatures.py` 脚本验证签名的有效性。
928
+
929
+ ### 3. 并发检测的超时问题
930
+
931
+ 全并发探测时,某些服务商可能响应较慢(超过10秒超时)。这可能导致有效的服务商被跳过。
932
+
933
+ **解决方案**:系统会对失败的模型进行串行重试(最多2次)。
934
+
935
+ ### 4. 智谱/Z.AI 密钥的双重检测
936
+
937
+ 智谱和 Z.AI 使用相同的 GLM 模型和 API 格式,但使用不同的 Base URL:
938
+
939
+ - 智谱:`https://open.bigmodel.cn/api/paas/v4`
940
+ - Z.AI:`https://api.z.ai/api/paas/v4`
941
+
942
+ 同一个密钥可能在两个平台都能工作,系统会返回第一个响应200的服务商。
943
+
944
+ ### 5. 模型列表的时效性
945
+
946
+ 模型列表从 Cherry Studio 同步,每日更新一次。新发布的模型可能需要等待同步后才能被检测到。
947
+
948
+
949
+ ## 更新日志
950
+
951
+
952
+ ### v4.0.0 (2026-06-21)
953
+
954
+ - **前端模块化重构**: 将单体 `index.html` (4725行) 重构为模块化结构:
955
+ - CSS 拆分: 5个主题文件 + 7个组件文件
956
+ - `tokens.css` — CSS 变量
957
+ - `base.css` — 基础样式、布局、响应式
958
+ - `components.css` — 组件入口 (@import)
959
+ - `components/` — 7个独立组件 (button, form, card, table, stat, nav, overlay)
960
+ - `modals.css` — 模态框样式
961
+ - `animations.css` — 动画和过渡
962
+ - JS 模块化: 12个ES模块
963
+ - `state.js` — 全局状态管理
964
+ - `utils.js` — 纯工具函数
965
+ - `api/` — 9个API模块 (client, stats, keys, check, test, balance, models, providers, misc)
966
+ - UI模块: toast, progress, confirm, keys-table, providers, batch, modals, model-detect
967
+ - `init.js` — 入口点和事件绑定
968
+ - `index.html` 精简至 467行
969
+ - **API模块拆分**: 按后端路由结构拆分前端API模块:
970
+ - `api/client.js` — 通用fetch逻辑 (safeFetch)
971
+ - `api/index.js` — 重新导出所有函数
972
+ - 每个路由模块对应独立文件
973
+ - **静态文件服务**: 添加 `StaticFiles` 中间件
974
+ - **错误处理增强**: loadKeys/loadStats 添加 try-catch 错误处理
975
+ - **ES模块修复**: 修复 State 对象只读绑定问题
976
+
977
+ - **Web模块重构**: 将单体 `web.py` (2094行) 重构为模块化包结构:
978
+ - `web/_app.py` — 应用入口 (239行)
979
+ - `web/middleware.py` — 中间件和错误处理器
980
+ - `web/progress.py` — ProgressTracker和SSE辅助
981
+ - `web/routes/` — 8个路由模块 (keys, check, test, balance, models, providers, stats, misc)
982
+ - **测试套件重构**:
983
+ - 合并补充测试文件 (test_parser_supplement, test_validator_supplement, test_ssrf_supplement)
984
+ - 删除重复测试文件 (test_web_fixes)
985
+ - 修复 mock 路径以匹配新的 web 模块结构
986
+ - 提取共享 fixtures 和帮助函数到 conftest.py
987
+ - 移除冗余 @pytest.mark.asyncio 装饰器
988
+ - 修复速率限制中间件的测试隔离问题
989
+ - **代码去重**: 消除 `api_check_single` 中的重复模型检查逻辑
990
+ - **调试系统清理**: 移除 `sys.path.insert` hack,使用干净的可选导入模式
991
+ - **重复路由修复**: 合并重复的 `GET /api/providers` 路由
992
+ - **日志显示修复**: 修复日志显示为 `[object Object]` 的问题
993
+ - **可扩展性**: 在 `routes/__init__.py` 中添加新路由、中间件和错误处理器的扩展指南
994
+
995
+ - **提供商系统重构**:
996
+ - 删除 `check_model` 属性,改为先从 API 获取模型列表再测试
997
+ - 删除所有提供商中的冗余方法(build_headers, get_models, test_token_limit, test_concurrency, _probe, check_real)
998
+ - 标准提供商从 ~77 行减少到 ~5-8 行
999
+ - 总共减少 ~2,400 行冗余代码
1000
+ - 添加 `chat_endpoint` 属性,自动从 `check_endpoint` 推导 chat completions 端点
1001
+
1002
+ - **检测逻辑修复**:
1003
+ - 修复检测逻辑不再因为 `/v1/models` 返回 200 就立即返回提供商
1004
+ - `/v1/models` 只用于获取模型列表,不能判断提供商
1005
+ - `/chat/completions` 返回 200 才能判断提供商
1006
+ - 添加前缀匹配逻辑,支持共享前缀(如 `sk-`)的提供商检测
1007
+ - 免费模型对检测非常重要,检测逻辑会测试所有模型包括免费模型
1008
+
1009
+ - **文档更新**:
1010
+ - 更新 README.md 的"提供商智能检测"部分,添加详细的检测流程说明
1011
+ - 更新 docs/DEVELOPMENT.md 的"检测系统"部分
1012
+ - 更新 docs/PROVIDER_REFACTORING_PLAN.md,添加"检测逻辑详解"部分
1013
+ - 在 detector.py 中添加详细注释,说明 `/v1/models` 和 `/chat/completions` 的区别
1014
+
1015
+ - **Bug 修复与改进**:
1016
+ - 修复 SDK 文档方法名与实际不符(`get_keys()` → `keys()` 等)
1017
+ - 修复删除密钥时报错 "No passphrase found",新增自动生成加密密钥功能
1018
+ - 修复签名验证报告端点 404 错误,改为动态生成报告
1019
+ - 修复添加自定义服务商按钮缺失,新增前端 UI
1020
+ ### v3.2.0 (2026-06-19)
1021
+
1022
+ - **模型检测按模型测试**: 支持在模型检测模态框中直接测试Token限制和并发能力
1023
+ - **提供商管理UI**: 添加前端UI用于添加/管理自定义提供商
1024
+ - **移除check_model**: 从ProviderBase中移除check_model属性,提供商必须从API获取模型
1025
+ - **并发测试API改进**: 改进错误处理,提供详细错误信息
1026
+ - **提供商Token测试修复**: 修复提供商使用错误模型或错误API端点的问题
1027
+ - **余额API修复**: 修复支持余额查询的服务商的余额检测
1028
+
1029
+
1030
+ ### v3.1.0 (2026-06-19)
1031
+
1032
+ - **提供商自动发现**: 使用pkgutil自动发现替代47个显式导入
1033
+ - **提供商元数据属性**: 所有提供商声明display_name、key_prefixes等类属性
1034
+ - **默认实现**: test_token_limit、test_concurrency等方法现在是具体方法
1035
+ - **YAML配置支持**: 可在config.yaml中定义自定义提供商
1036
+ - **提供商Web API**: 添加/api/providers CRUD端点
1037
+ - **代码精简**: 39个标准提供商从~100行减少到~15行
1038
+
1039
+
1040
+ ### v3.0.0 (2026-06-15)
1041
+
1042
+ - **精确匹配修复**: 能力检测从子串匹配改为 ^$ 锚定精确匹配,修复 o1 误匹配 o1-mini 等问题
1043
+ - **API Bug 修复**: 修复 /api/stats/chart 运行时崩溃、/api/keys 和 /api/keys/export 丢失 key 字段
1044
+ - **测试补充**: 新增 8 个端点的测试(test/token, test/concurrency, models/capabilities, models/check, progress/stream 等)
1045
+ - **前端缓存优化**: 模型类型切换使用本地缓存过滤,不再重复请求 API
1046
+ - **Cherry Studio 同步工作流修复**: 修复 GitHub Actions 路径错误和验证脚本
1047
+ - **Autofill 样式修复**: 修复浏览器 autofill 导致输入框底色变白
1048
+
1049
+ ### v2.2.2 (2026-06-14)
1050
+
1051
+ - **模型检测二级窗口**: 新增模型检测弹窗,支持搜索、类型筛选、全选、批量检测
1052
+ - **模型类型图标**: 显示模型能力图标(视觉/推理/联网/工具/嵌入/重排/免费)
1053
+ - **模型能力数据更新**: 从 Cherry Studio 同步最新模型能力数据(3184 条记录)
1054
+ - **检测逻辑优化**: DeepSeek 欠费密钥正确识别,不再误判为其他服务商
1055
+ - **指定模型检测**: 自动检测服务商时也支持指定模型检测
1056
+
1057
+ ### v2.2.1 (2026-06-14)
1058
+
1059
+ - **检测逻辑修复**: 自动识别后必须调用 check() 验证 key 有效性
1060
+ - **签名匹配修复**: 状态码不再硬编码 401,使用实际状态码
1061
+ - **不可靠服务商**: ppio、nvidia、modelscope 不参与加分
1062
+ - **模型选择器**: 新增模型下拉框,支持选择具体模型检测
1063
+ - **Cherry Studio 同步**: 修复 ownedBy 映射(alibaba→dashscope 等)
1064
+ - **测试补充**: 新增 test_parser_supplement、test_ssrf_supplement、test_validator_supplement
1065
+ ### v2.2.0 (2026-06-14)
1066
+
1067
+ - **检测逻辑重构**: 添加三步检测逻辑(/v1/models → 并发测试)
1068
+ - **URL 修复**: 从 check_endpoint 提取版本路径,修复 OpenCode 等服务商 404 问题
1069
+ - **并发优化**: /v1/models 和 chat/completions 都并发调用
1070
+ - **超时控制**: 所有网络请求 5 秒超时
1071
+ - **模型同步**: 从 Cherry Studio 同步模型数据,支持 ownedBy 映射
1072
+ - **新增服务商**: OpenCode Go、OpenCode Zen
1073
+
1074
+ ### v2.1.2 (2026-06-11)
1075
+
1076
+ - **Bug 修复**: 修复 `KeyManager.detect_provider()` 调用异步函数未 await 的问题
1077
+ - **测试扩展**: 提供商合约测试扩展至全部 44 个服务商
1078
+ - **测试新增**: 新增 `test_proxy.py`、`test_logger.py`、`test_tester.py`、`test_core.py`
1079
+ - **覆盖率提升**: 测试覆盖率从 74% 提升至 88%
1080
+
1081
+ ### v2.1.1
1082
+
1083
+ - 初始发布版本
1084
+
1085
+ ## 许可证
1086
+
1087
+ MIT License