whosellm 0.2.7__tar.gz → 0.3.0__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.7 → whosellm-0.3.0}/.bumpversion.toml +1 -1
- {whosellm-0.2.7 → whosellm-0.3.0}/CLAUDE.md +1 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/PKG-INFO +1 -1
- {whosellm-0.2.7 → whosellm-0.3.0}/pyproject.toml +1 -1
- {whosellm-0.2.7 → whosellm-0.3.0}/tests/e2e/test_anthropic.py +3 -1
- whosellm-0.3.0/tests/models/families/test_media_count_limit_config.py +74 -0
- whosellm-0.3.0/tests/test_media_count_limit.py +114 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/uv.lock +1 -1
- {whosellm-0.2.7 → whosellm-0.3.0}/whosellm/__init__.py +4 -2
- {whosellm-0.2.7 → whosellm-0.3.0}/whosellm/capabilities.py +48 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/whosellm/models/families/alibaba.py +7 -1
- {whosellm-0.2.7 → whosellm-0.3.0}/whosellm/models/families/anthropic.py +34 -1
- {whosellm-0.2.7 → whosellm-0.3.0}/whosellm/models/families/deepseek/deepseek_official.py +4 -1
- {whosellm-0.2.7 → whosellm-0.3.0}/whosellm/models/families/gemini.py +7 -1
- {whosellm-0.2.7 → whosellm-0.3.0}/whosellm/models/families/openai/openai_gpt_5_5.py +4 -1
- {whosellm-0.2.7 → whosellm-0.3.0}/whosellm/models/families/openai/openai_gpt_5_6.py +4 -1
- {whosellm-0.2.7 → whosellm-0.3.0}/whosellm/models/families/zhipu.py +10 -1
- {whosellm-0.2.7 → whosellm-0.3.0}/.claude/commands/interview.md +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/.claude/projects/-Users-jqq-PycharmProjects-llmeta/memory/MEMORY.md +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/.claude/projects/-Users-jqq-PycharmProjects-llmeta/memory/feedback_use_uv.md +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/.claude/settings.json +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/.claude/skills/code-review/SKILL.md +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/.claude/skills/create-skill/SKILL.md +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/.claude/skills/e2e-metadata/SKILL.md +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/.claude/skills/evolve/SKILL.md +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/.claude/skills/fix-review/SKILL.md +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/.claude/skills/release/SKILL.md +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/.claude/skills/review-provider-model/SKILL.md +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/.claude/skills/review-provider-model/providers/alibaba.md +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/.claude/skills/review-provider-model/providers/anthropic.md +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/.claude/skills/review-provider-model/providers/deepseek.md +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/.claude/skills/review-provider-model/providers/gemini.md +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/.claude/skills/review-provider-model/providers/openai.md +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/.claude/skills/review-provider-model/providers/others.md +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/.claude/skills/review-provider-model/providers/vidu.md +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/.claude/skills/review-provider-model/providers/zhipu.md +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/.claude/skills/update-provider-model/SKILL.md +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/.claude/skills/update-provider-model/testing.md +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/.coveragerc +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/.github/workflows/publish.yml +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/.github/workflows/tests.yml +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/.gitignore +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/.windsurf/workflows/addmodel.md +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/.windsurf/workflows/arch.md +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/.windsurf/workflows/testllmeta.md +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/CHANGELOG.md +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/LICENSE +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/README.md +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/docs/add_new_model_family.md +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/docs/refactor_proposal.md +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/docs/spec_model_family_redesign.md +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/examples/advanced_usage.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/examples/basic_usage.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/mypy.ini +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/pytest.ini +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/ruff.toml +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/tests/__init__.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/tests/e2e/__init__.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/tests/e2e/conftest.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/tests/e2e/test_google.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/tests/e2e/test_openai.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/tests/e2e/test_zhipu.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/tests/models/__init__.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/tests/models/families/__init__.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/tests/models/families/test_anthropic.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/tests/models/families/test_deepseek.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/tests/models/families/test_deepseek_tencent.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/tests/models/families/test_gemini.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/tests/models/families/test_glm41v_thinking.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/tests/models/families/test_glm45.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/tests/models/families/test_glm45v.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/tests/models/families/test_glm46.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/tests/models/families/test_glm46v.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/tests/models/families/test_glm5.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/tests/models/families/test_glm53.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/tests/models/families/test_glm5v_turbo.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/tests/models/families/test_gpt3_5.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/tests/models/families/test_gpt4_1.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/tests/models/families/test_gpt4o.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/tests/models/families/test_gpt5.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/tests/models/families/test_gpt6.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/tests/models/families/test_o1.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/tests/models/families/test_o3.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/tests/models/families/test_o4.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/tests/models/families/test_qwen.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/tests/models/families/test_qwen3_vl_models.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/tests/models/families/test_qwen_ollama.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/tests/models/families/test_qwen_plus.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/tests/test_auto_register.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/tests/test_claude_4x_versions.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/tests/test_llmeta.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/tests/test_model_version.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/tests/test_provider.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/tests/test_registry_merge.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/tests/test_specific_patterns.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/tests/test_variant_priority_config.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/whosellm/model_version.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/whosellm/models/__init__.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/whosellm/models/base.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/whosellm/models/config.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/whosellm/models/dynamic_enum.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/whosellm/models/families/__init__.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/whosellm/models/families/deepseek/__init__.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/whosellm/models/families/deepseek/tencent.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/whosellm/models/families/openai/__init__.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/whosellm/models/families/openai/openai_gpt_3_5.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/whosellm/models/families/openai/openai_gpt_4.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/whosellm/models/families/openai/openai_gpt_4_1.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/whosellm/models/families/openai/openai_gpt_4o.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/whosellm/models/families/openai/openai_gpt_5.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/whosellm/models/families/openai/openai_gpt_5_1.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/whosellm/models/families/openai/openai_gpt_5_2.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/whosellm/models/families/openai/openai_gpt_5_3.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/whosellm/models/families/openai/openai_gpt_5_4.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/whosellm/models/families/openai/openai_gpt_6.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/whosellm/models/families/openai/openai_o1.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/whosellm/models/families/openai/openai_o3.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/whosellm/models/families/openai/openai_o4.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/whosellm/models/families/others.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/whosellm/models/families/vidu.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/whosellm/models/patterns.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/whosellm/models/registry.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/whosellm/provider.py +0 -0
- {whosellm-0.2.7 → whosellm-0.3.0}/whosellm/py.typed +0 -0
|
@@ -112,6 +112,7 @@ 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
|
+
- `media_count_limit`(`MediaCountLimit`):单次请求的媒体数量上限。值域:`0`(确认不支持)、正整数(文档化上限)、`math.inf`(`UNLIMITED`,无文档化上限,仅受 token 预算约束);键缺失视同不支持(`limit > 0` 判断支持性,`UNLIMITED` 为 truthy)。`count_mode` 区分 `"per_type"`(各类型独立计数)与 `"combined"`(共享总池,如 GLM 系列)。
|
|
115
116
|
- `supports_json_outputs`:模型能否将输出限制为合法 JSON(`response_format: {type: "json_object"}`),但**不约束 JSON 结构**。
|
|
116
117
|
- `supports_structured_outputs`:模型能否按调用方提供的 **JSON Schema** 生成严格符合结构的输出(`response_format: {type: "json_schema", ...}`)。这是更强的能力,蕴含 `supports_json_outputs`。
|
|
117
118
|
- **`ModelFamilyConfig`**(`models/config.py`):配置 dataclass,定义模式、默认值和特定模型覆盖。创建时自动注册到全局注册表。
|
|
@@ -233,7 +233,9 @@ CLAUDE_30_MODELS = [
|
|
|
233
233
|
# 聚合 + 参数化
|
|
234
234
|
# ============================================================================
|
|
235
235
|
|
|
236
|
-
ALL_MODELS =
|
|
236
|
+
ALL_MODELS = (
|
|
237
|
+
CLAUDE_51_MODELS + CLAUDE_LATEST_MODELS + CLAUDE_45_MODELS + CLAUDE_41_MODELS + CLAUDE_40_MODELS + CLAUDE_30_MODELS
|
|
238
|
+
)
|
|
237
239
|
|
|
238
240
|
|
|
239
241
|
@pytest.mark.e2e
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# filename: test_media_count_limit_config.py
|
|
2
|
+
"""
|
|
3
|
+
各家族媒体数量限制的集成断言(官方文档来源见配置注释)
|
|
4
|
+
Per-family media count limit integration tests (official docs cited in config comments)
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import math
|
|
8
|
+
|
|
9
|
+
from whosellm import UNLIMITED
|
|
10
|
+
from whosellm.model_version import LLMeta
|
|
11
|
+
|
|
12
|
+
OFFICIAL = (
|
|
13
|
+
# (模型名, 官方图片数量上限/请求, 计数模式)
|
|
14
|
+
# 来源 / Sources:
|
|
15
|
+
# Anthropic: platform.claude.com/docs/en/build-with-claude/vision (600/1M ctx, 100/200k ctx)
|
|
16
|
+
# OpenAI: developers.openai.com/api/docs/guides/images-vision (1,500)
|
|
17
|
+
# Gemini: ai.google.dev/gemini-api/docs/image-understanding (3,600)
|
|
18
|
+
# Zhipu: docs.bigmodel.cn(GLM-5.3-Flash 未公开上限)/ zai-org/GLM-skills(GLM-4.6v ≤50)
|
|
19
|
+
# DeepSeek: api-docs.deepseek.com/guides/vision (600)
|
|
20
|
+
# Alibaba: help.aliyun.com/zh/model-studio/vision (256 URL / 250 base64)
|
|
21
|
+
("claude-fable-5-1", 600, "per_type"),
|
|
22
|
+
("claude-mythos-5-1", 600, "per_type"),
|
|
23
|
+
("claude-opus-5", 600, "per_type"),
|
|
24
|
+
("claude-sonnet-5", 600, "per_type"),
|
|
25
|
+
("claude-opus-4-8", 600, "per_type"),
|
|
26
|
+
("claude-sonnet-4-6", 600, "per_type"),
|
|
27
|
+
("gpt-5.6", 1500, "per_type"),
|
|
28
|
+
("gpt-5.5", 1500, "per_type"),
|
|
29
|
+
("gemini-3.8-flash", 3600, "per_type"),
|
|
30
|
+
("gemini-3.7-flash", 3600, "per_type"),
|
|
31
|
+
("glm-4.6v", 50, "combined"),
|
|
32
|
+
("glm-4.6v-flash", 50, "combined"),
|
|
33
|
+
("deepseek-v4-flash-vision-exp", 600, "per_type"),
|
|
34
|
+
("qwen3-vl-plus", 256, "per_type"),
|
|
35
|
+
("qwen3-vl-flash", 256, "per_type"),
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class TestLatestModels:
|
|
40
|
+
"""最新/次新模型的官方图片数量上限 / Official image count caps for latest models"""
|
|
41
|
+
|
|
42
|
+
def test_official_image_caps(self):
|
|
43
|
+
for model_name, expected, mode in OFFICIAL:
|
|
44
|
+
mcl = LLMeta(model_name).capabilities.media_count_limit
|
|
45
|
+
assert mcl.get("image") == expected, f"{model_name}: {mcl.get('image')} != {expected}"
|
|
46
|
+
assert mcl.count_mode == mode, f"{model_name}: count_mode mismatch"
|
|
47
|
+
assert mcl.is_supported("image") is True
|
|
48
|
+
|
|
49
|
+
def test_glm_combined_shared_pool(self):
|
|
50
|
+
"""GLM 系列为合并计数:图片/视频等素材共享 50 的总池"""
|
|
51
|
+
for model_name in ("glm-4.6v", "glm-4.6v-flash"):
|
|
52
|
+
mcl = LLMeta(model_name).capabilities.media_count_limit
|
|
53
|
+
assert mcl.count_mode == "combined"
|
|
54
|
+
assert mcl.combined == 50
|
|
55
|
+
|
|
56
|
+
def test_glm_5v_turbo_no_documented_cap(self):
|
|
57
|
+
"""GLM-5.3-Flash(glm-5v-turbo)官方未公开数量上限 → UNLIMITED"""
|
|
58
|
+
mcl = LLMeta("glm-5v-turbo").capabilities.media_count_limit
|
|
59
|
+
assert mcl.get("image") is math.inf
|
|
60
|
+
assert mcl.get("image") is UNLIMITED
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class TestUnmeasuredModels:
|
|
64
|
+
"""未实测模型:键缺失视同不支持 / Unmeasured models: missing keys treated as unsupported"""
|
|
65
|
+
|
|
66
|
+
def test_200k_claude_without_specific_cap(self):
|
|
67
|
+
"""老代模型未采集 → image 为 0(视同不支持),不误报为无上限"""
|
|
68
|
+
mcl = LLMeta("claude-3-5-haiku").capabilities.media_count_limit
|
|
69
|
+
assert mcl.get("image") == 0
|
|
70
|
+
assert bool(mcl.get("image")) is False
|
|
71
|
+
|
|
72
|
+
def test_text_only_model(self):
|
|
73
|
+
mcl = LLMeta("deepseek-v4-flash").capabilities.media_count_limit
|
|
74
|
+
assert mcl.get("image") == 0
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# filename: test_media_count_limit.py
|
|
2
|
+
"""
|
|
3
|
+
MediaCountLimit 单元测试 / MediaCountLimit unit tests
|
|
4
|
+
|
|
5
|
+
值域约定 / Value domain:
|
|
6
|
+
- 0 确认不支持 / confirmed unsupported
|
|
7
|
+
- 正整数 文档化上限 / documented cap
|
|
8
|
+
- UNLIMITED 无文档化上限,仅受上下文 token 预算约束 / no documented cap (math.inf)
|
|
9
|
+
- 键缺失 = 未实测,视同不支持 / missing key = unmeasured, treated as unsupported
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import math
|
|
13
|
+
from dataclasses import FrozenInstanceError
|
|
14
|
+
|
|
15
|
+
import pytest
|
|
16
|
+
|
|
17
|
+
from whosellm import UNLIMITED, MediaCountLimit
|
|
18
|
+
from whosellm.capabilities import ModelCapabilities
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class TestDefault:
|
|
22
|
+
"""默认行为 / Default behavior"""
|
|
23
|
+
|
|
24
|
+
def test_default_per_type_empty(self):
|
|
25
|
+
mcl = MediaCountLimit()
|
|
26
|
+
assert mcl.per_type == {}
|
|
27
|
+
assert mcl.count_mode == "per_type"
|
|
28
|
+
assert mcl.combined is None
|
|
29
|
+
|
|
30
|
+
def test_missing_key_returns_zero(self):
|
|
31
|
+
"""键缺失(未实测)视同不支持 / Missing keys are treated as unsupported"""
|
|
32
|
+
mcl = MediaCountLimit(per_type={"image": 10})
|
|
33
|
+
assert mcl.get("video") == 0
|
|
34
|
+
assert mcl.is_supported("video") is False
|
|
35
|
+
|
|
36
|
+
def test_model_capabilities_default(self):
|
|
37
|
+
caps = ModelCapabilities()
|
|
38
|
+
assert caps.media_count_limit == MediaCountLimit()
|
|
39
|
+
assert caps.media_count_limit.get("image") == 0
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class TestValueDomain:
|
|
43
|
+
"""取值域语义 / Value domain semantics: 0 / int / UNLIMITED"""
|
|
44
|
+
|
|
45
|
+
def test_zero_means_unsupported(self):
|
|
46
|
+
mcl = MediaCountLimit(per_type={"image": 0})
|
|
47
|
+
assert mcl.get("image") == 0
|
|
48
|
+
assert mcl.is_supported("image") is False
|
|
49
|
+
assert bool(mcl.get("image")) is False
|
|
50
|
+
|
|
51
|
+
def test_positive_int_is_documented_cap(self):
|
|
52
|
+
mcl = MediaCountLimit(per_type={"image": 600})
|
|
53
|
+
assert mcl.get("image") == 600
|
|
54
|
+
assert mcl.is_supported("image") is True
|
|
55
|
+
assert bool(mcl.get("image")) is True
|
|
56
|
+
|
|
57
|
+
def test_unlimited_is_truthy(self):
|
|
58
|
+
"""UNLIMITED 必须是 truthy——这是 0 与"无上限"在 bool 上下文的唯一区分点"""
|
|
59
|
+
mcl = MediaCountLimit(per_type={"image": UNLIMITED})
|
|
60
|
+
assert mcl.get("image") is math.inf
|
|
61
|
+
assert mcl.is_supported("image") is True
|
|
62
|
+
assert bool(mcl.get("image")) is True
|
|
63
|
+
|
|
64
|
+
def test_bool_never_conflates_zero_and_unlimited(self):
|
|
65
|
+
assert bool(0) is False
|
|
66
|
+
assert bool(UNLIMITED) is True
|
|
67
|
+
|
|
68
|
+
def test_unlimited_comparison_with_positive_ints(self):
|
|
69
|
+
assert UNLIMITED > 50
|
|
70
|
+
assert UNLIMITED > 0
|
|
71
|
+
assert not UNLIMITED < 600
|
|
72
|
+
|
|
73
|
+
def test_is_supported_uses_positive_check(self):
|
|
74
|
+
"""is_supported 基于 limit > 0,区分 0 / 正整数 / UNLIMITED"""
|
|
75
|
+
assert MediaCountLimit(per_type={"image": 0}).is_supported("image") is False
|
|
76
|
+
assert MediaCountLimit(per_type={"image": 1}).is_supported("image") is True
|
|
77
|
+
assert MediaCountLimit(per_type={"image": UNLIMITED}).is_supported("image") is True
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class TestCountingMode:
|
|
81
|
+
"""计数模式 / Counting mode"""
|
|
82
|
+
|
|
83
|
+
def test_per_type_mode_default(self):
|
|
84
|
+
mcl = MediaCountLimit(per_type={"image": 600})
|
|
85
|
+
assert mcl.count_mode == "per_type"
|
|
86
|
+
assert mcl.combined is None
|
|
87
|
+
|
|
88
|
+
def test_combined_mode(self):
|
|
89
|
+
mcl = MediaCountLimit(per_type={"image": 50, "video": 50}, count_mode="combined", combined=50)
|
|
90
|
+
assert mcl.count_mode == "combined"
|
|
91
|
+
assert mcl.combined == 50
|
|
92
|
+
assert mcl.get("image") == 50
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class TestImmutability:
|
|
96
|
+
"""不可变性 / Immutability (frozen dataclass)
|
|
97
|
+
|
|
98
|
+
注意:frozen 为浅冻结——字段引用不可重绑,与项目内既有 list 字段
|
|
99
|
+
(supported_image_mime_type 等)的语义保持一致。
|
|
100
|
+
Note: frozen is shallow — field rebinding is rejected, same semantics as
|
|
101
|
+
existing list fields (supported_image_mime_type etc.).
|
|
102
|
+
"""
|
|
103
|
+
|
|
104
|
+
def test_field_reassignment_rejected(self):
|
|
105
|
+
mcl = MediaCountLimit(per_type={"image": 50})
|
|
106
|
+
with pytest.raises(FrozenInstanceError):
|
|
107
|
+
mcl.per_type = {} # type: ignore[misc]
|
|
108
|
+
|
|
109
|
+
def test_default_factory_isolation(self):
|
|
110
|
+
"""default_factory 保证每个实例独立,不共享可变状态"""
|
|
111
|
+
a = ModelCapabilities()
|
|
112
|
+
b = ModelCapabilities()
|
|
113
|
+
assert a.media_count_limit is not b.media_count_limit
|
|
114
|
+
assert a.media_count_limit.per_type is not b.media_count_limit.per_type
|
|
@@ -7,15 +7,17 @@
|
|
|
7
7
|
LLMeta - 统一的大语言模型版本和能力管理库 / A unified LLM model version and capability management library
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
|
-
__version__ = "0.
|
|
10
|
+
__version__ = "0.3.0"
|
|
11
11
|
|
|
12
|
-
from whosellm.capabilities import ModelCapabilities
|
|
12
|
+
from whosellm.capabilities import UNLIMITED, MediaCountLimit, ModelCapabilities
|
|
13
13
|
from whosellm.model_version import LLMeta
|
|
14
14
|
from whosellm.models.base import ModelFamily
|
|
15
15
|
from whosellm.provider import Provider
|
|
16
16
|
|
|
17
17
|
__all__ = [
|
|
18
|
+
"UNLIMITED",
|
|
18
19
|
"LLMeta",
|
|
20
|
+
"MediaCountLimit",
|
|
19
21
|
"ModelCapabilities",
|
|
20
22
|
"ModelFamily",
|
|
21
23
|
"Provider",
|
|
@@ -7,7 +7,52 @@
|
|
|
7
7
|
模型能力定义 / Model capability definitions
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
|
+
import math
|
|
10
11
|
from dataclasses import dataclass, field
|
|
12
|
+
from typing import Literal
|
|
13
|
+
|
|
14
|
+
# 无文档化上限哨兵:该媒体类型官方未给出数量上限,仅受上下文 token 预算约束
|
|
15
|
+
# Sentinel for "no documented limit": the provider documents no count cap,
|
|
16
|
+
# only the context-window token budget constrains the input.
|
|
17
|
+
UNLIMITED = math.inf
|
|
18
|
+
|
|
19
|
+
MediaCount = int | float # 正整数 = 文档化上限;float 仅允许 UNLIMITED(math.inf)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass(frozen=True)
|
|
23
|
+
class MediaCountLimit:
|
|
24
|
+
"""
|
|
25
|
+
单次请求媒体数量上限 / Per-request media count limits
|
|
26
|
+
|
|
27
|
+
值域 / Value domain(键缺失 = 未实测,视同不支持):
|
|
28
|
+
- 0 确认不支持 / confirmed unsupported
|
|
29
|
+
- 正整数 文档化上限,单位:个(张/段/...)/ documented cap (count)
|
|
30
|
+
- UNLIMITED 无文档化上限,仅受上下文 token 预算约束 / no documented cap
|
|
31
|
+
|
|
32
|
+
count_mode:
|
|
33
|
+
- "per_type": 各类型独立计数(如 "image": 600 仅约束图片)/ per-type caps
|
|
34
|
+
- "combined": 所有媒体共享一个总池(如 GLM 系列图片+视频合并计数)/ shared pool
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
per_type: dict[str, MediaCount] = field(default_factory=dict)
|
|
38
|
+
count_mode: Literal["per_type", "combined"] = "per_type"
|
|
39
|
+
# combined 模式的共享上限(per_type 模式下应为 None)
|
|
40
|
+
# Shared pool cap for "combined" mode (None when count_mode == "per_type")
|
|
41
|
+
combined: MediaCount | None = None
|
|
42
|
+
|
|
43
|
+
def get(self, media_type: str) -> MediaCount:
|
|
44
|
+
"""
|
|
45
|
+
返回该媒体类型的上限;键缺失(未实测)返回 0(视同不支持)
|
|
46
|
+
Return the cap for a media type; missing keys (unmeasured) return 0 (treated as unsupported)
|
|
47
|
+
"""
|
|
48
|
+
return self.per_type.get(media_type, 0)
|
|
49
|
+
|
|
50
|
+
def is_supported(self, media_type: str) -> bool:
|
|
51
|
+
"""
|
|
52
|
+
该媒体类型是否支持(0 > 0 为 False,正整数与 UNLIMITED 均为 True)
|
|
53
|
+
Whether the media type is supported at all
|
|
54
|
+
"""
|
|
55
|
+
return self.get(media_type) > 0
|
|
11
56
|
|
|
12
57
|
|
|
13
58
|
@dataclass(frozen=True)
|
|
@@ -51,6 +96,9 @@ class ModelCapabilities:
|
|
|
51
96
|
) # 支持的图片MIME类型 / Supported image MIME types
|
|
52
97
|
supports_image_base64: bool = True # 是否支持base64编码的图片 / Whether base64-encoded images are supported
|
|
53
98
|
|
|
99
|
+
# 单次请求媒体(图片/视频/音频)数量限制 / Per-request media count limits
|
|
100
|
+
media_count_limit: MediaCountLimit = field(default_factory=MediaCountLimit)
|
|
101
|
+
|
|
54
102
|
# 视频相关限制 / Video-related limitations
|
|
55
103
|
max_video_size_mb: float | None = None # 最大视频大小(MB) / Maximum video size in MB
|
|
56
104
|
max_video_duration_seconds: int | None = None # 最大视频时长(秒) / Maximum video duration in seconds
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
阿里巴巴模型家族配置 / Alibaba model family configurations
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
|
-
from whosellm.capabilities import ModelCapabilities
|
|
10
|
+
from whosellm.capabilities import MediaCountLimit, ModelCapabilities
|
|
11
11
|
from whosellm.models.base import ModelFamily
|
|
12
12
|
from whosellm.models.config import ModelFamilyConfig, SpecificModelConfig
|
|
13
13
|
from whosellm.provider import Provider
|
|
@@ -142,6 +142,9 @@ QWEN = ModelFamilyConfig(
|
|
|
142
142
|
supports_structured_outputs=True,
|
|
143
143
|
max_tokens=32000,
|
|
144
144
|
context_window=256000,
|
|
145
|
+
# 官方:公网 URL/本地路径最多 256 张;Base64 250 张(help.aliyun.com/zh/model-studio/vision)
|
|
146
|
+
# Per docs: 256 images via URL/local path; 250 via base64
|
|
147
|
+
media_count_limit=MediaCountLimit(per_type={"image": 256}),
|
|
145
148
|
),
|
|
146
149
|
patterns=[
|
|
147
150
|
"qwen3-vl-plus-{year:4d}-{month:2d}-{day:2d}",
|
|
@@ -213,6 +216,9 @@ QWEN = ModelFamilyConfig(
|
|
|
213
216
|
supports_structured_outputs=True,
|
|
214
217
|
max_tokens=32000,
|
|
215
218
|
context_window=256000,
|
|
219
|
+
# 官方:公网 URL/本地路径最多 256 张;Base64 250 张(help.aliyun.com/zh/model-studio/vision)
|
|
220
|
+
# Per docs: 256 images via URL/local path; 250 via base64
|
|
221
|
+
media_count_limit=MediaCountLimit(per_type={"image": 256}),
|
|
216
222
|
),
|
|
217
223
|
patterns=[
|
|
218
224
|
"qwen3-vl-flash-{year:4d}-{month:2d}-{day:2d}",
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
Anthropic 模型家族配置 / Anthropic model family configurations
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
|
-
from whosellm.capabilities import ModelCapabilities
|
|
10
|
+
from whosellm.capabilities import MediaCountLimit, ModelCapabilities
|
|
11
11
|
from whosellm.models.base import ModelFamily
|
|
12
12
|
from whosellm.models.config import ModelFamilyConfig, SpecificModelConfig
|
|
13
13
|
from whosellm.provider import Provider
|
|
@@ -41,6 +41,9 @@ CLAUDE = ModelFamilyConfig(
|
|
|
41
41
|
supports_streaming=True,
|
|
42
42
|
max_tokens=64000,
|
|
43
43
|
context_window=200000,
|
|
44
|
+
# 200k 上下文模型:官方上限 100 张/请求;1M 模型为 600(见各 specific_models)
|
|
45
|
+
# 200k-context models: 100 images/request per docs; 1M models: 600 (see specific_models)
|
|
46
|
+
media_count_limit=MediaCountLimit(per_type={"image": 100}),
|
|
44
47
|
),
|
|
45
48
|
specific_models={
|
|
46
49
|
# Mythos-class(2026-06-09 发布):全新顶级层级,优先级高于 opus
|
|
@@ -59,6 +62,9 @@ CLAUDE = ModelFamilyConfig(
|
|
|
59
62
|
supports_computer_use=True,
|
|
60
63
|
max_tokens=128000,
|
|
61
64
|
context_window=1000000,
|
|
65
|
+
# 官方上限:1M 上下文模型最多 600 张图片/请求(https://docs.anthropic.com/en/docs/build-with-claude/vision)
|
|
66
|
+
# Per docs: up to 600 images per request for 1M-context models
|
|
67
|
+
media_count_limit=MediaCountLimit(per_type={"image": 600}),
|
|
62
68
|
),
|
|
63
69
|
patterns=[
|
|
64
70
|
"claude-fable-5-{snapshot:snapshot}",
|
|
@@ -79,6 +85,9 @@ CLAUDE = ModelFamilyConfig(
|
|
|
79
85
|
supports_computer_use=True,
|
|
80
86
|
max_tokens=128000,
|
|
81
87
|
context_window=1000000,
|
|
88
|
+
# 官方上限:1M 上下文模型最多 600 张图片/请求(https://docs.anthropic.com/en/docs/build-with-claude/vision)
|
|
89
|
+
# Per docs: up to 600 images per request for 1M-context models
|
|
90
|
+
media_count_limit=MediaCountLimit(per_type={"image": 600}),
|
|
82
91
|
),
|
|
83
92
|
patterns=[
|
|
84
93
|
"claude-mythos-5-{snapshot:snapshot}",
|
|
@@ -103,6 +112,9 @@ CLAUDE = ModelFamilyConfig(
|
|
|
103
112
|
supports_computer_use=True,
|
|
104
113
|
max_tokens=128000,
|
|
105
114
|
context_window=1000000,
|
|
115
|
+
# 官方上限:1M 上下文模型最多 600 张图片/请求(https://docs.anthropic.com/en/docs/build-with-claude/vision)
|
|
116
|
+
# Per docs: up to 600 images per request for 1M-context models
|
|
117
|
+
media_count_limit=MediaCountLimit(per_type={"image": 600}),
|
|
106
118
|
),
|
|
107
119
|
patterns=[
|
|
108
120
|
"claude-fable-5-1-{snapshot:snapshot}",
|
|
@@ -125,6 +137,9 @@ CLAUDE = ModelFamilyConfig(
|
|
|
125
137
|
supports_computer_use=True,
|
|
126
138
|
max_tokens=128000,
|
|
127
139
|
context_window=1000000,
|
|
140
|
+
# 官方上限:1M 上下文模型最多 600 张图片/请求(https://docs.anthropic.com/en/docs/build-with-claude/vision)
|
|
141
|
+
# Per docs: up to 600 images per request for 1M-context models
|
|
142
|
+
media_count_limit=MediaCountLimit(per_type={"image": 600}),
|
|
128
143
|
),
|
|
129
144
|
patterns=[
|
|
130
145
|
"claude-mythos-5-1-{snapshot:snapshot}",
|
|
@@ -149,6 +164,9 @@ CLAUDE = ModelFamilyConfig(
|
|
|
149
164
|
supports_computer_use=True,
|
|
150
165
|
max_tokens=128000,
|
|
151
166
|
context_window=1000000,
|
|
167
|
+
# 官方上限:1M 上下文模型最多 600 张图片/请求(https://docs.anthropic.com/en/docs/build-with-claude/vision)
|
|
168
|
+
# Per docs: up to 600 images per request for 1M-context models
|
|
169
|
+
media_count_limit=MediaCountLimit(per_type={"image": 600}),
|
|
152
170
|
),
|
|
153
171
|
patterns=[
|
|
154
172
|
"claude-opus-5-{snapshot:snapshot}",
|
|
@@ -169,6 +187,9 @@ CLAUDE = ModelFamilyConfig(
|
|
|
169
187
|
supports_computer_use=True,
|
|
170
188
|
max_tokens=128000,
|
|
171
189
|
context_window=1000000,
|
|
190
|
+
# 官方上限:1M 上下文模型最多 600 张图片/请求(https://docs.anthropic.com/en/docs/build-with-claude/vision)
|
|
191
|
+
# Per docs: up to 600 images per request for 1M-context models
|
|
192
|
+
media_count_limit=MediaCountLimit(per_type={"image": 600}),
|
|
172
193
|
),
|
|
173
194
|
patterns=[
|
|
174
195
|
"claude-sonnet-5-{snapshot:snapshot}",
|
|
@@ -189,6 +210,9 @@ CLAUDE = ModelFamilyConfig(
|
|
|
189
210
|
supports_computer_use=True,
|
|
190
211
|
max_tokens=128000,
|
|
191
212
|
context_window=1000000,
|
|
213
|
+
# 官方上限:1M 上下文模型最多 600 张图片/请求(https://docs.anthropic.com/en/docs/build-with-claude/vision)
|
|
214
|
+
# Per docs: up to 600 images per request for 1M-context models
|
|
215
|
+
media_count_limit=MediaCountLimit(per_type={"image": 600}),
|
|
192
216
|
),
|
|
193
217
|
patterns=[
|
|
194
218
|
"claude-opus-4-8-{snapshot:snapshot}",
|
|
@@ -209,6 +233,9 @@ CLAUDE = ModelFamilyConfig(
|
|
|
209
233
|
supports_computer_use=True,
|
|
210
234
|
max_tokens=128000,
|
|
211
235
|
context_window=1000000,
|
|
236
|
+
# 官方上限:1M 上下文模型最多 600 张图片/请求(https://docs.anthropic.com/en/docs/build-with-claude/vision)
|
|
237
|
+
# Per docs: up to 600 images per request for 1M-context models
|
|
238
|
+
media_count_limit=MediaCountLimit(per_type={"image": 600}),
|
|
212
239
|
),
|
|
213
240
|
patterns=[
|
|
214
241
|
"claude-opus-4-7-{snapshot:snapshot}",
|
|
@@ -229,6 +256,9 @@ CLAUDE = ModelFamilyConfig(
|
|
|
229
256
|
supports_computer_use=True,
|
|
230
257
|
max_tokens=128000,
|
|
231
258
|
context_window=1000000,
|
|
259
|
+
# 官方上限:1M 上下文模型最多 600 张图片/请求(https://docs.anthropic.com/en/docs/build-with-claude/vision)
|
|
260
|
+
# Per docs: up to 600 images per request for 1M-context models
|
|
261
|
+
media_count_limit=MediaCountLimit(per_type={"image": 600}),
|
|
232
262
|
),
|
|
233
263
|
patterns=[
|
|
234
264
|
"claude-opus-4-6-{snapshot:snapshot}",
|
|
@@ -249,6 +279,9 @@ CLAUDE = ModelFamilyConfig(
|
|
|
249
279
|
supports_computer_use=True,
|
|
250
280
|
max_tokens=64000,
|
|
251
281
|
context_window=1000000,
|
|
282
|
+
# 官方上限:1M 上下文模型最多 600 张图片/请求(https://docs.anthropic.com/en/docs/build-with-claude/vision)
|
|
283
|
+
# Per docs: up to 600 images per request for 1M-context models
|
|
284
|
+
media_count_limit=MediaCountLimit(per_type={"image": 600}),
|
|
252
285
|
),
|
|
253
286
|
patterns=[
|
|
254
287
|
"claude-sonnet-4-6-{snapshot:snapshot}",
|
|
@@ -16,7 +16,7 @@ Legacy aliases: deepseek-chat / deepseek-reasoner (announced to be deprecated;
|
|
|
16
16
|
currently map to deepseek-v4-flash non-thinking and thinking modes respectively).
|
|
17
17
|
"""
|
|
18
18
|
|
|
19
|
-
from whosellm.capabilities import ModelCapabilities
|
|
19
|
+
from whosellm.capabilities import MediaCountLimit, ModelCapabilities
|
|
20
20
|
from whosellm.models.base import ModelFamily
|
|
21
21
|
from whosellm.models.config import ModelFamilyConfig, SpecificModelConfig
|
|
22
22
|
from whosellm.provider import Provider
|
|
@@ -93,6 +93,9 @@ DEEPSEEK = ModelFamilyConfig(
|
|
|
93
93
|
supports_structured_outputs=False,
|
|
94
94
|
max_tokens=384_000,
|
|
95
95
|
context_window=1_000_000,
|
|
96
|
+
# 官方文档:单请求最多 600 张图片;≥15 张时单图长边降至 4096px(api-docs.deepseek.com/guides/vision)
|
|
97
|
+
# Per docs: up to 600 images per request; per-image long edge drops to 4096px with 15+ images
|
|
98
|
+
media_count_limit=MediaCountLimit(per_type={"image": 600}),
|
|
96
99
|
),
|
|
97
100
|
patterns=[
|
|
98
101
|
"deepseek-v4-flash-vision-exp",
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
Google Gemini 模型家族配置 / Google Gemini model family configurations
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
|
-
from whosellm.capabilities import ModelCapabilities
|
|
10
|
+
from whosellm.capabilities import MediaCountLimit, ModelCapabilities
|
|
11
11
|
from whosellm.models.base import ModelFamily
|
|
12
12
|
from whosellm.models.config import ModelFamilyConfig, SpecificModelConfig
|
|
13
13
|
from whosellm.provider import Provider
|
|
@@ -436,6 +436,9 @@ GEMINI = ModelFamilyConfig(
|
|
|
436
436
|
supports_code_interpreter=True,
|
|
437
437
|
max_tokens=65536,
|
|
438
438
|
context_window=1048576,
|
|
439
|
+
# 官方文档:单请求最多 3600 张图片(https://ai.google.dev/gemini-api/docs/image-understanding)
|
|
440
|
+
# Per docs: up to 3,600 image files per request
|
|
441
|
+
media_count_limit=MediaCountLimit(per_type={"image": 3600}),
|
|
439
442
|
),
|
|
440
443
|
patterns=["gemini-3.7-flash"],
|
|
441
444
|
),
|
|
@@ -460,6 +463,9 @@ GEMINI = ModelFamilyConfig(
|
|
|
460
463
|
supports_code_interpreter=True,
|
|
461
464
|
max_tokens=65536,
|
|
462
465
|
context_window=1048576,
|
|
466
|
+
# 官方文档:单请求最多 3600 张图片(https://ai.google.dev/gemini-api/docs/image-understanding)
|
|
467
|
+
# Per docs: up to 3,600 image files per request
|
|
468
|
+
media_count_limit=MediaCountLimit(per_type={"image": 3600}),
|
|
463
469
|
),
|
|
464
470
|
patterns=["gemini-3.8-flash"],
|
|
465
471
|
),
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from whosellm.capabilities import ModelCapabilities
|
|
1
|
+
from whosellm.capabilities import MediaCountLimit, ModelCapabilities
|
|
2
2
|
from whosellm.models.base import ModelFamily
|
|
3
3
|
from whosellm.models.config import ModelFamilyConfig, SpecificModelConfig
|
|
4
4
|
from whosellm.provider import Provider
|
|
@@ -33,6 +33,9 @@ GPT_5_5 = ModelFamilyConfig(
|
|
|
33
33
|
supports_computer_use=True,
|
|
34
34
|
max_tokens=128_000,
|
|
35
35
|
context_window=1_050_000,
|
|
36
|
+
# 官方文档:单请求最多 1500 张图片(https://developers.openai.com/api/docs/guides/images-vision)
|
|
37
|
+
# Per docs: up to 1,500 images per request
|
|
38
|
+
media_count_limit=MediaCountLimit(per_type={"image": 1500}),
|
|
36
39
|
),
|
|
37
40
|
specific_models={
|
|
38
41
|
"gpt-5.5": SpecificModelConfig(
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from whosellm.capabilities import ModelCapabilities
|
|
1
|
+
from whosellm.capabilities import MediaCountLimit, ModelCapabilities
|
|
2
2
|
from whosellm.models.base import ModelFamily
|
|
3
3
|
from whosellm.models.config import ModelFamilyConfig, SpecificModelConfig
|
|
4
4
|
from whosellm.provider import Provider
|
|
@@ -40,6 +40,9 @@ GPT_5_6 = ModelFamilyConfig(
|
|
|
40
40
|
supports_computer_use=True,
|
|
41
41
|
max_tokens=128_000,
|
|
42
42
|
context_window=1_050_000,
|
|
43
|
+
# 官方文档:单请求最多 1500 张图片(https://developers.openai.com/api/docs/guides/images-vision)
|
|
44
|
+
# Per docs: up to 1,500 images per request
|
|
45
|
+
media_count_limit=MediaCountLimit(per_type={"image": 1500}),
|
|
43
46
|
),
|
|
44
47
|
specific_models={
|
|
45
48
|
"gpt-5.6": SpecificModelConfig(
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
智谱 AI 模型家族配置 / Zhipu AI model family configurations
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
|
-
from whosellm.capabilities import ModelCapabilities
|
|
10
|
+
from whosellm.capabilities import UNLIMITED, MediaCountLimit, ModelCapabilities
|
|
11
11
|
from whosellm.models.base import ModelFamily
|
|
12
12
|
from whosellm.models.config import ModelFamilyConfig, SpecificModelConfig
|
|
13
13
|
from whosellm.provider import Provider
|
|
@@ -94,6 +94,9 @@ GLM_VISION = ModelFamilyConfig(
|
|
|
94
94
|
supports_streaming=True,
|
|
95
95
|
max_tokens=128000,
|
|
96
96
|
context_window=200000,
|
|
97
|
+
# 官方文档未公开单次请求图片数量上限,仅受 1M 上下文 token 预算约束
|
|
98
|
+
# No documented per-request image cap (only the context-window token budget)
|
|
99
|
+
media_count_limit=MediaCountLimit(per_type={"image": UNLIMITED}),
|
|
97
100
|
),
|
|
98
101
|
patterns=[
|
|
99
102
|
"glm-5v-turbo-{year:4d}-{month:2d}-{day:2d}",
|
|
@@ -210,6 +213,9 @@ GLM_VISION = ModelFamilyConfig(
|
|
|
210
213
|
supports_streaming=True,
|
|
211
214
|
max_tokens=128000,
|
|
212
215
|
context_window=128000,
|
|
216
|
+
# 官方:单请求最多 50 张图片(zai-org/GLM-skills,图片+视频等素材总量上限)
|
|
217
|
+
# Per official GLM-skills: up to 50 images per request (combined with other media)
|
|
218
|
+
media_count_limit=MediaCountLimit(per_type={"image": 50}, count_mode="combined", combined=50),
|
|
213
219
|
),
|
|
214
220
|
),
|
|
215
221
|
"glm-4.6v-flash": SpecificModelConfig(
|
|
@@ -227,6 +233,9 @@ GLM_VISION = ModelFamilyConfig(
|
|
|
227
233
|
supports_streaming=True,
|
|
228
234
|
max_tokens=128000,
|
|
229
235
|
context_window=128000,
|
|
236
|
+
# 官方:单请求最多 50 张图片(zai-org/GLM-skills,图片+视频等素材总量上限)
|
|
237
|
+
# Per official GLM-skills: up to 50 images per request (combined with other media)
|
|
238
|
+
media_count_limit=MediaCountLimit(per_type={"image": 50}, count_mode="combined", combined=50),
|
|
230
239
|
),
|
|
231
240
|
patterns=[
|
|
232
241
|
"glm-4.6v-flash-{year:4d}-{month:2d}-{day:2d}",
|
|
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
|
{whosellm-0.2.7 → whosellm-0.3.0}/.claude/skills/review-provider-model/providers/anthropic.md
RENAMED
|
File without changes
|
{whosellm-0.2.7 → whosellm-0.3.0}/.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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|