token-runtime 0.1.0a2__tar.gz → 0.1.0a3__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.
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/PKG-INFO +13 -4
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/README.md +12 -3
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/pyproject.toml +1 -1
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/src/token_runtime/cli.py +12 -5
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/src/token_runtime/compat_cert.py +3 -1
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/src/token_runtime/gateway.py +15 -12
- token_runtime-0.1.0a3/src/token_runtime/openai_agents_conformance.py +276 -0
- token_runtime-0.1.0a3/src/token_runtime/protocol_registry.py +49 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/src/token_runtime.egg-info/PKG-INFO +13 -4
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/src/token_runtime.egg-info/SOURCES.txt +9 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/tests/test_alpha2_release_candidate.py +1 -11
- token_runtime-0.1.0a3/tests/test_alpha3_release_candidate.py +55 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/tests/test_compat_cert.py +30 -0
- token_runtime-0.1.0a3/tests/test_current_release_candidate.py +31 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/tests/test_distribution.py +34 -10
- token_runtime-0.1.0a3/tests/test_distribution_version_generalization.py +53 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/tests/test_gateway.py +23 -2
- token_runtime-0.1.0a3/tests/test_llm_change_isolation.py +128 -0
- token_runtime-0.1.0a3/tests/test_onboarding_ux.py +39 -0
- token_runtime-0.1.0a3/tests/test_openai_agents_conformance.py +311 -0
- token_runtime-0.1.0a3/tests/test_protocol_registry.py +39 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/tests/test_public_classification.py +5 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/tests/test_public_ready.py +5 -2
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/tests/test_terminal_ui.py +6 -1
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/LICENSE +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/setup.cfg +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/src/token_runtime/__init__.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/src/token_runtime/adapters.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/src/token_runtime/agent_integrations.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/src/token_runtime/anthropic_adapter.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/src/token_runtime/anthropic_conformance.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/src/token_runtime/anthropic_preserved_thinking_cert.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/src/token_runtime/benchmark.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/src/token_runtime/capabilities.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/src/token_runtime/codex_recertification.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/src/token_runtime/compatibility.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/src/token_runtime/config.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/src/token_runtime/conformance.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/src/token_runtime/contracts.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/src/token_runtime/engine.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/src/token_runtime/feature_flags.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/src/token_runtime/gemini_adapter.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/src/token_runtime/gemini_conformance.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/src/token_runtime/integrations.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/src/token_runtime/metrics.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/src/token_runtime/model.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/src/token_runtime/openai_certification.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/src/token_runtime/planner.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/src/token_runtime/reducers.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/src/token_runtime/store.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/src/token_runtime/strategies.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/src/token_runtime/terminal_ui.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/src/token_runtime.egg-info/dependency_links.txt +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/src/token_runtime.egg-info/entry_points.txt +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/src/token_runtime.egg-info/requires.txt +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/src/token_runtime.egg-info/top_level.txt +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/tests/test_adapters.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/tests/test_agent_integration_safety.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/tests/test_agent_integrations.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/tests/test_anthropic_adapter.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/tests/test_anthropic_conformance.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/tests/test_anthropic_preserved_thinking_benchmark.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/tests/test_anthropic_preserved_thinking_cert.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/tests/test_anthropic_preserved_thinking_live.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/tests/test_benchmark.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/tests/test_capabilities.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/tests/test_cli.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/tests/test_codex_recertification.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/tests/test_compatibility.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/tests/test_config.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/tests/test_conformance.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/tests/test_docs_growth.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/tests/test_engine.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/tests/test_evolution_contracts.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/tests/test_evolution_runtime_isolation.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/tests/test_feature_flags.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/tests/test_gemini_adapter.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/tests/test_gemini_conformance.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/tests/test_governance.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/tests/test_integrations.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/tests/test_metrics.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/tests/test_model.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/tests/test_openai_certification.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/tests/test_oss_surface.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/tests/test_planner.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/tests/test_public_benchmark.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/tests/test_public_release_pipeline.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/tests/test_reducers.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/tests/test_store.py +0 -0
- {token_runtime-0.1.0a2 → token_runtime-0.1.0a3}/tests/test_strategies.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: token-runtime
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.0a3
|
|
4
4
|
Summary: Quality-preserving local context optimization runtime for LLM clients
|
|
5
5
|
Author: Tungvoc3199
|
|
6
6
|
License: Apache-2.0
|
|
@@ -37,7 +37,7 @@ Local-first adaptive context optimization for Codex and OpenAI-compatible AI age
|
|
|
37
37
|

|
|
38
38
|

|
|
39
39
|

|
|
40
|
-
[](https://github.com/Tungvoc3199/token-runtime/releases/tag/v0.1.0-alpha.3)
|
|
41
41
|
|
|
42
42
|
[Quickstart](#quickstart) · [Proof](#proof) · [How it works](#how-it-works) · [Safety](#safety-first-by-design) · [Compatibility](#compatibility) · [Compatibility matrix](docs/COMPATIBILITY.md) · [Security](SECURITY.md)
|
|
43
43
|
|
|
@@ -118,8 +118,16 @@ The V1 planner protects the active turn, protocol state, hard constraints, exact
|
|
|
118
118
|
|
|
119
119
|
Python 3.12+ is required.
|
|
120
120
|
|
|
121
|
+
Install the Alpha.3 package from PyPI after publication:
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
pip install token-runtime==0.1.0a3
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Or install it as an isolated CLI tool with `uv`:
|
|
128
|
+
|
|
121
129
|
```bash
|
|
122
|
-
|
|
130
|
+
uv tool install token-runtime==0.1.0a3
|
|
123
131
|
```
|
|
124
132
|
|
|
125
133
|
Preview integration changes first:
|
|
@@ -174,6 +182,7 @@ For Codex, TOKEN transactionally manages only the selected OpenAI-compatible pro
|
|
|
174
182
|
| Local OpenAI-compatible gateways/routers | ✅ Supported |
|
|
175
183
|
| Anthropic Messages adapter/conformance | 🛡️ `PASSTHROUGH_ONLY`; offline conformance, not production gateway-wired |
|
|
176
184
|
| Gemini GenerateContent adapter/conformance | 🛡️ `PASSTHROUGH_ONLY`; offline conformance, not production gateway-wired |
|
|
185
|
+
| OpenAI Agents API managed-session conformance | 🛡️ `PASSTHROUGH_ONLY`; offline conformance only, not production routing |
|
|
177
186
|
| Multimodal optimization | 🛡️ `PASSTHROUGH_ONLY` |
|
|
178
187
|
|
|
179
188
|
`CERTIFIED` is always version- and boundary-specific. Canonical source can also contain adapters that are not deployed in the active gateway. See the [compatibility matrix](docs/COMPATIBILITY.md) for the exact evidence boundary.
|
|
@@ -230,7 +239,7 @@ The approved 24-call live campaign found a real regression class. The final poli
|
|
|
230
239
|
- [Release process](docs/RELEASE-PROCESS.md)
|
|
231
240
|
- [GitHub governance](docs/GITHUB-GOVERNANCE.md)
|
|
232
241
|
- [Public benchmark](benchmarks/README.md)
|
|
233
|
-
- [v0.1.0-alpha.
|
|
242
|
+
- [v0.1.0-alpha.3 release notes](docs/releases/v0.1.0-alpha.3.md)
|
|
234
243
|
- [Apache-2.0 license](LICENSE)
|
|
235
244
|
|
|
236
245
|
---
|
|
@@ -12,7 +12,7 @@ Local-first adaptive context optimization for Codex and OpenAI-compatible AI age
|
|
|
12
12
|

|
|
13
13
|

|
|
14
14
|

|
|
15
|
-
[](https://github.com/Tungvoc3199/token-runtime/releases/tag/v0.1.0-alpha.3)
|
|
16
16
|
|
|
17
17
|
[Quickstart](#quickstart) · [Proof](#proof) · [How it works](#how-it-works) · [Safety](#safety-first-by-design) · [Compatibility](#compatibility) · [Compatibility matrix](docs/COMPATIBILITY.md) · [Security](SECURITY.md)
|
|
18
18
|
|
|
@@ -93,8 +93,16 @@ The V1 planner protects the active turn, protocol state, hard constraints, exact
|
|
|
93
93
|
|
|
94
94
|
Python 3.12+ is required.
|
|
95
95
|
|
|
96
|
+
Install the Alpha.3 package from PyPI after publication:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
pip install token-runtime==0.1.0a3
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Or install it as an isolated CLI tool with `uv`:
|
|
103
|
+
|
|
96
104
|
```bash
|
|
97
|
-
|
|
105
|
+
uv tool install token-runtime==0.1.0a3
|
|
98
106
|
```
|
|
99
107
|
|
|
100
108
|
Preview integration changes first:
|
|
@@ -149,6 +157,7 @@ For Codex, TOKEN transactionally manages only the selected OpenAI-compatible pro
|
|
|
149
157
|
| Local OpenAI-compatible gateways/routers | ✅ Supported |
|
|
150
158
|
| Anthropic Messages adapter/conformance | 🛡️ `PASSTHROUGH_ONLY`; offline conformance, not production gateway-wired |
|
|
151
159
|
| Gemini GenerateContent adapter/conformance | 🛡️ `PASSTHROUGH_ONLY`; offline conformance, not production gateway-wired |
|
|
160
|
+
| OpenAI Agents API managed-session conformance | 🛡️ `PASSTHROUGH_ONLY`; offline conformance only, not production routing |
|
|
152
161
|
| Multimodal optimization | 🛡️ `PASSTHROUGH_ONLY` |
|
|
153
162
|
|
|
154
163
|
`CERTIFIED` is always version- and boundary-specific. Canonical source can also contain adapters that are not deployed in the active gateway. See the [compatibility matrix](docs/COMPATIBILITY.md) for the exact evidence boundary.
|
|
@@ -205,7 +214,7 @@ The approved 24-call live campaign found a real regression class. The final poli
|
|
|
205
214
|
- [Release process](docs/RELEASE-PROCESS.md)
|
|
206
215
|
- [GitHub governance](docs/GITHUB-GOVERNANCE.md)
|
|
207
216
|
- [Public benchmark](benchmarks/README.md)
|
|
208
|
-
- [v0.1.0-alpha.
|
|
217
|
+
- [v0.1.0-alpha.3 release notes](docs/releases/v0.1.0-alpha.3.md)
|
|
209
218
|
- [Apache-2.0 license](LICENSE)
|
|
210
219
|
|
|
211
220
|
---
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "token-runtime"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.0a3"
|
|
8
8
|
description = "Quality-preserving local context optimization runtime for LLM clients"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "Apache-2.0" }
|
|
@@ -101,7 +101,10 @@ def doctor_report(config_path: Path) -> dict[str, bool]:
|
|
|
101
101
|
"gateway_reachable": False,
|
|
102
102
|
"upstream_reachable": False,
|
|
103
103
|
}
|
|
104
|
-
|
|
104
|
+
try:
|
|
105
|
+
config = load_config(config_path)
|
|
106
|
+
except FileNotFoundError:
|
|
107
|
+
return report
|
|
105
108
|
report["config_ok"] = True
|
|
106
109
|
|
|
107
110
|
state = Path(config.state_dir)
|
|
@@ -236,6 +239,14 @@ def main(argv=None, *, stdin=None, stdout=None) -> int:
|
|
|
236
239
|
})
|
|
237
240
|
return 0
|
|
238
241
|
|
|
242
|
+
if args.command == "doctor":
|
|
243
|
+
report = doctor_report(path)
|
|
244
|
+
payload = dict(report)
|
|
245
|
+
if not report["config_ok"]:
|
|
246
|
+
payload["next_step"] = "token install --upstream <UPSTREAM_URL>"
|
|
247
|
+
_write_json(stdout, payload)
|
|
248
|
+
return 0 if all(report.values()) else 1
|
|
249
|
+
|
|
239
250
|
config = load_config(path)
|
|
240
251
|
if args.command == "serve":
|
|
241
252
|
core, _ = _runtime(config)
|
|
@@ -245,10 +256,6 @@ def main(argv=None, *, stdin=None, stdout=None) -> int:
|
|
|
245
256
|
_, metrics = _runtime(config)
|
|
246
257
|
_write_json(stdout, metrics.summary())
|
|
247
258
|
return 0
|
|
248
|
-
if args.command == "doctor":
|
|
249
|
-
report = doctor_report(path)
|
|
250
|
-
_write_json(stdout, report)
|
|
251
|
-
return 0 if all(report.values()) else 1
|
|
252
259
|
if args.command == "optimize":
|
|
253
260
|
core, _ = _runtime(config)
|
|
254
261
|
prepared = core.prepare(args.endpoint, _read_input_bytes(stdin))
|
|
@@ -10,6 +10,7 @@ from .capabilities import CapabilityKey
|
|
|
10
10
|
from .codex_recertification import build_codex_01540_recertification
|
|
11
11
|
from .compatibility import CompatibilityRecord, CompatibilityState
|
|
12
12
|
from .gemini_conformance import build_gemini_conformance
|
|
13
|
+
from .openai_agents_conformance import build_openai_agents_conformance
|
|
13
14
|
from .openai_certification import build_openai_certification
|
|
14
15
|
|
|
15
16
|
|
|
@@ -395,6 +396,7 @@ def build_current_certification() -> CompatibilityCertificationBundle:
|
|
|
395
396
|
codex = build_codex_01540_recertification()
|
|
396
397
|
anthropic = build_anthropic_conformance()
|
|
397
398
|
gemini = build_gemini_conformance()
|
|
399
|
+
agents = build_openai_agents_conformance()
|
|
398
400
|
|
|
399
401
|
certified_evidence_ids = tuple(
|
|
400
402
|
sorted(
|
|
@@ -443,7 +445,7 @@ def build_current_certification() -> CompatibilityCertificationBundle:
|
|
|
443
445
|
)
|
|
444
446
|
)
|
|
445
447
|
|
|
446
|
-
for bundle in (anthropic, gemini):
|
|
448
|
+
for bundle in (anthropic, gemini, agents):
|
|
447
449
|
entries.append(
|
|
448
450
|
CertificationEntry(
|
|
449
451
|
scope=_scope_for_key(
|
|
@@ -9,8 +9,11 @@ from typing import Any
|
|
|
9
9
|
from urllib.error import HTTPError
|
|
10
10
|
from urllib.request import Request, urlopen
|
|
11
11
|
|
|
12
|
-
from .adapters import ChatCompletionsAdapter, ResponsesAdapter
|
|
13
12
|
from .metrics import MetricsStore
|
|
13
|
+
from .protocol_registry import (
|
|
14
|
+
ProtocolAdapterRegistry,
|
|
15
|
+
default_protocol_adapter_registry,
|
|
16
|
+
)
|
|
14
17
|
from .model import OptimizationDecision
|
|
15
18
|
|
|
16
19
|
|
|
@@ -27,13 +30,22 @@ class PreparedRequest:
|
|
|
27
30
|
|
|
28
31
|
|
|
29
32
|
class GatewayCore:
|
|
30
|
-
def __init__(
|
|
33
|
+
def __init__(
|
|
34
|
+
self,
|
|
35
|
+
*,
|
|
36
|
+
engine,
|
|
37
|
+
metrics: MetricsStore,
|
|
38
|
+
registry: ProtocolAdapterRegistry | None = None,
|
|
39
|
+
):
|
|
31
40
|
self.engine = engine
|
|
32
41
|
self.metrics = metrics
|
|
42
|
+
self.registry = (
|
|
43
|
+
registry if registry is not None else default_protocol_adapter_registry()
|
|
44
|
+
)
|
|
33
45
|
|
|
34
46
|
def prepare(self, path: str, raw_body: bytes) -> PreparedRequest:
|
|
35
47
|
started = time.perf_counter()
|
|
36
|
-
adapter = self.
|
|
48
|
+
adapter = self.registry.resolve(path)
|
|
37
49
|
if adapter is None:
|
|
38
50
|
prepared = PreparedRequest(raw_body, False, ("unsupported_endpoint",), None)
|
|
39
51
|
self._record(prepared, None, None, None, started)
|
|
@@ -68,15 +80,6 @@ class GatewayCore:
|
|
|
68
80
|
self._record(prepared, None, payload, adapter.name, started)
|
|
69
81
|
return prepared
|
|
70
82
|
|
|
71
|
-
@staticmethod
|
|
72
|
-
def _adapter(path: str):
|
|
73
|
-
clean_path = path.split("?", 1)[0]
|
|
74
|
-
if clean_path == "/v1/responses":
|
|
75
|
-
return ResponsesAdapter()
|
|
76
|
-
if clean_path == "/v1/chat/completions":
|
|
77
|
-
return ChatCompletionsAdapter()
|
|
78
|
-
return None
|
|
79
|
-
|
|
80
83
|
def _record(self, prepared, result, payload, adapter_name, started) -> None:
|
|
81
84
|
before = getattr(result, "before_estimated_tokens", None)
|
|
82
85
|
after = getattr(result, "after_estimated_tokens", None)
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Callable, Mapping, Sequence
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
from hashlib import sha256
|
|
6
|
+
import json
|
|
7
|
+
|
|
8
|
+
from .capabilities import CapabilityDetector, CapabilityKey, CapabilityProfile, CapabilityRegistry
|
|
9
|
+
from .compatibility import CompatibilityRecord, CompatibilityRegistry, CompatibilityState
|
|
10
|
+
from .conformance import ConformanceResult, require_conformance, run_conformance
|
|
11
|
+
|
|
12
|
+
OPENAI_AGENTS_API_KEY = CapabilityKey('generic-openai', 'openai_agents_api', 'openai')
|
|
13
|
+
SNAPSHOT_DATE = '2026-09-13'
|
|
14
|
+
CORPUS_VERSION = 'openai-agents-api-conformance-v1'
|
|
15
|
+
API_STATUS = 'public_beta'
|
|
16
|
+
OFFICIAL_SOURCE_REFS = (
|
|
17
|
+
'https://openai.com/index/introducing-the-agents-api/',
|
|
18
|
+
'https://openai.com/vi-VN/products/release-notes/',
|
|
19
|
+
'https://developers.openai.com/api/reference/python/resources/beta/subresources/agents/subresources/sessions/methods/create',
|
|
20
|
+
'https://developers.openai.com/api/reference/python/resources/beta/subresources/agents/subresources/sessions/subresources/events/methods/create',
|
|
21
|
+
'https://developers.openai.com/api/reference/python/resources/beta/subresources/agents/subresources/sessions/subresources/events/methods/stream',
|
|
22
|
+
'https://github.com/openai/openai-python/tree/e12b81d3bbf644ec7045e152d69bc4b68d69cd48/src/openai/types/beta',
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
TESTED_SHAPE_MANIFEST = {
|
|
26
|
+
'api_family': 'openai_agents_api',
|
|
27
|
+
'endpoint_families': (
|
|
28
|
+
'POST /agents/sessions',
|
|
29
|
+
'POST /agents/sessions/{session_id}/events',
|
|
30
|
+
'GET /agents/sessions/{session_id}/events',
|
|
31
|
+
),
|
|
32
|
+
'session_fields': ('environment', 'agent', 'agent_id', 'input', 'metadata', 'stream', 'vault_ids'),
|
|
33
|
+
'agent_fields': ('tools', 'multi_agent'),
|
|
34
|
+
'documented_exact_input_event_types': ('agent.session.input.message',),
|
|
35
|
+
'documented_input_event_categories': ('message', 'cancellation', 'tool_result'),
|
|
36
|
+
'stream_events': (
|
|
37
|
+
'agent.session.created',
|
|
38
|
+
'agent.session.turn.created',
|
|
39
|
+
'agent.session.environment.ready',
|
|
40
|
+
'agent.session.subagent.created',
|
|
41
|
+
'agent.session.turn.item.added',
|
|
42
|
+
'agent.session.turn.content_part.added',
|
|
43
|
+
'agent.session.turn.output_text.delta',
|
|
44
|
+
'agent.session.turn.reasoning_summary_text.delta',
|
|
45
|
+
'agent.output.command_execution_output.delta',
|
|
46
|
+
'agent.session.requires_action',
|
|
47
|
+
'error',
|
|
48
|
+
'agent.session.idle',
|
|
49
|
+
),
|
|
50
|
+
'synthetic_unknown_stream_events': ('agent.session.future.event',),
|
|
51
|
+
'identifier_fields': (
|
|
52
|
+
'event_id', 'session_id', 'item_id', 'turn_id', 'output_index',
|
|
53
|
+
'content_index', 'summary_index', 'session.id', 'subagent.session_id',
|
|
54
|
+
),
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
ASSERTION_IDS = (
|
|
58
|
+
'certification_state_passthrough_only',
|
|
59
|
+
'corpus_digest_matches_raw_ordered_bytes',
|
|
60
|
+
'idempotency_key_frozen',
|
|
61
|
+
'mcp_and_vault_fixture_covered',
|
|
62
|
+
'native_context_management_claim_bounded',
|
|
63
|
+
'near_match_non_inheritance',
|
|
64
|
+
'official_sources_bound',
|
|
65
|
+
'stream_duplicate_and_order_frozen',
|
|
66
|
+
'stream_identifiers_frozen',
|
|
67
|
+
'unknown_event_frozen',
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
SESSION_CREATE_FIXTURE = b'''{
|
|
71
|
+
"environment" : {"type":"none"},
|
|
72
|
+
"agent_id" : "agent_fixture_saved",
|
|
73
|
+
"agent" : {"name":"fixture-agent","tools":[{"type":"mcp","server_label":"fixture-mcp","server_url":"https://example.invalid/mcp"}],"multi_agent":{"enabled":true,"max_concurrent_subagents":2}},
|
|
74
|
+
"input" : [{"role":"user","content":"fixture input"}],
|
|
75
|
+
"metadata" : {"fixture":"agents-conformance"},
|
|
76
|
+
"stream" : true,
|
|
77
|
+
"vault_ids" : ["vault_fixture"],
|
|
78
|
+
"future_field" : {"opaque":true}
|
|
79
|
+
}'''
|
|
80
|
+
|
|
81
|
+
INPUT_EVENT_FIXTURES = (
|
|
82
|
+
('input-message', b'''{
|
|
83
|
+
"idempotency_key" : "idem_fixture_001",
|
|
84
|
+
"events" : [{"type":"agent.session.input.message","role":"user","content":"hello","future_nested":{"opaque":true}}]
|
|
85
|
+
}'''),
|
|
86
|
+
('input-cancellation', b'''{"fixture_category":"cancellation","payload":{"session_id":"sess_fixture","future_nested":{"opaque":true}}}'''),
|
|
87
|
+
('input-tool-result', b'''{"fixture_category":"tool_result","payload":{"call_id":"call_fixture","output":"ok","future_nested":{"opaque":true}}}'''),
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
_DUPLICATE_FRAME = b'{"type":"agent.session.idle","event_id":"evt_dup","session":{"id":"sess_fixture"}}'
|
|
91
|
+
STREAM_EVENT_FRAMES = (
|
|
92
|
+
('session-created', b'{"type":"agent.session.created","event_id":"evt_001","session":{"id":"sess_fixture"}}'),
|
|
93
|
+
('turn-created', b'{"type":"agent.session.turn.created","event_id":"evt_002","session_id":"sess_fixture","turn_id":"turn_fixture","turn":{"id":"turn_fixture","session_id":"sess_fixture"}}'),
|
|
94
|
+
('environment-ready', b'{"type":"agent.session.environment.ready","event_id":"evt_003","session_id":"sess_fixture","turn_id":"turn_fixture","environment":{"type":"none"}}'),
|
|
95
|
+
('subagent-created', b'{"type":"agent.session.subagent.created","event_id":"evt_004","subagent":{"id":"subagent_fixture","session_id":"sess_fixture"}}'),
|
|
96
|
+
('item-added', b'{"type":"agent.session.turn.item.added","event_id":"evt_005","session_id":"sess_fixture","turn_id":"turn_fixture","output_index":0,"item":{"id":"item_fixture"}}'),
|
|
97
|
+
('content-part-added', b'{"type":"agent.session.turn.content_part.added","event_id":"evt_006","session_id":"sess_fixture","turn_id":"turn_fixture","item_id":"item_fixture","output_index":0,"content_index":0,"part":{"type":"output_text","text":""}}'),
|
|
98
|
+
('output-text-delta-a', b'{"type":"agent.session.turn.output_text.delta","event_id":"evt_007","session_id":"sess_fixture","turn_id":"turn_fixture","item_id":"item_fixture","output_index":0,"content_index":0,"delta":"A"}'),
|
|
99
|
+
('output-text-delta-b', b'{"type":"agent.session.turn.output_text.delta","event_id":"evt_008","session_id":"sess_fixture","turn_id":"turn_fixture","item_id":"item_fixture","output_index":0,"content_index":0,"delta":"B"}'),
|
|
100
|
+
('reasoning-summary-delta', b'{"type":"agent.session.turn.reasoning_summary_text.delta","event_id":"evt_009","session_id":"sess_fixture","turn_id":"turn_fixture","item_id":"reason_fixture","output_index":0,"summary_index":0,"delta":"reason"}'),
|
|
101
|
+
('command-output-delta', b'{"type":"agent.output.command_execution_output.delta","event_id":"evt_010","session_id":"sess_fixture","turn_id":"turn_fixture","item_id":"command_fixture","output_index":0,"delta":"chunk"}'),
|
|
102
|
+
('requires-action', b'{"type":"agent.session.requires_action","event_id":"evt_011","session":{"id":"sess_fixture","required_actions":[{"id":"action_fixture"}]}}'),
|
|
103
|
+
('idle-error', b'{"type":"error","event_id":"evt_012","session_id":"sess_fixture","error":{"message":"fixture"}}'),
|
|
104
|
+
('unknown-future-event', b'{"type":"agent.session.future.event","session_id":"sess_fixture","opaque":{"nested":[1,2,3]}}'),
|
|
105
|
+
('duplicate-frame', _DUPLICATE_FRAME),
|
|
106
|
+
('duplicate-frame-copy', _DUPLICATE_FRAME),
|
|
107
|
+
)
|
|
108
|
+
RAW_CORPUS = (('session-create', SESSION_CREATE_FIXTURE), *INPUT_EVENT_FIXTURES, *STREAM_EVENT_FRAMES)
|
|
109
|
+
|
|
110
|
+
@dataclass(frozen=True, slots=True)
|
|
111
|
+
class OpenAIAgentsConformanceEvidence:
|
|
112
|
+
api_family: str
|
|
113
|
+
api_status: str
|
|
114
|
+
snapshot_date: str
|
|
115
|
+
official_source_refs: tuple[str, ...]
|
|
116
|
+
sdk_client_version: str | None
|
|
117
|
+
corpus_version: str
|
|
118
|
+
tested_endpoint_families: tuple[str, ...]
|
|
119
|
+
protected_fields: tuple[str, ...]
|
|
120
|
+
tested_input_event_shapes: tuple[str, ...]
|
|
121
|
+
tested_stream_event_shapes: tuple[str, ...]
|
|
122
|
+
tested_identifier_fields: tuple[str, ...]
|
|
123
|
+
assertion_ids: tuple[str, ...]
|
|
124
|
+
tested_shape_fingerprint: str
|
|
125
|
+
corpus_digest: str
|
|
126
|
+
evidence_fingerprint: str
|
|
127
|
+
evidence_id: str
|
|
128
|
+
|
|
129
|
+
@dataclass(frozen=True, slots=True)
|
|
130
|
+
class OpenAIAgentsConformanceBundle:
|
|
131
|
+
evidence: OpenAIAgentsConformanceEvidence
|
|
132
|
+
profile: CapabilityProfile
|
|
133
|
+
record: CompatibilityRecord
|
|
134
|
+
detector: CapabilityDetector
|
|
135
|
+
|
|
136
|
+
def _canonical_json_bytes(value: object) -> bytes:
|
|
137
|
+
return json.dumps(value, sort_keys=True, separators=(',', ':'), ensure_ascii=False).encode('utf-8')
|
|
138
|
+
|
|
139
|
+
def tested_shape_fingerprint(manifest: Mapping[str, object]) -> str:
|
|
140
|
+
return sha256(_canonical_json_bytes(manifest)).hexdigest()
|
|
141
|
+
|
|
142
|
+
def corpus_digest(records: Sequence[tuple[str, bytes]]) -> str:
|
|
143
|
+
digest = sha256()
|
|
144
|
+
for label, payload in records:
|
|
145
|
+
label_bytes = label.encode('utf-8')
|
|
146
|
+
digest.update(len(label_bytes).to_bytes(8, 'big'))
|
|
147
|
+
digest.update(label_bytes)
|
|
148
|
+
digest.update(len(payload).to_bytes(8, 'big'))
|
|
149
|
+
digest.update(payload)
|
|
150
|
+
return digest.hexdigest()
|
|
151
|
+
|
|
152
|
+
def evidence_fingerprint(*, tested_shape_fingerprint: str, corpus_digest: str, assertion_ids: Sequence[str], snapshot_date: str, official_source_refs: Sequence[str], sdk_client_version: str | None) -> str:
|
|
153
|
+
payload = {
|
|
154
|
+
'api_family': 'openai_agents_api', 'api_status': API_STATUS,
|
|
155
|
+
'snapshot_date': snapshot_date, 'official_source_refs': sorted(official_source_refs),
|
|
156
|
+
'sdk_client_version': sdk_client_version, 'corpus_version': CORPUS_VERSION,
|
|
157
|
+
'tested_shape_fingerprint': tested_shape_fingerprint, 'corpus_digest': corpus_digest,
|
|
158
|
+
'assertion_ids': sorted(assertion_ids),
|
|
159
|
+
}
|
|
160
|
+
return sha256(_canonical_json_bytes(payload)).hexdigest()
|
|
161
|
+
|
|
162
|
+
def _build_evidence() -> OpenAIAgentsConformanceEvidence:
|
|
163
|
+
shape_fp = tested_shape_fingerprint(TESTED_SHAPE_MANIFEST)
|
|
164
|
+
corpus_fp = corpus_digest(RAW_CORPUS)
|
|
165
|
+
evidence_fp = evidence_fingerprint(
|
|
166
|
+
tested_shape_fingerprint=shape_fp, corpus_digest=corpus_fp,
|
|
167
|
+
assertion_ids=ASSERTION_IDS, snapshot_date=SNAPSHOT_DATE,
|
|
168
|
+
official_source_refs=OFFICIAL_SOURCE_REFS, sdk_client_version=None,
|
|
169
|
+
)
|
|
170
|
+
return OpenAIAgentsConformanceEvidence(
|
|
171
|
+
api_family='openai_agents_api', api_status=API_STATUS, snapshot_date=SNAPSHOT_DATE,
|
|
172
|
+
official_source_refs=OFFICIAL_SOURCE_REFS, sdk_client_version=None,
|
|
173
|
+
corpus_version=CORPUS_VERSION,
|
|
174
|
+
tested_endpoint_families=tuple(TESTED_SHAPE_MANIFEST['endpoint_families']),
|
|
175
|
+
protected_fields=('session_state','agent_config','environment','metadata','native_compaction','native_recovery','tools','mcp','subagents','cancellation','usage_accounting','unknown_beta_fields'),
|
|
176
|
+
tested_input_event_shapes=(*TESTED_SHAPE_MANIFEST['documented_exact_input_event_types'], *TESTED_SHAPE_MANIFEST['documented_input_event_categories']),
|
|
177
|
+
tested_stream_event_shapes=tuple(TESTED_SHAPE_MANIFEST['stream_events']),
|
|
178
|
+
tested_identifier_fields=tuple(TESTED_SHAPE_MANIFEST['identifier_fields']),
|
|
179
|
+
assertion_ids=ASSERTION_IDS, tested_shape_fingerprint=shape_fp,
|
|
180
|
+
corpus_digest=corpus_fp, evidence_fingerprint=evidence_fp,
|
|
181
|
+
evidence_id=f'token-openai-agents-api-conformance-1:{SNAPSHOT_DATE}:{evidence_fp}',
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
def build_openai_agents_conformance() -> OpenAIAgentsConformanceBundle:
|
|
185
|
+
evidence = _build_evidence()
|
|
186
|
+
profile = CapabilityProfile(key=OPENAI_AGENTS_API_KEY, stateful_sessions=True, native_context_management=True, evidence_id=evidence.evidence_id)
|
|
187
|
+
record = CompatibilityRecord(key=OPENAI_AGENTS_API_KEY, state=CompatibilityState.PASSTHROUGH_ONLY, evidence_id=evidence.evidence_id, reason='offline_conformance_only')
|
|
188
|
+
capabilities = CapabilityRegistry()
|
|
189
|
+
capabilities.register(profile)
|
|
190
|
+
compatibility = CompatibilityRegistry()
|
|
191
|
+
compatibility.register(record)
|
|
192
|
+
return OpenAIAgentsConformanceBundle(evidence, profile, record, CapabilityDetector(capabilities, compatibility))
|
|
193
|
+
|
|
194
|
+
def _certification_state_passthrough_only() -> bool:
|
|
195
|
+
b = build_openai_agents_conformance()
|
|
196
|
+
return b.record.state is CompatibilityState.PASSTHROUGH_ONLY and b.record.reason == 'offline_conformance_only' and b.profile.evidence_id == b.record.evidence_id
|
|
197
|
+
|
|
198
|
+
def _corpus_digest_matches_raw_ordered_bytes() -> bool:
|
|
199
|
+
return corpus_digest(RAW_CORPUS) == build_openai_agents_conformance().evidence.corpus_digest
|
|
200
|
+
|
|
201
|
+
def _idempotency_key_frozen() -> bool:
|
|
202
|
+
p = json.loads(dict(INPUT_EVENT_FIXTURES)['input-message'].decode('utf-8'))
|
|
203
|
+
return p.get('idempotency_key') == 'idem_fixture_001'
|
|
204
|
+
|
|
205
|
+
def _mcp_and_vault_fixture_covered() -> bool:
|
|
206
|
+
p = json.loads(SESSION_CREATE_FIXTURE.decode('utf-8'))
|
|
207
|
+
return p.get('vault_ids') == ['vault_fixture'] and any(t.get('type') == 'mcp' for t in p['agent']['tools']) and p.get('future_field') == {'opaque': True}
|
|
208
|
+
|
|
209
|
+
def _native_context_management_claim_bounded() -> bool:
|
|
210
|
+
b = build_openai_agents_conformance()
|
|
211
|
+
p = b.profile
|
|
212
|
+
return p.stateful_sessions is True and p.native_context_management is True and p.streaming == 'unknown' and p.tool_calls == 'unknown' and p.reasoning_state == 'unknown' and p.exact_byte_preservation == 'unknown' and p.multimodal == 'unknown' and p.structured_output == 'unknown' and b.record.state is CompatibilityState.PASSTHROUGH_ONLY
|
|
213
|
+
|
|
214
|
+
def _near_match_non_inheritance() -> bool:
|
|
215
|
+
b = build_openai_agents_conformance()
|
|
216
|
+
near = (
|
|
217
|
+
CapabilityKey('generic-openai','responses','openai'),
|
|
218
|
+
CapabilityKey('generic-openai','chat_completions','openai'),
|
|
219
|
+
CapabilityKey('generic-openai','openai_agents_api_v2','openai'),
|
|
220
|
+
CapabilityKey('generic-openai','openai_agents_api','openai-compatible'),
|
|
221
|
+
CapabilityKey('generic-openai','openai_agents_api','openai',model_family='gpt-future'),
|
|
222
|
+
CapabilityKey('generic-openai','openai_agents_api','openai',client_version='1'),
|
|
223
|
+
CapabilityKey('codex','openai_agents_api','openai'),
|
|
224
|
+
)
|
|
225
|
+
return all(b.detector.detect(k).profile is None and b.detector.detect(k).compatibility.state is CompatibilityState.PASSTHROUGH_ONLY and b.detector.detect(k).compatibility.reason == 'unknown_capability' for k in near)
|
|
226
|
+
|
|
227
|
+
def _official_sources_bound() -> bool:
|
|
228
|
+
e = build_openai_agents_conformance().evidence
|
|
229
|
+
return e.snapshot_date == SNAPSHOT_DATE and e.sdk_client_version is None and e.official_source_refs == OFFICIAL_SOURCE_REFS
|
|
230
|
+
|
|
231
|
+
def _stream_duplicate_and_order_frozen() -> bool:
|
|
232
|
+
labels = tuple(label for label, _ in STREAM_EVENT_FRAMES)
|
|
233
|
+
frames = tuple(payload for _, payload in STREAM_EVENT_FRAMES)
|
|
234
|
+
return labels[-2:] == ('duplicate-frame','duplicate-frame-copy') and frames[-2] == frames[-1] and frames.count(frames[-2]) == 2 and labels.index('output-text-delta-a') < labels.index('output-text-delta-b')
|
|
235
|
+
|
|
236
|
+
def _stream_identifiers_frozen() -> bool:
|
|
237
|
+
parsed = {
|
|
238
|
+
label: json.loads(frame.decode('utf-8'))
|
|
239
|
+
for label, frame in STREAM_EVENT_FRAMES
|
|
240
|
+
}
|
|
241
|
+
known = {
|
|
242
|
+
label: event
|
|
243
|
+
for label, event in parsed.items()
|
|
244
|
+
if label != 'unknown-future-event'
|
|
245
|
+
}
|
|
246
|
+
if not all('event_id' in event for event in known.values()):
|
|
247
|
+
return False
|
|
248
|
+
for field in ('session_id', 'turn_id', 'item_id', 'output_index', 'content_index', 'summary_index'):
|
|
249
|
+
if not any(field in event for event in known.values()):
|
|
250
|
+
return False
|
|
251
|
+
return (
|
|
252
|
+
parsed['session-created']['session']['id'] == 'sess_fixture'
|
|
253
|
+
and parsed['subagent-created']['subagent']['session_id'] == 'sess_fixture'
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
def _unknown_event_frozen() -> bool:
|
|
257
|
+
return any(json.loads(f.decode('utf-8')).get('type') == 'agent.session.future.event' and json.loads(f.decode('utf-8')).get('opaque') == {'nested':[1,2,3]} for _,f in STREAM_EVENT_FRAMES)
|
|
258
|
+
|
|
259
|
+
def openai_agents_conformance_cases() -> Mapping[str, Callable[[], bool]]:
|
|
260
|
+
return {
|
|
261
|
+
'certification_state_passthrough_only': _certification_state_passthrough_only,
|
|
262
|
+
'corpus_digest_matches_raw_ordered_bytes': _corpus_digest_matches_raw_ordered_bytes,
|
|
263
|
+
'idempotency_key_frozen': _idempotency_key_frozen,
|
|
264
|
+
'mcp_and_vault_fixture_covered': _mcp_and_vault_fixture_covered,
|
|
265
|
+
'native_context_management_claim_bounded': _native_context_management_claim_bounded,
|
|
266
|
+
'near_match_non_inheritance': _near_match_non_inheritance,
|
|
267
|
+
'official_sources_bound': _official_sources_bound,
|
|
268
|
+
'stream_duplicate_and_order_frozen': _stream_duplicate_and_order_frozen,
|
|
269
|
+
'stream_identifiers_frozen': _stream_identifiers_frozen,
|
|
270
|
+
'unknown_event_frozen': _unknown_event_frozen,
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
def run_openai_agents_conformance() -> tuple[ConformanceResult, ...]:
|
|
274
|
+
results = run_conformance(openai_agents_conformance_cases())
|
|
275
|
+
require_conformance(results)
|
|
276
|
+
return results
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, Callable, Mapping, Protocol
|
|
4
|
+
|
|
5
|
+
from .adapters import ChatCompletionsAdapter, ResponsesAdapter
|
|
6
|
+
from .model import RequestEnvelope
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ProtocolAdapter(Protocol):
|
|
10
|
+
name: str
|
|
11
|
+
|
|
12
|
+
def parse(self, payload: Mapping[str, Any]) -> RequestEnvelope: ...
|
|
13
|
+
|
|
14
|
+
def serialize(self, envelope: RequestEnvelope) -> dict[str, Any]: ...
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
AdapterFactory = Callable[[], ProtocolAdapter]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class DuplicateProtocolAdapterError(ValueError):
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class ProtocolAdapterRegistry:
|
|
25
|
+
def __init__(self) -> None:
|
|
26
|
+
self._factories: dict[str, AdapterFactory] = {}
|
|
27
|
+
|
|
28
|
+
@staticmethod
|
|
29
|
+
def _normalize(path: str) -> str:
|
|
30
|
+
return path.split("?", 1)[0]
|
|
31
|
+
|
|
32
|
+
def register(self, endpoint: str, factory: AdapterFactory) -> None:
|
|
33
|
+
endpoint = self._normalize(endpoint)
|
|
34
|
+
if endpoint in self._factories:
|
|
35
|
+
raise DuplicateProtocolAdapterError(
|
|
36
|
+
f"duplicate protocol endpoint: {endpoint}"
|
|
37
|
+
)
|
|
38
|
+
self._factories[endpoint] = factory
|
|
39
|
+
|
|
40
|
+
def resolve(self, path: str) -> ProtocolAdapter | None:
|
|
41
|
+
factory = self._factories.get(self._normalize(path))
|
|
42
|
+
return None if factory is None else factory()
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def default_protocol_adapter_registry() -> ProtocolAdapterRegistry:
|
|
46
|
+
registry = ProtocolAdapterRegistry()
|
|
47
|
+
registry.register("/v1/responses", ResponsesAdapter)
|
|
48
|
+
registry.register("/v1/chat/completions", ChatCompletionsAdapter)
|
|
49
|
+
return registry
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: token-runtime
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.0a3
|
|
4
4
|
Summary: Quality-preserving local context optimization runtime for LLM clients
|
|
5
5
|
Author: Tungvoc3199
|
|
6
6
|
License: Apache-2.0
|
|
@@ -37,7 +37,7 @@ Local-first adaptive context optimization for Codex and OpenAI-compatible AI age
|
|
|
37
37
|

|
|
38
38
|

|
|
39
39
|

|
|
40
|
-
[](https://github.com/Tungvoc3199/token-runtime/releases/tag/v0.1.0-alpha.3)
|
|
41
41
|
|
|
42
42
|
[Quickstart](#quickstart) · [Proof](#proof) · [How it works](#how-it-works) · [Safety](#safety-first-by-design) · [Compatibility](#compatibility) · [Compatibility matrix](docs/COMPATIBILITY.md) · [Security](SECURITY.md)
|
|
43
43
|
|
|
@@ -118,8 +118,16 @@ The V1 planner protects the active turn, protocol state, hard constraints, exact
|
|
|
118
118
|
|
|
119
119
|
Python 3.12+ is required.
|
|
120
120
|
|
|
121
|
+
Install the Alpha.3 package from PyPI after publication:
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
pip install token-runtime==0.1.0a3
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Or install it as an isolated CLI tool with `uv`:
|
|
128
|
+
|
|
121
129
|
```bash
|
|
122
|
-
|
|
130
|
+
uv tool install token-runtime==0.1.0a3
|
|
123
131
|
```
|
|
124
132
|
|
|
125
133
|
Preview integration changes first:
|
|
@@ -174,6 +182,7 @@ For Codex, TOKEN transactionally manages only the selected OpenAI-compatible pro
|
|
|
174
182
|
| Local OpenAI-compatible gateways/routers | ✅ Supported |
|
|
175
183
|
| Anthropic Messages adapter/conformance | 🛡️ `PASSTHROUGH_ONLY`; offline conformance, not production gateway-wired |
|
|
176
184
|
| Gemini GenerateContent adapter/conformance | 🛡️ `PASSTHROUGH_ONLY`; offline conformance, not production gateway-wired |
|
|
185
|
+
| OpenAI Agents API managed-session conformance | 🛡️ `PASSTHROUGH_ONLY`; offline conformance only, not production routing |
|
|
177
186
|
| Multimodal optimization | 🛡️ `PASSTHROUGH_ONLY` |
|
|
178
187
|
|
|
179
188
|
`CERTIFIED` is always version- and boundary-specific. Canonical source can also contain adapters that are not deployed in the active gateway. See the [compatibility matrix](docs/COMPATIBILITY.md) for the exact evidence boundary.
|
|
@@ -230,7 +239,7 @@ The approved 24-call live campaign found a real regression class. The final poli
|
|
|
230
239
|
- [Release process](docs/RELEASE-PROCESS.md)
|
|
231
240
|
- [GitHub governance](docs/GITHUB-GOVERNANCE.md)
|
|
232
241
|
- [Public benchmark](benchmarks/README.md)
|
|
233
|
-
- [v0.1.0-alpha.
|
|
242
|
+
- [v0.1.0-alpha.3 release notes](docs/releases/v0.1.0-alpha.3.md)
|
|
234
243
|
- [Apache-2.0 license](LICENSE)
|
|
235
244
|
|
|
236
245
|
---
|
|
@@ -24,8 +24,10 @@ src/token_runtime/gemini_conformance.py
|
|
|
24
24
|
src/token_runtime/integrations.py
|
|
25
25
|
src/token_runtime/metrics.py
|
|
26
26
|
src/token_runtime/model.py
|
|
27
|
+
src/token_runtime/openai_agents_conformance.py
|
|
27
28
|
src/token_runtime/openai_certification.py
|
|
28
29
|
src/token_runtime/planner.py
|
|
30
|
+
src/token_runtime/protocol_registry.py
|
|
29
31
|
src/token_runtime/reducers.py
|
|
30
32
|
src/token_runtime/store.py
|
|
31
33
|
src/token_runtime/strategies.py
|
|
@@ -40,6 +42,7 @@ tests/test_adapters.py
|
|
|
40
42
|
tests/test_agent_integration_safety.py
|
|
41
43
|
tests/test_agent_integrations.py
|
|
42
44
|
tests/test_alpha2_release_candidate.py
|
|
45
|
+
tests/test_alpha3_release_candidate.py
|
|
43
46
|
tests/test_anthropic_adapter.py
|
|
44
47
|
tests/test_anthropic_conformance.py
|
|
45
48
|
tests/test_anthropic_preserved_thinking_benchmark.py
|
|
@@ -53,7 +56,9 @@ tests/test_compat_cert.py
|
|
|
53
56
|
tests/test_compatibility.py
|
|
54
57
|
tests/test_config.py
|
|
55
58
|
tests/test_conformance.py
|
|
59
|
+
tests/test_current_release_candidate.py
|
|
56
60
|
tests/test_distribution.py
|
|
61
|
+
tests/test_distribution_version_generalization.py
|
|
57
62
|
tests/test_docs_growth.py
|
|
58
63
|
tests/test_engine.py
|
|
59
64
|
tests/test_evolution_contracts.py
|
|
@@ -64,11 +69,15 @@ tests/test_gemini_adapter.py
|
|
|
64
69
|
tests/test_gemini_conformance.py
|
|
65
70
|
tests/test_governance.py
|
|
66
71
|
tests/test_integrations.py
|
|
72
|
+
tests/test_llm_change_isolation.py
|
|
67
73
|
tests/test_metrics.py
|
|
68
74
|
tests/test_model.py
|
|
75
|
+
tests/test_onboarding_ux.py
|
|
76
|
+
tests/test_openai_agents_conformance.py
|
|
69
77
|
tests/test_openai_certification.py
|
|
70
78
|
tests/test_oss_surface.py
|
|
71
79
|
tests/test_planner.py
|
|
80
|
+
tests/test_protocol_registry.py
|
|
72
81
|
tests/test_public_benchmark.py
|
|
73
82
|
tests/test_public_classification.py
|
|
74
83
|
tests/test_public_ready.py
|