agentshim 0.17.0__tar.gz → 0.18.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.
- {agentshim-0.17.0 → agentshim-0.18.0}/CHANGELOG.md +13 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/PKG-INFO +1 -1
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/__init__.py +11 -1
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/core/__init__.py +6 -0
- agentshim-0.18.0/agentshim/core/models.py +247 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/core/profile.py +10 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/providers/claude/provider.py +12 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/providers/codex/provider.py +11 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/providers/opencode/provider.py +3 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/pyproject.toml +1 -1
- {agentshim-0.17.0 → agentshim-0.18.0}/.gitignore +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/LICENSE +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/README.md +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/agent.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/core/_files.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/core/auth.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/core/checkpoints.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/core/clock.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/core/conversation.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/core/env.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/core/errors.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/core/events.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/core/home.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/core/ids.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/core/mcp.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/core/permissions.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/core/pricing.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/core/provider.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/core/schema.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/core/session_policy.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/core/skills.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/core/status.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/core/stream.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/core/turn.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/core/usage.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/execution/__init__.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/execution/confinement.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/execution/docker.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/execution/executor.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/execution/host.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/execution/process.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/execution/transform.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/oneshot.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/probe.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/providers/__init__.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/providers/claude/__init__.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/providers/claude/events.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/providers/claude/failures.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/providers/claude/hooks/__init__.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/providers/claude/hooks/confine_reads.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/providers/claude/parser.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/providers/claude/probe.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/providers/claude/rate_limits.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/providers/claude/sandbox.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/providers/claude/scripted.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/providers/claude/stream_argv.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/providers/claude/stream_transport.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/providers/claude/user_hooks.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/providers/codex/__init__.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/providers/codex/_toml.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/providers/codex/app_server/__init__.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/providers/codex/app_server/_wire.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/providers/codex/app_server/approvals.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/providers/codex/app_server/channel.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/providers/codex/app_server/errors.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/providers/codex/app_server/items.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/providers/codex/app_server/mcp.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/providers/codex/app_server/permissions.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/providers/codex/app_server/profile.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/providers/codex/app_server/protocol.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/providers/codex/app_server/rate_limits.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/providers/codex/app_server/transport.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/providers/codex/app_server/usage.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/providers/codex/events.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/providers/codex/failures.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/providers/codex/mcp_scope.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/providers/codex/parser.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/providers/codex/probe.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/providers/codex/rules.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/providers/codex/sandbox.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/providers/codex/scripted.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/providers/codex/skills.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/providers/copilot/__init__.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/providers/copilot/events.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/providers/copilot/parser.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/providers/copilot/probe.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/providers/copilot/provider.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/providers/copilot/scripted.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/providers/gemini/__init__.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/providers/gemini/events.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/providers/gemini/parser.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/providers/gemini/probe.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/providers/gemini/provider.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/providers/gemini/scripted.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/providers/opencode/__init__.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/providers/opencode/events.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/providers/opencode/parser.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/providers/opencode/probe.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/providers/opencode/scripted.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/py.typed +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/runtime.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/session.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/testing/__init__.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/testing/claude_stream.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/testing/clock.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/testing/codex_app_server.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/testing/confinement.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/testing/contracts.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/testing/conversation.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/testing/probe.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/agentshim/testing/process.py +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/tests/fixtures/claude_stream/README.md +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/tests/fixtures/codex/README.md +0 -0
- {agentshim-0.17.0 → agentshim-0.18.0}/tests/fixtures/codex_app_server/README.md +0 -0
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.18.0 (2026-10-16)
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- `context_window(provider, model)` and `model_spec(provider, model)`: a model's context
|
|
8
|
+
window in tokens from a static, versioned table (`ModelSpec`, `ModelTable`,
|
|
9
|
+
`default_models()`), each entry citing the vendor page it was read from. Covers the
|
|
10
|
+
Claude 5 family (`claude-fable-5-1`, `claude-opus-5-5`, `claude-sonnet-5-5`,
|
|
11
|
+
`claude-haiku-5-5`, all 1M) and current OpenAI models. An unknown model, or a
|
|
12
|
+
bracketed variant, returns `None`; the lookup never matches by prefix.
|
|
13
|
+
- `ProviderProfile.models`: the model ids a provider's CLI is known to run, the default
|
|
14
|
+
first, and `ProviderProfile.default_model`. Claude, Codex and opencode declare theirs.
|
|
15
|
+
|
|
3
16
|
## 0.17.0 (2026-10-15)
|
|
4
17
|
|
|
5
18
|
### Added
|
|
@@ -57,6 +57,8 @@ from .core import (
|
|
|
57
57
|
McpServer,
|
|
58
58
|
McpTransport,
|
|
59
59
|
ModelPricing,
|
|
60
|
+
ModelSpec,
|
|
61
|
+
ModelTable,
|
|
60
62
|
NativeMode,
|
|
61
63
|
NativePermissions,
|
|
62
64
|
NoopInstallation,
|
|
@@ -124,13 +126,16 @@ from .core import (
|
|
|
124
126
|
UsageReport,
|
|
125
127
|
compact_json,
|
|
126
128
|
compose_event_handlers,
|
|
129
|
+
context_window,
|
|
127
130
|
cost_usd,
|
|
131
|
+
default_models,
|
|
128
132
|
default_pricing,
|
|
129
133
|
dialect_problems,
|
|
130
134
|
host_auth_plan,
|
|
131
135
|
install_config_file,
|
|
132
136
|
interactive_env,
|
|
133
137
|
materialize,
|
|
138
|
+
model_spec,
|
|
134
139
|
normalize,
|
|
135
140
|
normalized_usage,
|
|
136
141
|
parse_json_object,
|
|
@@ -171,7 +176,7 @@ from .providers.opencode import OpencodeProvider
|
|
|
171
176
|
from .runtime import Agent
|
|
172
177
|
from .session import Session, Turn, TurnTicket, map_mcp_servers
|
|
173
178
|
|
|
174
|
-
__version__ = "0.
|
|
179
|
+
__version__ = "0.18.0"
|
|
175
180
|
|
|
176
181
|
__all__ = [
|
|
177
182
|
"Agent",
|
|
@@ -244,6 +249,8 @@ __all__ = [
|
|
|
244
249
|
"McpServer",
|
|
245
250
|
"McpTransport",
|
|
246
251
|
"ModelPricing",
|
|
252
|
+
"ModelSpec",
|
|
253
|
+
"ModelTable",
|
|
247
254
|
"NativeMode",
|
|
248
255
|
"NativePermissions",
|
|
249
256
|
"NoRunningTurnError",
|
|
@@ -327,7 +334,9 @@ __all__ = [
|
|
|
327
334
|
"compact_json",
|
|
328
335
|
"compose_event_handlers",
|
|
329
336
|
"confine",
|
|
337
|
+
"context_window",
|
|
330
338
|
"cost_usd",
|
|
339
|
+
"default_models",
|
|
331
340
|
"default_pricing",
|
|
332
341
|
"dialect_problems",
|
|
333
342
|
"get_provider",
|
|
@@ -336,6 +345,7 @@ __all__ = [
|
|
|
336
345
|
"interactive_env",
|
|
337
346
|
"map_mcp_servers",
|
|
338
347
|
"materialize",
|
|
348
|
+
"model_spec",
|
|
339
349
|
"normalize",
|
|
340
350
|
"normalized_usage",
|
|
341
351
|
"parse_json_object",
|
|
@@ -95,6 +95,7 @@ from .mcp import (
|
|
|
95
95
|
StdioMcpServer,
|
|
96
96
|
install_config_file,
|
|
97
97
|
)
|
|
98
|
+
from .models import ModelSpec, ModelTable, context_window, default_models, model_spec
|
|
98
99
|
from .permissions import ApprovalPolicy, NativeMode, NativePermissions
|
|
99
100
|
from .pricing import ModelPricing, PricingTable, cost_usd, default_pricing, price_for
|
|
100
101
|
from .profile import (
|
|
@@ -175,6 +176,8 @@ __all__ = [
|
|
|
175
176
|
"McpServer",
|
|
176
177
|
"McpTransport",
|
|
177
178
|
"ModelPricing",
|
|
179
|
+
"ModelSpec",
|
|
180
|
+
"ModelTable",
|
|
178
181
|
"NativeMode",
|
|
179
182
|
"NativePermissions",
|
|
180
183
|
"NoRunningTurnError",
|
|
@@ -242,13 +245,16 @@ __all__ = [
|
|
|
242
245
|
"UsageReport",
|
|
243
246
|
"compact_json",
|
|
244
247
|
"compose_event_handlers",
|
|
248
|
+
"context_window",
|
|
245
249
|
"cost_usd",
|
|
250
|
+
"default_models",
|
|
246
251
|
"default_pricing",
|
|
247
252
|
"dialect_problems",
|
|
248
253
|
"host_auth_plan",
|
|
249
254
|
"install_config_file",
|
|
250
255
|
"interactive_env",
|
|
251
256
|
"materialize",
|
|
257
|
+
"model_spec",
|
|
252
258
|
"normalize",
|
|
253
259
|
"normalized_usage",
|
|
254
260
|
"parse_json_object",
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
"""Static, versioned per-model facts: each model's context window and where it was read.
|
|
2
|
+
|
|
3
|
+
The table is data: one :class:`ModelSpec` per ``(vendor, model)``, each with
|
|
4
|
+
the official page its numbers were read from and the date they were checked,
|
|
5
|
+
and a table-level ``version`` and ``last_updated``. It is keyed by vendor, the
|
|
6
|
+
same way as the pricing table, because a context window belongs to the model,
|
|
7
|
+
whichever CLI runs it.
|
|
8
|
+
|
|
9
|
+
A lookup of a model the table does not know returns ``None``: an unknown
|
|
10
|
+
window is never guessed from a similar name, a prefix or a model family. A
|
|
11
|
+
bracketed variant (``claude-opus-5-5[1m]``) also returns ``None``, because a
|
|
12
|
+
variant may change the window. Callers pin or extend assumptions by passing
|
|
13
|
+
their own :class:`ModelTable` (see :meth:`ModelTable.with_entries`).
|
|
14
|
+
|
|
15
|
+
Which models a provider CLI is known to run, and which of them is its
|
|
16
|
+
default, is per-provider data on ``ProviderProfile.models``.
|
|
17
|
+
|
|
18
|
+
Updating the table is described in ``docs/models.md``.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
import datetime as dt
|
|
24
|
+
import re
|
|
25
|
+
from dataclasses import dataclass, field, replace
|
|
26
|
+
from typing import TYPE_CHECKING, cast
|
|
27
|
+
|
|
28
|
+
from agentshim.core.pricing import PROVIDER_VENDORS
|
|
29
|
+
|
|
30
|
+
if TYPE_CHECKING:
|
|
31
|
+
from collections.abc import Iterable
|
|
32
|
+
|
|
33
|
+
_DATE_SUFFIX = re.compile(r"-\d{8}$")
|
|
34
|
+
|
|
35
|
+
OPENAI_MODELS_URL = "https://developers.openai.com/api/docs/models"
|
|
36
|
+
ANTHROPIC_CONTEXT_WINDOWS_URL = (
|
|
37
|
+
"https://platform.claude.com/docs/en/build-with-claude/context-windows"
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@dataclass(frozen=True)
|
|
42
|
+
class ModelSpec:
|
|
43
|
+
"""One model's context window, with where and when it was read.
|
|
44
|
+
|
|
45
|
+
Attributes:
|
|
46
|
+
vendor: Who serves the model (``"openai"``, ``"anthropic"``).
|
|
47
|
+
model: The model id as the vendor names it.
|
|
48
|
+
context_window: The model's context window in tokens (input and
|
|
49
|
+
output together), at the vendor's default for that id.
|
|
50
|
+
source: URL of the official page the number was read from.
|
|
51
|
+
checked: Date the number was last checked against ``source``.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
vendor: str
|
|
55
|
+
model: str
|
|
56
|
+
context_window: int
|
|
57
|
+
source: str
|
|
58
|
+
checked: dt.date
|
|
59
|
+
|
|
60
|
+
def __post_init__(self) -> None:
|
|
61
|
+
"""Reject a missing name, source or date, and a window that is not a positive int."""
|
|
62
|
+
for name in ("vendor", "model"):
|
|
63
|
+
value: object = getattr(self, name)
|
|
64
|
+
if not isinstance(value, str) or not value.strip():
|
|
65
|
+
msg = f"{name} must be a non-empty string"
|
|
66
|
+
raise ValueError(msg)
|
|
67
|
+
source = cast("object", self.source)
|
|
68
|
+
if not isinstance(source, str) or not source.startswith("https://"):
|
|
69
|
+
msg = f"{self.vendor}/{self.model}: source must be an https:// URL"
|
|
70
|
+
raise ValueError(msg)
|
|
71
|
+
checked = cast("object", self.checked)
|
|
72
|
+
if not isinstance(checked, dt.date) or isinstance(checked, dt.datetime):
|
|
73
|
+
msg = f"{self.vendor}/{self.model}: checked must be a datetime.date"
|
|
74
|
+
raise TypeError(msg)
|
|
75
|
+
window = cast("object", self.context_window)
|
|
76
|
+
if isinstance(window, bool) or not isinstance(window, int) or window <= 0:
|
|
77
|
+
msg = f"{self.vendor}/{self.model}: context_window must be a positive int"
|
|
78
|
+
raise ValueError(msg)
|
|
79
|
+
|
|
80
|
+
@property
|
|
81
|
+
def key(self) -> tuple[str, str]:
|
|
82
|
+
"""The ``(vendor, model)`` this entry describes."""
|
|
83
|
+
return (self.vendor, self.model)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@dataclass(frozen=True)
|
|
87
|
+
class ModelTable:
|
|
88
|
+
"""A versioned set of :class:`ModelSpec` entries.
|
|
89
|
+
|
|
90
|
+
Attributes:
|
|
91
|
+
version: Names this set of facts; change it whenever a fact changes.
|
|
92
|
+
last_updated: Date the table as a whole was last updated. No entry
|
|
93
|
+
may have been checked later.
|
|
94
|
+
entries: One entry per ``(vendor, model)``.
|
|
95
|
+
"""
|
|
96
|
+
|
|
97
|
+
version: str
|
|
98
|
+
last_updated: dt.date
|
|
99
|
+
entries: tuple[ModelSpec, ...] = field(default=())
|
|
100
|
+
|
|
101
|
+
def __post_init__(self) -> None:
|
|
102
|
+
"""Reject a missing version, duplicate keys, and entries checked after ``last_updated``."""
|
|
103
|
+
version = cast("object", self.version)
|
|
104
|
+
if not isinstance(version, str) or not version.strip():
|
|
105
|
+
msg = "version must be a non-empty string"
|
|
106
|
+
raise ValueError(msg)
|
|
107
|
+
object.__setattr__(self, "entries", tuple(self.entries))
|
|
108
|
+
seen: set[tuple[str, str]] = set()
|
|
109
|
+
for entry in self.entries:
|
|
110
|
+
if not isinstance(cast("object", entry), ModelSpec):
|
|
111
|
+
msg = f"entries must be ModelSpec, got {entry!r}"
|
|
112
|
+
raise TypeError(msg)
|
|
113
|
+
if entry.key in seen:
|
|
114
|
+
msg = f"duplicate model entry for {entry.vendor}/{entry.model}"
|
|
115
|
+
raise ValueError(msg)
|
|
116
|
+
seen.add(entry.key)
|
|
117
|
+
if entry.checked > self.last_updated:
|
|
118
|
+
msg = (
|
|
119
|
+
f"{entry.vendor}/{entry.model} was checked {entry.checked}, "
|
|
120
|
+
f"after the table's last_updated {self.last_updated}"
|
|
121
|
+
)
|
|
122
|
+
raise ValueError(msg)
|
|
123
|
+
|
|
124
|
+
def get(self, vendor: str, model: str) -> ModelSpec | None:
|
|
125
|
+
"""The entry for exactly ``(vendor, model)``, or ``None``."""
|
|
126
|
+
for entry in self.entries:
|
|
127
|
+
if entry.key == (vendor, model):
|
|
128
|
+
return entry
|
|
129
|
+
return None
|
|
130
|
+
|
|
131
|
+
def with_entries(
|
|
132
|
+
self,
|
|
133
|
+
entries: Iterable[ModelSpec],
|
|
134
|
+
*,
|
|
135
|
+
version: str,
|
|
136
|
+
last_updated: dt.date | None = None,
|
|
137
|
+
) -> ModelTable:
|
|
138
|
+
"""A new table with *entries* added, replacing any with the same key."""
|
|
139
|
+
added = tuple(entries)
|
|
140
|
+
by_key = {entry.key: entry for entry in self.entries}
|
|
141
|
+
by_key.update({entry.key: entry for entry in added})
|
|
142
|
+
latest = max([self.last_updated, *(entry.checked for entry in added)])
|
|
143
|
+
return replace(
|
|
144
|
+
self,
|
|
145
|
+
version=version,
|
|
146
|
+
last_updated=last_updated or latest,
|
|
147
|
+
entries=tuple(by_key.values()),
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def model_spec(
|
|
152
|
+
provider: str, model: str | None, table: ModelTable | None = None
|
|
153
|
+
) -> ModelSpec | None:
|
|
154
|
+
"""The facts about *model* run through *provider*, or ``None`` when they are not known.
|
|
155
|
+
|
|
156
|
+
Accepts a provider name (``codex``, ``claude``) or a vendor name, and a
|
|
157
|
+
model with a ``vendor/`` prefix (opencode's ``provider/model``). Drops a
|
|
158
|
+
trailing date snapshot (``claude-haiku-4-5-20251001``). Returns ``None``
|
|
159
|
+
for a bracketed variant (``claude-opus-5-5[1m]``), for a provider that
|
|
160
|
+
runs no known vendor's models, and for any model the table lacks; it
|
|
161
|
+
never matches by prefix or family.
|
|
162
|
+
"""
|
|
163
|
+
if not model:
|
|
164
|
+
return None
|
|
165
|
+
name = model.strip().lower()
|
|
166
|
+
if "[" in name:
|
|
167
|
+
return None
|
|
168
|
+
vendor = PROVIDER_VENDORS.get(provider.strip().lower())
|
|
169
|
+
if "/" in name:
|
|
170
|
+
prefix, _, rest = name.partition("/")
|
|
171
|
+
vendor = PROVIDER_VENDORS.get(prefix)
|
|
172
|
+
name = rest
|
|
173
|
+
if vendor is None or not name:
|
|
174
|
+
return None
|
|
175
|
+
return (table or DEFAULT_MODELS).get(vendor, _DATE_SUFFIX.sub("", name))
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def context_window(provider: str, model: str | None, table: ModelTable | None = None) -> int | None:
|
|
179
|
+
"""*model*'s context window in tokens when run through *provider*, or ``None`` when not known."""
|
|
180
|
+
spec = model_spec(provider, model, table)
|
|
181
|
+
return None if spec is None else spec.context_window
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def default_models() -> ModelTable:
|
|
185
|
+
"""The table agentshim ships (:data:`DEFAULT_MODELS`)."""
|
|
186
|
+
return DEFAULT_MODELS
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
_CHECKED = dt.date(2026, 10, 16)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def _openai(model: str, context_window: int) -> ModelSpec:
|
|
193
|
+
# Each model's page under OPENAI_MODELS_URL ("<n> context window").
|
|
194
|
+
return ModelSpec(
|
|
195
|
+
vendor="openai",
|
|
196
|
+
model=model,
|
|
197
|
+
context_window=context_window,
|
|
198
|
+
source=f"{OPENAI_MODELS_URL}/{model}",
|
|
199
|
+
checked=_CHECKED,
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def _anthropic(model: str, context_window: int) -> ModelSpec:
|
|
204
|
+
# "Context window sizes by model": the 1M models list 1M as the default
|
|
205
|
+
# with no beta header; "other Claude models ... have a 200k-token" window.
|
|
206
|
+
return ModelSpec(
|
|
207
|
+
vendor="anthropic",
|
|
208
|
+
model=model,
|
|
209
|
+
context_window=context_window,
|
|
210
|
+
source=ANTHROPIC_CONTEXT_WINDOWS_URL,
|
|
211
|
+
checked=_CHECKED,
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
_ONE_MILLION = 1_000_000
|
|
216
|
+
_OPENAI_1_05M = 1_050_000
|
|
217
|
+
|
|
218
|
+
#: The table agentshim ships. Update it as described in ``docs/models.md``.
|
|
219
|
+
#: A model whose window could not be read from its vendor's page is left out.
|
|
220
|
+
DEFAULT_MODELS = ModelTable(
|
|
221
|
+
version="2026-10-16",
|
|
222
|
+
last_updated=_CHECKED,
|
|
223
|
+
entries=(
|
|
224
|
+
# OpenAI (Codex): each model page reads "1,050,000 context window".
|
|
225
|
+
_openai("gpt-6-astra", _OPENAI_1_05M),
|
|
226
|
+
_openai("gpt-6.1-sol", _OPENAI_1_05M),
|
|
227
|
+
_openai("gpt-6-sol", _OPENAI_1_05M),
|
|
228
|
+
_openai("gpt-6-luna", _OPENAI_1_05M),
|
|
229
|
+
_openai("gpt-5.6-sol", _OPENAI_1_05M),
|
|
230
|
+
_openai("gpt-5.6-terra", _OPENAI_1_05M),
|
|
231
|
+
_openai("gpt-5.6-luna", _OPENAI_1_05M),
|
|
232
|
+
_openai("gpt-5.5", _OPENAI_1_05M),
|
|
233
|
+
# Anthropic (Claude).
|
|
234
|
+
_anthropic("claude-fable-5-1", _ONE_MILLION),
|
|
235
|
+
_anthropic("claude-fable-5", _ONE_MILLION),
|
|
236
|
+
_anthropic("claude-opus-5-5", _ONE_MILLION),
|
|
237
|
+
_anthropic("claude-opus-5", _ONE_MILLION),
|
|
238
|
+
_anthropic("claude-opus-4-8", _ONE_MILLION),
|
|
239
|
+
_anthropic("claude-opus-4-7", _ONE_MILLION),
|
|
240
|
+
_anthropic("claude-opus-4-6", _ONE_MILLION),
|
|
241
|
+
_anthropic("claude-sonnet-5-5", _ONE_MILLION),
|
|
242
|
+
_anthropic("claude-sonnet-5", _ONE_MILLION),
|
|
243
|
+
_anthropic("claude-sonnet-4-6", _ONE_MILLION),
|
|
244
|
+
_anthropic("claude-haiku-5-5", _ONE_MILLION),
|
|
245
|
+
_anthropic("claude-haiku-4-5", 200_000),
|
|
246
|
+
),
|
|
247
|
+
)
|
|
@@ -237,3 +237,13 @@ class ProviderProfile:
|
|
|
237
237
|
#: The settings files the CLI reads at start that can change how it logs
|
|
238
238
|
#: in, or ``None`` if it has none (0.17.0+).
|
|
239
239
|
settings: SettingsSpec | None = None
|
|
240
|
+
#: The model ids agentshim knows this CLI runs, as its ``--model`` flag
|
|
241
|
+
#: takes them, the provider's default first (0.18.0+). Empty means
|
|
242
|
+
#: agentshim knows of none: a caller may still pass any model, at its own
|
|
243
|
+
#: risk. A model's facts (its context window) are in ``model_spec``.
|
|
244
|
+
models: tuple[str, ...] = ()
|
|
245
|
+
|
|
246
|
+
@property
|
|
247
|
+
def default_model(self) -> str | None:
|
|
248
|
+
"""The model to run when a caller must name one and has none: ``models[0]``, or ``None``."""
|
|
249
|
+
return self.models[0] if self.models else None
|
|
@@ -69,6 +69,18 @@ STRICT_MCP_FLAG = "--strict-mcp-config"
|
|
|
69
69
|
|
|
70
70
|
PROFILE = ProviderProfile(
|
|
71
71
|
name="claude",
|
|
72
|
+
# Model ids Claude Code's ``--model`` takes and agentshim has run, the
|
|
73
|
+
# default first. Context windows are in ``agentshim.core.models``.
|
|
74
|
+
models=(
|
|
75
|
+
"claude-opus-5",
|
|
76
|
+
"claude-opus-5-5",
|
|
77
|
+
"claude-fable-5-1",
|
|
78
|
+
"claude-fable-5",
|
|
79
|
+
"claude-sonnet-5-5",
|
|
80
|
+
"claude-sonnet-5",
|
|
81
|
+
"claude-haiku-5-5",
|
|
82
|
+
"claude-haiku-4-5",
|
|
83
|
+
),
|
|
72
84
|
display_name="Claude Code",
|
|
73
85
|
binary="claude",
|
|
74
86
|
supports_resume=True,
|
|
@@ -73,6 +73,17 @@ _HTTP_HEADERS_UNSUPPORTED = (
|
|
|
73
73
|
|
|
74
74
|
PROFILE = ProviderProfile(
|
|
75
75
|
name="codex",
|
|
76
|
+
# Model ids Codex's ``--model`` takes and agentshim has run, the default
|
|
77
|
+
# first. Context windows are in ``agentshim.core.models``.
|
|
78
|
+
models=(
|
|
79
|
+
"gpt-5.6-sol",
|
|
80
|
+
"gpt-5.6-luna",
|
|
81
|
+
"gpt-5.6-terra",
|
|
82
|
+
"gpt-5.5",
|
|
83
|
+
"gpt-6-astra",
|
|
84
|
+
"gpt-6-sol",
|
|
85
|
+
"gpt-6-luna",
|
|
86
|
+
),
|
|
76
87
|
display_name="Codex",
|
|
77
88
|
binary="codex",
|
|
78
89
|
supports_resume=True,
|
|
@@ -47,6 +47,9 @@ _NO_WORKSPACE = "opencode installs MCP servers into <cwd>/opencode.json; the tur
|
|
|
47
47
|
|
|
48
48
|
PROFILE = ProviderProfile(
|
|
49
49
|
name="opencode",
|
|
50
|
+
# ``provider/model`` ids opencode's ``--model`` takes and agentshim has
|
|
51
|
+
# run, the default first.
|
|
52
|
+
models=("google-vertex/gemini-3-pro-preview",),
|
|
50
53
|
display_name="opencode",
|
|
51
54
|
binary="opencode",
|
|
52
55
|
supports_resume=True,
|
|
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
|
|
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
|