whosellm 0.2.0__tar.gz → 0.2.2__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 (115) hide show
  1. {whosellm-0.2.0 → whosellm-0.2.2}/.bumpversion.toml +1 -1
  2. {whosellm-0.2.0 → whosellm-0.2.2}/.claude/skills/review-provider-model/providers/anthropic.md +1 -1
  3. {whosellm-0.2.0 → whosellm-0.2.2}/CLAUDE.md +2 -0
  4. {whosellm-0.2.0 → whosellm-0.2.2}/PKG-INFO +3 -1
  5. {whosellm-0.2.0 → whosellm-0.2.2}/README.md +2 -0
  6. {whosellm-0.2.0 → whosellm-0.2.2}/pyproject.toml +1 -1
  7. {whosellm-0.2.0 → whosellm-0.2.2}/tests/e2e/test_zhipu.py +31 -14
  8. whosellm-0.2.2/tests/models/families/test_deepseek.py +176 -0
  9. {whosellm-0.2.0 → whosellm-0.2.2}/tests/models/families/test_deepseek_tencent.py +23 -1
  10. whosellm-0.2.2/tests/models/families/test_glm45.py +21 -0
  11. {whosellm-0.2.0 → whosellm-0.2.2}/tests/models/families/test_glm45v.py +14 -0
  12. {whosellm-0.2.0 → whosellm-0.2.2}/tests/models/families/test_glm46v.py +14 -0
  13. {whosellm-0.2.0 → whosellm-0.2.2}/tests/models/families/test_glm5.py +3 -3
  14. {whosellm-0.2.0 → whosellm-0.2.2}/tests/models/families/test_gpt5.py +1 -4
  15. {whosellm-0.2.0 → whosellm-0.2.2}/tests/test_auto_register.py +2 -1
  16. {whosellm-0.2.0 → whosellm-0.2.2}/tests/test_llmeta.py +1 -1
  17. {whosellm-0.2.0 → whosellm-0.2.2}/uv.lock +1 -1
  18. {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/__init__.py +1 -1
  19. {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/capabilities.py +0 -1
  20. whosellm-0.2.2/whosellm/models/families/deepseek/deepseek_official.py +117 -0
  21. {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/models/families/deepseek/tencent.py +22 -2
  22. {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/models/families/openai/openai_gpt_4_1.py +0 -1
  23. {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/models/families/openai/openai_gpt_5.py +0 -4
  24. {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/models/families/openai/openai_gpt_5_1.py +0 -1
  25. {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/models/families/openai/openai_gpt_5_2.py +0 -2
  26. {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/models/families/openai/openai_gpt_5_4.py +0 -4
  27. {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/models/families/zhipu.py +25 -15
  28. whosellm-0.2.0/tests/models/families/test_deepseek.py +0 -105
  29. whosellm-0.2.0/whosellm/models/families/deepseek/deepseek_official.py +0 -69
  30. {whosellm-0.2.0 → whosellm-0.2.2}/.claude/commands/interview.md +0 -0
  31. {whosellm-0.2.0 → whosellm-0.2.2}/.claude/projects/-Users-jqq-PycharmProjects-llmeta/memory/MEMORY.md +0 -0
  32. {whosellm-0.2.0 → whosellm-0.2.2}/.claude/projects/-Users-jqq-PycharmProjects-llmeta/memory/feedback_use_uv.md +0 -0
  33. {whosellm-0.2.0 → whosellm-0.2.2}/.claude/skills/code-review/SKILL.md +0 -0
  34. {whosellm-0.2.0 → whosellm-0.2.2}/.claude/skills/create-skill/SKILL.md +0 -0
  35. {whosellm-0.2.0 → whosellm-0.2.2}/.claude/skills/e2e-metadata/SKILL.md +0 -0
  36. {whosellm-0.2.0 → whosellm-0.2.2}/.claude/skills/evolve/SKILL.md +0 -0
  37. {whosellm-0.2.0 → whosellm-0.2.2}/.claude/skills/fix-review/SKILL.md +0 -0
  38. {whosellm-0.2.0 → whosellm-0.2.2}/.claude/skills/release/SKILL.md +0 -0
  39. {whosellm-0.2.0 → whosellm-0.2.2}/.claude/skills/review-provider-model/SKILL.md +0 -0
  40. {whosellm-0.2.0 → whosellm-0.2.2}/.claude/skills/review-provider-model/providers/alibaba.md +0 -0
  41. {whosellm-0.2.0 → whosellm-0.2.2}/.claude/skills/review-provider-model/providers/deepseek.md +0 -0
  42. {whosellm-0.2.0 → whosellm-0.2.2}/.claude/skills/review-provider-model/providers/gemini.md +0 -0
  43. {whosellm-0.2.0 → whosellm-0.2.2}/.claude/skills/review-provider-model/providers/openai.md +0 -0
  44. {whosellm-0.2.0 → whosellm-0.2.2}/.claude/skills/review-provider-model/providers/others.md +0 -0
  45. {whosellm-0.2.0 → whosellm-0.2.2}/.claude/skills/review-provider-model/providers/vidu.md +0 -0
  46. {whosellm-0.2.0 → whosellm-0.2.2}/.claude/skills/review-provider-model/providers/zhipu.md +0 -0
  47. {whosellm-0.2.0 → whosellm-0.2.2}/.claude/skills/update-provider-model/SKILL.md +0 -0
  48. {whosellm-0.2.0 → whosellm-0.2.2}/.claude/skills/update-provider-model/testing.md +0 -0
  49. {whosellm-0.2.0 → whosellm-0.2.2}/.coveragerc +0 -0
  50. {whosellm-0.2.0 → whosellm-0.2.2}/.github/workflows/publish.yml +0 -0
  51. {whosellm-0.2.0 → whosellm-0.2.2}/.github/workflows/tests.yml +0 -0
  52. {whosellm-0.2.0 → whosellm-0.2.2}/.gitignore +0 -0
  53. {whosellm-0.2.0 → whosellm-0.2.2}/.windsurf/workflows/addmodel.md +0 -0
  54. {whosellm-0.2.0 → whosellm-0.2.2}/.windsurf/workflows/arch.md +0 -0
  55. {whosellm-0.2.0 → whosellm-0.2.2}/.windsurf/workflows/testllmeta.md +0 -0
  56. {whosellm-0.2.0 → whosellm-0.2.2}/CHANGELOG.md +0 -0
  57. {whosellm-0.2.0 → whosellm-0.2.2}/LICENSE +0 -0
  58. {whosellm-0.2.0 → whosellm-0.2.2}/docs/add_new_model_family.md +0 -0
  59. {whosellm-0.2.0 → whosellm-0.2.2}/docs/refactor_proposal.md +0 -0
  60. {whosellm-0.2.0 → whosellm-0.2.2}/docs/spec_model_family_redesign.md +0 -0
  61. {whosellm-0.2.0 → whosellm-0.2.2}/examples/advanced_usage.py +0 -0
  62. {whosellm-0.2.0 → whosellm-0.2.2}/examples/basic_usage.py +0 -0
  63. {whosellm-0.2.0 → whosellm-0.2.2}/mypy.ini +0 -0
  64. {whosellm-0.2.0 → whosellm-0.2.2}/pytest.ini +0 -0
  65. {whosellm-0.2.0 → whosellm-0.2.2}/ruff.toml +0 -0
  66. {whosellm-0.2.0 → whosellm-0.2.2}/tests/__init__.py +0 -0
  67. {whosellm-0.2.0 → whosellm-0.2.2}/tests/e2e/__init__.py +0 -0
  68. {whosellm-0.2.0 → whosellm-0.2.2}/tests/e2e/conftest.py +0 -0
  69. {whosellm-0.2.0 → whosellm-0.2.2}/tests/e2e/test_anthropic.py +0 -0
  70. {whosellm-0.2.0 → whosellm-0.2.2}/tests/e2e/test_google.py +0 -0
  71. {whosellm-0.2.0 → whosellm-0.2.2}/tests/e2e/test_openai.py +0 -0
  72. {whosellm-0.2.0 → whosellm-0.2.2}/tests/models/__init__.py +0 -0
  73. {whosellm-0.2.0 → whosellm-0.2.2}/tests/models/families/__init__.py +0 -0
  74. {whosellm-0.2.0 → whosellm-0.2.2}/tests/models/families/test_anthropic.py +0 -0
  75. {whosellm-0.2.0 → whosellm-0.2.2}/tests/models/families/test_gemini.py +0 -0
  76. {whosellm-0.2.0 → whosellm-0.2.2}/tests/models/families/test_glm46.py +0 -0
  77. {whosellm-0.2.0 → whosellm-0.2.2}/tests/models/families/test_gpt3_5.py +0 -0
  78. {whosellm-0.2.0 → whosellm-0.2.2}/tests/models/families/test_gpt4_1.py +0 -0
  79. {whosellm-0.2.0 → whosellm-0.2.2}/tests/models/families/test_gpt4o.py +0 -0
  80. {whosellm-0.2.0 → whosellm-0.2.2}/tests/models/families/test_o1.py +0 -0
  81. {whosellm-0.2.0 → whosellm-0.2.2}/tests/models/families/test_o3.py +0 -0
  82. {whosellm-0.2.0 → whosellm-0.2.2}/tests/models/families/test_o4.py +0 -0
  83. {whosellm-0.2.0 → whosellm-0.2.2}/tests/models/families/test_qwen.py +0 -0
  84. {whosellm-0.2.0 → whosellm-0.2.2}/tests/models/families/test_qwen3_vl_models.py +0 -0
  85. {whosellm-0.2.0 → whosellm-0.2.2}/tests/models/families/test_qwen_ollama.py +0 -0
  86. {whosellm-0.2.0 → whosellm-0.2.2}/tests/models/families/test_qwen_plus.py +0 -0
  87. {whosellm-0.2.0 → whosellm-0.2.2}/tests/test_model_version.py +0 -0
  88. {whosellm-0.2.0 → whosellm-0.2.2}/tests/test_provider.py +0 -0
  89. {whosellm-0.2.0 → whosellm-0.2.2}/tests/test_registry_merge.py +0 -0
  90. {whosellm-0.2.0 → whosellm-0.2.2}/tests/test_specific_patterns.py +0 -0
  91. {whosellm-0.2.0 → whosellm-0.2.2}/tests/test_variant_priority_config.py +0 -0
  92. {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/model_version.py +0 -0
  93. {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/models/__init__.py +0 -0
  94. {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/models/base.py +0 -0
  95. {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/models/config.py +0 -0
  96. {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/models/dynamic_enum.py +0 -0
  97. {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/models/families/__init__.py +0 -0
  98. {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/models/families/alibaba.py +0 -0
  99. {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/models/families/anthropic.py +0 -0
  100. {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/models/families/deepseek/__init__.py +0 -0
  101. {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/models/families/gemini.py +0 -0
  102. {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/models/families/openai/__init__.py +0 -0
  103. {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/models/families/openai/openai_gpt_3_5.py +0 -0
  104. {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/models/families/openai/openai_gpt_4.py +0 -0
  105. {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/models/families/openai/openai_gpt_4o.py +0 -0
  106. {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/models/families/openai/openai_gpt_5_3.py +0 -0
  107. {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/models/families/openai/openai_o1.py +0 -0
  108. {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/models/families/openai/openai_o3.py +0 -0
  109. {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/models/families/openai/openai_o4.py +0 -0
  110. {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/models/families/others.py +0 -0
  111. {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/models/families/vidu.py +0 -0
  112. {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/models/patterns.py +0 -0
  113. {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/models/registry.py +0 -0
  114. {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/provider.py +0 -0
  115. {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/py.typed +0 -0
@@ -2,7 +2,7 @@
2
2
  # 文档 / Documentation: https://callowayproject.github.io/bump-my-version/
3
3
 
4
4
  [tool.bumpversion]
5
- current_version = "0.2.0"
5
+ current_version = "0.2.2"
6
6
  parse = """(?x)
7
7
  (?P<major>0|[1-9]\\d*)\\.
8
8
  (?P<minor>0|[1-9]\\d*)\\.
@@ -42,5 +42,5 @@ Claude 有两种命名格式(新旧版本不同):
42
42
  - Claude 的 `max_tokens` 因版本不同差异较大(3-haiku: 4000, 3-5-haiku: 8000, opus: 32000, sonnet: 64000)
43
43
  - 所有 Claude 模型的 `context_window` 目前统一为 200000
44
44
  - `supports_thinking` 从 3.7-sonnet 开始支持,3.5 及更早版本不支持
45
- - `supports_computer_use` 和 `supports_mcp` 等能力需要逐模型确认
45
+ - `supports_computer_use` 等能力需要逐模型确认
46
46
  - 变体优先级:`haiku=0, sonnet=3, opus=5`
@@ -112,6 +112,8 @@ LLMeta("gpt-4o-mini")
112
112
  - **`Provider`**(`provider.py`):动态字符串枚举(OpenAI、Anthropic、Zhipu 等),运行时可通过 `add_member()` 扩展。
113
113
  - **`ModelFamily`**(`models/base.py`):动态字符串枚举,运行时可通过 `add_member()` 扩展。
114
114
  - **`ModelCapabilities`**(`capabilities.py`):冻结 dataclass,描述模型功能(thinking、vision、audio、video、function_calling 等)和限制(context_window、max_tokens 等)。
115
+ - `supports_json_outputs`:模型能否将输出限制为合法 JSON(`response_format: {type: "json_object"}`),但**不约束 JSON 结构**。
116
+ - `supports_structured_outputs`:模型能否按调用方提供的 **JSON Schema** 生成严格符合结构的输出(`response_format: {type: "json_schema", ...}`)。这是更强的能力,蕴含 `supports_json_outputs`。
115
117
  - **`ModelFamilyConfig`**(`models/config.py`):配置 dataclass,定义模式、默认值和特定模型覆盖。创建时自动注册到全局注册表。
116
118
  - **`SpecificModelConfig`**(`models/config.py`):预注册的特定模型变体配置。
117
119
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: whosellm
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: A unified LLM model version and capability management library
5
5
  Author-email: JQQ <jqq1716@gmail.com>
6
6
  License: MIT
@@ -50,6 +50,8 @@ Description-Content-Type: text/markdown
50
50
  - 是否支持视频
51
51
  - 是否支持 PDF
52
52
  - 各类资源的大小和格式限制
53
+ - 是否支持 JSON 输出(`supports_json_outputs`)— 限制输出为合法 JSON,不约束结构
54
+ - 是否支持结构化输出(`supports_structured_outputs`)— 按给定 JSON Schema 严格生成,蕴含 JSON 输出
53
55
  6. **参数验证** - 针对具体模型,提供请求参数验证的可选实现,基于 VRL 脚本语言自动整改参数
54
56
 
55
57
  ## 核心概念 / Core Concepts
@@ -25,6 +25,8 @@
25
25
  - 是否支持视频
26
26
  - 是否支持 PDF
27
27
  - 各类资源的大小和格式限制
28
+ - 是否支持 JSON 输出(`supports_json_outputs`)— 限制输出为合法 JSON,不约束结构
29
+ - 是否支持结构化输出(`supports_structured_outputs`)— 按给定 JSON Schema 严格生成,蕴含 JSON 输出
28
30
  6. **参数验证** - 针对具体模型,提供请求参数验证的可选实现,基于 VRL 脚本语言自动整改参数
29
31
 
30
32
  ## 核心概念 / Core Concepts
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "whosellm"
3
- version = "0.2.0"
3
+ version = "0.2.2"
4
4
  description = "A unified LLM model version and capability management library"
5
5
  authors = [
6
6
  {name = "JQQ", email = "jqq1716@gmail.com"}
@@ -1,7 +1,12 @@
1
1
  """ZhipuAI E2E 元数据测试。
2
2
 
3
- 来源: https://docs.z.ai/guides/llm/ | https://docs.z.ai/guides/vlm/
3
+ 来源: https://docs.bigmodel.cn/api-reference/模型-api/对话补全.md
4
+ https://docs.bigmodel.cn/cn/guide/capabilities/struct-output.md
4
5
  采集日期: 2026-04-12
6
+
7
+ NOTE: ZhipuAI API 仅支持 response_format: {type: "json_object"},不支持 json_schema 类型。
8
+ 因此所有模型 supports_structured_outputs=False。
9
+ Vision 模型不支持 response_format 参数,因此 supports_json_outputs 也为 False。
5
10
  """
6
11
 
7
12
  import pytest
@@ -12,7 +17,7 @@ from .conftest import assert_model_metadata
12
17
 
13
18
  # ============================================================================
14
19
  # GLM Family — Version 5.0
15
- # 来源: https://docs.z.ai/guides/llm/glm-5
20
+ # 来源: https://docs.bigmodel.cn/cn/guide/models/text/glm-5.md
16
21
  # ============================================================================
17
22
 
18
23
  GLM_50_MODELS = [
@@ -27,7 +32,8 @@ GLM_50_MODELS = [
27
32
  "supports_vision": False,
28
33
  "supports_streaming": True,
29
34
  "supports_function_calling": True,
30
- "supports_structured_outputs": True,
35
+ "supports_structured_outputs": False,
36
+ "supports_json_outputs": True,
31
37
  "context_window": 200_000,
32
38
  "max_tokens": 128_000,
33
39
  },
@@ -36,7 +42,7 @@ GLM_50_MODELS = [
36
42
 
37
43
  # ============================================================================
38
44
  # GLM Family — Version 4.7
39
- # 来源: https://docs.z.ai/guides/llm/glm-4.7
45
+ # 来源: https://docs.bigmodel.cn/cn/guide/models/text/glm-4.7.md
40
46
  # ============================================================================
41
47
 
42
48
  GLM_47_MODELS = [
@@ -51,7 +57,8 @@ GLM_47_MODELS = [
51
57
  "supports_vision": False,
52
58
  "supports_streaming": True,
53
59
  "supports_function_calling": True,
54
- "supports_structured_outputs": True,
60
+ "supports_structured_outputs": False,
61
+ "supports_json_outputs": True,
55
62
  "context_window": 200_000,
56
63
  "max_tokens": 128_000,
57
64
  },
@@ -67,7 +74,8 @@ GLM_47_MODELS = [
67
74
  "supports_vision": False,
68
75
  "supports_streaming": True,
69
76
  "supports_function_calling": True,
70
- "supports_structured_outputs": True,
77
+ "supports_structured_outputs": False,
78
+ "supports_json_outputs": True,
71
79
  "context_window": 200_000,
72
80
  "max_tokens": 128_000,
73
81
  },
@@ -83,7 +91,8 @@ GLM_47_MODELS = [
83
91
  "supports_vision": False,
84
92
  "supports_streaming": True,
85
93
  "supports_function_calling": True,
86
- "supports_structured_outputs": True,
94
+ "supports_structured_outputs": False,
95
+ "supports_json_outputs": True,
87
96
  "context_window": 200_000,
88
97
  "max_tokens": 128_000,
89
98
  },
@@ -92,7 +101,7 @@ GLM_47_MODELS = [
92
101
 
93
102
  # ============================================================================
94
103
  # GLM Family — Version 4.6
95
- # 来源: https://docs.z.ai/guides/llm/glm-4.6
104
+ # 来源: https://docs.bigmodel.cn/cn/guide/models/text/glm-4.6.md
96
105
  # ============================================================================
97
106
 
98
107
  GLM_46_MODELS = [
@@ -108,6 +117,7 @@ GLM_46_MODELS = [
108
117
  "supports_streaming": True,
109
118
  "supports_function_calling": True,
110
119
  "supports_structured_outputs": False,
120
+ "supports_json_outputs": True,
111
121
  "supports_web_search": True,
112
122
  "context_window": 200_000,
113
123
  "max_tokens": 128_000,
@@ -117,7 +127,7 @@ GLM_46_MODELS = [
117
127
 
118
128
  # ============================================================================
119
129
  # GLM Family — Version 4.5
120
- # 来源: https://docs.z.ai/guides/llm/glm-4.5
130
+ # 来源: https://docs.bigmodel.cn/cn/guide/models/text/glm-4.5.md
121
131
  # ============================================================================
122
132
 
123
133
  GLM_45_MODELS = [
@@ -132,7 +142,8 @@ GLM_45_MODELS = [
132
142
  "supports_vision": False,
133
143
  "supports_streaming": True,
134
144
  "supports_function_calling": True,
135
- "supports_structured_outputs": True,
145
+ "supports_structured_outputs": False,
146
+ "supports_json_outputs": True,
136
147
  "context_window": 128_000,
137
148
  "max_tokens": 96_000,
138
149
  },
@@ -141,7 +152,8 @@ GLM_45_MODELS = [
141
152
 
142
153
  # ============================================================================
143
154
  # GLM-Vision Family — Version 4.6
144
- # 来源: https://docs.z.ai/guides/vlm/glm-4.6v
155
+ # 来源: https://docs.bigmodel.cn/cn/guide/models/vision/glm-4.6v.md
156
+ # NOTE: Vision 模型不支持 response_format 参数
145
157
  # ============================================================================
146
158
 
147
159
  GLM_VISION_46_MODELS = [
@@ -157,7 +169,8 @@ GLM_VISION_46_MODELS = [
157
169
  "supports_video": True,
158
170
  "supports_streaming": True,
159
171
  "supports_function_calling": True,
160
- "supports_structured_outputs": True,
172
+ "supports_structured_outputs": False,
173
+ "supports_json_outputs": False,
161
174
  "context_window": 128_000,
162
175
  "max_tokens": 128_000,
163
176
  },
@@ -174,7 +187,8 @@ GLM_VISION_46_MODELS = [
174
187
  "supports_video": True,
175
188
  "supports_streaming": True,
176
189
  "supports_function_calling": True,
177
- "supports_structured_outputs": True,
190
+ "supports_structured_outputs": False,
191
+ "supports_json_outputs": False,
178
192
  "context_window": 128_000,
179
193
  "max_tokens": 128_000,
180
194
  },
@@ -183,7 +197,8 @@ GLM_VISION_46_MODELS = [
183
197
 
184
198
  # ============================================================================
185
199
  # GLM-Vision Family — Version 4.5
186
- # 来源: https://docs.z.ai/guides/vlm/glm-4.5v
200
+ # 来源: https://docs.bigmodel.cn/cn/guide/models/vision/glm-4.5v.md
201
+ # NOTE: Vision 模型不支持 response_format 参数
187
202
  # ============================================================================
188
203
 
189
204
  GLM_VISION_45_MODELS = [
@@ -199,6 +214,8 @@ GLM_VISION_45_MODELS = [
199
214
  "supports_video": True,
200
215
  "supports_streaming": True,
201
216
  "supports_function_calling": False,
217
+ "supports_structured_outputs": False,
218
+ "supports_json_outputs": False,
202
219
  "context_window": 64_000,
203
220
  "max_tokens": 16_384,
204
221
  },
@@ -0,0 +1,176 @@
1
+ # filename: test_deepseek.py
2
+ # @Time : 2025/11/9 16:08
3
+ # @Author : Cascade AI
4
+ """
5
+ DeepSeek 模型家族测试 / DeepSeek model family tests
6
+ """
7
+
8
+ from whosellm.models.base import ModelFamily
9
+ from whosellm.models.registry import get_specific_model_config
10
+ from whosellm.provider import Provider
11
+
12
+
13
+ def test_deepseek_default_capabilities() -> None:
14
+ """验证 DeepSeek 官方家族默认能力(V4 基线) / Validate DeepSeek official family default capabilities (V4 baseline)"""
15
+ from whosellm import LLMeta
16
+
17
+ # 使用官方 Provider 前缀确保获取官方配置
18
+ model = LLMeta("deepseek::deepseek-chat")
19
+ capabilities = model.capabilities
20
+
21
+ assert capabilities.supports_streaming is True
22
+ assert capabilities.supports_function_calling is True
23
+ # V4 系列:1M 上下文,384K 最大输出
24
+ assert capabilities.max_tokens == 384_000
25
+ assert capabilities.context_window == 1_000_000
26
+ # deepseek-chat 是 v4-flash 非思考模式的别名
27
+ assert capabilities.supports_thinking is False
28
+
29
+
30
+ def test_deepseek_chat_specific_model() -> None:
31
+ """验证 deepseek-chat 特定模型配置 / Validate deepseek-chat specific configuration"""
32
+ config = get_specific_model_config("deepseek-chat")
33
+
34
+ assert config is not None
35
+ version, variant, capabilities = config
36
+ assert version == "4.0"
37
+ assert variant == "chat"
38
+ assert capabilities is not None
39
+ assert capabilities.supports_function_calling is True
40
+ assert capabilities.supports_streaming is True
41
+ assert capabilities.max_tokens == 384_000
42
+ assert capabilities.context_window == 1_000_000
43
+
44
+
45
+ def test_deepseek_chat_pattern_matching() -> None:
46
+ """验证 deepseek-chat 模式匹配 / Validate deepseek-chat pattern matching"""
47
+ from whosellm import LLMeta
48
+
49
+ for name in ["deepseek-chat", "deepseek-chat-beta", "deepseek-chat-v3.2-exp"]:
50
+ # 使用 Provider 前缀确保匹配官方配置
51
+ model = LLMeta(f"deepseek::{name}")
52
+
53
+ assert model.family == ModelFamily.DEEPSEEK
54
+ assert model.variant == "chat"
55
+ assert model.version == "4.0"
56
+ assert model.provider == Provider.DEEPSEEK
57
+
58
+
59
+ def test_deepseek_reasoner_specific_model() -> None:
60
+ """验证 deepseek-reasoner 特定模型配置 / Validate deepseek-reasoner specific configuration"""
61
+ config = get_specific_model_config("deepseek-reasoner")
62
+
63
+ assert config is not None
64
+ version, variant, capabilities = config
65
+ assert version == "4.0"
66
+ assert variant == "reasoner"
67
+ assert capabilities is not None
68
+ assert capabilities.supports_thinking is True
69
+ assert capabilities.max_tokens == 384_000
70
+ assert capabilities.context_window == 1_000_000
71
+
72
+
73
+ def test_deepseek_base_pattern_without_variant() -> None:
74
+ """验证无型号名称时的默认匹配 / Validate default match without explicit variant"""
75
+ from whosellm import LLMeta
76
+
77
+ # 使用 Provider 前缀确保匹配官方配置
78
+ model = LLMeta("deepseek::deepseek-chat")
79
+
80
+ assert model.family == ModelFamily.DEEPSEEK
81
+ assert model.variant == "chat"
82
+ assert model.version == "4.0"
83
+ assert model.capabilities.supports_function_calling is True
84
+
85
+
86
+ def test_deepseek_reasoner_does_not_use_chat_capabilities() -> None:
87
+ """验证 reasoner 不会意外继承 chat 的能力 / Ensure reasoner capabilities override family defaults"""
88
+ from whosellm import LLMeta
89
+
90
+ model = LLMeta("deepseek::deepseek-reasoner")
91
+
92
+ assert model.variant == "reasoner"
93
+ assert model.capabilities.supports_function_calling is True
94
+ assert model.capabilities.supports_thinking is True
95
+
96
+
97
+ def test_deepseek_no_structured_outputs() -> None:
98
+ """验证 DeepSeek 官方模型不支持 structured_outputs(仅支持 json_object)"""
99
+ from whosellm import LLMeta
100
+
101
+ for model_id in ["deepseek-chat", "deepseek-reasoner", "deepseek-v4-flash", "deepseek-v4-pro"]:
102
+ model = LLMeta(f"deepseek::{model_id}")
103
+ assert model.capabilities.supports_structured_outputs is False, (
104
+ f"{model_id}: DeepSeek API 仅支持 response_format={{type:'json_object'}},"
105
+ "不支持 json_schema 类型,supports_structured_outputs 应为 False"
106
+ )
107
+ assert model.capabilities.supports_json_outputs is True, (
108
+ f"{model_id}: DeepSeek API 支持 response_format={{type:'json_object'}},supports_json_outputs 应为 True"
109
+ )
110
+
111
+
112
+ def test_deepseek_v4_flash_specific_model() -> None:
113
+ """验证 deepseek-v4-flash 特定模型配置 / Validate deepseek-v4-flash specific configuration"""
114
+ from whosellm import LLMeta
115
+
116
+ model = LLMeta("deepseek::deepseek-v4-flash")
117
+
118
+ assert model.family == ModelFamily.DEEPSEEK
119
+ assert model.provider == Provider.DEEPSEEK
120
+ assert model.version == "4.0"
121
+ assert model.variant == "flash"
122
+
123
+ caps = model.capabilities
124
+ assert caps.supports_thinking is True
125
+ assert caps.supports_function_calling is True
126
+ assert caps.supports_streaming is True
127
+ assert caps.supports_json_outputs is True
128
+ assert caps.supports_structured_outputs is False
129
+ assert caps.max_tokens == 384_000
130
+ assert caps.context_window == 1_000_000
131
+
132
+
133
+ def test_deepseek_v4_pro_specific_model() -> None:
134
+ """验证 deepseek-v4-pro 特定模型配置 / Validate deepseek-v4-pro specific configuration"""
135
+ from whosellm import LLMeta
136
+
137
+ model = LLMeta("deepseek::deepseek-v4-pro")
138
+
139
+ assert model.family == ModelFamily.DEEPSEEK
140
+ assert model.provider == Provider.DEEPSEEK
141
+ assert model.version == "4.0"
142
+ assert model.variant == "pro"
143
+
144
+ caps = model.capabilities
145
+ assert caps.supports_thinking is True
146
+ assert caps.supports_function_calling is True
147
+ assert caps.max_tokens == 384_000
148
+ assert caps.context_window == 1_000_000
149
+
150
+
151
+ def test_deepseek_v4_variant_ordering() -> None:
152
+ """验证 v4-flash < v4-pro 的排序关系 / Validate v4-flash < v4-pro ordering"""
153
+ from whosellm import LLMeta
154
+
155
+ flash = LLMeta("deepseek::deepseek-v4-flash")
156
+ pro = LLMeta("deepseek::deepseek-v4-pro")
157
+
158
+ assert flash < pro
159
+
160
+
161
+ def test_deepseek_versioned_pattern_matches_official_family() -> None:
162
+ """验证带版本号的 DS 模型名能被官方 family 识别 / Validate versioned DS names match the official family"""
163
+ from whosellm import LLMeta
164
+
165
+ # V4 是 DS 官方首次开放版本号命名的系列
166
+ model = LLMeta("deepseek::deepseek-v4-flash")
167
+ assert model.family == ModelFamily.DEEPSEEK
168
+ assert model.provider == Provider.DEEPSEEK
169
+ assert model.version == "4.0"
170
+
171
+ # 带小数版本号也能匹配 family(即使当前没有 specific_model 条目)
172
+ model_v32 = LLMeta("deepseek::deepseek-v3.2-exp")
173
+ assert model_v32.family == ModelFamily.DEEPSEEK
174
+ assert model_v32.provider == Provider.DEEPSEEK
175
+ assert model_v32.version == "3.2"
176
+ assert model_v32.variant == "exp"
@@ -66,6 +66,8 @@ def test_tencent_deepseek_r1_0528() -> None:
66
66
  assert capabilities is not None
67
67
  assert capabilities.supports_thinking is True
68
68
  assert capabilities.context_window == 128000
69
+ # 腾讯云 LKE 官方文档明确列为不支持 Function Calling
70
+ assert capabilities.supports_function_calling is False
69
71
 
70
72
 
71
73
  def test_tencent_deepseek_v3_1() -> None:
@@ -109,6 +111,22 @@ def test_tencent_deepseek_v3_2_exp() -> None:
109
111
  assert capabilities.context_window == 128000
110
112
 
111
113
 
114
+ def test_tencent_deepseek_v3_2() -> None:
115
+ """测试腾讯云 DeepSeek-V3.2(GA) / Test Tencent DeepSeek-V3.2 (GA)"""
116
+ config = get_specific_model_config("deepseek-v3.2")
117
+
118
+ assert config is not None
119
+ version, variant, capabilities = config
120
+ assert version == "v3.2"
121
+ assert variant == "v3.2"
122
+ assert capabilities is not None
123
+ assert capabilities.supports_thinking is True
124
+ assert capabilities.supports_function_calling is True
125
+ assert capabilities.supports_streaming is True
126
+ assert capabilities.max_tokens == 32000
127
+ assert capabilities.context_window == 128000
128
+
129
+
112
130
  def test_tencent_deepseek_pattern_matching() -> None:
113
131
  """测试腾讯云 DeepSeek 模式匹配 / Test Tencent DeepSeek pattern matching"""
114
132
  # 测试 V3 系列
@@ -158,8 +176,12 @@ def test_tencent_deepseek_no_collision_with_official() -> None:
158
176
  assert tencent_v3.variant == "base"
159
177
 
160
178
  # 不同的能力配置
161
- assert official_chat.capabilities.max_tokens == 8000
179
+ # 官方 deepseek-chat 当前指向 V4-flash 非思考模式(1M 上下文,384K 输出)
180
+ assert official_chat.capabilities.max_tokens == 384_000
181
+ assert official_chat.capabilities.context_window == 1_000_000
182
+ # 腾讯云保持其自有的 DeepSeek 部署规格
162
183
  assert tencent_v3.capabilities.max_tokens == 16000
184
+ assert tencent_v3.capabilities.context_window == 64000
163
185
 
164
186
 
165
187
  def test_tencent_deepseek_invalid_model_names() -> None:
@@ -0,0 +1,21 @@
1
+ """GLM-4.5 模型家族测试 / GLM-4.5 model family tests."""
2
+
3
+ from whosellm.models.base import ModelFamily
4
+ from whosellm.models.registry import match_model_pattern
5
+
6
+
7
+ def test_glm45_no_structured_outputs() -> None:
8
+ """验证 glm-4.5 不支持 structured_outputs(ZhipuAI 仅支持 json_object,不支持 json_schema)"""
9
+ matched = match_model_pattern("glm-4.5")
10
+
11
+ assert matched is not None
12
+ assert matched["family"] == ModelFamily.GLM
13
+
14
+ capabilities = matched["capabilities"]
15
+ assert capabilities.supports_structured_outputs is False, (
16
+ "glm-4.5: ZhipuAI API 仅支持 response_format={type:'json_object'},"
17
+ "不支持 json_schema 类型,supports_structured_outputs 应为 False"
18
+ )
19
+ assert capabilities.supports_json_outputs is True, (
20
+ "glm-4.5: ZhipuAI API 支持 response_format={type:'json_object'},supports_json_outputs 应为 True"
21
+ )
@@ -68,6 +68,20 @@ def test_glm45v_mmdd_pattern() -> None:
68
68
  assert parsed.day == 1
69
69
 
70
70
 
71
+ def test_glm45v_no_structured_or_json_outputs() -> None:
72
+ """验证 glm-4.5v 不支持 structured_outputs 和 json_outputs(Vision 模型不支持 response_format)"""
73
+ matched = match_model_pattern("glm-4.5v")
74
+
75
+ assert matched is not None
76
+ capabilities = matched["capabilities"]
77
+ assert capabilities.supports_structured_outputs is False, (
78
+ "glm-4.5v: Vision 模型不支持 response_format 参数,supports_structured_outputs 应为 False"
79
+ )
80
+ assert capabilities.supports_json_outputs is False, (
81
+ "glm-4.5v: Vision 模型不支持 response_format 参数,supports_json_outputs 应为 False"
82
+ )
83
+
84
+
71
85
  def test_glm45v_variant_full_date_pattern() -> None:
72
86
  """验证变体带完整日期的模式 / Validate variant with YYYY-MM-DD pattern"""
73
87
  model_name = "glm-4.5v-plus-2025-11-08"
@@ -50,6 +50,20 @@ def test_glm46v_flash_specific_model_capabilities() -> None:
50
50
  assert capabilities.context_window == 128000
51
51
 
52
52
 
53
+ def test_glm46v_no_structured_or_json_outputs() -> None:
54
+ """验证 glm-4.6v 不支持 structured_outputs 和 json_outputs(Vision 模型不支持 response_format)"""
55
+ matched = match_model_pattern("glm-4.6v")
56
+
57
+ assert matched is not None
58
+ capabilities = matched["capabilities"]
59
+ assert capabilities.supports_structured_outputs is False, (
60
+ "glm-4.6v: Vision 模型不支持 response_format 参数,supports_structured_outputs 应为 False"
61
+ )
62
+ assert capabilities.supports_json_outputs is False, (
63
+ "glm-4.6v: Vision 模型不支持 response_format 参数,supports_json_outputs 应为 False"
64
+ )
65
+
66
+
53
67
  def test_glm46v_mmdd_pattern() -> None:
54
68
  """验证 glm-4.6v 带 MMDD 日期后缀模式 / Validate glm-4.6v pattern with MMDD date suffix"""
55
69
  model_name = "glm-4.6v-0815"
@@ -35,9 +35,9 @@ class TestGLM5SpecificModel:
35
35
  assert capabilities is not None
36
36
  assert capabilities.supports_thinking is True
37
37
  assert capabilities.supports_function_calling is True
38
- assert capabilities.supports_structured_outputs is True
38
+ assert capabilities.supports_structured_outputs is False
39
39
  assert capabilities.supports_streaming is True
40
- assert capabilities.supports_mcp is True
40
+
41
41
  assert capabilities.max_tokens == 128000
42
42
  assert capabilities.context_window == 200000
43
43
 
@@ -108,7 +108,7 @@ class TestGLM5LLMetaIntegration:
108
108
  assert model.variant == "base"
109
109
  assert model.version == "5.0"
110
110
  assert model.capabilities.supports_thinking is True
111
- assert model.capabilities.supports_mcp is True
111
+
112
112
  assert model.capabilities.supports_function_calling is True
113
113
 
114
114
  def test_glm5_llmeta_with_provider_prefix(self) -> None:
@@ -45,7 +45,7 @@ def test_gpt5_base_capabilities():
45
45
  assert m.capabilities.supports_file_search is True
46
46
  assert m.capabilities.supports_image_generation is True
47
47
  assert m.capabilities.supports_code_interpreter is True
48
- assert m.capabilities.supports_mcp is True
48
+
49
49
  assert m.capabilities.max_tokens == 128_000
50
50
  assert m.capabilities.context_window == 400_000
51
51
 
@@ -191,7 +191,6 @@ def test_gpt5_4_model():
191
191
  assert m.capabilities.supports_thinking is True
192
192
  assert m.capabilities.supports_vision is True
193
193
  assert m.capabilities.supports_computer_use is True
194
- assert m.capabilities.supports_mcp is True
195
194
 
196
195
 
197
196
  def test_gpt5_4_with_date_suffix():
@@ -237,7 +236,6 @@ def test_gpt5_4_mini_model():
237
236
  assert m.capabilities.supports_fine_tuning is False
238
237
  assert m.capabilities.supports_distillation is True
239
238
  assert m.capabilities.supports_computer_use is True
240
- assert m.capabilities.supports_mcp is True
241
239
 
242
240
 
243
241
  def test_gpt5_4_mini_with_date_suffix():
@@ -265,7 +263,6 @@ def test_gpt5_4_nano_model():
265
263
  assert m.capabilities.supports_fine_tuning is False
266
264
  assert m.capabilities.supports_distillation is True
267
265
  assert m.capabilities.supports_computer_use is False
268
- assert m.capabilities.supports_mcp is True
269
266
 
270
267
 
271
268
  def test_gpt5_4_nano_with_date_suffix():
@@ -102,7 +102,8 @@ class TestAutoRegister(unittest.TestCase):
102
102
  assert model.family == ModelFamily.DEEPSEEK
103
103
  assert model.provider == Provider.DEEPSEEK
104
104
  assert model.capabilities.supports_function_calling is True
105
- assert model.capabilities.context_window == 128000
105
+ # V4 deepseek-chat 别名为 v4-flash 非思考模式,上下文扩展为 1M
106
+ assert model.capabilities.context_window == 1_000_000
106
107
 
107
108
  def test_auto_register_qwen_variant(self) -> None:
108
109
  """测试自动注册 Qwen 新型号 / Test auto-register Qwen new variant"""
@@ -16,7 +16,7 @@ def test_glm45_base_capabilities() -> None:
16
16
  capabilities = model.capabilities
17
17
  assert capabilities.supports_thinking is True
18
18
  assert capabilities.supports_function_calling is True
19
- assert capabilities.supports_structured_outputs is True
19
+ assert capabilities.supports_structured_outputs is False
20
20
  assert capabilities.supports_streaming is True
21
21
  assert capabilities.supports_vision is False
22
22
  assert capabilities.supports_video is False
@@ -968,7 +968,7 @@ wheels = [
968
968
 
969
969
  [[package]]
970
970
  name = "whosellm"
971
- version = "0.2.0"
971
+ version = "0.2.2"
972
972
  source = { editable = "." }
973
973
  dependencies = [
974
974
  { name = "parse" },
@@ -7,7 +7,7 @@
7
7
  LLMeta - 统一的大语言模型版本和能力管理库 / A unified LLM model version and capability management library
8
8
  """
9
9
 
10
- __version__ = "0.2.0"
10
+ __version__ = "0.2.2"
11
11
 
12
12
  from whosellm.capabilities import ModelCapabilities
13
13
  from whosellm.model_version import LLMeta
@@ -38,7 +38,6 @@ class ModelCapabilities:
38
38
  supports_audio_generation: bool = False # 是否支持音频生成工具 / Whether audio generation tool is supported
39
39
  supports_code_interpreter: bool = False # 是否支持代码解释器 / Whether code interpreter tool is supported
40
40
  supports_computer_use: bool = False # 是否支持电脑远程操作 / Whether computer use tool is supported
41
- supports_mcp: bool = False # 是否支持 MCP / Whether MCP integration is supported
42
41
 
43
42
  # 通用限制 / General limitations
44
43
  max_tokens: int | None = None # 最大输出token数 / Maximum number of tokens