whosellm 0.3.0__tar.gz → 0.3.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 (128) hide show
  1. {whosellm-0.3.0 → whosellm-0.3.1}/.bumpversion.toml +1 -1
  2. {whosellm-0.3.0 → whosellm-0.3.1}/PKG-INFO +1 -1
  3. {whosellm-0.3.0 → whosellm-0.3.1}/pyproject.toml +1 -1
  4. {whosellm-0.3.0 → whosellm-0.3.1}/tests/e2e/test_anthropic.py +35 -1
  5. whosellm-0.3.1/tests/e2e/test_deepseek.py +151 -0
  6. {whosellm-0.3.0 → whosellm-0.3.1}/tests/e2e/test_openai.py +41 -1
  7. {whosellm-0.3.0 → whosellm-0.3.1}/tests/models/families/test_anthropic.py +68 -2
  8. {whosellm-0.3.0 → whosellm-0.3.1}/tests/models/families/test_deepseek.py +99 -0
  9. whosellm-0.3.1/tests/models/families/test_gpt6.py +166 -0
  10. {whosellm-0.3.0 → whosellm-0.3.1}/tests/models/families/test_media_count_limit_config.py +12 -2
  11. {whosellm-0.3.0 → whosellm-0.3.1}/tests/test_variant_priority_config.py +9 -6
  12. {whosellm-0.3.0 → whosellm-0.3.1}/uv.lock +1 -1
  13. {whosellm-0.3.0 → whosellm-0.3.1}/whosellm/__init__.py +1 -1
  14. {whosellm-0.3.0 → whosellm-0.3.1}/whosellm/models/families/anthropic.py +30 -0
  15. whosellm-0.3.1/whosellm/models/families/deepseek/deepseek_official.py +191 -0
  16. whosellm-0.3.1/whosellm/models/families/openai/openai_gpt_6.py +94 -0
  17. whosellm-0.3.0/tests/models/families/test_gpt6.py +0 -70
  18. whosellm-0.3.0/whosellm/models/families/deepseek/deepseek_official.py +0 -145
  19. whosellm-0.3.0/whosellm/models/families/openai/openai_gpt_6.py +0 -56
  20. {whosellm-0.3.0 → whosellm-0.3.1}/.claude/commands/interview.md +0 -0
  21. {whosellm-0.3.0 → whosellm-0.3.1}/.claude/projects/-Users-jqq-PycharmProjects-llmeta/memory/MEMORY.md +0 -0
  22. {whosellm-0.3.0 → whosellm-0.3.1}/.claude/projects/-Users-jqq-PycharmProjects-llmeta/memory/feedback_use_uv.md +0 -0
  23. {whosellm-0.3.0 → whosellm-0.3.1}/.claude/settings.json +0 -0
  24. {whosellm-0.3.0 → whosellm-0.3.1}/.claude/skills/code-review/SKILL.md +0 -0
  25. {whosellm-0.3.0 → whosellm-0.3.1}/.claude/skills/create-skill/SKILL.md +0 -0
  26. {whosellm-0.3.0 → whosellm-0.3.1}/.claude/skills/e2e-metadata/SKILL.md +0 -0
  27. {whosellm-0.3.0 → whosellm-0.3.1}/.claude/skills/evolve/SKILL.md +0 -0
  28. {whosellm-0.3.0 → whosellm-0.3.1}/.claude/skills/fix-review/SKILL.md +0 -0
  29. {whosellm-0.3.0 → whosellm-0.3.1}/.claude/skills/release/SKILL.md +0 -0
  30. {whosellm-0.3.0 → whosellm-0.3.1}/.claude/skills/review-provider-model/SKILL.md +0 -0
  31. {whosellm-0.3.0 → whosellm-0.3.1}/.claude/skills/review-provider-model/providers/alibaba.md +0 -0
  32. {whosellm-0.3.0 → whosellm-0.3.1}/.claude/skills/review-provider-model/providers/anthropic.md +0 -0
  33. {whosellm-0.3.0 → whosellm-0.3.1}/.claude/skills/review-provider-model/providers/deepseek.md +0 -0
  34. {whosellm-0.3.0 → whosellm-0.3.1}/.claude/skills/review-provider-model/providers/gemini.md +0 -0
  35. {whosellm-0.3.0 → whosellm-0.3.1}/.claude/skills/review-provider-model/providers/openai.md +0 -0
  36. {whosellm-0.3.0 → whosellm-0.3.1}/.claude/skills/review-provider-model/providers/others.md +0 -0
  37. {whosellm-0.3.0 → whosellm-0.3.1}/.claude/skills/review-provider-model/providers/vidu.md +0 -0
  38. {whosellm-0.3.0 → whosellm-0.3.1}/.claude/skills/review-provider-model/providers/zhipu.md +0 -0
  39. {whosellm-0.3.0 → whosellm-0.3.1}/.claude/skills/update-provider-model/SKILL.md +0 -0
  40. {whosellm-0.3.0 → whosellm-0.3.1}/.claude/skills/update-provider-model/testing.md +0 -0
  41. {whosellm-0.3.0 → whosellm-0.3.1}/.coveragerc +0 -0
  42. {whosellm-0.3.0 → whosellm-0.3.1}/.github/workflows/publish.yml +0 -0
  43. {whosellm-0.3.0 → whosellm-0.3.1}/.github/workflows/tests.yml +0 -0
  44. {whosellm-0.3.0 → whosellm-0.3.1}/.gitignore +0 -0
  45. {whosellm-0.3.0 → whosellm-0.3.1}/.windsurf/workflows/addmodel.md +0 -0
  46. {whosellm-0.3.0 → whosellm-0.3.1}/.windsurf/workflows/arch.md +0 -0
  47. {whosellm-0.3.0 → whosellm-0.3.1}/.windsurf/workflows/testllmeta.md +0 -0
  48. {whosellm-0.3.0 → whosellm-0.3.1}/CHANGELOG.md +0 -0
  49. {whosellm-0.3.0 → whosellm-0.3.1}/CLAUDE.md +0 -0
  50. {whosellm-0.3.0 → whosellm-0.3.1}/LICENSE +0 -0
  51. {whosellm-0.3.0 → whosellm-0.3.1}/README.md +0 -0
  52. {whosellm-0.3.0 → whosellm-0.3.1}/docs/add_new_model_family.md +0 -0
  53. {whosellm-0.3.0 → whosellm-0.3.1}/docs/refactor_proposal.md +0 -0
  54. {whosellm-0.3.0 → whosellm-0.3.1}/docs/spec_model_family_redesign.md +0 -0
  55. {whosellm-0.3.0 → whosellm-0.3.1}/examples/advanced_usage.py +0 -0
  56. {whosellm-0.3.0 → whosellm-0.3.1}/examples/basic_usage.py +0 -0
  57. {whosellm-0.3.0 → whosellm-0.3.1}/mypy.ini +0 -0
  58. {whosellm-0.3.0 → whosellm-0.3.1}/pytest.ini +0 -0
  59. {whosellm-0.3.0 → whosellm-0.3.1}/ruff.toml +0 -0
  60. {whosellm-0.3.0 → whosellm-0.3.1}/tests/__init__.py +0 -0
  61. {whosellm-0.3.0 → whosellm-0.3.1}/tests/e2e/__init__.py +0 -0
  62. {whosellm-0.3.0 → whosellm-0.3.1}/tests/e2e/conftest.py +0 -0
  63. {whosellm-0.3.0 → whosellm-0.3.1}/tests/e2e/test_google.py +0 -0
  64. {whosellm-0.3.0 → whosellm-0.3.1}/tests/e2e/test_zhipu.py +0 -0
  65. {whosellm-0.3.0 → whosellm-0.3.1}/tests/models/__init__.py +0 -0
  66. {whosellm-0.3.0 → whosellm-0.3.1}/tests/models/families/__init__.py +0 -0
  67. {whosellm-0.3.0 → whosellm-0.3.1}/tests/models/families/test_deepseek_tencent.py +0 -0
  68. {whosellm-0.3.0 → whosellm-0.3.1}/tests/models/families/test_gemini.py +0 -0
  69. {whosellm-0.3.0 → whosellm-0.3.1}/tests/models/families/test_glm41v_thinking.py +0 -0
  70. {whosellm-0.3.0 → whosellm-0.3.1}/tests/models/families/test_glm45.py +0 -0
  71. {whosellm-0.3.0 → whosellm-0.3.1}/tests/models/families/test_glm45v.py +0 -0
  72. {whosellm-0.3.0 → whosellm-0.3.1}/tests/models/families/test_glm46.py +0 -0
  73. {whosellm-0.3.0 → whosellm-0.3.1}/tests/models/families/test_glm46v.py +0 -0
  74. {whosellm-0.3.0 → whosellm-0.3.1}/tests/models/families/test_glm5.py +0 -0
  75. {whosellm-0.3.0 → whosellm-0.3.1}/tests/models/families/test_glm53.py +0 -0
  76. {whosellm-0.3.0 → whosellm-0.3.1}/tests/models/families/test_glm5v_turbo.py +0 -0
  77. {whosellm-0.3.0 → whosellm-0.3.1}/tests/models/families/test_gpt3_5.py +0 -0
  78. {whosellm-0.3.0 → whosellm-0.3.1}/tests/models/families/test_gpt4_1.py +0 -0
  79. {whosellm-0.3.0 → whosellm-0.3.1}/tests/models/families/test_gpt4o.py +0 -0
  80. {whosellm-0.3.0 → whosellm-0.3.1}/tests/models/families/test_gpt5.py +0 -0
  81. {whosellm-0.3.0 → whosellm-0.3.1}/tests/models/families/test_o1.py +0 -0
  82. {whosellm-0.3.0 → whosellm-0.3.1}/tests/models/families/test_o3.py +0 -0
  83. {whosellm-0.3.0 → whosellm-0.3.1}/tests/models/families/test_o4.py +0 -0
  84. {whosellm-0.3.0 → whosellm-0.3.1}/tests/models/families/test_qwen.py +0 -0
  85. {whosellm-0.3.0 → whosellm-0.3.1}/tests/models/families/test_qwen3_vl_models.py +0 -0
  86. {whosellm-0.3.0 → whosellm-0.3.1}/tests/models/families/test_qwen_ollama.py +0 -0
  87. {whosellm-0.3.0 → whosellm-0.3.1}/tests/models/families/test_qwen_plus.py +0 -0
  88. {whosellm-0.3.0 → whosellm-0.3.1}/tests/test_auto_register.py +0 -0
  89. {whosellm-0.3.0 → whosellm-0.3.1}/tests/test_claude_4x_versions.py +0 -0
  90. {whosellm-0.3.0 → whosellm-0.3.1}/tests/test_llmeta.py +0 -0
  91. {whosellm-0.3.0 → whosellm-0.3.1}/tests/test_media_count_limit.py +0 -0
  92. {whosellm-0.3.0 → whosellm-0.3.1}/tests/test_model_version.py +0 -0
  93. {whosellm-0.3.0 → whosellm-0.3.1}/tests/test_provider.py +0 -0
  94. {whosellm-0.3.0 → whosellm-0.3.1}/tests/test_registry_merge.py +0 -0
  95. {whosellm-0.3.0 → whosellm-0.3.1}/tests/test_specific_patterns.py +0 -0
  96. {whosellm-0.3.0 → whosellm-0.3.1}/whosellm/capabilities.py +0 -0
  97. {whosellm-0.3.0 → whosellm-0.3.1}/whosellm/model_version.py +0 -0
  98. {whosellm-0.3.0 → whosellm-0.3.1}/whosellm/models/__init__.py +0 -0
  99. {whosellm-0.3.0 → whosellm-0.3.1}/whosellm/models/base.py +0 -0
  100. {whosellm-0.3.0 → whosellm-0.3.1}/whosellm/models/config.py +0 -0
  101. {whosellm-0.3.0 → whosellm-0.3.1}/whosellm/models/dynamic_enum.py +0 -0
  102. {whosellm-0.3.0 → whosellm-0.3.1}/whosellm/models/families/__init__.py +0 -0
  103. {whosellm-0.3.0 → whosellm-0.3.1}/whosellm/models/families/alibaba.py +0 -0
  104. {whosellm-0.3.0 → whosellm-0.3.1}/whosellm/models/families/deepseek/__init__.py +0 -0
  105. {whosellm-0.3.0 → whosellm-0.3.1}/whosellm/models/families/deepseek/tencent.py +0 -0
  106. {whosellm-0.3.0 → whosellm-0.3.1}/whosellm/models/families/gemini.py +0 -0
  107. {whosellm-0.3.0 → whosellm-0.3.1}/whosellm/models/families/openai/__init__.py +0 -0
  108. {whosellm-0.3.0 → whosellm-0.3.1}/whosellm/models/families/openai/openai_gpt_3_5.py +0 -0
  109. {whosellm-0.3.0 → whosellm-0.3.1}/whosellm/models/families/openai/openai_gpt_4.py +0 -0
  110. {whosellm-0.3.0 → whosellm-0.3.1}/whosellm/models/families/openai/openai_gpt_4_1.py +0 -0
  111. {whosellm-0.3.0 → whosellm-0.3.1}/whosellm/models/families/openai/openai_gpt_4o.py +0 -0
  112. {whosellm-0.3.0 → whosellm-0.3.1}/whosellm/models/families/openai/openai_gpt_5.py +0 -0
  113. {whosellm-0.3.0 → whosellm-0.3.1}/whosellm/models/families/openai/openai_gpt_5_1.py +0 -0
  114. {whosellm-0.3.0 → whosellm-0.3.1}/whosellm/models/families/openai/openai_gpt_5_2.py +0 -0
  115. {whosellm-0.3.0 → whosellm-0.3.1}/whosellm/models/families/openai/openai_gpt_5_3.py +0 -0
  116. {whosellm-0.3.0 → whosellm-0.3.1}/whosellm/models/families/openai/openai_gpt_5_4.py +0 -0
  117. {whosellm-0.3.0 → whosellm-0.3.1}/whosellm/models/families/openai/openai_gpt_5_5.py +0 -0
  118. {whosellm-0.3.0 → whosellm-0.3.1}/whosellm/models/families/openai/openai_gpt_5_6.py +0 -0
  119. {whosellm-0.3.0 → whosellm-0.3.1}/whosellm/models/families/openai/openai_o1.py +0 -0
  120. {whosellm-0.3.0 → whosellm-0.3.1}/whosellm/models/families/openai/openai_o3.py +0 -0
  121. {whosellm-0.3.0 → whosellm-0.3.1}/whosellm/models/families/openai/openai_o4.py +0 -0
  122. {whosellm-0.3.0 → whosellm-0.3.1}/whosellm/models/families/others.py +0 -0
  123. {whosellm-0.3.0 → whosellm-0.3.1}/whosellm/models/families/vidu.py +0 -0
  124. {whosellm-0.3.0 → whosellm-0.3.1}/whosellm/models/families/zhipu.py +0 -0
  125. {whosellm-0.3.0 → whosellm-0.3.1}/whosellm/models/patterns.py +0 -0
  126. {whosellm-0.3.0 → whosellm-0.3.1}/whosellm/models/registry.py +0 -0
  127. {whosellm-0.3.0 → whosellm-0.3.1}/whosellm/provider.py +0 -0
  128. {whosellm-0.3.0 → whosellm-0.3.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.3.0"
5
+ current_version = "0.3.1"
6
6
  parse = """(?x)
7
7
  (?P<major>0|[1-9]\\d*)\\.
8
8
  (?P<minor>0|[1-9]\\d*)\\.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: whosellm
3
- Version: 0.3.0
3
+ Version: 0.3.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
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "whosellm"
3
- version = "0.3.0"
3
+ version = "0.3.1"
4
4
  description = "A unified LLM model version and capability management library"
5
5
  authors = [
6
6
  {name = "JQQ", email = "jqq1716@gmail.com"}
@@ -2,6 +2,7 @@
2
2
 
3
3
  来源: https://platform.claude.com/docs/en/docs/about-claude/models
4
4
  采集日期: 2026-04-12(2026-09-04 增补 Fable 5.1 / Mythos 5.1)
5
+ 2026-09-23 增补 Opus 5.5
5
6
  """
6
7
 
7
8
  import pytest
@@ -10,6 +11,33 @@ from whosellm import ModelFamily, Provider
10
11
 
11
12
  from .conftest import assert_model_metadata
12
13
 
14
+ # ============================================================================
15
+ # Claude Family — Version 5.5 (2026-09-22 发布 / Released 2026-09-22)
16
+ # 来源: https://platform.claude.com/docs/en/release-notes/overview
17
+ # ============================================================================
18
+
19
+ CLAUDE_55_MODELS = [
20
+ (
21
+ "claude-opus-5-5",
22
+ {
23
+ # 1M ctx (默认且最大) / 128K out;adaptive thinking 常开且不可关闭
24
+ # 1M ctx (default & max) / 128K out; adaptive thinking always on, cannot be disabled
25
+ "provider": Provider.ANTHROPIC,
26
+ "family": ModelFamily.CLAUDE,
27
+ "version": "5.5",
28
+ "variant": "opus",
29
+ "supports_thinking": True,
30
+ "supports_vision": True,
31
+ "supports_streaming": True,
32
+ "supports_function_calling": True,
33
+ "supports_structured_outputs": True,
34
+ "supports_computer_use": True,
35
+ "context_window": 1_000_000,
36
+ "max_tokens": 128_000,
37
+ },
38
+ ),
39
+ ]
40
+
13
41
  # ============================================================================
14
42
  # Claude Family — Version 5.1 (2026-09-01 发布 / Released 2026-09-01)
15
43
  # 来源: https://platform.claude.com/docs/en/models/fable-5-1/overview
@@ -234,7 +262,13 @@ CLAUDE_30_MODELS = [
234
262
  # ============================================================================
235
263
 
236
264
  ALL_MODELS = (
237
- CLAUDE_51_MODELS + CLAUDE_LATEST_MODELS + CLAUDE_45_MODELS + CLAUDE_41_MODELS + CLAUDE_40_MODELS + CLAUDE_30_MODELS
265
+ CLAUDE_55_MODELS
266
+ + CLAUDE_51_MODELS
267
+ + CLAUDE_LATEST_MODELS
268
+ + CLAUDE_45_MODELS
269
+ + CLAUDE_41_MODELS
270
+ + CLAUDE_40_MODELS
271
+ + CLAUDE_30_MODELS
238
272
  )
239
273
 
240
274
 
@@ -0,0 +1,151 @@
1
+ # filename: test_deepseek.py
2
+ """
3
+ DeepSeek 官方模型元数据 E2E 测试 / DeepSeek official model metadata E2E tests.
4
+
5
+ 来源 / Sources:
6
+ https://api-docs.deepseek.com/zh-cn/updates/(2026-09-10 DeepSeek-V4.1-Flash 发布)
7
+ https://api-docs.deepseek.com/zh-cn/quick_start/pricing
8
+ https://api-docs.deepseek.com/zh-cn/guides/vision
9
+ 采集日期 / Collected: 2026-09-28(含 api.deepseek.com 实探复核 / verified against the live API)
10
+
11
+ 约定 / Conventions:
12
+ * 旧名(deepseek-v4-flash / -vision-exp / deepseek-chat / deepseek-reasoner)版本仍按名字解析为
13
+ 4.0,能力按当前实际服务模型(转发到 DeepSeek-V4.1-Flash)标注。
14
+ Legacy names keep their name-derived version (4.0) while capabilities follow the
15
+ currently serving model (routed to DeepSeek-V4.1-Flash).
16
+ * deepseek-v4-pro 未被路由,保持纯文本(无图像输入)。
17
+ """
18
+
19
+ import pytest
20
+
21
+ from whosellm import ModelFamily, Provider
22
+
23
+ from .conftest import assert_model_metadata
24
+
25
+ # ============================================================================
26
+ # DeepSeek V4.1-Flash 正式名(2026-09-10 发布)/ Canonical name of DeepSeek-V4.1-Flash
27
+ # 官方:"将模型名称更改为 deepseek-flash 即可调用最新的 V4.1 Flash 模型"
28
+ # ============================================================================
29
+
30
+ V41_FLASH_MODELS = [
31
+ (
32
+ "deepseek-flash",
33
+ {
34
+ "provider": Provider.DEEPSEEK,
35
+ "family": ModelFamily.DEEPSEEK,
36
+ # 名字无版本号 → 按官方模型版本 DeepSeek-V4.1-Flash 标注
37
+ "version": "4.1",
38
+ "variant": "flash",
39
+ "supports_thinking": True, # 默认开启,effort 默认 high
40
+ "supports_vision": True, # 原生多模态视觉理解
41
+ "supports_function_calling": True,
42
+ "supports_streaming": True,
43
+ "supports_json_outputs": True,
44
+ "supports_structured_outputs": False, # 仅 json_object,无 json_schema
45
+ "context_window": 1_000_000,
46
+ "max_tokens": 384_000,
47
+ },
48
+ ),
49
+ ]
50
+
51
+ # ============================================================================
52
+ # 兼容路由旧名(官方措辞"暂时路由")/ Routed legacy names ("temporarily routed" per vendor)
53
+ # ============================================================================
54
+
55
+ ROUTED_LEGACY_MODELS = [
56
+ (
57
+ "deepseek-v4-flash",
58
+ {
59
+ "provider": Provider.DEEPSEEK,
60
+ "family": ModelFamily.DEEPSEEK,
61
+ "version": "4.0", # 版本轴按名字 / version parses from the name
62
+ "variant": "flash",
63
+ "supports_thinking": True,
64
+ "supports_vision": True, # 能力轴跟实际服务模型 / capabilities follow the serving model
65
+ "supports_function_calling": True,
66
+ "supports_streaming": True,
67
+ "context_window": 1_000_000,
68
+ "max_tokens": 384_000,
69
+ },
70
+ ),
71
+ (
72
+ "deepseek-v4-flash-vision-exp",
73
+ {
74
+ "provider": Provider.DEEPSEEK,
75
+ "family": ModelFamily.DEEPSEEK,
76
+ "version": "4.0",
77
+ "variant": "flash-vision-exp",
78
+ "supports_thinking": True,
79
+ "supports_vision": True,
80
+ "context_window": 1_000_000,
81
+ "max_tokens": 384_000,
82
+ },
83
+ ),
84
+ (
85
+ "deepseek-chat",
86
+ {
87
+ "provider": Provider.DEEPSEEK,
88
+ "family": ModelFamily.DEEPSEEK,
89
+ "version": "4.0",
90
+ "variant": "chat",
91
+ "supports_thinking": False, # 别名默认非思考模式 / alias defaults to non-thinking
92
+ "supports_vision": True,
93
+ "context_window": 1_000_000,
94
+ "max_tokens": 384_000,
95
+ },
96
+ ),
97
+ (
98
+ "deepseek-reasoner",
99
+ {
100
+ "provider": Provider.DEEPSEEK,
101
+ "family": ModelFamily.DEEPSEEK,
102
+ "version": "4.0",
103
+ "variant": "reasoner",
104
+ "supports_thinking": True, # 别名默认思考模式 / alias defaults to thinking
105
+ "supports_vision": True,
106
+ "context_window": 1_000_000,
107
+ "max_tokens": 384_000,
108
+ },
109
+ ),
110
+ ]
111
+
112
+ # ============================================================================
113
+ # 未被路由:deepseek-v4-pro 仍是 DeepSeek-V4-Pro-0813,纯文本
114
+ # Not routed: deepseek-v4-pro is still DeepSeek-V4-Pro-0813, text-only
115
+ # 实探 2026-09-28:带图不报错但静默忽略并编造答案 → supports_vision 必须为 False
116
+ # ============================================================================
117
+
118
+ TEXT_ONLY_MODELS = [
119
+ (
120
+ "deepseek-v4-pro",
121
+ {
122
+ "provider": Provider.DEEPSEEK,
123
+ "family": ModelFamily.DEEPSEEK,
124
+ "version": "4.0",
125
+ "variant": "pro",
126
+ "supports_thinking": True,
127
+ "supports_vision": False,
128
+ "supports_function_calling": True,
129
+ "supports_streaming": True,
130
+ "context_window": 1_000_000,
131
+ "max_tokens": 384_000,
132
+ },
133
+ ),
134
+ ]
135
+
136
+ # ============================================================================
137
+ # 聚合 + 参数化 / Aggregation & parametrization
138
+ # ============================================================================
139
+
140
+ ALL_MODELS = V41_FLASH_MODELS + ROUTED_LEGACY_MODELS + TEXT_ONLY_MODELS
141
+
142
+
143
+ @pytest.mark.e2e
144
+ @pytest.mark.parametrize(
145
+ "model_name,expected",
146
+ ALL_MODELS,
147
+ ids=[m[0] for m in ALL_MODELS],
148
+ )
149
+ def test_model_metadata(model_name: str, expected: dict) -> None: # type: ignore[type-arg]
150
+ """验证 DeepSeek 模型元数据与官方文档一致。"""
151
+ assert_model_metadata(model_name, expected)
@@ -15,6 +15,7 @@ from .conftest import assert_model_metadata
15
15
  # 来源: https://developers.openai.com/api/docs/models/gpt-5.4(2026-04-12 采集)
16
16
  # https://developers.openai.com/api/docs/models/gpt-5.6-sol(2026-08-20 采集 5.5/5.6)
17
17
  # https://developers.openai.com/api/docs/models/gpt-6-astra(2026-09-04 采集 6-astra/5.6-cyber)
18
+ # https://developers.openai.com/api/docs/models/gpt-6-sol(2026-09-23 采集 6-sol/6-luna)
18
19
  # ============================================================================
19
20
 
20
21
  GPT_MODELS = [
@@ -219,7 +220,7 @@ GPT_MODELS = [
219
220
  },
220
221
  ),
221
222
  # ------------------------------------------------------------------
222
- # GPT-6 Astra 系列(2026-09-04 采集)
223
+ # GPT-6 天体档位系列(2026-09-04 采集 astra)
223
224
  # 来源: https://developers.openai.com/api/docs/models/gpt-6-astra
224
225
  # ------------------------------------------------------------------
225
226
  (
@@ -239,6 +240,45 @@ GPT_MODELS = [
239
240
  "max_tokens": 128_000,
240
241
  },
241
242
  ),
243
+ # ------------------------------------------------------------------
244
+ # GPT-6 Sol / Luna(2026-09-22 发布,2026-09-23 采集)
245
+ # 与 astra 同窗口同工具面;sol 中档主力、luna 低成本档,GPT-6 无 terra 档
246
+ # 来源: https://developers.openai.com/api/docs/models/gpt-6-sol
247
+ # ------------------------------------------------------------------
248
+ (
249
+ "gpt-6-sol",
250
+ {
251
+ "provider": Provider.OPENAI,
252
+ "family": ModelFamily.GPT,
253
+ "version": "6.0",
254
+ "variant": "sol",
255
+ "supports_thinking": True,
256
+ "supports_vision": True,
257
+ "supports_streaming": True,
258
+ "supports_function_calling": True,
259
+ "supports_structured_outputs": True,
260
+ "supports_computer_use": True,
261
+ "context_window": 1_050_000,
262
+ "max_tokens": 128_000,
263
+ },
264
+ ),
265
+ (
266
+ "gpt-6-luna",
267
+ {
268
+ "provider": Provider.OPENAI,
269
+ "family": ModelFamily.GPT,
270
+ "version": "6.0",
271
+ "variant": "luna",
272
+ "supports_thinking": True,
273
+ "supports_vision": True,
274
+ "supports_streaming": True,
275
+ "supports_function_calling": True,
276
+ "supports_structured_outputs": True,
277
+ "supports_computer_use": True,
278
+ "context_window": 1_050_000,
279
+ "max_tokens": 128_000,
280
+ },
281
+ ),
242
282
  ]
243
283
 
244
284
  # ============================================================================
@@ -253,7 +253,7 @@ class TestClaudeMythosClassOrdering:
253
253
  assert LLMeta("claude-mythos-5") > LLMeta("claude-fable-5")
254
254
 
255
255
  def test_mythos5_is_top(self):
256
- """mythos-5 为当前最高 / mythos-5 is the highest among current Claude models"""
256
+ """mythos-5 在所列模型中最高 / mythos-5 is the highest among the models listed"""
257
257
  models = [
258
258
  LLMeta("claude-mythos-5"),
259
259
  LLMeta("claude-fable-5"),
@@ -264,6 +264,57 @@ class TestClaudeMythosClassOrdering:
264
264
  assert max(models).variant == "mythos"
265
265
 
266
266
 
267
+ class TestClaudeOpus55:
268
+ """Claude Opus 5.5 测试(2026-09-22 发布,当前最新) / Claude Opus 5.5 tests (released 2026-09-22)"""
269
+
270
+ def test_specific_model_config(self):
271
+ """验证 claude-opus-5-5 配置 / Validate claude-opus-5-5 config"""
272
+ config = get_specific_model_config("claude-opus-5-5")
273
+ assert config is not None
274
+ version, variant, capabilities = config
275
+ assert version == "5.5"
276
+ assert variant == "opus"
277
+ assert capabilities is not None
278
+ assert capabilities.supports_vision is True
279
+ assert capabilities.supports_thinking is True # 自适应思考常开且不可关闭 / always on, cannot be disabled
280
+ assert capabilities.supports_function_calling is True
281
+ assert capabilities.supports_streaming is True
282
+ assert capabilities.supports_structured_outputs is True
283
+ assert capabilities.supports_computer_use is True
284
+ assert capabilities.max_tokens == 128000
285
+ assert capabilities.context_window == 1000000
286
+
287
+ def test_pattern_match(self):
288
+ """验证 claude-opus-5-5 模式匹配 / Validate claude-opus-5-5 pattern match"""
289
+ matched = match_model_pattern("claude-opus-5-5")
290
+ assert matched is not None
291
+ assert matched["family"] == ModelFamily.CLAUDE
292
+ assert matched["variant"] == "opus"
293
+ assert matched["provider"] == Provider.ANTHROPIC
294
+ assert matched["_from_specific_model"] == "claude-opus-5-5"
295
+
296
+ @pytest.mark.parametrize("model_name", ["claude-opus-5-5-20260921", "claude-opus-5-5@20260921"])
297
+ def test_pattern_with_snapshot(self, model_name: str):
298
+ """验证带 snapshot 的解析(- 与 @ 两种格式,版本号不被吞) / Validate snapshot forms keep version 5.5"""
299
+ meta = LLMeta(model_name)
300
+ assert meta.family == ModelFamily.CLAUDE
301
+ assert meta.version == "5.5"
302
+ assert meta.variant == "opus"
303
+
304
+ def test_not_swallowed_by_opus5(self):
305
+ """claude-opus-5-5 不得被 claude-opus-5 的配置吞掉 / opus-5-5 must not be captured by opus-5
306
+
307
+ 回归保护:opus-5 的子模式在 opus-5-5 之前注册,若 snapshot 宽度匹配不当会退化为 5.0
308
+ Regression guard: opus-5's sub-patterns register before opus-5-5's; a loose snapshot
309
+ width would silently degrade opus-5-5 to version 5.0
310
+ """
311
+ meta = LLMeta("claude-opus-5-5")
312
+ assert meta.version == "5.5"
313
+ assert meta.variant == "opus"
314
+ assert meta.capabilities.context_window == 1000000
315
+ assert meta.capabilities.max_tokens == 128000
316
+
317
+
267
318
  class TestClaudeOpus5:
268
319
  """Claude Opus 5 测试(2026-07-24 GA) / Claude Opus 5 tests"""
269
320
 
@@ -345,6 +396,19 @@ class TestClaude5Ordering:
345
396
  """opus-5 (v5.0) 高于 opus-4-8 (v4.8) / opus-5 outranks opus-4-8 by version"""
346
397
  assert LLMeta("claude-opus-5") > LLMeta("claude-opus-4-8")
347
398
 
399
+ def test_opus55_outranks_opus5(self):
400
+ """opus-5-5 (v5.5) 高于 opus-5 (v5.0) / opus-5-5 outranks opus-5 by version"""
401
+ assert LLMeta("claude-opus-5-5") > LLMeta("claude-opus-5")
402
+ assert LLMeta("claude-opus-5-5") > LLMeta("claude-fable-5-1")
403
+
404
+ def test_opus55_outranks_mythos5_by_version(self):
405
+ """版本优先于档位:opus-5-5 (v5.5) 高于 mythos-5 (v5.0),尽管 mythos 变体档更高
406
+ Version precedes tier: opus-5-5 outranks mythos-5 despite mythos's higher variant tier
407
+ """
408
+ assert LLMeta("claude-opus-5-5") > LLMeta("claude-mythos-5")
409
+ # 同版本 5.0 内档位排序不受影响 / tier ordering within v5.0 is unaffected
410
+ assert LLMeta("claude-mythos-5") > LLMeta("claude-fable-5")
411
+
348
412
  def test_sonnet5_outranks_sonnet46(self):
349
413
  """sonnet-5 (v5.0) 高于 sonnet-4-6 (v4.6) / sonnet-5 outranks sonnet-4-6 by version"""
350
414
  assert LLMeta("claude-sonnet-5") > LLMeta("claude-sonnet-4-6")
@@ -358,7 +422,9 @@ class TestClaude5Ordering:
358
422
  assert LLMeta("claude-fable-5") > LLMeta("claude-opus-5")
359
423
 
360
424
  def test_mythos5_is_top(self):
361
- """mythos-5 仍为当前最高 / mythos-5 remains the highest among current Claude models"""
425
+ """同代 v5.0 内 mythos 档最高(v5.1/v5.5 按版本另行比较)
426
+ mythos is the top tier within the v5.0 generation (v5.1/v5.5 compare by version)
427
+ """
362
428
  models = [
363
429
  LLMeta("claude-mythos-5"),
364
430
  LLMeta("claude-fable-5"),
@@ -209,3 +209,102 @@ def test_deepseek_v4_flash_vision_exp_llmeta() -> None:
209
209
  assert model.variant == "flash-vision-exp"
210
210
  assert model.capabilities.context_window == 1_000_000
211
211
  assert model.capabilities.supports_vision is True
212
+
213
+
214
+ class TestDeepSeekRouting:
215
+ """DeepSeek 旧模型名的路由实况 / Routing reality of DeepSeek legacy names.
216
+
217
+ 官方原文(2026-09-10 changelog):"旧版本模型 V4 Flash 与 V4 Flash Vision Exp 现已下线,
218
+ 出于兼容考虑,模型名 deepseek-v4-flash、deepseek-v4-flash-vision-exp 将被暂时路由到 V4.1 Flash。"
219
+ Official: legacy V4 Flash / V4 Flash Vision Exp are offline; their model names are
220
+ *temporarily* routed to V4.1 Flash.
221
+
222
+ 实探证据(2026-09-28,api.deepseek.com,见 /tmp/probe_deepseek.py):
223
+ - GET /models 仅返回 deepseek-flash 与 deepseek-v4-pro
224
+ - deepseek-v4-flash / deepseek-v4-flash-vision-exp / deepseek-chat / deepseek-reasoner
225
+ 的响应 model 字段均回显 deepseek-flash,且能正确读出白底红方块
226
+ - deepseek-v4-pro 回显自身,带图不报错但静默忽略并编造答案
227
+
228
+ ⚠️ 路由是"暂时"的:官方撤销后本类测试会失败,届时需重新探针并回改配置
229
+ (能力轴跟端点现状,版本轴跟名字血统)。/ The routing is temporary: when the vendor
230
+ removes it these tests fail on purpose, prompting a re-probe and a config rollback.
231
+ """
232
+
233
+ def test_deepseek_flash_canonical_name(self) -> None:
234
+ """deepseek-flash 为官方正式名(无版本号),对应 DeepSeek-V4.1-Flash"""
235
+ from whosellm import LLMeta
236
+
237
+ model = LLMeta("deepseek-flash")
238
+ assert model.provider == Provider.DEEPSEEK
239
+ assert model.family == ModelFamily.DEEPSEEK
240
+ # 名字无版本号 → 按官方模型版本标注 4.1
241
+ assert model.version == "4.1"
242
+ assert model.variant == "flash"
243
+
244
+ caps = model.capabilities
245
+ assert caps.supports_vision is True
246
+ assert caps.supports_thinking is True # 默认开启 / on by default
247
+ assert caps.media_count_limit.get("image") == 600
248
+ assert caps.context_window == 1_000_000
249
+ assert caps.max_tokens == 384_000
250
+
251
+ def test_v4_flash_routed_to_v41_flash(self) -> None:
252
+ """deepseek-v4-flash 已被路由到 V4.1 Flash,视觉能力随之可用"""
253
+ from whosellm import LLMeta
254
+
255
+ model = LLMeta("deepseek-v4-flash")
256
+ # 版本轴仍按名字解析为 4.0 / version still parses from the name (4.0)
257
+ assert model.version == "4.0"
258
+ assert model.variant == "flash"
259
+ # 能力轴跟随实际服务模型 / capabilities follow the serving model
260
+ assert model.capabilities.supports_vision is True
261
+ assert model.capabilities.media_count_limit.get("image") == 600
262
+
263
+ def test_vision_exp_routed_to_v41_flash(self) -> None:
264
+ """deepseek-v4-flash-vision-exp 同样被路由到 V4.1 Flash"""
265
+ from whosellm import LLMeta
266
+
267
+ model = LLMeta("deepseek-v4-flash-vision-exp")
268
+ assert model.version == "4.0"
269
+ assert model.capabilities.supports_vision is True
270
+ assert model.capabilities.media_count_limit.get("image") == 600
271
+
272
+ def test_chat_and_reasoner_routed_have_vision(self) -> None:
273
+ """deepseek-chat / deepseek-reasoner 路由到 flash,同样可读图
274
+
275
+ 两者保留各自的默认思考模式语义(chat 非思考、reasoner 思考),
276
+ 但视觉能力跟随实际服务模型。
277
+ """
278
+ from whosellm import LLMeta
279
+
280
+ chat = LLMeta("deepseek-chat")
281
+ assert chat.capabilities.supports_thinking is False # 默认非思考 / non-thinking by default
282
+ assert chat.capabilities.supports_vision is True
283
+ assert chat.capabilities.media_count_limit.get("image") == 600
284
+
285
+ reasoner = LLMeta("deepseek-reasoner")
286
+ assert reasoner.capabilities.supports_thinking is True
287
+ assert reasoner.capabilities.supports_vision is True
288
+ assert reasoner.capabilities.media_count_limit.get("image") == 600
289
+
290
+ def test_v4_pro_not_routed_stays_text_only(self) -> None:
291
+ """deepseek-v4-pro 未被路由,保持纯文本(带图会静默幻觉,绝不能标 True)
292
+
293
+ 实探:带图请求返回 200,图片被静默忽略,答案系编造;无图对照亦编造;
294
+ 直接追问时模型承认"看不到图片 / [Unsupported Image]"。
295
+ 来源:https://api-docs.deepseek.com/zh-cn/quick_start/pricing(图像理解 不支持)
296
+ """
297
+ from whosellm import LLMeta
298
+
299
+ model = LLMeta("deepseek-v4-pro")
300
+ assert model.version == "4.0"
301
+ assert model.variant == "pro"
302
+ assert model.capabilities.supports_vision is False
303
+ assert model.capabilities.media_count_limit.get("image") == 0 # 键缺失视同不支持
304
+
305
+ def test_version_axis_orders_flash_above_pro(self) -> None:
306
+ """版本优先于档位:V4.1 Flash > V4 Pro(与官方"全面超越 V4 Pro"表述一致)"""
307
+ from whosellm import LLMeta
308
+
309
+ assert LLMeta("deepseek-flash") > LLMeta("deepseek-v4-pro")
310
+ assert LLMeta("deepseek-v4-pro") > LLMeta("deepseek-v4-flash")
@@ -0,0 +1,166 @@
1
+ # filename: test_gpt6.py
2
+ # @Time : 2026/09/04
3
+ # @Author : JQQ
4
+ # @Email : jiaqia@qknode.com
5
+ # @Software: PyCharm
6
+ """
7
+ GPT-6 模型家族测试 / GPT-6 model family tests
8
+ """
9
+
10
+ from datetime import date
11
+
12
+ from whosellm import LLMeta
13
+ from whosellm.models.base import ModelFamily
14
+
15
+ # ============================================================================
16
+ # GPT-6 Astra 测试 / GPT-6 Astra Tests
17
+ # ============================================================================
18
+
19
+
20
+ def test_gpt6_astra_model():
21
+ """测试GPT-6 Astra模型(2026-09-04 发布,旗舰) / Test GPT-6 Astra model"""
22
+ m = LLMeta("gpt-6-astra")
23
+ assert m.family == ModelFamily.GPT
24
+ assert m.version == "6.0"
25
+ assert m.variant == "astra"
26
+ assert m.capabilities.context_window == 1_050_000
27
+ assert m.capabilities.max_tokens == 128_000
28
+ assert m.capabilities.supports_thinking is True
29
+ assert m.capabilities.supports_vision is True
30
+ assert m.capabilities.supports_function_calling is True
31
+ assert m.capabilities.supports_streaming is True
32
+ assert m.capabilities.supports_structured_outputs is True
33
+ assert m.capabilities.supports_web_search is True
34
+ assert m.capabilities.supports_file_search is True
35
+ assert m.capabilities.supports_image_generation is True
36
+ assert m.capabilities.supports_code_interpreter is True
37
+ assert m.capabilities.supports_computer_use is True
38
+ assert m.capabilities.supports_fine_tuning is False
39
+
40
+
41
+ def test_gpt6_astra_snapshot():
42
+ """测试带日期后缀的GPT-6 Astra / Test GPT-6 Astra with date suffix"""
43
+ m = LLMeta("gpt-6-astra-2026-09-04")
44
+ assert m.family == ModelFamily.GPT
45
+ assert m.version == "6.0"
46
+ assert m.variant == "astra"
47
+ assert m.release_date == date(2026, 9, 4)
48
+
49
+
50
+ def test_gpt6_sol_model():
51
+ """测试GPT-6 Sol模型(2026-09-22 发布,中档主力) / Test GPT-6 Sol model"""
52
+ m = LLMeta("gpt-6-sol")
53
+ assert m.family == ModelFamily.GPT
54
+ assert m.version == "6.0"
55
+ assert m.variant == "sol"
56
+ assert m.capabilities.context_window == 1_050_000
57
+ assert m.capabilities.max_tokens == 128_000
58
+ assert m.capabilities.supports_thinking is True
59
+ assert m.capabilities.supports_vision is True
60
+ assert m.capabilities.supports_function_calling is True
61
+ assert m.capabilities.supports_streaming is True
62
+ assert m.capabilities.supports_structured_outputs is True
63
+ assert m.capabilities.supports_web_search is True
64
+ assert m.capabilities.supports_file_search is True
65
+ assert m.capabilities.supports_image_generation is True
66
+ assert m.capabilities.supports_code_interpreter is True
67
+ assert m.capabilities.supports_computer_use is True
68
+ assert m.capabilities.supports_fine_tuning is False
69
+
70
+
71
+ def test_gpt6_luna_model():
72
+ """测试GPT-6 Luna模型(2026-09-22 发布,低成本档) / Test GPT-6 Luna model"""
73
+ m = LLMeta("gpt-6-luna")
74
+ assert m.family == ModelFamily.GPT
75
+ assert m.version == "6.0"
76
+ assert m.variant == "luna"
77
+ # 与 sol/astra 同窗口同工具面 / Same window and tool surface as sol/astra
78
+ assert m.capabilities.context_window == 1_050_000
79
+ assert m.capabilities.max_tokens == 128_000
80
+ assert m.capabilities.supports_thinking is True
81
+ assert m.capabilities.supports_vision is True
82
+ assert m.capabilities.supports_function_calling is True
83
+ assert m.capabilities.supports_streaming is True
84
+ assert m.capabilities.supports_structured_outputs is True
85
+ assert m.capabilities.supports_web_search is True
86
+ assert m.capabilities.supports_file_search is True
87
+ assert m.capabilities.supports_image_generation is True
88
+ assert m.capabilities.supports_code_interpreter is True
89
+ assert m.capabilities.supports_computer_use is True
90
+ assert m.capabilities.supports_fine_tuning is False
91
+
92
+
93
+ def test_gpt6_sol_snapshot():
94
+ """测试带日期后缀的GPT-6 Sol / Test GPT-6 Sol with date suffix"""
95
+ m = LLMeta("gpt-6-sol-2026-09-22")
96
+ assert m.family == ModelFamily.GPT
97
+ assert m.version == "6.0"
98
+ assert m.variant == "sol"
99
+ assert m.release_date == date(2026, 9, 22)
100
+
101
+
102
+ def test_gpt6_luna_snapshot():
103
+ """测试带日期后缀的GPT-6 Luna / Test GPT-6 Luna with date suffix"""
104
+ m = LLMeta("gpt-6-luna-2026-09-22")
105
+ assert m.family == ModelFamily.GPT
106
+ assert m.version == "6.0"
107
+ assert m.variant == "luna"
108
+ assert m.release_date == date(2026, 9, 22)
109
+
110
+
111
+ def test_gpt6_media_count_limit():
112
+ """测试GPT-6三档图片数量上限(版本级继承) / Test GPT-6 image count cap (version-level inherited)
113
+
114
+ 官方文档:单请求最多 1500 张图片
115
+ Per docs: up to 1,500 images per request
116
+ """
117
+ for model_name in ("gpt-6-astra", "gpt-6-sol", "gpt-6-luna"):
118
+ mcl = LLMeta(model_name).capabilities.media_count_limit
119
+ assert mcl.get("image") == 1500, f"{model_name}: {mcl.get('image')}"
120
+ assert mcl.count_mode == "per_type"
121
+
122
+
123
+ def test_gpt6_parent_pattern_inheritance():
124
+ """测试 parent pattern 匹配的 GPT-6 变体继承版本级 capabilities
125
+ Test parent pattern matched GPT-6 variant inherits version-level capabilities
126
+ """
127
+ m = LLMeta("gpt-6-turbo")
128
+
129
+ assert m.family == ModelFamily.GPT
130
+ assert m.version == "6.0"
131
+ assert m.variant == "turbo"
132
+ # 应继承 GPT-6.0 版本级 caps,而非 family default
133
+ assert m.capabilities.context_window == 1_050_000
134
+ assert m.capabilities.supports_thinking is True
135
+ assert m.capabilities.supports_computer_use is True
136
+
137
+
138
+ def test_gpt6_version_ordering():
139
+ """测试GPT-6版本排序 / Test GPT-6 version ordering"""
140
+ v56 = LLMeta("gpt-5.6")
141
+ v60 = LLMeta("gpt-6-astra")
142
+
143
+ assert v56 < v60
144
+
145
+
146
+ def test_gpt6_tier_ordering():
147
+ """测试GPT-6天体档位优先级 / Test GPT-6 celestial tier priority
148
+
149
+ 官方档位:luna(0) 低成本 < sol(1) 中档主力 < astra(5) 旗舰
150
+ Official tiers: luna(0) < sol(1) < astra(5)
151
+ """
152
+ luna = LLMeta("gpt-6-luna")
153
+ sol = LLMeta("gpt-6-sol")
154
+ astra = LLMeta("gpt-6-astra")
155
+
156
+ assert luna < sol < astra
157
+
158
+
159
+ def test_gpt6_cross_version_ordering():
160
+ """测试GPT-6与上一代跨版本排序 / Test GPT-6 cross-version ordering vs previous gen
161
+
162
+ 版本优先于档位:GPT-6 最低档的 luna 仍高于 GPT-5.6 最高档的 sol
163
+ Version precedes tier: GPT-6 luna (lowest tier) still outranks GPT-5.6 sol
164
+ """
165
+ assert LLMeta("gpt-5.6-sol") < LLMeta("gpt-6-luna")
166
+ assert LLMeta("gpt-5.5-pro") < LLMeta("gpt-6-luna")