whosellm 0.2.2__tar.gz → 0.2.4__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.2 → whosellm-0.2.4}/.bumpversion.toml +7 -1
- whosellm-0.2.4/.claude/settings.json +5 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/CHANGELOG.md +22 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/PKG-INFO +1 -1
- {whosellm-0.2.2 → whosellm-0.2.4}/pyproject.toml +1 -1
- {whosellm-0.2.2 → whosellm-0.2.4}/tests/models/families/test_anthropic.py +88 -0
- whosellm-0.2.4/tests/test_claude_4x_versions.py +124 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/uv.lock +1 -1
- {whosellm-0.2.2 → whosellm-0.2.4}/whosellm/__init__.py +1 -1
- {whosellm-0.2.2 → whosellm-0.2.4}/whosellm/models/config.py +5 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/whosellm/models/families/anthropic.py +134 -17
- {whosellm-0.2.2 → whosellm-0.2.4}/whosellm/models/patterns.py +14 -1
- {whosellm-0.2.2 → whosellm-0.2.4}/.claude/commands/interview.md +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/.claude/projects/-Users-jqq-PycharmProjects-llmeta/memory/MEMORY.md +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/.claude/projects/-Users-jqq-PycharmProjects-llmeta/memory/feedback_use_uv.md +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/.claude/skills/code-review/SKILL.md +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/.claude/skills/create-skill/SKILL.md +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/.claude/skills/e2e-metadata/SKILL.md +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/.claude/skills/evolve/SKILL.md +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/.claude/skills/fix-review/SKILL.md +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/.claude/skills/release/SKILL.md +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/.claude/skills/review-provider-model/SKILL.md +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/.claude/skills/review-provider-model/providers/alibaba.md +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/.claude/skills/review-provider-model/providers/anthropic.md +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/.claude/skills/review-provider-model/providers/deepseek.md +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/.claude/skills/review-provider-model/providers/gemini.md +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/.claude/skills/review-provider-model/providers/openai.md +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/.claude/skills/review-provider-model/providers/others.md +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/.claude/skills/review-provider-model/providers/vidu.md +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/.claude/skills/review-provider-model/providers/zhipu.md +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/.claude/skills/update-provider-model/SKILL.md +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/.claude/skills/update-provider-model/testing.md +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/.coveragerc +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/.github/workflows/publish.yml +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/.github/workflows/tests.yml +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/.gitignore +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/.windsurf/workflows/addmodel.md +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/.windsurf/workflows/arch.md +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/.windsurf/workflows/testllmeta.md +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/CLAUDE.md +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/LICENSE +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/README.md +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/docs/add_new_model_family.md +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/docs/refactor_proposal.md +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/docs/spec_model_family_redesign.md +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/examples/advanced_usage.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/examples/basic_usage.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/mypy.ini +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/pytest.ini +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/ruff.toml +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/tests/__init__.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/tests/e2e/__init__.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/tests/e2e/conftest.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/tests/e2e/test_anthropic.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/tests/e2e/test_google.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/tests/e2e/test_openai.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/tests/e2e/test_zhipu.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/tests/models/__init__.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/tests/models/families/__init__.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/tests/models/families/test_deepseek.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/tests/models/families/test_deepseek_tencent.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/tests/models/families/test_gemini.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/tests/models/families/test_glm45.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/tests/models/families/test_glm45v.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/tests/models/families/test_glm46.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/tests/models/families/test_glm46v.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/tests/models/families/test_glm5.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/tests/models/families/test_gpt3_5.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/tests/models/families/test_gpt4_1.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/tests/models/families/test_gpt4o.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/tests/models/families/test_gpt5.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/tests/models/families/test_o1.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/tests/models/families/test_o3.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/tests/models/families/test_o4.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/tests/models/families/test_qwen.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/tests/models/families/test_qwen3_vl_models.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/tests/models/families/test_qwen_ollama.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/tests/models/families/test_qwen_plus.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/tests/test_auto_register.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/tests/test_llmeta.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/tests/test_model_version.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/tests/test_provider.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/tests/test_registry_merge.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/tests/test_specific_patterns.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/tests/test_variant_priority_config.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/whosellm/capabilities.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/whosellm/model_version.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/whosellm/models/__init__.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/whosellm/models/base.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/whosellm/models/dynamic_enum.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/whosellm/models/families/__init__.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/whosellm/models/families/alibaba.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/whosellm/models/families/deepseek/__init__.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/whosellm/models/families/deepseek/deepseek_official.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/whosellm/models/families/deepseek/tencent.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/whosellm/models/families/gemini.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/whosellm/models/families/openai/__init__.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/whosellm/models/families/openai/openai_gpt_3_5.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/whosellm/models/families/openai/openai_gpt_4.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/whosellm/models/families/openai/openai_gpt_4_1.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/whosellm/models/families/openai/openai_gpt_4o.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/whosellm/models/families/openai/openai_gpt_5.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/whosellm/models/families/openai/openai_gpt_5_1.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/whosellm/models/families/openai/openai_gpt_5_2.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/whosellm/models/families/openai/openai_gpt_5_3.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/whosellm/models/families/openai/openai_gpt_5_4.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/whosellm/models/families/openai/openai_o1.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/whosellm/models/families/openai/openai_o3.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/whosellm/models/families/openai/openai_o4.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/whosellm/models/families/others.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/whosellm/models/families/vidu.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/whosellm/models/families/zhipu.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/whosellm/models/registry.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/whosellm/provider.py +0 -0
- {whosellm-0.2.2 → whosellm-0.2.4}/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.
|
|
5
|
+
current_version = "0.2.4"
|
|
6
6
|
parse = """(?x)
|
|
7
7
|
(?P<major>0|[1-9]\\d*)\\.
|
|
8
8
|
(?P<minor>0|[1-9]\\d*)\\.
|
|
@@ -45,6 +45,12 @@ filename = "whosellm/__init__.py"
|
|
|
45
45
|
search = "__version__ = \"{current_version}\""
|
|
46
46
|
replace = "__version__ = \"{new_version}\""
|
|
47
47
|
|
|
48
|
+
# uv.lock 中项目自身的版本号(锚定到 whosellm 包块,避免误改其他依赖的 version)
|
|
49
|
+
[[tool.bumpversion.files]]
|
|
50
|
+
filename = "uv.lock"
|
|
51
|
+
search = "name = \"whosellm\"\nversion = \"{current_version}\""
|
|
52
|
+
replace = "name = \"whosellm\"\nversion = \"{new_version}\""
|
|
53
|
+
|
|
48
54
|
[tool.bumpversion.parts.pre_l]
|
|
49
55
|
values = ["a", "b", "rc"]
|
|
50
56
|
|
|
@@ -2,6 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
|
|
5
|
+
## [0.2.3] - Unreleased
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- 新增 Claude Mythos-class 模型配置(2026-06-09 发布,官方规格核实):`claude-fable-5`(公开版)、`claude-mythos-5`(Project Glasswing 受邀版,能力同 Fable 5)。均为 1M 上下文、128K 最大输出、结构化输出 + computer use,自适应思考常开。引入全新顶级变体层级,优先级高于 opus(`haiku=0 < sonnet=3 < opus=5 < fable=6 < mythos=7`),因此 `claude-fable-5 > claude-opus-4-8`(版本 5.0 > 4.8)。新增父模式 `claude-{variant}-{major}@{snapshot}` 以支持单版本号模型的 `@` 日期快照形式
|
|
9
|
+
- 新增 Claude Opus 4.x 模型配置(官方规格核实):`claude-opus-4-8`、`claude-opus-4-7`(1M 上下文,128K 最大输出,结构化输出 + computer use)、`claude-opus-4-5`(200K 上下文,64K 最大输出)
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
- 修复 Claude 4.x 别名版本解析回退 bug:`claude-opus-4-5` / `4-7` / `4-8` 等**无日期别名**此前被 `claude-opus-4-{snapshot:8d}` 模式的"最大宽度"语义把单个数字 minor 误吞为 snapshot,导致版本回退为 `4.0`。改用精确 8 位的 `{snapshot:snapshot}` 自定义类型(`patterns._convert_snapshot`)后,单字段 snapshot 不再吞掉 minor,现存与未来未注册别名均能解析出正确版本(如 `4.8`),版本比较稳定可用
|
|
13
|
+
|
|
14
|
+
## [0.2.2] - 2026-04-24
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
- DeepSeek V4 系列模型支持:`deepseek-v4-flash`、`deepseek-v4-pro`(1M 上下文,384K 最大输出,支持思考/非思考双模式)
|
|
18
|
+
- DeepSeek 官方 Provider 支持版本号命名模式(`deepseek-v{major}.{minor}-{variant}` 等),V4 起可通过版本号直接调用
|
|
19
|
+
- 腾讯云 DeepSeek-V3.2(GA 版,685B MoE,稀疏注意力)
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
- `deepseek-chat` / `deepseek-reasoner` 能力基线升级为 V4-flash 非思考/思考模式别名(1M 上下文,384K 输出)
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
- 腾讯云 `deepseek-r1-0528` 的 `supports_function_calling` 修正为 `False`(官方文档明确列为不支持)
|
|
26
|
+
|
|
5
27
|
## [Unreleased]
|
|
6
28
|
|
|
7
29
|
### Added
|
|
@@ -5,6 +5,7 @@ Anthropic Claude 模型家族测试 / Anthropic Claude model family tests
|
|
|
5
5
|
|
|
6
6
|
import pytest
|
|
7
7
|
|
|
8
|
+
from whosellm.model_version import LLMeta
|
|
8
9
|
from whosellm.models.base import ModelFamily
|
|
9
10
|
from whosellm.models.registry import get_default_capabilities, get_specific_model_config, match_model_pattern
|
|
10
11
|
from whosellm.provider import Provider
|
|
@@ -108,6 +109,93 @@ class TestClaudeSonnet46:
|
|
|
108
109
|
assert matched["_from_specific_model"] == "claude-sonnet-4-6"
|
|
109
110
|
|
|
110
111
|
|
|
112
|
+
class TestClaudeFable5:
|
|
113
|
+
"""Claude Fable 5 测试(Mythos-class,2026-06-09 GA) / Claude Fable 5 tests"""
|
|
114
|
+
|
|
115
|
+
def test_specific_model_config(self):
|
|
116
|
+
"""验证 claude-fable-5 配置 / Validate claude-fable-5 config"""
|
|
117
|
+
config = get_specific_model_config("claude-fable-5")
|
|
118
|
+
assert config is not None
|
|
119
|
+
version, variant, capabilities = config
|
|
120
|
+
assert version == "5.0"
|
|
121
|
+
assert variant == "fable"
|
|
122
|
+
assert capabilities is not None
|
|
123
|
+
assert capabilities.supports_vision is True
|
|
124
|
+
assert capabilities.supports_thinking is True
|
|
125
|
+
assert capabilities.supports_function_calling is True
|
|
126
|
+
assert capabilities.supports_streaming is True
|
|
127
|
+
assert capabilities.supports_structured_outputs is True
|
|
128
|
+
assert capabilities.supports_computer_use is True
|
|
129
|
+
assert capabilities.max_tokens == 128000
|
|
130
|
+
assert capabilities.context_window == 1000000
|
|
131
|
+
|
|
132
|
+
def test_pattern_match(self):
|
|
133
|
+
"""验证 claude-fable-5 模式匹配 / Validate claude-fable-5 pattern match"""
|
|
134
|
+
matched = match_model_pattern("claude-fable-5")
|
|
135
|
+
assert matched is not None
|
|
136
|
+
assert matched["family"] == ModelFamily.CLAUDE
|
|
137
|
+
assert matched["variant"] == "fable"
|
|
138
|
+
assert matched["provider"] == Provider.ANTHROPIC
|
|
139
|
+
|
|
140
|
+
@pytest.mark.parametrize("model_name", ["claude-fable-5-20260609", "claude-fable-5@20260609"])
|
|
141
|
+
def test_pattern_with_snapshot(self, model_name: str):
|
|
142
|
+
"""验证带 snapshot 的解析(- 与 @ 两种格式,版本号不被吞) / Validate snapshot forms keep version 5.0"""
|
|
143
|
+
meta = LLMeta(model_name)
|
|
144
|
+
assert meta.family == ModelFamily.CLAUDE
|
|
145
|
+
assert meta.version == "5.0"
|
|
146
|
+
assert meta.variant == "fable"
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
class TestClaudeMythos5:
|
|
150
|
+
"""Claude Mythos 5 测试(Glasswing 受邀版) / Claude Mythos 5 tests"""
|
|
151
|
+
|
|
152
|
+
def test_specific_model_config(self):
|
|
153
|
+
"""验证 claude-mythos-5 配置 / Validate claude-mythos-5 config"""
|
|
154
|
+
config = get_specific_model_config("claude-mythos-5")
|
|
155
|
+
assert config is not None
|
|
156
|
+
version, variant, capabilities = config
|
|
157
|
+
assert version == "5.0"
|
|
158
|
+
assert variant == "mythos"
|
|
159
|
+
assert capabilities is not None
|
|
160
|
+
assert capabilities.supports_vision is True
|
|
161
|
+
assert capabilities.supports_thinking is True
|
|
162
|
+
assert capabilities.supports_structured_outputs is True
|
|
163
|
+
assert capabilities.supports_computer_use is True
|
|
164
|
+
assert capabilities.max_tokens == 128000
|
|
165
|
+
assert capabilities.context_window == 1000000
|
|
166
|
+
|
|
167
|
+
def test_pattern_match(self):
|
|
168
|
+
"""验证 claude-mythos-5 模式匹配 / Validate claude-mythos-5 pattern match"""
|
|
169
|
+
matched = match_model_pattern("claude-mythos-5")
|
|
170
|
+
assert matched is not None
|
|
171
|
+
assert matched["family"] == ModelFamily.CLAUDE
|
|
172
|
+
assert matched["variant"] == "mythos"
|
|
173
|
+
assert matched["provider"] == Provider.ANTHROPIC
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
class TestClaudeMythosClassOrdering:
|
|
177
|
+
"""Mythos-class 版本比较:mythos > fable > opus > sonnet / Mythos-class ordering"""
|
|
178
|
+
|
|
179
|
+
def test_fable5_outranks_opus48(self):
|
|
180
|
+
"""fable-5 (v5.0) 高于 opus-4-8 (v4.8) / fable-5 outranks opus-4-8 by version"""
|
|
181
|
+
assert LLMeta("claude-fable-5") > LLMeta("claude-opus-4-8")
|
|
182
|
+
|
|
183
|
+
def test_mythos5_outranks_fable5(self):
|
|
184
|
+
"""同版本下 mythos 变体优先级高于 fable / mythos outranks fable on variant priority"""
|
|
185
|
+
assert LLMeta("claude-mythos-5") > LLMeta("claude-fable-5")
|
|
186
|
+
|
|
187
|
+
def test_mythos5_is_top(self):
|
|
188
|
+
"""mythos-5 为当前最高 / mythos-5 is the highest among current Claude models"""
|
|
189
|
+
models = [
|
|
190
|
+
LLMeta("claude-mythos-5"),
|
|
191
|
+
LLMeta("claude-fable-5"),
|
|
192
|
+
LLMeta("claude-opus-4-8"),
|
|
193
|
+
LLMeta("claude-sonnet-4-6"),
|
|
194
|
+
LLMeta("claude-haiku-4-5"),
|
|
195
|
+
]
|
|
196
|
+
assert max(models).variant == "mythos"
|
|
197
|
+
|
|
198
|
+
|
|
111
199
|
class TestClaudeExistingModels:
|
|
112
200
|
"""验证现有模型未受影响 / Validate existing models are not affected"""
|
|
113
201
|
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# filename: test_claude_4x_versions.py
|
|
2
|
+
"""
|
|
3
|
+
回归测试:Claude 4.x 别名版本解析 + 版本比较稳定性
|
|
4
|
+
Regression tests: Claude 4.x alias version parsing + version-comparison stability
|
|
5
|
+
|
|
6
|
+
背景 / Context:
|
|
7
|
+
TFRobotV2 需要门控 Claude 4.6+ 移除的 assistant prefill。决定**用版本比较**判断
|
|
8
|
+
(`family == CLAUDE and parse_version(version) >= (4, 6)`),而非依赖能力位。
|
|
9
|
+
因此 whosellm 必须保证 4.x 别名(含未注册的新别名)版本解析正确、比较稳定。
|
|
10
|
+
|
|
11
|
+
根因:parse 的 ``{snapshot:8d}`` 是最大宽度,会把 ``claude-opus-4-5`` 的 ``5`` 当 snapshot,
|
|
12
|
+
使 minor 被吞、版本回退 4.0。修复后通过精确 8 位 snapshot 自定义类型解决。
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import pytest
|
|
16
|
+
|
|
17
|
+
from whosellm import LLMeta
|
|
18
|
+
from whosellm.models.base import ModelFamily, parse_version
|
|
19
|
+
|
|
20
|
+
# 官方 prefill 真值表(platform.claude.com,逐字核实):不支持 prefill 的恰为版本 >= 4.6
|
|
21
|
+
# Authoritative prefill truth table: models WITHOUT prefill are exactly version >= 4.6
|
|
22
|
+
_PREFILL_REMOVED = {
|
|
23
|
+
"claude-opus-4-8",
|
|
24
|
+
"claude-opus-4-7",
|
|
25
|
+
"claude-opus-4-6",
|
|
26
|
+
"claude-sonnet-4-6",
|
|
27
|
+
}
|
|
28
|
+
_PREFILL_SUPPORTED = {
|
|
29
|
+
"claude-opus-4-5",
|
|
30
|
+
"claude-opus-4-1",
|
|
31
|
+
"claude-opus-4-0",
|
|
32
|
+
"claude-sonnet-4-5",
|
|
33
|
+
"claude-sonnet-4-0",
|
|
34
|
+
"claude-haiku-4-5",
|
|
35
|
+
"claude-3-7-sonnet",
|
|
36
|
+
"claude-3-5-haiku",
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class TestClaude4xAliasVersionParsing:
|
|
41
|
+
"""未注册/新增的 Claude 4.x 别名应解析出正确版本,而非回退 family 默认 4.0"""
|
|
42
|
+
|
|
43
|
+
@pytest.mark.parametrize(
|
|
44
|
+
"model_name,expected_version,expected_variant",
|
|
45
|
+
[
|
|
46
|
+
("claude-opus-4-8", "4.8", "opus"),
|
|
47
|
+
("claude-opus-4-7", "4.7", "opus"),
|
|
48
|
+
("claude-opus-4-6", "4.6", "opus"),
|
|
49
|
+
("claude-opus-4-5", "4.5", "opus"),
|
|
50
|
+
("claude-opus-4-5-20251101", "4.5", "opus"),
|
|
51
|
+
("claude-haiku-4-5-20251001", "4.5", "haiku"),
|
|
52
|
+
("claude-sonnet-4-6", "4.6", "sonnet"),
|
|
53
|
+
("claude-sonnet-4-5", "4.5", "sonnet"),
|
|
54
|
+
],
|
|
55
|
+
)
|
|
56
|
+
def test_alias_version(self, model_name: str, expected_version: str, expected_variant: str):
|
|
57
|
+
meta = LLMeta(model_name)
|
|
58
|
+
assert meta.version == expected_version, f"{model_name} 解析出错误版本 {meta.version}"
|
|
59
|
+
assert meta.variant == expected_variant
|
|
60
|
+
assert meta.family == ModelFamily.CLAUDE
|
|
61
|
+
|
|
62
|
+
def test_dated_4_0_snapshot_still_resolves_to_4_0(self):
|
|
63
|
+
"""带 8 位日期的原始 4.0 snapshot 仍应识别为 4.0,不被精确宽度修复破坏"""
|
|
64
|
+
meta = LLMeta("claude-opus-4-20250514")
|
|
65
|
+
assert meta.version == "4.0"
|
|
66
|
+
assert meta.variant == "opus"
|
|
67
|
+
|
|
68
|
+
def test_short_digit_not_swallowed_as_snapshot(self):
|
|
69
|
+
"""根因回归:单个数字 minor 不得被 {snapshot} 吞掉"""
|
|
70
|
+
assert LLMeta("claude-opus-4-5").version == "4.5"
|
|
71
|
+
assert LLMeta("claude-opus-4-7").version == "4.7"
|
|
72
|
+
assert LLMeta("claude-opus-4-8").version == "4.8"
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class TestClaude4xCapabilities:
|
|
76
|
+
"""新增模型应带官方真实能力(非臆测/镜像)"""
|
|
77
|
+
|
|
78
|
+
@pytest.mark.parametrize(
|
|
79
|
+
"model_name,ctx,max_out",
|
|
80
|
+
[
|
|
81
|
+
("claude-opus-4-8", 1000000, 128000),
|
|
82
|
+
("claude-opus-4-7", 1000000, 128000),
|
|
83
|
+
("claude-opus-4-5", 200000, 64000),
|
|
84
|
+
],
|
|
85
|
+
)
|
|
86
|
+
def test_new_model_specs(self, model_name: str, ctx: int, max_out: int):
|
|
87
|
+
caps = LLMeta(model_name).capabilities
|
|
88
|
+
assert caps.context_window == ctx
|
|
89
|
+
assert caps.max_tokens == max_out
|
|
90
|
+
assert caps.supports_structured_outputs is True
|
|
91
|
+
assert caps.supports_computer_use is True
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class TestClaude4xVersionComparison:
|
|
95
|
+
"""版本比较稳定可用——TFRobotV2 门控的基石"""
|
|
96
|
+
|
|
97
|
+
def test_monotonic_opus_chain(self):
|
|
98
|
+
assert LLMeta("claude-opus-4-5") < LLMeta("claude-opus-4-6")
|
|
99
|
+
assert LLMeta("claude-opus-4-6") < LLMeta("claude-opus-4-7")
|
|
100
|
+
assert LLMeta("claude-opus-4-7") < LLMeta("claude-opus-4-8")
|
|
101
|
+
|
|
102
|
+
def test_cross_minor(self):
|
|
103
|
+
assert LLMeta("claude-opus-4-1") < LLMeta("claude-opus-4-6")
|
|
104
|
+
assert LLMeta("claude-opus-4-0") < LLMeta("claude-opus-4-5")
|
|
105
|
+
|
|
106
|
+
def test_dated_alias_equivalent_version(self):
|
|
107
|
+
"""带日期与不带日期解析出相同版本元组"""
|
|
108
|
+
assert LLMeta("claude-opus-4-5").version == LLMeta("claude-opus-4-5-20251101").version
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class TestVersionGateMatchesPrefillTruth:
|
|
112
|
+
"""关键:纯版本门控 `parse_version(version) >= (4, 6)` 的真假必须与官方 prefill 真值表完全一致"""
|
|
113
|
+
|
|
114
|
+
@pytest.mark.parametrize("model_name", sorted(_PREFILL_REMOVED))
|
|
115
|
+
def test_no_prefill_models_are_ge_4_6(self, model_name: str):
|
|
116
|
+
meta = LLMeta(model_name)
|
|
117
|
+
assert meta.family == ModelFamily.CLAUDE
|
|
118
|
+
assert parse_version(meta.version) >= (4, 6), f"{model_name} 应被版本门控判为不支持 prefill"
|
|
119
|
+
|
|
120
|
+
@pytest.mark.parametrize("model_name", sorted(_PREFILL_SUPPORTED))
|
|
121
|
+
def test_prefill_models_are_lt_4_6(self, model_name: str):
|
|
122
|
+
meta = LLMeta(model_name)
|
|
123
|
+
assert meta.family == ModelFamily.CLAUDE
|
|
124
|
+
assert parse_version(meta.version) < (4, 6), f"{model_name} 应被版本门控判为支持 prefill"
|
|
@@ -189,6 +189,11 @@ class ModelFamilyConfig:
|
|
|
189
189
|
else:
|
|
190
190
|
type_spec = ""
|
|
191
191
|
|
|
192
|
+
# 自定义 snapshot 类型要求精确 8 位(见 patterns._convert_snapshot)
|
|
193
|
+
# Custom snapshot type requires exactly 8 digits (see patterns._convert_snapshot)
|
|
194
|
+
if type_spec == "snapshot":
|
|
195
|
+
return "20240101"
|
|
196
|
+
|
|
192
197
|
if type_spec.endswith("d"):
|
|
193
198
|
width_str = type_spec[:-1].strip()
|
|
194
199
|
width = int(width_str) if width_str.isdigit() else 1
|
|
@@ -23,14 +23,15 @@ CLAUDE = ModelFamilyConfig(
|
|
|
23
23
|
variant_default="sonnet",
|
|
24
24
|
variant_priority_default=(3,), # sonnet 的默认优先级 / default priority for sonnet
|
|
25
25
|
patterns=[
|
|
26
|
-
"claude-{variant:variant}-{major:d}-{minor:d}@{snapshot:
|
|
27
|
-
"claude-{variant:variant}-{major:d}-{minor:d}-{snapshot:
|
|
26
|
+
"claude-{variant:variant}-{major:d}-{minor:d}@{snapshot:snapshot}",
|
|
27
|
+
"claude-{variant:variant}-{major:d}-{minor:d}-{snapshot:snapshot}",
|
|
28
28
|
"claude-{variant:variant}-{major:d}-{minor:d}",
|
|
29
|
-
"claude-{variant:variant}-{major:d}-{snapshot:
|
|
29
|
+
"claude-{variant:variant}-{major:d}-{snapshot:snapshot}",
|
|
30
|
+
"claude-{variant:variant}-{major:d}@{snapshot:snapshot}",
|
|
30
31
|
"claude-{variant:variant}-{major:d}",
|
|
31
|
-
"claude-{major:d}-{minor:d}-{variant:variant}-{snapshot:
|
|
32
|
+
"claude-{major:d}-{minor:d}-{variant:variant}-{snapshot:snapshot}",
|
|
32
33
|
"claude-{major:d}-{minor:d}-{variant:variant}",
|
|
33
|
-
"claude-{major:d}-{variant:variant}-{snapshot:
|
|
34
|
+
"claude-{major:d}-{variant:variant}-{snapshot:snapshot}",
|
|
34
35
|
"claude-{major:d}-{variant:variant}",
|
|
35
36
|
],
|
|
36
37
|
capabilities=ModelCapabilities(
|
|
@@ -42,6 +43,89 @@ CLAUDE = ModelFamilyConfig(
|
|
|
42
43
|
context_window=200000,
|
|
43
44
|
),
|
|
44
45
|
specific_models={
|
|
46
|
+
# Mythos-class(2026-06-09 发布):全新顶级层级,优先级高于 opus
|
|
47
|
+
# Mythos-class (released 2026-06-09): new top tier, ranks above opus
|
|
48
|
+
# Fable 5 为公开版,Mythos 5 为 Glasswing 受邀版(能力相同,去掉安全分类器)
|
|
49
|
+
"claude-fable-5": SpecificModelConfig(
|
|
50
|
+
version_default="5.0",
|
|
51
|
+
variant_default="fable",
|
|
52
|
+
variant_priority=(6,),
|
|
53
|
+
capabilities=ModelCapabilities(
|
|
54
|
+
supports_vision=True,
|
|
55
|
+
supports_thinking=True, # 自适应思考常开 / adaptive thinking always on
|
|
56
|
+
supports_function_calling=True,
|
|
57
|
+
supports_streaming=True,
|
|
58
|
+
supports_structured_outputs=True,
|
|
59
|
+
supports_computer_use=True,
|
|
60
|
+
max_tokens=128000,
|
|
61
|
+
context_window=1000000,
|
|
62
|
+
),
|
|
63
|
+
patterns=[
|
|
64
|
+
"claude-fable-5-{snapshot:snapshot}",
|
|
65
|
+
"claude-fable-5",
|
|
66
|
+
"claude-fable-5@{snapshot:snapshot}",
|
|
67
|
+
],
|
|
68
|
+
),
|
|
69
|
+
"claude-mythos-5": SpecificModelConfig(
|
|
70
|
+
version_default="5.0",
|
|
71
|
+
variant_default="mythos",
|
|
72
|
+
variant_priority=(7,),
|
|
73
|
+
capabilities=ModelCapabilities(
|
|
74
|
+
supports_vision=True,
|
|
75
|
+
supports_thinking=True, # 自适应思考常开 / adaptive thinking always on
|
|
76
|
+
supports_function_calling=True,
|
|
77
|
+
supports_streaming=True,
|
|
78
|
+
supports_structured_outputs=True,
|
|
79
|
+
supports_computer_use=True,
|
|
80
|
+
max_tokens=128000,
|
|
81
|
+
context_window=1000000,
|
|
82
|
+
),
|
|
83
|
+
patterns=[
|
|
84
|
+
"claude-mythos-5-{snapshot:snapshot}",
|
|
85
|
+
"claude-mythos-5",
|
|
86
|
+
"claude-mythos-5@{snapshot:snapshot}",
|
|
87
|
+
],
|
|
88
|
+
),
|
|
89
|
+
"claude-opus-4-8": SpecificModelConfig(
|
|
90
|
+
version_default="4.8",
|
|
91
|
+
variant_default="opus",
|
|
92
|
+
variant_priority=(5,),
|
|
93
|
+
capabilities=ModelCapabilities(
|
|
94
|
+
supports_vision=True,
|
|
95
|
+
supports_thinking=True,
|
|
96
|
+
supports_function_calling=True,
|
|
97
|
+
supports_streaming=True,
|
|
98
|
+
supports_structured_outputs=True,
|
|
99
|
+
supports_computer_use=True,
|
|
100
|
+
max_tokens=128000,
|
|
101
|
+
context_window=1000000,
|
|
102
|
+
),
|
|
103
|
+
patterns=[
|
|
104
|
+
"claude-opus-4-8-{snapshot:snapshot}",
|
|
105
|
+
"claude-opus-4-8",
|
|
106
|
+
"claude-opus-4-8@{snapshot:snapshot}",
|
|
107
|
+
],
|
|
108
|
+
),
|
|
109
|
+
"claude-opus-4-7": SpecificModelConfig(
|
|
110
|
+
version_default="4.7",
|
|
111
|
+
variant_default="opus",
|
|
112
|
+
variant_priority=(5,),
|
|
113
|
+
capabilities=ModelCapabilities(
|
|
114
|
+
supports_vision=True,
|
|
115
|
+
supports_thinking=True,
|
|
116
|
+
supports_function_calling=True,
|
|
117
|
+
supports_streaming=True,
|
|
118
|
+
supports_structured_outputs=True,
|
|
119
|
+
supports_computer_use=True,
|
|
120
|
+
max_tokens=128000,
|
|
121
|
+
context_window=1000000,
|
|
122
|
+
),
|
|
123
|
+
patterns=[
|
|
124
|
+
"claude-opus-4-7-{snapshot:snapshot}",
|
|
125
|
+
"claude-opus-4-7",
|
|
126
|
+
"claude-opus-4-7@{snapshot:snapshot}",
|
|
127
|
+
],
|
|
128
|
+
),
|
|
45
129
|
"claude-opus-4-6": SpecificModelConfig(
|
|
46
130
|
version_default="4.6",
|
|
47
131
|
variant_default="opus",
|
|
@@ -57,9 +141,9 @@ CLAUDE = ModelFamilyConfig(
|
|
|
57
141
|
context_window=1000000,
|
|
58
142
|
),
|
|
59
143
|
patterns=[
|
|
60
|
-
"claude-opus-4-6-{snapshot:
|
|
144
|
+
"claude-opus-4-6-{snapshot:snapshot}",
|
|
61
145
|
"claude-opus-4-6",
|
|
62
|
-
"claude-opus-4-6@{snapshot:
|
|
146
|
+
"claude-opus-4-6@{snapshot:snapshot}",
|
|
63
147
|
],
|
|
64
148
|
),
|
|
65
149
|
"claude-sonnet-4-6": SpecificModelConfig(
|
|
@@ -77,9 +161,9 @@ CLAUDE = ModelFamilyConfig(
|
|
|
77
161
|
context_window=1000000,
|
|
78
162
|
),
|
|
79
163
|
patterns=[
|
|
80
|
-
"claude-sonnet-4-6-{snapshot:
|
|
164
|
+
"claude-sonnet-4-6-{snapshot:snapshot}",
|
|
81
165
|
"claude-sonnet-4-6",
|
|
82
|
-
"claude-sonnet-4-6@{snapshot:
|
|
166
|
+
"claude-sonnet-4-6@{snapshot:snapshot}",
|
|
83
167
|
],
|
|
84
168
|
),
|
|
85
169
|
"claude-sonnet-4-5": SpecificModelConfig(
|
|
@@ -97,7 +181,11 @@ CLAUDE = ModelFamilyConfig(
|
|
|
97
181
|
max_tokens=64000,
|
|
98
182
|
context_window=200000,
|
|
99
183
|
),
|
|
100
|
-
patterns=[
|
|
184
|
+
patterns=[
|
|
185
|
+
"claude-sonnet-4-5-{snapshot:snapshot}",
|
|
186
|
+
"claude-sonnet-4-5",
|
|
187
|
+
"claude-sonnet-4-5@{snapshot:snapshot}",
|
|
188
|
+
],
|
|
101
189
|
),
|
|
102
190
|
"claude-haiku-4-5": SpecificModelConfig(
|
|
103
191
|
version_default="4.5",
|
|
@@ -114,7 +202,32 @@ CLAUDE = ModelFamilyConfig(
|
|
|
114
202
|
max_tokens=64000,
|
|
115
203
|
context_window=200000,
|
|
116
204
|
),
|
|
117
|
-
patterns=[
|
|
205
|
+
patterns=[
|
|
206
|
+
"claude-haiku-4-5-{snapshot:snapshot}",
|
|
207
|
+
"claude-haiku-4-5",
|
|
208
|
+
"claude-haiku-4-5@{snapshot:snapshot}",
|
|
209
|
+
],
|
|
210
|
+
),
|
|
211
|
+
"claude-opus-4-5": SpecificModelConfig(
|
|
212
|
+
version_default="4.5",
|
|
213
|
+
variant_default="opus",
|
|
214
|
+
variant_priority=(5,),
|
|
215
|
+
capabilities=ModelCapabilities(
|
|
216
|
+
supports_vision=True,
|
|
217
|
+
supports_pdf=True,
|
|
218
|
+
supports_thinking=True,
|
|
219
|
+
supports_function_calling=True,
|
|
220
|
+
supports_streaming=True,
|
|
221
|
+
supports_structured_outputs=True,
|
|
222
|
+
supports_computer_use=True,
|
|
223
|
+
max_tokens=64000,
|
|
224
|
+
context_window=200000,
|
|
225
|
+
),
|
|
226
|
+
patterns=[
|
|
227
|
+
"claude-opus-4-5-{snapshot:snapshot}",
|
|
228
|
+
"claude-opus-4-5",
|
|
229
|
+
"claude-opus-4-5@{snapshot:snapshot}",
|
|
230
|
+
],
|
|
118
231
|
),
|
|
119
232
|
"claude-opus-4-1": SpecificModelConfig(
|
|
120
233
|
version_default="4.1",
|
|
@@ -131,7 +244,7 @@ CLAUDE = ModelFamilyConfig(
|
|
|
131
244
|
max_tokens=32000,
|
|
132
245
|
context_window=200000,
|
|
133
246
|
),
|
|
134
|
-
patterns=["claude-opus-4-1-{snapshot:
|
|
247
|
+
patterns=["claude-opus-4-1-{snapshot:snapshot}", "claude-opus-4-1", "claude-opus-4-1@{snapshot:snapshot}"],
|
|
135
248
|
),
|
|
136
249
|
"claude-sonnet-4-0": SpecificModelConfig(
|
|
137
250
|
version_default="4.0",
|
|
@@ -148,7 +261,11 @@ CLAUDE = ModelFamilyConfig(
|
|
|
148
261
|
max_tokens=64000,
|
|
149
262
|
context_window=200000,
|
|
150
263
|
),
|
|
151
|
-
patterns=[
|
|
264
|
+
patterns=[
|
|
265
|
+
"claude-sonnet-4-{snapshot:snapshot}",
|
|
266
|
+
"claude-sonnet-4-0",
|
|
267
|
+
"claude-sonnet-4-0@{snapshot:snapshot}",
|
|
268
|
+
],
|
|
152
269
|
),
|
|
153
270
|
"claude-3-7-sonnet": SpecificModelConfig(
|
|
154
271
|
version_default="3.7",
|
|
@@ -165,7 +282,7 @@ CLAUDE = ModelFamilyConfig(
|
|
|
165
282
|
max_tokens=64000,
|
|
166
283
|
context_window=200000,
|
|
167
284
|
),
|
|
168
|
-
patterns=["claude-3-7-sonnet-{snapshot:
|
|
285
|
+
patterns=["claude-3-7-sonnet-{snapshot:snapshot}", "claude-3-7-sonnet-latest", "claude-3-7-sonnet"],
|
|
169
286
|
),
|
|
170
287
|
"claude-opus-4-0": SpecificModelConfig(
|
|
171
288
|
version_default="4.0",
|
|
@@ -182,7 +299,7 @@ CLAUDE = ModelFamilyConfig(
|
|
|
182
299
|
max_tokens=32000,
|
|
183
300
|
context_window=200000,
|
|
184
301
|
),
|
|
185
|
-
patterns=["claude-opus-4-{snapshot:
|
|
302
|
+
patterns=["claude-opus-4-{snapshot:snapshot}", "claude-opus-4-0", "claude-opus-4-0@{snapshot:snapshot}"],
|
|
186
303
|
),
|
|
187
304
|
"claude-3-5-haiku": SpecificModelConfig(
|
|
188
305
|
version_default="3.5",
|
|
@@ -198,7 +315,7 @@ CLAUDE = ModelFamilyConfig(
|
|
|
198
315
|
max_tokens=8000,
|
|
199
316
|
context_window=200000,
|
|
200
317
|
),
|
|
201
|
-
patterns=["claude-3-5-haiku-{snapshot:
|
|
318
|
+
patterns=["claude-3-5-haiku-{snapshot:snapshot}", "claude-3-5-haiku-latest", "claude-3-5-haiku"],
|
|
202
319
|
),
|
|
203
320
|
"claude-3-haiku": SpecificModelConfig(
|
|
204
321
|
version_default="3.0",
|
|
@@ -213,7 +330,7 @@ CLAUDE = ModelFamilyConfig(
|
|
|
213
330
|
max_tokens=4096,
|
|
214
331
|
context_window=200000,
|
|
215
332
|
),
|
|
216
|
-
patterns=["claude-3-haiku-{snapshot:
|
|
333
|
+
patterns=["claude-3-haiku-{snapshot:snapshot}"],
|
|
217
334
|
),
|
|
218
335
|
},
|
|
219
336
|
)
|
|
@@ -35,7 +35,20 @@ def _convert_variant(text: str) -> str:
|
|
|
35
35
|
return text
|
|
36
36
|
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
@parse.with_pattern(r"\d{8}")
|
|
39
|
+
def _convert_snapshot(text: str) -> int:
|
|
40
|
+
"""快照日期戳:精确 8 位(YYYYMMDD) / Snapshot date stamp: exactly 8 digits (YYYYMMDD)
|
|
41
|
+
|
|
42
|
+
使用自定义类型而非 parse 内置 ``{:8d}``:后者的宽度是"最多 8 位",会把单个数字(如
|
|
43
|
+
``claude-opus-4-5`` 里的 ``5``)误当作 snapshot 匹配,导致 minor 版本号被吞、版本回退。
|
|
44
|
+
Using a custom type instead of parse's built-in ``{:8d}``: the latter's width means
|
|
45
|
+
"up to 8 digits" and would wrongly swallow a single digit (e.g. the ``5`` in
|
|
46
|
+
``claude-opus-4-5``) as a snapshot, dropping the minor version.
|
|
47
|
+
"""
|
|
48
|
+
return int(text)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
DEFAULT_EXTRA_TYPES: dict[str, Any] = {"variant": _convert_variant, "snapshot": _convert_snapshot}
|
|
39
52
|
|
|
40
53
|
|
|
41
54
|
def _merge_extra_types(extra_types: dict[str, Any] | None) -> dict[str, Any]:
|
|
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
|
{whosellm-0.2.2 → whosellm-0.2.4}/.claude/skills/review-provider-model/providers/anthropic.md
RENAMED
|
File without changes
|
{whosellm-0.2.2 → whosellm-0.2.4}/.claude/skills/review-provider-model/providers/deepseek.md
RENAMED
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|