whosellm 0.2.0__tar.gz → 0.2.1__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 (113) hide show
  1. {whosellm-0.2.0 → whosellm-0.2.1}/.bumpversion.toml +1 -1
  2. {whosellm-0.2.0 → whosellm-0.2.1}/.claude/skills/review-provider-model/providers/anthropic.md +1 -1
  3. {whosellm-0.2.0 → whosellm-0.2.1}/CLAUDE.md +2 -0
  4. {whosellm-0.2.0 → whosellm-0.2.1}/PKG-INFO +3 -1
  5. {whosellm-0.2.0 → whosellm-0.2.1}/README.md +2 -0
  6. {whosellm-0.2.0 → whosellm-0.2.1}/pyproject.toml +1 -1
  7. {whosellm-0.2.0 → whosellm-0.2.1}/tests/e2e/test_zhipu.py +31 -14
  8. {whosellm-0.2.0 → whosellm-0.2.1}/tests/models/families/test_deepseek.py +15 -0
  9. whosellm-0.2.1/tests/models/families/test_glm45.py +21 -0
  10. {whosellm-0.2.0 → whosellm-0.2.1}/tests/models/families/test_glm45v.py +14 -0
  11. {whosellm-0.2.0 → whosellm-0.2.1}/tests/models/families/test_glm46v.py +14 -0
  12. {whosellm-0.2.0 → whosellm-0.2.1}/tests/models/families/test_glm5.py +3 -3
  13. {whosellm-0.2.0 → whosellm-0.2.1}/tests/models/families/test_gpt5.py +4 -4
  14. {whosellm-0.2.0 → whosellm-0.2.1}/tests/test_llmeta.py +1 -1
  15. {whosellm-0.2.0 → whosellm-0.2.1}/uv.lock +1 -1
  16. {whosellm-0.2.0 → whosellm-0.2.1}/whosellm/__init__.py +1 -1
  17. {whosellm-0.2.0 → whosellm-0.2.1}/whosellm/capabilities.py +0 -1
  18. {whosellm-0.2.0 → whosellm-0.2.1}/whosellm/models/families/deepseek/deepseek_official.py +3 -0
  19. {whosellm-0.2.0 → whosellm-0.2.1}/whosellm/models/families/openai/openai_gpt_4_1.py +1 -1
  20. {whosellm-0.2.0 → whosellm-0.2.1}/whosellm/models/families/openai/openai_gpt_5.py +4 -4
  21. {whosellm-0.2.0 → whosellm-0.2.1}/whosellm/models/families/openai/openai_gpt_5_1.py +1 -1
  22. {whosellm-0.2.0 → whosellm-0.2.1}/whosellm/models/families/openai/openai_gpt_5_2.py +2 -2
  23. {whosellm-0.2.0 → whosellm-0.2.1}/whosellm/models/families/openai/openai_gpt_5_4.py +4 -4
  24. {whosellm-0.2.0 → whosellm-0.2.1}/whosellm/models/families/zhipu.py +30 -15
  25. {whosellm-0.2.0 → whosellm-0.2.1}/.claude/commands/interview.md +0 -0
  26. {whosellm-0.2.0 → whosellm-0.2.1}/.claude/projects/-Users-jqq-PycharmProjects-llmeta/memory/MEMORY.md +0 -0
  27. {whosellm-0.2.0 → whosellm-0.2.1}/.claude/projects/-Users-jqq-PycharmProjects-llmeta/memory/feedback_use_uv.md +0 -0
  28. {whosellm-0.2.0 → whosellm-0.2.1}/.claude/skills/code-review/SKILL.md +0 -0
  29. {whosellm-0.2.0 → whosellm-0.2.1}/.claude/skills/create-skill/SKILL.md +0 -0
  30. {whosellm-0.2.0 → whosellm-0.2.1}/.claude/skills/e2e-metadata/SKILL.md +0 -0
  31. {whosellm-0.2.0 → whosellm-0.2.1}/.claude/skills/evolve/SKILL.md +0 -0
  32. {whosellm-0.2.0 → whosellm-0.2.1}/.claude/skills/fix-review/SKILL.md +0 -0
  33. {whosellm-0.2.0 → whosellm-0.2.1}/.claude/skills/release/SKILL.md +0 -0
  34. {whosellm-0.2.0 → whosellm-0.2.1}/.claude/skills/review-provider-model/SKILL.md +0 -0
  35. {whosellm-0.2.0 → whosellm-0.2.1}/.claude/skills/review-provider-model/providers/alibaba.md +0 -0
  36. {whosellm-0.2.0 → whosellm-0.2.1}/.claude/skills/review-provider-model/providers/deepseek.md +0 -0
  37. {whosellm-0.2.0 → whosellm-0.2.1}/.claude/skills/review-provider-model/providers/gemini.md +0 -0
  38. {whosellm-0.2.0 → whosellm-0.2.1}/.claude/skills/review-provider-model/providers/openai.md +0 -0
  39. {whosellm-0.2.0 → whosellm-0.2.1}/.claude/skills/review-provider-model/providers/others.md +0 -0
  40. {whosellm-0.2.0 → whosellm-0.2.1}/.claude/skills/review-provider-model/providers/vidu.md +0 -0
  41. {whosellm-0.2.0 → whosellm-0.2.1}/.claude/skills/review-provider-model/providers/zhipu.md +0 -0
  42. {whosellm-0.2.0 → whosellm-0.2.1}/.claude/skills/update-provider-model/SKILL.md +0 -0
  43. {whosellm-0.2.0 → whosellm-0.2.1}/.claude/skills/update-provider-model/testing.md +0 -0
  44. {whosellm-0.2.0 → whosellm-0.2.1}/.coveragerc +0 -0
  45. {whosellm-0.2.0 → whosellm-0.2.1}/.github/workflows/publish.yml +0 -0
  46. {whosellm-0.2.0 → whosellm-0.2.1}/.github/workflows/tests.yml +0 -0
  47. {whosellm-0.2.0 → whosellm-0.2.1}/.gitignore +0 -0
  48. {whosellm-0.2.0 → whosellm-0.2.1}/.windsurf/workflows/addmodel.md +0 -0
  49. {whosellm-0.2.0 → whosellm-0.2.1}/.windsurf/workflows/arch.md +0 -0
  50. {whosellm-0.2.0 → whosellm-0.2.1}/.windsurf/workflows/testllmeta.md +0 -0
  51. {whosellm-0.2.0 → whosellm-0.2.1}/CHANGELOG.md +0 -0
  52. {whosellm-0.2.0 → whosellm-0.2.1}/LICENSE +0 -0
  53. {whosellm-0.2.0 → whosellm-0.2.1}/docs/add_new_model_family.md +0 -0
  54. {whosellm-0.2.0 → whosellm-0.2.1}/docs/refactor_proposal.md +0 -0
  55. {whosellm-0.2.0 → whosellm-0.2.1}/docs/spec_model_family_redesign.md +0 -0
  56. {whosellm-0.2.0 → whosellm-0.2.1}/examples/advanced_usage.py +0 -0
  57. {whosellm-0.2.0 → whosellm-0.2.1}/examples/basic_usage.py +0 -0
  58. {whosellm-0.2.0 → whosellm-0.2.1}/mypy.ini +0 -0
  59. {whosellm-0.2.0 → whosellm-0.2.1}/pytest.ini +0 -0
  60. {whosellm-0.2.0 → whosellm-0.2.1}/ruff.toml +0 -0
  61. {whosellm-0.2.0 → whosellm-0.2.1}/tests/__init__.py +0 -0
  62. {whosellm-0.2.0 → whosellm-0.2.1}/tests/e2e/__init__.py +0 -0
  63. {whosellm-0.2.0 → whosellm-0.2.1}/tests/e2e/conftest.py +0 -0
  64. {whosellm-0.2.0 → whosellm-0.2.1}/tests/e2e/test_anthropic.py +0 -0
  65. {whosellm-0.2.0 → whosellm-0.2.1}/tests/e2e/test_google.py +0 -0
  66. {whosellm-0.2.0 → whosellm-0.2.1}/tests/e2e/test_openai.py +0 -0
  67. {whosellm-0.2.0 → whosellm-0.2.1}/tests/models/__init__.py +0 -0
  68. {whosellm-0.2.0 → whosellm-0.2.1}/tests/models/families/__init__.py +0 -0
  69. {whosellm-0.2.0 → whosellm-0.2.1}/tests/models/families/test_anthropic.py +0 -0
  70. {whosellm-0.2.0 → whosellm-0.2.1}/tests/models/families/test_deepseek_tencent.py +0 -0
  71. {whosellm-0.2.0 → whosellm-0.2.1}/tests/models/families/test_gemini.py +0 -0
  72. {whosellm-0.2.0 → whosellm-0.2.1}/tests/models/families/test_glm46.py +0 -0
  73. {whosellm-0.2.0 → whosellm-0.2.1}/tests/models/families/test_gpt3_5.py +0 -0
  74. {whosellm-0.2.0 → whosellm-0.2.1}/tests/models/families/test_gpt4_1.py +0 -0
  75. {whosellm-0.2.0 → whosellm-0.2.1}/tests/models/families/test_gpt4o.py +0 -0
  76. {whosellm-0.2.0 → whosellm-0.2.1}/tests/models/families/test_o1.py +0 -0
  77. {whosellm-0.2.0 → whosellm-0.2.1}/tests/models/families/test_o3.py +0 -0
  78. {whosellm-0.2.0 → whosellm-0.2.1}/tests/models/families/test_o4.py +0 -0
  79. {whosellm-0.2.0 → whosellm-0.2.1}/tests/models/families/test_qwen.py +0 -0
  80. {whosellm-0.2.0 → whosellm-0.2.1}/tests/models/families/test_qwen3_vl_models.py +0 -0
  81. {whosellm-0.2.0 → whosellm-0.2.1}/tests/models/families/test_qwen_ollama.py +0 -0
  82. {whosellm-0.2.0 → whosellm-0.2.1}/tests/models/families/test_qwen_plus.py +0 -0
  83. {whosellm-0.2.0 → whosellm-0.2.1}/tests/test_auto_register.py +0 -0
  84. {whosellm-0.2.0 → whosellm-0.2.1}/tests/test_model_version.py +0 -0
  85. {whosellm-0.2.0 → whosellm-0.2.1}/tests/test_provider.py +0 -0
  86. {whosellm-0.2.0 → whosellm-0.2.1}/tests/test_registry_merge.py +0 -0
  87. {whosellm-0.2.0 → whosellm-0.2.1}/tests/test_specific_patterns.py +0 -0
  88. {whosellm-0.2.0 → whosellm-0.2.1}/tests/test_variant_priority_config.py +0 -0
  89. {whosellm-0.2.0 → whosellm-0.2.1}/whosellm/model_version.py +0 -0
  90. {whosellm-0.2.0 → whosellm-0.2.1}/whosellm/models/__init__.py +0 -0
  91. {whosellm-0.2.0 → whosellm-0.2.1}/whosellm/models/base.py +0 -0
  92. {whosellm-0.2.0 → whosellm-0.2.1}/whosellm/models/config.py +0 -0
  93. {whosellm-0.2.0 → whosellm-0.2.1}/whosellm/models/dynamic_enum.py +0 -0
  94. {whosellm-0.2.0 → whosellm-0.2.1}/whosellm/models/families/__init__.py +0 -0
  95. {whosellm-0.2.0 → whosellm-0.2.1}/whosellm/models/families/alibaba.py +0 -0
  96. {whosellm-0.2.0 → whosellm-0.2.1}/whosellm/models/families/anthropic.py +0 -0
  97. {whosellm-0.2.0 → whosellm-0.2.1}/whosellm/models/families/deepseek/__init__.py +0 -0
  98. {whosellm-0.2.0 → whosellm-0.2.1}/whosellm/models/families/deepseek/tencent.py +0 -0
  99. {whosellm-0.2.0 → whosellm-0.2.1}/whosellm/models/families/gemini.py +0 -0
  100. {whosellm-0.2.0 → whosellm-0.2.1}/whosellm/models/families/openai/__init__.py +0 -0
  101. {whosellm-0.2.0 → whosellm-0.2.1}/whosellm/models/families/openai/openai_gpt_3_5.py +0 -0
  102. {whosellm-0.2.0 → whosellm-0.2.1}/whosellm/models/families/openai/openai_gpt_4.py +0 -0
  103. {whosellm-0.2.0 → whosellm-0.2.1}/whosellm/models/families/openai/openai_gpt_4o.py +0 -0
  104. {whosellm-0.2.0 → whosellm-0.2.1}/whosellm/models/families/openai/openai_gpt_5_3.py +0 -0
  105. {whosellm-0.2.0 → whosellm-0.2.1}/whosellm/models/families/openai/openai_o1.py +0 -0
  106. {whosellm-0.2.0 → whosellm-0.2.1}/whosellm/models/families/openai/openai_o3.py +0 -0
  107. {whosellm-0.2.0 → whosellm-0.2.1}/whosellm/models/families/openai/openai_o4.py +0 -0
  108. {whosellm-0.2.0 → whosellm-0.2.1}/whosellm/models/families/others.py +0 -0
  109. {whosellm-0.2.0 → whosellm-0.2.1}/whosellm/models/families/vidu.py +0 -0
  110. {whosellm-0.2.0 → whosellm-0.2.1}/whosellm/models/patterns.py +0 -0
  111. {whosellm-0.2.0 → whosellm-0.2.1}/whosellm/models/registry.py +0 -0
  112. {whosellm-0.2.0 → whosellm-0.2.1}/whosellm/provider.py +0 -0
  113. {whosellm-0.2.0 → whosellm-0.2.1}/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.1"
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.1
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.1"
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
  },
@@ -91,6 +91,21 @@ def test_deepseek_reasoner_does_not_use_chat_capabilities() -> None:
91
91
  assert model.capabilities.supports_thinking is True
92
92
 
93
93
 
94
+ def test_deepseek_no_structured_outputs() -> None:
95
+ """验证 DeepSeek 官方模型不支持 structured_outputs(仅支持 json_object)"""
96
+ from whosellm import LLMeta
97
+
98
+ for model_id in ["deepseek-chat", "deepseek-reasoner"]:
99
+ model = LLMeta(f"deepseek::{model_id}")
100
+ assert model.capabilities.supports_structured_outputs is False, (
101
+ f"{model_id}: DeepSeek API 仅支持 response_format={{type:'json_object'}},"
102
+ "不支持 json_schema 类型,supports_structured_outputs 应为 False"
103
+ )
104
+ assert model.capabilities.supports_json_outputs is True, (
105
+ f"{model_id}: DeepSeek API 支持 response_format={{type:'json_object'}},supports_json_outputs 应为 True"
106
+ )
107
+
108
+
94
109
  def test_deepseek_official_invalid_model_names() -> None:
95
110
  """验证 DeepSeek 官方不支持的模型名称会被识别为 UNKNOWN / Validate unsupported official model names are recognized as UNKNOWN"""
96
111
  from whosellm import LLMeta
@@ -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,7 @@ 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
194
+
195
195
 
196
196
 
197
197
  def test_gpt5_4_with_date_suffix():
@@ -237,7 +237,7 @@ def test_gpt5_4_mini_model():
237
237
  assert m.capabilities.supports_fine_tuning is False
238
238
  assert m.capabilities.supports_distillation is True
239
239
  assert m.capabilities.supports_computer_use is True
240
- assert m.capabilities.supports_mcp is True
240
+
241
241
 
242
242
 
243
243
  def test_gpt5_4_mini_with_date_suffix():
@@ -265,7 +265,7 @@ def test_gpt5_4_nano_model():
265
265
  assert m.capabilities.supports_fine_tuning is False
266
266
  assert m.capabilities.supports_distillation is True
267
267
  assert m.capabilities.supports_computer_use is False
268
- assert m.capabilities.supports_mcp is True
268
+
269
269
 
270
270
 
271
271
  def test_gpt5_4_nano_with_date_suffix():
@@ -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.1"
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.1"
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
@@ -28,6 +28,7 @@ DEEPSEEK = ModelFamilyConfig(
28
28
  capabilities=ModelCapabilities(
29
29
  supports_function_calling=True,
30
30
  supports_streaming=True,
31
+ supports_structured_outputs=False,
31
32
  max_tokens=8000,
32
33
  context_window=128000,
33
34
  ),
@@ -38,6 +39,7 @@ DEEPSEEK = ModelFamilyConfig(
38
39
  capabilities=ModelCapabilities(
39
40
  supports_function_calling=True,
40
41
  supports_streaming=True,
42
+ supports_structured_outputs=False,
41
43
  max_tokens=8000,
42
44
  context_window=128000,
43
45
  ),
@@ -56,6 +58,7 @@ DEEPSEEK = ModelFamilyConfig(
56
58
  # DS Reasoner模型原生是不支持工具调用的,但是官方做了优化,如果向R1模型发起带有Tools调用的请求,会自动路由至Chat模型。
57
59
  # 因此在这里标记为支持FunctionCall
58
60
  supports_function_calling=True,
61
+ supports_structured_outputs=False,
59
62
  max_tokens=64000,
60
63
  context_window=128000,
61
64
  ),
@@ -31,7 +31,7 @@ GPT_4_1 = ModelFamilyConfig(
31
31
  supports_file_search=True,
32
32
  supports_image_generation=True,
33
33
  supports_code_interpreter=True,
34
- supports_mcp=True,
34
+
35
35
  max_tokens=32768,
36
36
  context_window=1_047_576,
37
37
  ),
@@ -33,7 +33,7 @@ GPT_5 = ModelFamilyConfig(
33
33
  supports_file_search=True,
34
34
  supports_image_generation=True,
35
35
  supports_code_interpreter=True,
36
- supports_mcp=True,
36
+
37
37
  max_tokens=128_000,
38
38
  context_window=400_000,
39
39
  ),
@@ -66,7 +66,7 @@ GPT_5 = ModelFamilyConfig(
66
66
  supports_file_search=True,
67
67
  supports_image_generation=False,
68
68
  supports_code_interpreter=True,
69
- supports_mcp=True,
69
+
70
70
  max_tokens=128_000,
71
71
  context_window=400_000,
72
72
  ),
@@ -93,7 +93,7 @@ GPT_5 = ModelFamilyConfig(
93
93
  supports_file_search=True,
94
94
  supports_image_generation=False,
95
95
  supports_code_interpreter=False,
96
- supports_mcp=True,
96
+
97
97
  max_tokens=16_384,
98
98
  context_window=128_000,
99
99
  ),
@@ -121,7 +121,7 @@ GPT_5 = ModelFamilyConfig(
121
121
  supports_image_generation=True,
122
122
  supports_code_interpreter=True,
123
123
  supports_computer_use=True,
124
- supports_mcp=True,
124
+
125
125
  max_tokens=128_000,
126
126
  context_window=400_000,
127
127
  ),
@@ -25,7 +25,7 @@ GPT_5_1 = ModelFamilyConfig(
25
25
  supports_file_search=True,
26
26
  supports_image_generation=True,
27
27
  supports_code_interpreter=True,
28
- supports_mcp=True,
28
+
29
29
  max_tokens=128_000,
30
30
  context_window=1_050_000,
31
31
  ),
@@ -25,7 +25,7 @@ GPT_5_2 = ModelFamilyConfig(
25
25
  supports_file_search=True,
26
26
  supports_image_generation=True,
27
27
  supports_code_interpreter=True,
28
- supports_mcp=True,
28
+
29
29
  max_tokens=128_000,
30
30
  context_window=1_050_000,
31
31
  ),
@@ -57,7 +57,7 @@ GPT_5_2 = ModelFamilyConfig(
57
57
  supports_image_generation=True,
58
58
  supports_code_interpreter=True,
59
59
  supports_computer_use=True,
60
- supports_mcp=True,
60
+
61
61
  max_tokens=128_000,
62
62
  context_window=1_050_000,
63
63
  ),
@@ -40,7 +40,7 @@ GPT_5_4 = ModelFamilyConfig(
40
40
  supports_image_generation=True,
41
41
  supports_code_interpreter=True,
42
42
  supports_computer_use=True,
43
- supports_mcp=True,
43
+
44
44
  max_tokens=128_000,
45
45
  context_window=1_050_000,
46
46
  ),
@@ -72,7 +72,7 @@ GPT_5_4 = ModelFamilyConfig(
72
72
  supports_image_generation=True,
73
73
  supports_code_interpreter=False,
74
74
  supports_computer_use=True,
75
- supports_mcp=True,
75
+
76
76
  max_tokens=128_000,
77
77
  context_window=1_050_000,
78
78
  ),
@@ -98,7 +98,7 @@ GPT_5_4 = ModelFamilyConfig(
98
98
  supports_image_generation=True,
99
99
  supports_code_interpreter=True,
100
100
  supports_computer_use=True,
101
- supports_mcp=True,
101
+
102
102
  max_tokens=128_000,
103
103
  context_window=400_000,
104
104
  ),
@@ -124,7 +124,7 @@ GPT_5_4 = ModelFamilyConfig(
124
124
  supports_image_generation=True,
125
125
  supports_code_interpreter=True,
126
126
  supports_computer_use=False,
127
- supports_mcp=True,
127
+
128
128
  max_tokens=128_000,
129
129
  context_window=400_000,
130
130
  ),
@@ -69,6 +69,8 @@ GLM_VISION = ModelFamilyConfig(
69
69
  supports_vision=True,
70
70
  supports_video=True,
71
71
  supports_pdf=True,
72
+ supports_structured_outputs=False,
73
+ supports_json_outputs=False,
72
74
  supports_streaming=True,
73
75
  max_tokens=8192,
74
76
  context_window=64000,
@@ -83,6 +85,8 @@ GLM_VISION = ModelFamilyConfig(
83
85
  capabilities=ModelCapabilities(
84
86
  supports_vision=True,
85
87
  supports_video=True,
88
+ supports_structured_outputs=False,
89
+ supports_json_outputs=False,
86
90
  supports_streaming=True,
87
91
  max_tokens=8192,
88
92
  context_window=8192,
@@ -103,6 +107,8 @@ GLM_VISION = ModelFamilyConfig(
103
107
  capabilities=ModelCapabilities(
104
108
  supports_vision=True,
105
109
  supports_video=True, # plus 支持视频
110
+ supports_structured_outputs=False,
111
+ supports_json_outputs=False,
106
112
  supports_streaming=True,
107
113
  max_tokens=8192,
108
114
  context_window=8192,
@@ -123,6 +129,8 @@ GLM_VISION = ModelFamilyConfig(
123
129
  variant_priority=(0,), # flash 的优先级 / flash priority
124
130
  capabilities=ModelCapabilities(
125
131
  supports_vision=True,
132
+ supports_structured_outputs=False,
133
+ supports_json_outputs=False,
126
134
  supports_streaming=True,
127
135
  max_tokens=8192,
128
136
  context_window=8192,
@@ -138,6 +146,8 @@ GLM_VISION = ModelFamilyConfig(
138
146
  capabilities=ModelCapabilities(
139
147
  supports_vision=True,
140
148
  supports_function_calling=True,
149
+ supports_structured_outputs=False,
150
+ supports_json_outputs=False,
141
151
  supports_streaming=True,
142
152
  max_tokens=8192,
143
153
  context_window=128000,
@@ -154,6 +164,8 @@ GLM_VISION = ModelFamilyConfig(
154
164
  supports_vision=True,
155
165
  supports_video=True,
156
166
  supports_pdf=True,
167
+ supports_structured_outputs=False,
168
+ supports_json_outputs=False,
157
169
  supports_streaming=True,
158
170
  max_tokens=16384,
159
171
  context_window=64000,
@@ -169,6 +181,8 @@ GLM_VISION = ModelFamilyConfig(
169
181
  supports_vision=True,
170
182
  supports_video=True,
171
183
  supports_pdf=True,
184
+ supports_structured_outputs=False,
185
+ supports_json_outputs=False,
172
186
  supports_streaming=True,
173
187
  max_tokens=128000,
174
188
  context_window=128000,
@@ -184,6 +198,8 @@ GLM_VISION = ModelFamilyConfig(
184
198
  supports_vision=True,
185
199
  supports_video=True,
186
200
  supports_pdf=True,
201
+ supports_structured_outputs=False,
202
+ supports_json_outputs=False,
187
203
  supports_streaming=True,
188
204
  max_tokens=128000,
189
205
  context_window=128000,
@@ -230,7 +246,6 @@ GLM_TEXT = ModelFamilyConfig(
230
246
  supports_structured_outputs=False,
231
247
  supports_streaming=True,
232
248
  supports_web_search=True,
233
- supports_mcp=True,
234
249
  max_tokens=128000,
235
250
  context_window=200000,
236
251
  ),
@@ -243,9 +258,9 @@ GLM_TEXT = ModelFamilyConfig(
243
258
  capabilities=ModelCapabilities(
244
259
  supports_thinking=True,
245
260
  supports_function_calling=True,
246
- supports_structured_outputs=True,
261
+ supports_structured_outputs=False,
247
262
  supports_streaming=True,
248
- supports_mcp=True,
263
+
249
264
  max_tokens=128000,
250
265
  context_window=200000,
251
266
  ),
@@ -258,9 +273,9 @@ GLM_TEXT = ModelFamilyConfig(
258
273
  capabilities=ModelCapabilities(
259
274
  supports_thinking=True,
260
275
  supports_function_calling=True,
261
- supports_structured_outputs=True,
276
+ supports_structured_outputs=False,
262
277
  supports_streaming=True,
263
- supports_mcp=True,
278
+
264
279
  max_tokens=128000,
265
280
  context_window=200000,
266
281
  ),
@@ -272,9 +287,9 @@ GLM_TEXT = ModelFamilyConfig(
272
287
  capabilities=ModelCapabilities(
273
288
  supports_thinking=True,
274
289
  supports_function_calling=True,
275
- supports_structured_outputs=True,
290
+ supports_structured_outputs=False,
276
291
  supports_streaming=True,
277
- supports_mcp=True,
292
+
278
293
  max_tokens=128000,
279
294
  context_window=200000,
280
295
  ),
@@ -291,9 +306,9 @@ GLM_TEXT = ModelFamilyConfig(
291
306
  capabilities=ModelCapabilities(
292
307
  supports_thinking=True,
293
308
  supports_function_calling=True,
294
- supports_structured_outputs=True,
309
+ supports_structured_outputs=False,
295
310
  supports_streaming=True,
296
- supports_mcp=True,
311
+
297
312
  max_tokens=128000,
298
313
  context_window=200000,
299
314
  ),
@@ -314,7 +329,7 @@ GLM_TEXT = ModelFamilyConfig(
314
329
  supports_streaming=True,
315
330
  supports_structured_outputs=False,
316
331
  supports_web_search=True,
317
- supports_mcp=True,
332
+
318
333
  max_tokens=128000,
319
334
  context_window=200000,
320
335
  ),
@@ -327,7 +342,7 @@ GLM_TEXT = ModelFamilyConfig(
327
342
  capabilities=ModelCapabilities(
328
343
  supports_thinking=True,
329
344
  supports_function_calling=True,
330
- supports_structured_outputs=True,
345
+ supports_structured_outputs=False,
331
346
  supports_streaming=True,
332
347
  max_tokens=96000,
333
348
  context_window=128000,
@@ -340,7 +355,7 @@ GLM_TEXT = ModelFamilyConfig(
340
355
  capabilities=ModelCapabilities(
341
356
  supports_thinking=True,
342
357
  supports_function_calling=True,
343
- supports_structured_outputs=True,
358
+ supports_structured_outputs=False,
344
359
  supports_streaming=True,
345
360
  max_tokens=96000,
346
361
  context_window=128000,
@@ -358,7 +373,7 @@ GLM_TEXT = ModelFamilyConfig(
358
373
  capabilities=ModelCapabilities(
359
374
  supports_thinking=True,
360
375
  supports_function_calling=True,
361
- supports_structured_outputs=True,
376
+ supports_structured_outputs=False,
362
377
  supports_streaming=True,
363
378
  max_tokens=96000,
364
379
  context_window=128000,
@@ -376,7 +391,7 @@ GLM_TEXT = ModelFamilyConfig(
376
391
  capabilities=ModelCapabilities(
377
392
  supports_thinking=True,
378
393
  supports_function_calling=True,
379
- supports_structured_outputs=True,
394
+ supports_structured_outputs=False,
380
395
  supports_streaming=True,
381
396
  max_tokens=96000,
382
397
  context_window=128000,
@@ -394,7 +409,7 @@ GLM_TEXT = ModelFamilyConfig(
394
409
  capabilities=ModelCapabilities(
395
410
  supports_thinking=True,
396
411
  supports_function_calling=True,
397
- supports_structured_outputs=True,
412
+ supports_structured_outputs=False,
398
413
  supports_streaming=True,
399
414
  max_tokens=96000,
400
415
  context_window=128000,
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes