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