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.
- {whosellm-0.2.0 → whosellm-0.2.2}/.bumpversion.toml +1 -1
- {whosellm-0.2.0 → whosellm-0.2.2}/.claude/skills/review-provider-model/providers/anthropic.md +1 -1
- {whosellm-0.2.0 → whosellm-0.2.2}/CLAUDE.md +2 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/PKG-INFO +3 -1
- {whosellm-0.2.0 → whosellm-0.2.2}/README.md +2 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/pyproject.toml +1 -1
- {whosellm-0.2.0 → whosellm-0.2.2}/tests/e2e/test_zhipu.py +31 -14
- whosellm-0.2.2/tests/models/families/test_deepseek.py +176 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/tests/models/families/test_deepseek_tencent.py +23 -1
- whosellm-0.2.2/tests/models/families/test_glm45.py +21 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/tests/models/families/test_glm45v.py +14 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/tests/models/families/test_glm46v.py +14 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/tests/models/families/test_glm5.py +3 -3
- {whosellm-0.2.0 → whosellm-0.2.2}/tests/models/families/test_gpt5.py +1 -4
- {whosellm-0.2.0 → whosellm-0.2.2}/tests/test_auto_register.py +2 -1
- {whosellm-0.2.0 → whosellm-0.2.2}/tests/test_llmeta.py +1 -1
- {whosellm-0.2.0 → whosellm-0.2.2}/uv.lock +1 -1
- {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/__init__.py +1 -1
- {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/capabilities.py +0 -1
- whosellm-0.2.2/whosellm/models/families/deepseek/deepseek_official.py +117 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/models/families/deepseek/tencent.py +22 -2
- {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/models/families/openai/openai_gpt_4_1.py +0 -1
- {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/models/families/openai/openai_gpt_5.py +0 -4
- {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/models/families/openai/openai_gpt_5_1.py +0 -1
- {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/models/families/openai/openai_gpt_5_2.py +0 -2
- {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/models/families/openai/openai_gpt_5_4.py +0 -4
- {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/models/families/zhipu.py +25 -15
- whosellm-0.2.0/tests/models/families/test_deepseek.py +0 -105
- whosellm-0.2.0/whosellm/models/families/deepseek/deepseek_official.py +0 -69
- {whosellm-0.2.0 → whosellm-0.2.2}/.claude/commands/interview.md +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/.claude/projects/-Users-jqq-PycharmProjects-llmeta/memory/MEMORY.md +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/.claude/projects/-Users-jqq-PycharmProjects-llmeta/memory/feedback_use_uv.md +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/.claude/skills/code-review/SKILL.md +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/.claude/skills/create-skill/SKILL.md +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/.claude/skills/e2e-metadata/SKILL.md +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/.claude/skills/evolve/SKILL.md +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/.claude/skills/fix-review/SKILL.md +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/.claude/skills/release/SKILL.md +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/.claude/skills/review-provider-model/SKILL.md +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/.claude/skills/review-provider-model/providers/alibaba.md +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/.claude/skills/review-provider-model/providers/deepseek.md +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/.claude/skills/review-provider-model/providers/gemini.md +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/.claude/skills/review-provider-model/providers/openai.md +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/.claude/skills/review-provider-model/providers/others.md +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/.claude/skills/review-provider-model/providers/vidu.md +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/.claude/skills/review-provider-model/providers/zhipu.md +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/.claude/skills/update-provider-model/SKILL.md +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/.claude/skills/update-provider-model/testing.md +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/.coveragerc +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/.github/workflows/publish.yml +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/.github/workflows/tests.yml +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/.gitignore +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/.windsurf/workflows/addmodel.md +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/.windsurf/workflows/arch.md +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/.windsurf/workflows/testllmeta.md +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/CHANGELOG.md +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/LICENSE +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/docs/add_new_model_family.md +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/docs/refactor_proposal.md +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/docs/spec_model_family_redesign.md +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/examples/advanced_usage.py +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/examples/basic_usage.py +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/mypy.ini +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/pytest.ini +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/ruff.toml +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/tests/__init__.py +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/tests/e2e/__init__.py +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/tests/e2e/conftest.py +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/tests/e2e/test_anthropic.py +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/tests/e2e/test_google.py +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/tests/e2e/test_openai.py +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/tests/models/__init__.py +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/tests/models/families/__init__.py +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/tests/models/families/test_anthropic.py +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/tests/models/families/test_gemini.py +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/tests/models/families/test_glm46.py +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/tests/models/families/test_gpt3_5.py +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/tests/models/families/test_gpt4_1.py +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/tests/models/families/test_gpt4o.py +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/tests/models/families/test_o1.py +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/tests/models/families/test_o3.py +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/tests/models/families/test_o4.py +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/tests/models/families/test_qwen.py +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/tests/models/families/test_qwen3_vl_models.py +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/tests/models/families/test_qwen_ollama.py +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/tests/models/families/test_qwen_plus.py +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/tests/test_model_version.py +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/tests/test_provider.py +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/tests/test_registry_merge.py +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/tests/test_specific_patterns.py +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/tests/test_variant_priority_config.py +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/model_version.py +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/models/__init__.py +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/models/base.py +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/models/config.py +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/models/dynamic_enum.py +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/models/families/__init__.py +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/models/families/alibaba.py +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/models/families/anthropic.py +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/models/families/deepseek/__init__.py +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/models/families/gemini.py +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/models/families/openai/__init__.py +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/models/families/openai/openai_gpt_3_5.py +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/models/families/openai/openai_gpt_4.py +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/models/families/openai/openai_gpt_4o.py +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/models/families/openai/openai_gpt_5_3.py +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/models/families/openai/openai_o1.py +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/models/families/openai/openai_o3.py +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/models/families/openai/openai_o4.py +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/models/families/others.py +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/models/families/vidu.py +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/models/patterns.py +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/models/registry.py +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/provider.py +0 -0
- {whosellm-0.2.0 → whosellm-0.2.2}/whosellm/py.typed +0 -0
{whosellm-0.2.0 → whosellm-0.2.2}/.claude/skills/review-provider-model/providers/anthropic.md
RENAMED
|
@@ -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`
|
|
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.
|
|
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,7 +1,12 @@
|
|
|
1
1
|
"""ZhipuAI E2E 元数据测试。
|
|
2
2
|
|
|
3
|
-
来源: https://docs.
|
|
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.
|
|
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":
|
|
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.
|
|
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":
|
|
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":
|
|
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":
|
|
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.
|
|
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.
|
|
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":
|
|
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.
|
|
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":
|
|
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":
|
|
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.
|
|
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
|
-
|
|
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
|
|
38
|
+
assert capabilities.supports_structured_outputs is False
|
|
39
39
|
assert capabilities.supports_streaming is True
|
|
40
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
@@ -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
|