codex-ai 0.2.3__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.
- {codex_ai-0.2.3 → codex_ai-0.2.4}/PKG-INFO +12 -8
- {codex_ai-0.2.3 → codex_ai-0.2.4}/README.md +10 -6
- {codex_ai-0.2.3 → codex_ai-0.2.4}/docs/en/architecture/core/README.md +19 -15
- {codex_ai-0.2.3 → codex_ai-0.2.4}/docs/en/architecture/core/data_flow.md +5 -3
- {codex_ai-0.2.3 → codex_ai-0.2.4}/docs/en/architecture/providers/README.md +3 -3
- {codex_ai-0.2.3 → codex_ai-0.2.4}/docs/ru/architecture/core/README.md +18 -15
- {codex_ai-0.2.3 → codex_ai-0.2.4}/docs/ru/architecture/core/data_flow.md +5 -3
- {codex_ai-0.2.3 → codex_ai-0.2.4}/docs/ru/architecture/providers/README.md +3 -3
- {codex_ai-0.2.3 → codex_ai-0.2.4}/pyproject.toml +1 -1
- {codex_ai-0.2.3 → codex_ai-0.2.4}/src/codex_ai/core/__init__.py +1 -1
- {codex_ai-0.2.3 → codex_ai-0.2.4}/src/codex_ai/core/dispatcher.py +6 -5
- {codex_ai-0.2.3 → codex_ai-0.2.4}/src/codex_ai/core/protocol.py +10 -9
- {codex_ai-0.2.3 → codex_ai-0.2.4}/src/codex_ai/providers/__init__.py +1 -1
- {codex_ai-0.2.3 → codex_ai-0.2.4}/src/codex_ai/providers/gemini.py +4 -3
- {codex_ai-0.2.3 → codex_ai-0.2.4}/src/codex_ai/providers/openai.py +4 -3
- {codex_ai-0.2.3 → codex_ai-0.2.4}/.github/workflows/ci.yml +0 -0
- {codex_ai-0.2.3 → codex_ai-0.2.4}/.github/workflows/docs.yml +0 -0
- {codex_ai-0.2.3 → codex_ai-0.2.4}/.github/workflows/publish.yml +0 -0
- {codex_ai-0.2.3 → codex_ai-0.2.4}/.gitignore +0 -0
- {codex_ai-0.2.3 → codex_ai-0.2.4}/.nojekyll +0 -0
- {codex_ai-0.2.3 → codex_ai-0.2.4}/.pre-commit-config.yaml +0 -0
- {codex_ai-0.2.3 → codex_ai-0.2.4}/.python-version +0 -0
- {codex_ai-0.2.3 → codex_ai-0.2.4}/.secrets.baseline +0 -0
- {codex_ai-0.2.3 → codex_ai-0.2.4}/CHANGELOG.md +0 -0
- {codex_ai-0.2.3 → codex_ai-0.2.4}/LICENSE +0 -0
- {codex_ai-0.2.3 → codex_ai-0.2.4}/docs/changelog.md +0 -0
- {codex_ai-0.2.3 → codex_ai-0.2.4}/docs/en/api/core/dispatcher.md +0 -0
- {codex_ai-0.2.3 → codex_ai-0.2.4}/docs/en/api/core/exceptions.md +0 -0
- {codex_ai-0.2.3 → codex_ai-0.2.4}/docs/en/api/core/protocol.md +0 -0
- {codex_ai-0.2.3 → codex_ai-0.2.4}/docs/en/api/core/router.md +0 -0
- {codex_ai-0.2.3 → codex_ai-0.2.4}/docs/en/api/core/sync.md +0 -0
- {codex_ai-0.2.3 → codex_ai-0.2.4}/docs/en/api/index.md +0 -0
- {codex_ai-0.2.3 → codex_ai-0.2.4}/docs/en/api/providers/gemini.md +0 -0
- {codex_ai-0.2.3 → codex_ai-0.2.4}/docs/en/api/providers/openai.md +0 -0
- {codex_ai-0.2.3 → codex_ai-0.2.4}/docs/en/architecture/providers/data_flow.md +0 -0
- {codex_ai-0.2.3 → codex_ai-0.2.4}/docs/index.md +0 -0
- {codex_ai-0.2.3 → codex_ai-0.2.4}/docs/ru/architecture/providers/data_flow.md +0 -0
- {codex_ai-0.2.3 → codex_ai-0.2.4}/docs/stylesheets/extra.css +0 -0
- {codex_ai-0.2.3 → codex_ai-0.2.4}/mkdocs.yml +0 -0
- {codex_ai-0.2.3 → codex_ai-0.2.4}/src/codex_ai/__init__.py +0 -0
- {codex_ai-0.2.3 → codex_ai-0.2.4}/src/codex_ai/core/exceptions.py +0 -0
- {codex_ai-0.2.3 → codex_ai-0.2.4}/src/codex_ai/core/router.py +0 -0
- {codex_ai-0.2.3 → codex_ai-0.2.4}/src/codex_ai/core/sync.py +0 -0
- {codex_ai-0.2.3 → codex_ai-0.2.4}/src/codex_ai/py.typed +0 -0
- {codex_ai-0.2.3 → codex_ai-0.2.4}/tests/conftest.py +0 -0
- {codex_ai-0.2.3 → codex_ai-0.2.4}/tests/integration/__init__.py +0 -0
- {codex_ai-0.2.3 → codex_ai-0.2.4}/tests/integration/conftest.py +0 -0
- {codex_ai-0.2.3 → codex_ai-0.2.4}/tests/integration/test_providers_integration.py +0 -0
- {codex_ai-0.2.3 → codex_ai-0.2.4}/tests/unit/__init__.py +0 -0
- {codex_ai-0.2.3 → codex_ai-0.2.4}/tests/unit/conftest.py +0 -0
- {codex_ai-0.2.3 → codex_ai-0.2.4}/tests/unit/core/__init__.py +0 -0
- {codex_ai-0.2.3 → codex_ai-0.2.4}/tests/unit/core/test_dispatcher.py +0 -0
- {codex_ai-0.2.3 → codex_ai-0.2.4}/tests/unit/core/test_exceptions.py +0 -0
- {codex_ai-0.2.3 → codex_ai-0.2.4}/tests/unit/core/test_protocol.py +0 -0
- {codex_ai-0.2.3 → codex_ai-0.2.4}/tests/unit/core/test_router.py +0 -0
- {codex_ai-0.2.3 → codex_ai-0.2.4}/tests/unit/core/test_sync.py +0 -0
- {codex_ai-0.2.3 → codex_ai-0.2.4}/tests/unit/providers/__init__.py +0 -0
- {codex_ai-0.2.3 → codex_ai-0.2.4}/tests/unit/providers/test_gemini_provider.py +0 -0
- {codex_ai-0.2.3 → codex_ai-0.2.4}/tests/unit/providers/test_openai_provider.py +0 -0
- {codex_ai-0.2.3 → codex_ai-0.2.4}/tests/unit/test_public_api.py +0 -0
- {codex_ai-0.2.3 → codex_ai-0.2.4}/tools/__init__.py +0 -0
- {codex_ai-0.2.3 → codex_ai-0.2.4}/tools/dev/README.md +0 -0
- {codex_ai-0.2.3 → codex_ai-0.2.4}/tools/dev/__init__.py +0 -0
- {codex_ai-0.2.3 → codex_ai-0.2.4}/tools/dev/check.py +0 -0
- {codex_ai-0.2.3 → codex_ai-0.2.4}/tools/dev/generate_project_tree.py +0 -0
- {codex_ai-0.2.3 → codex_ai-0.2.4}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: codex-ai
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.4
|
|
4
4
|
Summary: Gemini-first and OpenAI provider helpers for Codex
|
|
5
5
|
Project-URL: Homepage, https://github.com/codexdlc/codex-ai
|
|
6
6
|
Project-URL: Documentation, https://codexdlc.github.io/codex-ai/
|
|
@@ -16,7 +16,7 @@ Classifier: License :: OSI Approved :: Apache Software License
|
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.12
|
|
17
17
|
Classifier: Programming Language :: Python :: 3.13
|
|
18
18
|
Requires-Python: >=3.12
|
|
19
|
-
Requires-Dist: codex-core<0.
|
|
19
|
+
Requires-Dist: codex-core<0.5.0,>=0.2.2
|
|
20
20
|
Requires-Dist: pydantic<3.0,>=2.0
|
|
21
21
|
Provides-Extra: all
|
|
22
22
|
Requires-Dist: google-genai==2.3.0; extra == 'all'
|
|
@@ -49,10 +49,10 @@ Description-Content-Type: text/markdown
|
|
|
49
49
|
|
|
50
50
|
[](https://pypi.org/project/codex-ai/)
|
|
51
51
|
[](https://pypi.org/project/codex-ai/)
|
|
52
|
-
[](https://github.com/codexdlc/codex-ai/actions/workflows/ci.yml)
|
|
53
53
|
[](https://github.com/codexdlc/codex-ai/blob/main/LICENSE)
|
|
54
54
|
|
|
55
|
-
Gemini-first
|
|
55
|
+
Gemini-first API helpers for the Codex ecosystem. The active surface is direct Gemini text, JSON, Gemini image, and Imagen generation. OpenAI remains as a small text-only adapter, and the router/dispatcher layer is kept for legacy text workflows.
|
|
56
56
|
|
|
57
57
|
## Install
|
|
58
58
|
|
|
@@ -105,7 +105,7 @@ the Gemini provider retries once with `2K`. Use `generate_imagen_bytes()` for
|
|
|
105
105
|
Imagen models; that path uses `generate_images` and passes the requested MIME as
|
|
106
106
|
`output_mime_type`.
|
|
107
107
|
|
|
108
|
-
## Router
|
|
108
|
+
## Legacy Text Router
|
|
109
109
|
|
|
110
110
|
```python
|
|
111
111
|
from codex_ai import GeminiProvider, LLMDispatcher, LLMMessage, LLMRouter, PromptResult
|
|
@@ -127,13 +127,17 @@ dispatcher.include_router(router)
|
|
|
127
127
|
response = await dispatcher.process("chat", text="Hello!")
|
|
128
128
|
```
|
|
129
129
|
|
|
130
|
+
Use this path only when you already have prompt builders registered through `LLMRouter`.
|
|
131
|
+
New Gemini integrations should call `generate_text()`, `generate_json()`,
|
|
132
|
+
`generate_image_bytes()`, or `generate_imagen_bytes()` directly.
|
|
133
|
+
|
|
130
134
|
## Modules
|
|
131
135
|
|
|
132
136
|
| Module | Extra | Description |
|
|
133
137
|
| :--- | :--- | :--- |
|
|
134
|
-
| `codex_ai.
|
|
135
|
-
| `codex_ai.providers.
|
|
136
|
-
| `codex_ai.
|
|
138
|
+
| `codex_ai.providers.gemini` | `[gemini]` | Primary API: Gemini text, JSON, Gemini image, and Imagen generation via pinned `google-genai` |
|
|
139
|
+
| `codex_ai.providers.openai` | `[openai]` | Text-only OpenAI Chat Completions adapter |
|
|
140
|
+
| `codex_ai.core` | - | Legacy text router/dispatcher contracts and shared provider exceptions |
|
|
137
141
|
|
|
138
142
|
## Development
|
|
139
143
|
|
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://pypi.org/project/codex-ai/)
|
|
4
4
|
[](https://pypi.org/project/codex-ai/)
|
|
5
|
-
[](https://github.com/codexdlc/codex-ai/actions/workflows/ci.yml)
|
|
6
6
|
[](https://github.com/codexdlc/codex-ai/blob/main/LICENSE)
|
|
7
7
|
|
|
8
|
-
Gemini-first
|
|
8
|
+
Gemini-first API helpers for the Codex ecosystem. The active surface is direct Gemini text, JSON, Gemini image, and Imagen generation. OpenAI remains as a small text-only adapter, and the router/dispatcher layer is kept for legacy text workflows.
|
|
9
9
|
|
|
10
10
|
## Install
|
|
11
11
|
|
|
@@ -58,7 +58,7 @@ the Gemini provider retries once with `2K`. Use `generate_imagen_bytes()` for
|
|
|
58
58
|
Imagen models; that path uses `generate_images` and passes the requested MIME as
|
|
59
59
|
`output_mime_type`.
|
|
60
60
|
|
|
61
|
-
## Router
|
|
61
|
+
## Legacy Text Router
|
|
62
62
|
|
|
63
63
|
```python
|
|
64
64
|
from codex_ai import GeminiProvider, LLMDispatcher, LLMMessage, LLMRouter, PromptResult
|
|
@@ -80,13 +80,17 @@ dispatcher.include_router(router)
|
|
|
80
80
|
response = await dispatcher.process("chat", text="Hello!")
|
|
81
81
|
```
|
|
82
82
|
|
|
83
|
+
Use this path only when you already have prompt builders registered through `LLMRouter`.
|
|
84
|
+
New Gemini integrations should call `generate_text()`, `generate_json()`,
|
|
85
|
+
`generate_image_bytes()`, or `generate_imagen_bytes()` directly.
|
|
86
|
+
|
|
83
87
|
## Modules
|
|
84
88
|
|
|
85
89
|
| Module | Extra | Description |
|
|
86
90
|
| :--- | :--- | :--- |
|
|
87
|
-
| `codex_ai.
|
|
88
|
-
| `codex_ai.providers.
|
|
89
|
-
| `codex_ai.
|
|
91
|
+
| `codex_ai.providers.gemini` | `[gemini]` | Primary API: Gemini text, JSON, Gemini image, and Imagen generation via pinned `google-genai` |
|
|
92
|
+
| `codex_ai.providers.openai` | `[openai]` | Text-only OpenAI Chat Completions adapter |
|
|
93
|
+
| `codex_ai.core` | - | Legacy text router/dispatcher contracts and shared provider exceptions |
|
|
90
94
|
|
|
91
95
|
## Development
|
|
92
96
|
|
|
@@ -2,19 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
## Purpose
|
|
4
4
|
|
|
5
|
-
`codex_ai.core` is the orchestration layer
|
|
5
|
+
`codex_ai.core` is the legacy text orchestration layer. It keeps existing `LLMRouter`/`LLMDispatcher` prompt-builder workflows working while the active API surface moves to direct provider methods.
|
|
6
6
|
|
|
7
7
|
## Why It's a Module
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Older Codex integrations use mode-based prompt builders. This module keeps that shape stable without making it the primary abstraction for new work:
|
|
10
10
|
|
|
11
|
-
|
|
|
12
|
-
|
|
13
|
-
|
|
|
14
|
-
|
|
|
15
|
-
|
|
|
11
|
+
| Need | Current role |
|
|
12
|
+
|------|--------------|
|
|
13
|
+
| Keep registered prompt builders working | `LLMRouter` maps modes to builders |
|
|
14
|
+
| Run existing text flows without rewriting callers | `LLMDispatcher.process()` still calls `provider.answer()` |
|
|
15
|
+
| Bridge sync-only contexts | `SyncLLMDispatcher` remains available for CLI/WSGI code |
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
For new Gemini work, prefer `GeminiProvider.generate_text()`, `generate_json()`,
|
|
18
|
+
`generate_image_bytes()`, and `generate_imagen_bytes()` directly.
|
|
19
|
+
|
|
20
|
+
The retained text pipeline is:
|
|
18
21
|
|
|
19
22
|
```
|
|
20
23
|
@router.prompt("mode") → PromptResult (frozen DTO) → LLMProviderProtocol.answer()
|
|
@@ -32,7 +35,7 @@ Working directly with LLM SDKs across a codebase creates three recurring problem
|
|
|
32
35
|
│ include_router(router)
|
|
33
36
|
▼
|
|
34
37
|
┌──────────────────────┐
|
|
35
|
-
│ LLMDispatcher │
|
|
38
|
+
│ LLMDispatcher │ legacy text builder → provider
|
|
36
39
|
│ │
|
|
37
40
|
│ .process(mode, **kw)│
|
|
38
41
|
└──────┬───────────────┘
|
|
@@ -52,19 +55,20 @@ Working directly with LLM SDKs across a codebase creates three recurring problem
|
|
|
52
55
|
|
|
53
56
|
| Component | Class | Role |
|
|
54
57
|
|-----------|-------|------|
|
|
55
|
-
| `protocol.py` | `PromptResult` | Frozen DTO
|
|
56
|
-
| `protocol.py` | `LLMProviderProtocol` | Structural
|
|
58
|
+
| `protocol.py` | `PromptResult` | Frozen DTO used by legacy text prompt builders |
|
|
59
|
+
| `protocol.py` | `LLMProviderProtocol` | Structural text compatibility protocol for `answer()` |
|
|
57
60
|
| `protocol.py` | `PromptBuilder` | Type alias for `async def (...) -> PromptResult` |
|
|
58
61
|
| `router.py` | `LLMRouter` | Registry: maps `mode` strings to builder functions via decorator |
|
|
59
|
-
| `dispatcher.py` | `LLMDispatcher` |
|
|
62
|
+
| `dispatcher.py` | `LLMDispatcher` | Runs legacy text prompts and delegates direct provider convenience methods |
|
|
60
63
|
| `sync.py` | `SyncLLMDispatcher` | Wraps `LLMDispatcher` with `asyncio.run()` for WSGI/CLI contexts |
|
|
61
64
|
| `exceptions.py` | `LLMProviderError` | Base exception raised by all provider implementations |
|
|
62
65
|
|
|
63
66
|
## Key Design Decisions
|
|
64
67
|
|
|
65
|
-
- **
|
|
66
|
-
-
|
|
67
|
-
-
|
|
68
|
+
- **Direct provider APIs first** — Gemini capabilities are exposed as explicit methods instead of being forced through a universal provider interface.
|
|
69
|
+
- **Frozen DTO (`PromptResult`)** — kept for legacy builders and cannot be mutated downstream.
|
|
70
|
+
- **`@runtime_checkable` Protocol** — retained for runtime checks at compatibility boundaries.
|
|
71
|
+
- **Mode-based dispatch is legacy text infrastructure** — `dispatcher.process("chat", ...)` maps to a registered builder for existing flows.
|
|
68
72
|
- **All logs at `DEBUG`** — dispatcher emits only debug-level messages. Production log level controls visibility without code changes.
|
|
69
73
|
- **`SyncLLMDispatcher` for Django only** — uses `asyncio.run()` which creates a new event loop. Never call from inside an async context (ARQ, async views, bots) — use `LLMDispatcher` directly.
|
|
70
74
|
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# Core — Data Flow
|
|
2
2
|
|
|
3
|
-
## Request Lifecycle
|
|
3
|
+
## Legacy Text Request Lifecycle
|
|
4
|
+
|
|
5
|
+
This flow is retained for existing `LLMRouter` integrations. New Gemini work should use direct provider methods.
|
|
4
6
|
|
|
5
7
|
```
|
|
6
8
|
1. Application calls:
|
|
@@ -13,14 +15,14 @@
|
|
|
13
15
|
prompt = await builder(text="Hello!")
|
|
14
16
|
# → PromptResult(messages=[LLMMessage(role="user", content="Hello!")])
|
|
15
17
|
|
|
16
|
-
4. Dispatcher calls
|
|
18
|
+
4. Dispatcher calls the text compatibility method:
|
|
17
19
|
response = await self._provider.answer(prompt, **kw)
|
|
18
20
|
|
|
19
21
|
5. Provider returns text:
|
|
20
22
|
"Hi there! How can I help?"
|
|
21
23
|
```
|
|
22
24
|
|
|
23
|
-
## Component Interactions
|
|
25
|
+
## Legacy Component Interactions
|
|
24
26
|
|
|
25
27
|
```
|
|
26
28
|
Application
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## Purpose
|
|
4
4
|
|
|
5
|
-
`codex_ai.providers` contains concrete
|
|
5
|
+
`codex_ai.providers` contains concrete SDK adapters, not a broad interchangeable provider framework. Gemini is the product focus; OpenAI is retained only for text generation.
|
|
6
6
|
|
|
7
7
|
Gemini is the primary target and exposes direct methods for text, JSON, and image generation:
|
|
8
8
|
|
|
@@ -13,7 +13,7 @@ await gemini.generate_image_bytes(...)
|
|
|
13
13
|
await gemini.generate_imagen_bytes(...)
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
-
OpenAI is kept as a text
|
|
16
|
+
OpenAI is kept as a text-only adapter with the same `generate_text(...)` convenience.
|
|
17
17
|
|
|
18
18
|
## Architecture
|
|
19
19
|
|
|
@@ -50,4 +50,4 @@ LLMRouter builder -> PromptResult -> LLMDispatcher.process() -> provider.answer(
|
|
|
50
50
|
- `generate_image_bytes()` uses Gemini `generate_content` with image modality. Its `response_mime_type` is only a preferred/fallback MIME type, and Gemini image controls are passed through `image_config`.
|
|
51
51
|
- `generate_image_bytes()` retries a rejected `image_config={"image_size": "4K"}` request once as `2K`.
|
|
52
52
|
- `generate_imagen_bytes()` uses Imagen `generate_images` and passes the requested MIME as `output_mime_type`.
|
|
53
|
-
- Anthropic, OpenRouter, and multi-provider failover are not active APIs in this
|
|
53
|
+
- Anthropic, OpenRouter, and multi-provider failover are not active APIs in this line.
|
|
@@ -2,19 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
## Назначение
|
|
4
4
|
|
|
5
|
-
`codex_ai.core` —
|
|
5
|
+
`codex_ai.core` — legacy-слой текстовой оркестрации. Он сохраняет существующие workflow на `LLMRouter`/`LLMDispatcher`, пока активная API-поверхность переехала в прямые методы провайдеров.
|
|
6
6
|
|
|
7
7
|
## Зачем это модуль
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Старые Codex-интеграции используют mode-based prompt builders. Этот модуль сохраняет такую форму, но больше не является основной абстракцией для новой работы:
|
|
10
10
|
|
|
11
|
-
|
|
|
12
|
-
|
|
13
|
-
|
|
|
14
|
-
|
|
|
15
|
-
|
|
|
11
|
+
| Потребность | Текущая роль |
|
|
12
|
+
|-------------|--------------|
|
|
13
|
+
| Сохранить зарегистрированные prompt builders | `LLMRouter` связывает modes с билдерами |
|
|
14
|
+
| Не переписывать существующие текстовые вызовы | `LLMDispatcher.process()` продолжает вызывать `provider.answer()` |
|
|
15
|
+
| Поддержать sync-only контексты | `SyncLLMDispatcher` остается для CLI/WSGI кода |
|
|
16
16
|
|
|
17
|
-
`
|
|
17
|
+
Для новой Gemini-интеграции лучше использовать прямые методы `GeminiProvider.generate_text()`, `generate_json()`, `generate_image_bytes()` и `generate_imagen_bytes()`.
|
|
18
|
+
|
|
19
|
+
Сохраненный текстовый pipeline:
|
|
18
20
|
|
|
19
21
|
```
|
|
20
22
|
@router.prompt("mode") → PromptResult (frozen DTO) → LLMProviderProtocol.answer()
|
|
@@ -32,7 +34,7 @@
|
|
|
32
34
|
│ include_router(router)
|
|
33
35
|
▼
|
|
34
36
|
┌──────────────────────┐
|
|
35
|
-
│ LLMDispatcher │
|
|
37
|
+
│ LLMDispatcher │ legacy text builder → provider
|
|
36
38
|
│ │
|
|
37
39
|
│ .process(mode, **kw)│
|
|
38
40
|
└──────┬───────────────┘
|
|
@@ -52,19 +54,20 @@
|
|
|
52
54
|
|
|
53
55
|
| Компонент | Класс | Роль |
|
|
54
56
|
|-----------|-------|------|
|
|
55
|
-
| `protocol.py` | `PromptResult` | Frozen DTO
|
|
56
|
-
| `protocol.py` | `LLMProviderProtocol` | Структурный
|
|
57
|
+
| `protocol.py` | `PromptResult` | Frozen DTO для legacy text prompt builders |
|
|
58
|
+
| `protocol.py` | `LLMProviderProtocol` | Структурный текстовый compatibility-протокол для `answer()` |
|
|
57
59
|
| `protocol.py` | `PromptBuilder` | Type alias для `async def (...) -> PromptResult` |
|
|
58
60
|
| `router.py` | `LLMRouter` | Реестр: связывает строки `mode` с функциями-билдерами через декоратор |
|
|
59
|
-
| `dispatcher.py` | `LLMDispatcher` |
|
|
61
|
+
| `dispatcher.py` | `LLMDispatcher` | Выполняет legacy text prompts и делегирует прямые provider convenience methods |
|
|
60
62
|
| `sync.py` | `SyncLLMDispatcher` | Оборачивает `LLMDispatcher` через `asyncio.run()` для WSGI/CLI |
|
|
61
63
|
| `exceptions.py` | `LLMProviderError` | Базовое исключение, поднимаемое всеми реализациями провайдеров |
|
|
62
64
|
|
|
63
65
|
## Ключевые решения
|
|
64
66
|
|
|
65
|
-
- **
|
|
66
|
-
-
|
|
67
|
-
-
|
|
67
|
+
- **Direct provider APIs first** — возможности Gemini раскрываются явными методами, а не проталкиваются через универсальный provider interface.
|
|
68
|
+
- **Frozen DTO (`PromptResult`)** — сохранен для legacy builders и не может быть изменён downstream.
|
|
69
|
+
- **`@runtime_checkable` Protocol** — оставлен для runtime-проверок на compatibility boundaries.
|
|
70
|
+
- **Mode-based dispatch — legacy text infrastructure** — `dispatcher.process("chat", ...)` находит зарегистрированный билдер для существующих flows.
|
|
68
71
|
- **Все логи на `DEBUG`** — диспетчер пишет только debug-сообщения. Уровень логирования в продакшне управляет видимостью без изменений кода.
|
|
69
72
|
- **`SyncLLMDispatcher` только для Django** — использует `asyncio.run()`, создающий новый event loop. Никогда не вызывать из async-контекста (ARQ, async views, боты) — используйте `LLMDispatcher` напрямую.
|
|
70
73
|
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# Core — Поток данных
|
|
2
2
|
|
|
3
|
-
## Жизненный цикл запроса
|
|
3
|
+
## Жизненный цикл legacy text запроса
|
|
4
|
+
|
|
5
|
+
Этот flow сохранен для существующих интеграций на `LLMRouter`. Новую Gemini-интеграцию лучше писать через прямые методы провайдера.
|
|
4
6
|
|
|
5
7
|
```
|
|
6
8
|
1. Приложение вызывает:
|
|
@@ -13,14 +15,14 @@
|
|
|
13
15
|
prompt = await builder(text="Привет!")
|
|
14
16
|
# → PromptResult(messages=[LLMMessage(role="user", content="Привет!")])
|
|
15
17
|
|
|
16
|
-
4. Диспетчер вызывает
|
|
18
|
+
4. Диспетчер вызывает текстовый compatibility method:
|
|
17
19
|
response = await self._provider.answer(prompt, **kw)
|
|
18
20
|
|
|
19
21
|
5. Провайдер возвращает текст:
|
|
20
22
|
"Привет! Чем могу помочь?"
|
|
21
23
|
```
|
|
22
24
|
|
|
23
|
-
## Взаимодействие
|
|
25
|
+
## Взаимодействие legacy-компонентов
|
|
24
26
|
|
|
25
27
|
```
|
|
26
28
|
Приложение
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## Назначение
|
|
4
4
|
|
|
5
|
-
`codex_ai.providers` содержит
|
|
5
|
+
`codex_ai.providers` содержит конкретные SDK-адаптеры, а не широкий interchangeable provider framework. Фокус продукта — Gemini; OpenAI сохранен только для текстовой генерации.
|
|
6
6
|
|
|
7
7
|
Gemini является основным направлением и дает прямые методы для текста, JSON и картинок:
|
|
8
8
|
|
|
@@ -13,7 +13,7 @@ await gemini.generate_image_bytes(...)
|
|
|
13
13
|
await gemini.generate_imagen_bytes(...)
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
-
OpenAI оставлен как
|
|
16
|
+
OpenAI оставлен как text-only адаптер с `generate_text(...)`.
|
|
17
17
|
|
|
18
18
|
## Архитектура
|
|
19
19
|
|
|
@@ -50,4 +50,4 @@ LLMRouter builder -> PromptResult -> LLMDispatcher.process() -> provider.answer(
|
|
|
50
50
|
- `generate_image_bytes()` использует Gemini `generate_content` с image modality. Его `response_mime_type` является только preferred/fallback MIME type, а Gemini image controls передаются через `image_config`.
|
|
51
51
|
- `generate_image_bytes()` один раз повторяет отклоненный `image_config={"image_size": "4K"}` запрос как `2K`.
|
|
52
52
|
- `generate_imagen_bytes()` использует Imagen `generate_images` и передает requested MIME как `output_mime_type`.
|
|
53
|
-
- Anthropic, OpenRouter и multi-provider failover не являются активными API в этой
|
|
53
|
+
- Anthropic, OpenRouter и multi-provider failover не являются активными API в этой линейке.
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"""
|
|
2
2
|
codex_ai.core.dispatcher
|
|
3
3
|
=========================
|
|
4
|
-
LLMDispatcher —
|
|
4
|
+
LLMDispatcher — legacy text prompt dispatcher plus direct provider delegation.
|
|
5
5
|
|
|
6
6
|
Registers routers, selects the correct builder by mode,
|
|
7
|
-
and delegates response generation to the provider.
|
|
7
|
+
and delegates text response generation to the provider's ``answer()`` method.
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
10
|
from __future__ import annotations
|
|
@@ -27,13 +27,14 @@ log = logging.getLogger(__name__)
|
|
|
27
27
|
|
|
28
28
|
class LLMDispatcher:
|
|
29
29
|
"""
|
|
30
|
-
Orchestrates prompt building and
|
|
30
|
+
Orchestrates legacy text prompt building and provider calls.
|
|
31
31
|
|
|
32
32
|
Connects one or more LLMRouters, selects the builder by mode,
|
|
33
|
-
calls it with provided kwargs, then passes the result to the provider
|
|
33
|
+
calls it with provided kwargs, then passes the result to the provider's
|
|
34
|
+
text compatibility ``answer()`` method.
|
|
34
35
|
|
|
35
36
|
Args:
|
|
36
|
-
provider:
|
|
37
|
+
provider: Text-compatible provider implementing LLMProviderProtocol.
|
|
37
38
|
|
|
38
39
|
Example:
|
|
39
40
|
```python
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
"""
|
|
2
2
|
codex_ai.core.protocol
|
|
3
3
|
=======================
|
|
4
|
-
Core types and contracts for
|
|
4
|
+
Core types and contracts for legacy text routing and direct provider helpers.
|
|
5
5
|
|
|
6
6
|
PromptResult — frozen DTO returned by every prompt builder.
|
|
7
|
-
LLMProviderProtocol —
|
|
7
|
+
LLMProviderProtocol — legacy text compatibility contract for ``answer()``.
|
|
8
8
|
TextGenerationProvider — optional adapter contract for direct text generation.
|
|
9
9
|
JsonGenerationProvider — optional adapter contract for direct JSON generation.
|
|
10
10
|
ImageGenerationProvider — optional adapter contract for binary image generation.
|
|
@@ -39,11 +39,10 @@ class PromptResult(BaseDTO):
|
|
|
39
39
|
"""
|
|
40
40
|
Frozen DTO produced by a prompt builder.
|
|
41
41
|
|
|
42
|
-
Passed directly to the
|
|
42
|
+
Passed directly to the provider's legacy text ``answer()`` method.
|
|
43
43
|
|
|
44
44
|
Attributes:
|
|
45
|
-
messages:
|
|
46
|
-
or Gemini contents). Each provider interprets this field as needed.
|
|
45
|
+
messages: Ordered text message list consumed by compatibility adapters.
|
|
47
46
|
system: Optional system/developer instruction (top-level string, used by Gemini
|
|
48
47
|
and OpenAI o-series models that accept a dedicated system field).
|
|
49
48
|
|
|
@@ -68,9 +67,11 @@ class PromptResult(BaseDTO):
|
|
|
68
67
|
@runtime_checkable
|
|
69
68
|
class LLMProviderProtocol(Protocol):
|
|
70
69
|
"""
|
|
71
|
-
|
|
70
|
+
Legacy text adapter contract.
|
|
72
71
|
|
|
73
|
-
|
|
72
|
+
New Gemini integrations should prefer direct provider methods such as
|
|
73
|
+
``generate_text()``, ``generate_json()``, and ``generate_image_bytes()``.
|
|
74
|
+
This protocol remains for router/dispatcher text compatibility.
|
|
74
75
|
|
|
75
76
|
Example:
|
|
76
77
|
```python
|
|
@@ -82,14 +83,14 @@ class LLMProviderProtocol(Protocol):
|
|
|
82
83
|
|
|
83
84
|
async def answer(self, prompt: PromptResult, **kw: Any) -> str:
|
|
84
85
|
"""
|
|
85
|
-
Send
|
|
86
|
+
Send a legacy text prompt and return the response text.
|
|
86
87
|
|
|
87
88
|
Args:
|
|
88
89
|
prompt: Frozen DTO with messages and system instruction.
|
|
89
90
|
**kw: Extra provider-specific kwargs (temperature, max_tokens, etc.).
|
|
90
91
|
|
|
91
92
|
Returns:
|
|
92
|
-
Response text from the
|
|
93
|
+
Response text from the provider.
|
|
93
94
|
"""
|
|
94
95
|
...
|
|
95
96
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""
|
|
2
2
|
codex_ai.providers.gemini
|
|
3
3
|
==========================
|
|
4
|
-
GeminiProvider —
|
|
4
|
+
GeminiProvider — Gemini-first direct API backed by google-genai.
|
|
5
5
|
|
|
6
6
|
Requires: ``pip install codex-ai[gemini]``
|
|
7
7
|
"""
|
|
@@ -34,9 +34,10 @@ _DEFAULT_IMAGEN_MODEL = "imagen-3.0-generate-002"
|
|
|
34
34
|
|
|
35
35
|
class GeminiProvider:
|
|
36
36
|
"""
|
|
37
|
-
|
|
37
|
+
Direct Gemini adapter using the google-genai SDK.
|
|
38
38
|
|
|
39
|
-
Implements
|
|
39
|
+
Implements legacy text compatibility through ``answer()`` and exposes
|
|
40
|
+
direct Gemini methods for text, JSON, Gemini image, and Imagen generation.
|
|
40
41
|
|
|
41
42
|
Args:
|
|
42
43
|
api_key: Google AI API key.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""
|
|
2
2
|
codex_ai.providers.openai
|
|
3
3
|
==========================
|
|
4
|
-
OpenAIProvider —
|
|
4
|
+
OpenAIProvider — text-only adapter backed by OpenAI's Chat Completions API.
|
|
5
5
|
|
|
6
6
|
Requires: ``pip install codex-ai[openai]``
|
|
7
7
|
"""
|
|
@@ -28,9 +28,10 @@ _DEFAULT_MODEL = "gpt-4o-mini"
|
|
|
28
28
|
|
|
29
29
|
class OpenAIProvider:
|
|
30
30
|
"""
|
|
31
|
-
|
|
31
|
+
Text-only adapter using OpenAI Chat Completions.
|
|
32
32
|
|
|
33
|
-
Implements
|
|
33
|
+
Implements legacy text compatibility through ``answer()`` and direct
|
|
34
|
+
``generate_text()`` convenience.
|
|
34
35
|
|
|
35
36
|
Args:
|
|
36
37
|
api_key: OpenAI API key.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|