aihi-models 0.1.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.
@@ -0,0 +1,25 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ node_modules/
4
+ .pnpm-store/
5
+ .venv/
6
+ venv/
7
+ *.egg-info/
8
+ dist/
9
+ build/
10
+ .pytest_cache/
11
+ .ruff_cache/
12
+ .mypy_cache/
13
+ .aihi/settings.local.json
14
+ .aihi/audit.jsonl
15
+ .DS_Store
16
+
17
+ # Local legacy SQLite compatibility fixture and SQLite WAL sidecars.
18
+ /tests/fixtures/session_schema_v1.sqlite3*
19
+
20
+ # Superpowers working documents: local process artefacts, not project docs.
21
+ /docs/superpowers/
22
+
23
+ # Architecture decision records and RFC drafts are local working notes.
24
+ /docs/adr/
25
+ /docs/rfcs/
@@ -0,0 +1,133 @@
1
+ Metadata-Version: 2.5
2
+ Name: aihi-models
3
+ Version: 0.1.0
4
+ Summary: Provider-neutral model contracts and provider adapters for AIHI
5
+ License: MIT
6
+ Classifier: Programming Language :: Python :: 3.11
7
+ Classifier: Programming Language :: Python :: 3.12
8
+ Classifier: Programming Language :: Python :: 3.13
9
+ Classifier: Typing :: Typed
10
+ Requires-Python: >=3.11
11
+ Requires-Dist: httpx>=0.27
12
+ Description-Content-Type: text/markdown
13
+
14
+ # aihi-models
15
+
16
+ [English] | [简体中文](README.zh-CN.md)
17
+
18
+ Provider-neutral model contracts and provider adapters for AIHI.
19
+
20
+ `aihi-models` is the lowest Python layer in the repository. It normalizes messages, streamed output, tool definitions, usage, provider failures, and wire serialization so the agent runtime does not depend on one vendor SDK.
21
+
22
+ ## Why this package exists
23
+
24
+ The package deliberately owns model-facing primitives only:
25
+
26
+ - immutable request/response and content-block contracts;
27
+ - normalized streaming chunks for text and tool input;
28
+ - provider adapters and transport abstractions;
29
+ - typed provider errors and context-length classification;
30
+ - versioned message serialization and token estimation.
31
+
32
+ It does **not** own model routing, a gateway, application configuration, prompt policy, sessions, tools, or an agent loop. Those belong to the application/runtime layers.
33
+
34
+ ## Supported providers
35
+
36
+ | Adapter | Use case | Notes |
37
+ | --- | --- | --- |
38
+ | `OpenAIProvider` | OpenAI Chat Completions | Configure the endpoint and API key explicitly. |
39
+ | `AnthropicProvider` | Anthropic Messages API | Normalizes Anthropic content blocks to the common stream model. |
40
+ | `DeepSeekProvider` | DeepSeek chat models | Uses DeepSeek's OpenAI-compatible API by default. |
41
+ | `OpenAICompatibleProvider` | Other OpenAI-compatible endpoints | Requires an explicit full `base_url` for the chat-completions endpoint. |
42
+ | `FakeProvider` | Tests and local contract fixtures | Deterministic scripted responses; no network access. |
43
+
44
+ Providers are flat modules under `src/aihi/models/providers`. Credentials and model selection are supplied by the application; constructors do not silently read environment variables.
45
+
46
+ ## Installation
47
+
48
+ From the repository workspace:
49
+
50
+ ```bash
51
+ uv sync
52
+ ```
53
+
54
+ For a local editable install:
55
+
56
+ ```bash
57
+ uv pip install -e packages/aihi/models
58
+ ```
59
+
60
+ The package requires Python 3.11+ and depends on `httpx` for the default HTTP transport.
61
+
62
+ ## Minimal example
63
+
64
+ ```python
65
+ import asyncio
66
+
67
+ from aihi.models import FakeProvider, FakeStep, Message, ModelRequest
68
+
69
+ provider = FakeProvider([
70
+ FakeStep(text="Hello from a deterministic provider."),
71
+ ])
72
+
73
+ request = ModelRequest(
74
+ model="fake-model",
75
+ messages=[Message.text("user", "Say hello.")],
76
+ )
77
+
78
+ async def main() -> None:
79
+ chunks = [chunk async for chunk in provider.stream(request)]
80
+ print(chunks[-1])
81
+
82
+
83
+ asyncio.run(main())
84
+ ```
85
+
86
+ Providers also expose normalized asynchronous streaming. A stream is made of typed chunks such as `BlockStart`, `TextDelta`, `ToolInputDelta`, and `MessageEnd`, allowing the runtime to render or persist output without vendor-specific branching.
87
+
88
+ ## Public API
89
+
90
+ The package root re-exports the stable building blocks:
91
+
92
+ - Contracts: `Message`, `ModelRequest`, `ModelResponse`, `ModelToolDefinition`, `Capabilities`, content blocks, and `Usage`.
93
+ - Providers: `OpenAIProvider`, `AnthropicProvider`, `DeepSeekProvider`, `OpenAICompatibleProvider`, and `FakeProvider`.
94
+ - Errors: `ProviderError`, `ProviderHTTPError`, `ProviderProtocolError`, `ProviderTimeout`, and `ProviderContextLengthError`.
95
+ - Serialization: `encode_message`, `decode_message`, `ModelMessageEnvelope`, and `MESSAGE_SCHEMA_VERSION`.
96
+ - Transport: `HttpxTransport`, `JsonTransport`, and `HttpRequest`.
97
+
98
+ Import from `aihi.models` rather than reaching into private modules.
99
+
100
+ ## Compatibility and errors
101
+
102
+ Provider adapters map vendor responses into one common response/stream vocabulary and classify failures into stable error types. Callers should handle `ProviderTimeout`, HTTP failures, protocol failures, and context-length failures separately when deciding whether to retry, compact context, or surface an error.
103
+
104
+ Message envelopes are versioned. Unknown schema versions raise `UnsupportedMessageSchema`; applications should persist the versioned envelope rather than relying on a provider's native JSON.
105
+
106
+ ## Development
107
+
108
+ Run package tests and repository-wide static checks from the root:
109
+
110
+ ```bash
111
+ uv run pytest packages/aihi/models/tests
112
+ uv run ruff check packages/aihi/models
113
+ uv run mypy
114
+ ```
115
+
116
+ Build a wheel without resolving dependencies from the network:
117
+
118
+ ```bash
119
+ uv run python -m build --wheel --no-isolation packages/aihi/models
120
+ ```
121
+
122
+ ## Security notes
123
+
124
+ - Pass API keys from the application boundary and keep them out of model messages and persisted events.
125
+ - Treat provider response text and tool arguments as untrusted input.
126
+ - Use the default `httpx` transport or provide a transport with equivalent timeout and TLS behavior.
127
+ - The fake provider is intended for tests; it is not a fallback for production failures.
128
+
129
+ ## Related packages
130
+
131
+ - [`aihi-agent`](../agent/README.md) builds the provider-neutral runtime on these contracts.
132
+ - [`aihi-code-agent`](../code-agent/README.md) composes providers into a coding-agent application.
133
+ - [Repository architecture](../../../docs/ARCHITECTURE.md)
@@ -0,0 +1,120 @@
1
+ # aihi-models
2
+
3
+ [English] | [简体中文](README.zh-CN.md)
4
+
5
+ Provider-neutral model contracts and provider adapters for AIHI.
6
+
7
+ `aihi-models` is the lowest Python layer in the repository. It normalizes messages, streamed output, tool definitions, usage, provider failures, and wire serialization so the agent runtime does not depend on one vendor SDK.
8
+
9
+ ## Why this package exists
10
+
11
+ The package deliberately owns model-facing primitives only:
12
+
13
+ - immutable request/response and content-block contracts;
14
+ - normalized streaming chunks for text and tool input;
15
+ - provider adapters and transport abstractions;
16
+ - typed provider errors and context-length classification;
17
+ - versioned message serialization and token estimation.
18
+
19
+ It does **not** own model routing, a gateway, application configuration, prompt policy, sessions, tools, or an agent loop. Those belong to the application/runtime layers.
20
+
21
+ ## Supported providers
22
+
23
+ | Adapter | Use case | Notes |
24
+ | --- | --- | --- |
25
+ | `OpenAIProvider` | OpenAI Chat Completions | Configure the endpoint and API key explicitly. |
26
+ | `AnthropicProvider` | Anthropic Messages API | Normalizes Anthropic content blocks to the common stream model. |
27
+ | `DeepSeekProvider` | DeepSeek chat models | Uses DeepSeek's OpenAI-compatible API by default. |
28
+ | `OpenAICompatibleProvider` | Other OpenAI-compatible endpoints | Requires an explicit full `base_url` for the chat-completions endpoint. |
29
+ | `FakeProvider` | Tests and local contract fixtures | Deterministic scripted responses; no network access. |
30
+
31
+ Providers are flat modules under `src/aihi/models/providers`. Credentials and model selection are supplied by the application; constructors do not silently read environment variables.
32
+
33
+ ## Installation
34
+
35
+ From the repository workspace:
36
+
37
+ ```bash
38
+ uv sync
39
+ ```
40
+
41
+ For a local editable install:
42
+
43
+ ```bash
44
+ uv pip install -e packages/aihi/models
45
+ ```
46
+
47
+ The package requires Python 3.11+ and depends on `httpx` for the default HTTP transport.
48
+
49
+ ## Minimal example
50
+
51
+ ```python
52
+ import asyncio
53
+
54
+ from aihi.models import FakeProvider, FakeStep, Message, ModelRequest
55
+
56
+ provider = FakeProvider([
57
+ FakeStep(text="Hello from a deterministic provider."),
58
+ ])
59
+
60
+ request = ModelRequest(
61
+ model="fake-model",
62
+ messages=[Message.text("user", "Say hello.")],
63
+ )
64
+
65
+ async def main() -> None:
66
+ chunks = [chunk async for chunk in provider.stream(request)]
67
+ print(chunks[-1])
68
+
69
+
70
+ asyncio.run(main())
71
+ ```
72
+
73
+ Providers also expose normalized asynchronous streaming. A stream is made of typed chunks such as `BlockStart`, `TextDelta`, `ToolInputDelta`, and `MessageEnd`, allowing the runtime to render or persist output without vendor-specific branching.
74
+
75
+ ## Public API
76
+
77
+ The package root re-exports the stable building blocks:
78
+
79
+ - Contracts: `Message`, `ModelRequest`, `ModelResponse`, `ModelToolDefinition`, `Capabilities`, content blocks, and `Usage`.
80
+ - Providers: `OpenAIProvider`, `AnthropicProvider`, `DeepSeekProvider`, `OpenAICompatibleProvider`, and `FakeProvider`.
81
+ - Errors: `ProviderError`, `ProviderHTTPError`, `ProviderProtocolError`, `ProviderTimeout`, and `ProviderContextLengthError`.
82
+ - Serialization: `encode_message`, `decode_message`, `ModelMessageEnvelope`, and `MESSAGE_SCHEMA_VERSION`.
83
+ - Transport: `HttpxTransport`, `JsonTransport`, and `HttpRequest`.
84
+
85
+ Import from `aihi.models` rather than reaching into private modules.
86
+
87
+ ## Compatibility and errors
88
+
89
+ Provider adapters map vendor responses into one common response/stream vocabulary and classify failures into stable error types. Callers should handle `ProviderTimeout`, HTTP failures, protocol failures, and context-length failures separately when deciding whether to retry, compact context, or surface an error.
90
+
91
+ Message envelopes are versioned. Unknown schema versions raise `UnsupportedMessageSchema`; applications should persist the versioned envelope rather than relying on a provider's native JSON.
92
+
93
+ ## Development
94
+
95
+ Run package tests and repository-wide static checks from the root:
96
+
97
+ ```bash
98
+ uv run pytest packages/aihi/models/tests
99
+ uv run ruff check packages/aihi/models
100
+ uv run mypy
101
+ ```
102
+
103
+ Build a wheel without resolving dependencies from the network:
104
+
105
+ ```bash
106
+ uv run python -m build --wheel --no-isolation packages/aihi/models
107
+ ```
108
+
109
+ ## Security notes
110
+
111
+ - Pass API keys from the application boundary and keep them out of model messages and persisted events.
112
+ - Treat provider response text and tool arguments as untrusted input.
113
+ - Use the default `httpx` transport or provide a transport with equivalent timeout and TLS behavior.
114
+ - The fake provider is intended for tests; it is not a fallback for production failures.
115
+
116
+ ## Related packages
117
+
118
+ - [`aihi-agent`](../agent/README.md) builds the provider-neutral runtime on these contracts.
119
+ - [`aihi-code-agent`](../code-agent/README.md) composes providers into a coding-agent application.
120
+ - [Repository architecture](../../../docs/ARCHITECTURE.md)
@@ -0,0 +1,67 @@
1
+ # aihi-models
2
+
3
+ [English](README.md) | **简体中文**
4
+
5
+ AIHI 的 Provider-neutral 模型契约和 Provider 适配器。它是仓库中最低层的 Python 包,负责统一
6
+ 消息、流式输出、工具定义、用量、Provider 错误、序列化和 token 估算。
7
+
8
+ ## 职责边界
9
+
10
+ - 不可变的请求、响应和 Content Block 契约。
11
+ - 文本、工具输入和推理载荷的标准化 Stream Chunk。
12
+ - Provider Protocol、HTTP transport 和 Provider adapters。
13
+ - 类型化 Provider 错误、Context Length 分类和 Message Schema 版本化 codec。
14
+
15
+ 本包不负责 ModelRouter、ModelGateway、应用配置、Prompt、Session、ToolSpec 或 Agent loop。
16
+ 这些能力由应用层或 `aihi-agent` 负责。
17
+
18
+ ## 支持的 Provider
19
+
20
+ | Adapter | 用途 |
21
+ | --- | --- |
22
+ | `OpenAIProvider` | OpenAI Chat Completions |
23
+ | `AnthropicProvider` | Anthropic Messages API |
24
+ | `DeepSeekProvider` | DeepSeek;复用 OpenAI-compatible 协议 |
25
+ | `OpenAICompatibleProvider` | 其他兼容 OpenAI API 的服务;必须传入完整 endpoint |
26
+ | `FakeProvider` | 测试和离线 fixture |
27
+
28
+ Provider 是扁平模块,凭据、模型选择和多个 Provider 的组合由应用层提供,不会静默读取环境变量。
29
+
30
+ ## 安装
31
+
32
+ ```bash
33
+ uv sync
34
+ uv pip install -e packages/aihi/models
35
+ ```
36
+
37
+ 要求 Python 3.11+,默认 HTTP transport 依赖 `httpx`。
38
+
39
+ ## 最小示例
40
+
41
+ ```python
42
+ from aihi.models import Message, ModelRequest
43
+ from aihi.models.providers.fake import FakeProvider
44
+
45
+ provider = FakeProvider()
46
+ request = ModelRequest(
47
+ model="fake-model",
48
+ messages=(Message.user("Say hello"),),
49
+ )
50
+ async for chunk in provider.stream(request):
51
+ print(chunk)
52
+ ```
53
+
54
+ ## 公共 API 与兼容性
55
+
56
+ 跨包只使用 `aihi.models` 顶层导出。Message codec 使用独立版本号;旧事件缺少版本时按 v1 读取。
57
+ Provider stream 在首个 chunk 之后不得自动切换 Provider,错误必须包含稳定 code 和 `retryable`。
58
+
59
+ ## 开发
60
+
61
+ ```bash
62
+ pytest packages/aihi/models/tests
63
+ ruff check packages/aihi/models
64
+ mypy packages/aihi/models/src
65
+ ```
66
+
67
+ 详见仓库 [架构文档](../../../docs/ARCHITECTURE.zh-CN.md)。
@@ -0,0 +1,24 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "aihi-models"
7
+ version = "0.1.0"
8
+ description = "Provider-neutral model contracts and provider adapters for AIHI"
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ license = { text = "MIT" }
12
+ dependencies = [
13
+ "httpx>=0.27",
14
+ ]
15
+ classifiers = [
16
+ "Programming Language :: Python :: 3.11",
17
+ "Programming Language :: Python :: 3.12",
18
+ "Programming Language :: Python :: 3.13",
19
+ "Typing :: Typed",
20
+ ]
21
+
22
+ [tool.hatch.build.targets.wheel]
23
+ packages = ["src/aihi"]
24
+ artifacts = ["src/aihi/models/py.typed"]
@@ -0,0 +1,115 @@
1
+ """Public API for model contracts and Provider adapters."""
2
+
3
+ from aihi.models.base import (
4
+ BlockEnd,
5
+ BlockStart,
6
+ MessageEnd,
7
+ MessageStart,
8
+ Provider,
9
+ StreamChunk,
10
+ TextDelta,
11
+ ThinkingDelta,
12
+ ToolInputDelta,
13
+ )
14
+ from aihi.models.errors import (
15
+ ModelsError,
16
+ ProviderContextLengthError,
17
+ ProviderError,
18
+ ProviderFailure,
19
+ ProviderHTTPError,
20
+ ProviderProtocolError,
21
+ ProviderTimeout,
22
+ )
23
+ from aihi.models.providers import (
24
+ AnthropicProvider,
25
+ DeepSeekProvider,
26
+ OpenAICompatibleProvider,
27
+ OpenAIProvider,
28
+ )
29
+ from aihi.models.providers.anthropic import AnthropicConfig
30
+ from aihi.models.providers.deepseek import DeepSeekConfig
31
+ from aihi.models.providers.fake import FakeProvider, FakeStep
32
+ from aihi.models.providers.openai import OpenAIConfig
33
+ from aihi.models.serialization import (
34
+ MESSAGE_SCHEMA_VERSION,
35
+ ModelMessageEnvelope,
36
+ UnsupportedMessageSchema,
37
+ decode_message,
38
+ encode_message,
39
+ )
40
+ from aihi.models.tokens import estimate_messages_tokens, estimate_text_tokens
41
+ from aihi.models.transport import HttpRequest, HttpxTransport, JsonTransport
42
+ from aihi.models.types import (
43
+ Capabilities,
44
+ ContentBlock,
45
+ ImageBlock,
46
+ JsonObject,
47
+ Message,
48
+ ModelRequest,
49
+ ModelResponse,
50
+ ModelToolDefinition,
51
+ Role,
52
+ StopReason,
53
+ TextBlock,
54
+ ThinkingBlock,
55
+ ToolCallBlock,
56
+ ToolResultBlock,
57
+ Usage,
58
+ block_from_dict,
59
+ )
60
+
61
+ __all__ = [
62
+ "AnthropicConfig",
63
+ "AnthropicProvider",
64
+ "BlockEnd",
65
+ "BlockStart",
66
+ "Capabilities",
67
+ "ContentBlock",
68
+ "DeepSeekConfig",
69
+ "DeepSeekProvider",
70
+ "FakeProvider",
71
+ "FakeStep",
72
+ "HttpRequest",
73
+ "HttpxTransport",
74
+ "ImageBlock",
75
+ "JsonObject",
76
+ "JsonTransport",
77
+ "MESSAGE_SCHEMA_VERSION",
78
+ "Message",
79
+ "MessageEnd",
80
+ "MessageStart",
81
+ "ModelMessageEnvelope",
82
+ "ModelRequest",
83
+ "ModelResponse",
84
+ "ModelToolDefinition",
85
+ "ModelsError",
86
+ "OpenAICompatibleProvider",
87
+ "OpenAIConfig",
88
+ "OpenAIProvider",
89
+ "Provider",
90
+ "ProviderContextLengthError",
91
+ "ProviderError",
92
+ "ProviderFailure",
93
+ "ProviderHTTPError",
94
+ "ProviderProtocolError",
95
+ "ProviderTimeout",
96
+ "Role",
97
+ "StopReason",
98
+ "StreamChunk",
99
+ "TextBlock",
100
+ "TextDelta",
101
+ "ThinkingBlock",
102
+ "ThinkingDelta",
103
+ "ToolCallBlock",
104
+ "ToolInputDelta",
105
+ "ToolResultBlock",
106
+ "UnsupportedMessageSchema",
107
+ "Usage",
108
+ "block_from_dict",
109
+ "decode_message",
110
+ "encode_message",
111
+ "estimate_messages_tokens",
112
+ "estimate_text_tokens",
113
+ ]
114
+
115
+ __version__ = "0.1.0"
@@ -0,0 +1,13 @@
1
+ """Private identifiers for model-protocol value objects."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import secrets
6
+
7
+
8
+ def new_id(prefix: str) -> str:
9
+ return f"{prefix}_{secrets.token_hex(12)}"
10
+
11
+
12
+ def new_message_id() -> str:
13
+ return new_id("msg")
@@ -0,0 +1,70 @@
1
+ """Provider protocol and normalized wire-level streaming chunks."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import AsyncIterator
6
+ from dataclasses import dataclass
7
+ from typing import Literal, Protocol, TypeAlias
8
+
9
+ from aihi.models.types import Capabilities, ModelRequest, ModelResponse
10
+
11
+
12
+ @dataclass(frozen=True, slots=True)
13
+ class MessageStart:
14
+ model: str
15
+ kind: Literal["message_start"] = "message_start"
16
+
17
+
18
+ @dataclass(frozen=True, slots=True)
19
+ class BlockStart:
20
+ index: int
21
+ block_kind: str
22
+ kind: Literal["block_start"] = "block_start"
23
+
24
+
25
+ @dataclass(frozen=True, slots=True)
26
+ class TextDelta:
27
+ index: int
28
+ text: str
29
+ kind: Literal["text_delta"] = "text_delta"
30
+
31
+
32
+ @dataclass(frozen=True, slots=True)
33
+ class ThinkingDelta:
34
+ index: int
35
+ text: str
36
+ kind: Literal["thinking_delta"] = "thinking_delta"
37
+
38
+
39
+ @dataclass(frozen=True, slots=True)
40
+ class ToolInputDelta:
41
+ index: int
42
+ partial_json: str
43
+ kind: Literal["tool_input_delta"] = "tool_input_delta"
44
+
45
+
46
+ @dataclass(frozen=True, slots=True)
47
+ class BlockEnd:
48
+ index: int
49
+ kind: Literal["block_end"] = "block_end"
50
+
51
+
52
+ @dataclass(frozen=True, slots=True)
53
+ class MessageEnd:
54
+ response: ModelResponse
55
+ kind: Literal["message_end"] = "message_end"
56
+
57
+
58
+ StreamChunk: TypeAlias = (
59
+ MessageStart | BlockStart | TextDelta | ThinkingDelta | ToolInputDelta | BlockEnd | MessageEnd
60
+ )
61
+
62
+
63
+ class Provider(Protocol):
64
+ name: str
65
+
66
+ def capabilities(self, model: str) -> Capabilities: ...
67
+
68
+ def stream(self, request: ModelRequest) -> AsyncIterator[StreamChunk]: ...
69
+
70
+ async def count_tokens(self, request: ModelRequest) -> int: ...
@@ -0,0 +1,79 @@
1
+ """Stable Provider error taxonomy."""
2
+
3
+ from __future__ import annotations
4
+
5
+
6
+ class ModelsError(Exception):
7
+ code = "models_error"
8
+ retryable = False
9
+
10
+ def __init__(self, message: str, *, details: dict[str, object] | None = None) -> None:
11
+ super().__init__(message)
12
+ self.details = details or {}
13
+
14
+
15
+ class ProviderError(ModelsError):
16
+ code = "provider_failure"
17
+
18
+
19
+ class ProviderProtocolError(ProviderError):
20
+ code = "provider_protocol_error"
21
+
22
+
23
+ class ProviderHTTPError(ProviderError):
24
+ code = "provider_http_error"
25
+
26
+
27
+ class ProviderContextLengthError(ProviderHTTPError):
28
+ code = "provider_context_length"
29
+
30
+
31
+ class ProviderTimeout(ProviderError):
32
+ code = "provider_timeout"
33
+ retryable = True
34
+
35
+
36
+ def is_context_length_message(message: str) -> bool:
37
+ normalized = message.casefold().replace("-", "_").replace(" ", "_")
38
+ explicit_markers = (
39
+ "context_length",
40
+ "context_window",
41
+ "maximum_context",
42
+ "max_context",
43
+ "context_exceeded",
44
+ "context_limit",
45
+ "context_size",
46
+ )
47
+ if any(marker in normalized for marker in explicit_markers):
48
+ return True
49
+ if not any(term in normalized for term in ("context", "prompt", "input")):
50
+ return False
51
+ return any(
52
+ marker in normalized
53
+ for marker in (
54
+ "prompt_is_too_long",
55
+ "prompt_too_long",
56
+ "input_is_too_long",
57
+ "input_too_long",
58
+ "too_many_tokens",
59
+ "token_limit",
60
+ "maximum_tokens",
61
+ "max_tokens",
62
+ )
63
+ )
64
+
65
+
66
+ # Compatibility name for the stable provider-failure contract.
67
+ ProviderFailure = ProviderError
68
+
69
+
70
+ __all__ = [
71
+ "ModelsError",
72
+ "ProviderContextLengthError",
73
+ "ProviderError",
74
+ "ProviderFailure",
75
+ "ProviderHTTPError",
76
+ "ProviderProtocolError",
77
+ "ProviderTimeout",
78
+ "is_context_length_message",
79
+ ]
@@ -0,0 +1,13 @@
1
+ """Built-in model provider adapters."""
2
+
3
+ from aihi.models.providers.anthropic import AnthropicProvider
4
+ from aihi.models.providers.deepseek import DeepSeekProvider
5
+ from aihi.models.providers.openai import OpenAIProvider
6
+ from aihi.models.providers.openai_compatible import OpenAICompatibleProvider
7
+
8
+ __all__ = [
9
+ "AnthropicProvider",
10
+ "DeepSeekProvider",
11
+ "OpenAICompatibleProvider",
12
+ "OpenAIProvider",
13
+ ]