agent-tokenops 0.1.2__tar.gz → 0.2.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.
- {agent_tokenops-0.1.2/src/agent_tokenops.egg-info → agent_tokenops-0.2.0}/PKG-INFO +6 -2
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/README.md +5 -1
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/pyproject.toml +1 -1
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0/src/agent_tokenops.egg-info}/PKG-INFO +6 -2
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/agent_tokenops.egg-info/SOURCES.txt +1 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/__init__.py +1 -1
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/control/client.py +19 -15
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/control/http.py +12 -2
- agent_tokenops-0.2.0/src/tokenops/control/http_store.py +217 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/control/store.py +37 -2
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/tests/test_store.py +61 -1
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/LICENSE.txt +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/setup.cfg +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/agent_tokenops.egg-info/dependency_links.txt +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/agent_tokenops.egg-info/requires.txt +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/agent_tokenops.egg-info/top_level.txt +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/adapters/__init__.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/adapters/langchain.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/config/__init__.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/config/default.yaml +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/config/loader.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/control/__init__.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/control/attribution.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/control/boundary.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/control/config.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/control/context.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/control/core.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/control/crossing.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/control/engine.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/control/governance_cache.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/control/instrument.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/control/integration.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/control/ledger.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/control/models.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/control/policies/__init__.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/control/policies/_util.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/control/policies/concurrency_cap.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/control/policies/context_compaction.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/control/policies/cost_budget.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/control/policies/cost_guard.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/control/policies/output_runaway.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/control/policies/pre_call_worst_case.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/control/policies/progress_guard.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/control/policies/step_cap.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/control/policies/tool_fix.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/control/policies/tool_output_cap.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/control/policies/trajectory_hint.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/control/pricing.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/control/propagate.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/control/request_context.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/control/run.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/control/trajectory/__init__.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/control/trajectory/compress.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/control/trajectory/enqueue.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/control/trajectory/gates.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/control/trajectory/hint.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/control/trajectory/scope.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/control/trajectory/serialize.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/control/trajectory/worker.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/env.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/providers/__init__.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/providers/anthropic.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/providers/factory.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/providers/openai.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/providers/types.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/server/__init__.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/server/__main__.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/server/app.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/ui/__init__.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/ui/app.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/ui/run_detail.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/ui/store_client.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/ui/theme.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/ui/views/__init__.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/ui/views/admin.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/ui/views/dashboard.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/tests/test_actuators.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/tests/test_apply.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/tests/test_attribution.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/tests/test_attribution_ledger_policies_e2e.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/tests/test_boundary.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/tests/test_chronicle_boundary.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/tests/test_concurrency_cap.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/tests/test_config.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/tests/test_context_compaction.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/tests/test_control_plane_app.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/tests/test_control_plane_client.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/tests/test_cost_budget.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/tests/test_cost_guard.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/tests/test_cross_process_budget_gating.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/tests/test_crossing_hook.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/tests/test_governance_config_cache.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/tests/test_governance_per_agent.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/tests/test_integration.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/tests/test_ledger.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/tests/test_llm_boundary_precall.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/tests/test_output_runaway.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/tests/test_policies_wrap_integration.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/tests/test_pre_call_worst_case.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/tests/test_preview_mode.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/tests/test_pricing.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/tests/test_progress_guard.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/tests/test_propagate.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/tests/test_server_enforcement.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/tests/test_step_cap.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/tests/test_thread_safety.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/tests/test_tokenops_run.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/tests/test_tool_fix.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/tests/test_tool_output_cap.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/tests/test_trajectory_hint.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/tests/test_trajectory_hint_e2e.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/tests/test_wrap_adapters.py +0 -0
- {agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/tests/test_wrap_complete_chronicle.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agent-tokenops
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: TokenOps control plane and SDK for run-aware agent token governance
|
|
5
5
|
Author: Susheem Koul, Tisha Chawla
|
|
6
6
|
License-Expression: MIT
|
|
@@ -60,7 +60,11 @@ Cap spend and steer behavior across a whole agent workflow — not per request
|
|
|
60
60
|
|
|
61
61
|
<br>
|
|
62
62
|
|
|
63
|
-
<
|
|
63
|
+
<a href="https://github.com/theagentplane/tokenops/raw/main/examples/demo-assets/videos/02_governance_on_budget_cap.webm">
|
|
64
|
+
<img src="https://raw.githubusercontent.com/theagentplane/tokenops/main/examples/demo-assets/videos/02_governance_on_budget_cap.gif" alt="TokenOps demo: a governed Research to Summarize run halts when worst-case cost exceeds the remaining run budget, then the Dashboard shows spend and governance per agent" width="720" />
|
|
65
|
+
</a>
|
|
66
|
+
|
|
67
|
+
<sub><i>Governed Research → Summarize run: the budget cap halts spend mid-run, then the Dashboard attributes cost per agent. <a href="https://github.com/theagentplane/tokenops/raw/main/examples/demo-assets/videos/02_governance_on_budget_cap.webm">Full video</a>.</i></sub>
|
|
64
68
|
|
|
65
69
|
</div>
|
|
66
70
|
|
|
@@ -14,7 +14,11 @@ Cap spend and steer behavior across a whole agent workflow — not per request
|
|
|
14
14
|
|
|
15
15
|
<br>
|
|
16
16
|
|
|
17
|
-
<
|
|
17
|
+
<a href="https://github.com/theagentplane/tokenops/raw/main/examples/demo-assets/videos/02_governance_on_budget_cap.webm">
|
|
18
|
+
<img src="https://raw.githubusercontent.com/theagentplane/tokenops/main/examples/demo-assets/videos/02_governance_on_budget_cap.gif" alt="TokenOps demo: a governed Research to Summarize run halts when worst-case cost exceeds the remaining run budget, then the Dashboard shows spend and governance per agent" width="720" />
|
|
19
|
+
</a>
|
|
20
|
+
|
|
21
|
+
<sub><i>Governed Research → Summarize run: the budget cap halts spend mid-run, then the Dashboard attributes cost per agent. <a href="https://github.com/theagentplane/tokenops/raw/main/examples/demo-assets/videos/02_governance_on_budget_cap.webm">Full video</a>.</i></sub>
|
|
18
22
|
|
|
19
23
|
</div>
|
|
20
24
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agent-tokenops
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: TokenOps control plane and SDK for run-aware agent token governance
|
|
5
5
|
Author: Susheem Koul, Tisha Chawla
|
|
6
6
|
License-Expression: MIT
|
|
@@ -60,7 +60,11 @@ Cap spend and steer behavior across a whole agent workflow — not per request
|
|
|
60
60
|
|
|
61
61
|
<br>
|
|
62
62
|
|
|
63
|
-
<
|
|
63
|
+
<a href="https://github.com/theagentplane/tokenops/raw/main/examples/demo-assets/videos/02_governance_on_budget_cap.webm">
|
|
64
|
+
<img src="https://raw.githubusercontent.com/theagentplane/tokenops/main/examples/demo-assets/videos/02_governance_on_budget_cap.gif" alt="TokenOps demo: a governed Research to Summarize run halts when worst-case cost exceeds the remaining run budget, then the Dashboard shows spend and governance per agent" width="720" />
|
|
65
|
+
</a>
|
|
66
|
+
|
|
67
|
+
<sub><i>Governed Research → Summarize run: the budget cap halts spend mid-run, then the Dashboard attributes cost per agent. <a href="https://github.com/theagentplane/tokenops/raw/main/examples/demo-assets/videos/02_governance_on_budget_cap.webm">Full video</a>.</i></sub>
|
|
64
68
|
|
|
65
69
|
</div>
|
|
66
70
|
|
|
@@ -24,6 +24,7 @@ src/tokenops/control/crossing.py
|
|
|
24
24
|
src/tokenops/control/engine.py
|
|
25
25
|
src/tokenops/control/governance_cache.py
|
|
26
26
|
src/tokenops/control/http.py
|
|
27
|
+
src/tokenops/control/http_store.py
|
|
27
28
|
src/tokenops/control/instrument.py
|
|
28
29
|
src/tokenops/control/integration.py
|
|
29
30
|
src/tokenops/control/ledger.py
|
|
@@ -15,9 +15,10 @@ from __future__ import annotations
|
|
|
15
15
|
import logging
|
|
16
16
|
import os
|
|
17
17
|
from collections.abc import Mapping
|
|
18
|
-
from typing import Any
|
|
18
|
+
from typing import Any, cast
|
|
19
19
|
|
|
20
20
|
from tokenops.control.http import post_run, post_run_sync
|
|
21
|
+
from tokenops.control.http_store import HttpStore
|
|
21
22
|
from tokenops.control.models import (
|
|
22
23
|
GovernanceMode,
|
|
23
24
|
RunAlreadyRegisteredError,
|
|
@@ -58,23 +59,27 @@ class ControlPlaneClient:
|
|
|
58
59
|
raise ValueError("exactly one of url or store is required")
|
|
59
60
|
self._url = url.rstrip("/") if url else None
|
|
60
61
|
self._store = store
|
|
61
|
-
self._hybrid_store:
|
|
62
|
+
self._hybrid_store: Any = None
|
|
62
63
|
self._timeout = timeout
|
|
63
64
|
|
|
64
65
|
@classmethod
|
|
65
66
|
def from_env(cls, *, timeout: float = 30.0) -> ControlPlaneClient:
|
|
66
67
|
"""Build a client from ``TOKENOPS_URL`` / ``TOKENOPS_EMBEDDED`` / ``TOKENOPS_DB``.
|
|
67
68
|
|
|
68
|
-
* ``TOKENOPS_URL`` set and ``TOKENOPS_EMBEDDED`` not ``1``
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
Also installs the Chronicle crossing hook (idempotent; design notes §17).
|
|
69
|
+
* ``TOKENOPS_URL`` or ``CONTROL_PLANE_URL`` set and ``TOKENOPS_EMBEDDED`` not ``1``
|
|
70
|
+
→ HTTP to the plane (no local SQLite).
|
|
71
|
+
* otherwise → embedded :class:`Store` at ``TOKENOPS_DB``.
|
|
72
72
|
"""
|
|
73
73
|
from tokenops.control.crossing import install_crossing_hook
|
|
74
74
|
|
|
75
75
|
install_crossing_hook()
|
|
76
76
|
embedded = os.environ.get("TOKENOPS_EMBEDDED", "").strip() == "1"
|
|
77
|
-
url = (
|
|
77
|
+
url = (
|
|
78
|
+
os.environ.get("CONTROL_PLANE_URL")
|
|
79
|
+
or os.environ.get("TOKENOPS_URL")
|
|
80
|
+
or os.environ.get("TOKENOPS_CONTROL_PLANE_URL")
|
|
81
|
+
or ""
|
|
82
|
+
).strip()
|
|
78
83
|
if url and not embedded:
|
|
79
84
|
return cls(url=url, timeout=timeout)
|
|
80
85
|
db = os.environ.get("TOKENOPS_DB", "tokenops.db")
|
|
@@ -98,19 +103,18 @@ class ControlPlaneClient:
|
|
|
98
103
|
return self._store
|
|
99
104
|
|
|
100
105
|
def require_store(self) -> Store:
|
|
101
|
-
"""Backing
|
|
106
|
+
"""Backing store for ledger / config / dashboard rows.
|
|
102
107
|
|
|
103
|
-
Embedded mode returns the in-process Store. Remote
|
|
104
|
-
|
|
105
|
-
plane APIs are HTTP (§4 / later waves).
|
|
108
|
+
Embedded mode returns the in-process SQLite Store. Remote mode returns
|
|
109
|
+
:class:`HttpStore` — never a local DB file.
|
|
106
110
|
"""
|
|
107
111
|
if self._store is not None:
|
|
108
112
|
return self._store
|
|
109
113
|
if self._hybrid_store is None:
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
self._hybrid_store =
|
|
113
|
-
return self._hybrid_store
|
|
114
|
+
assert self._url is not None
|
|
115
|
+
key = os.environ.get("CONTROL_PLANE_API_KEY") or os.environ.get("TOKENOPS_API_KEY")
|
|
116
|
+
self._hybrid_store = HttpStore(self._url, api_key=key, timeout=self._timeout)
|
|
117
|
+
return cast(Store, self._hybrid_store)
|
|
114
118
|
|
|
115
119
|
def register_run(
|
|
116
120
|
self,
|
|
@@ -6,6 +6,7 @@ wrap handlers here so Halt/Throttled map to HTTP responses.
|
|
|
6
6
|
|
|
7
7
|
from __future__ import annotations
|
|
8
8
|
|
|
9
|
+
import os
|
|
9
10
|
from collections.abc import Awaitable, Callable, Mapping
|
|
10
11
|
from typing import Any
|
|
11
12
|
|
|
@@ -107,6 +108,15 @@ def _raise_for_response(response: httpx.Response) -> None:
|
|
|
107
108
|
) from exc
|
|
108
109
|
|
|
109
110
|
|
|
111
|
+
def _plane_auth_headers() -> dict[str, str]:
|
|
112
|
+
key = (
|
|
113
|
+
os.environ.get("CONTROL_PLANE_API_KEY") or os.environ.get("TOKENOPS_API_KEY") or ""
|
|
114
|
+
).strip()
|
|
115
|
+
if not key:
|
|
116
|
+
return {}
|
|
117
|
+
return {"Authorization": f"Bearer {key}"}
|
|
118
|
+
|
|
119
|
+
|
|
110
120
|
async def post_run(
|
|
111
121
|
url: str,
|
|
112
122
|
payload: dict[str, Any],
|
|
@@ -116,7 +126,7 @@ async def post_run(
|
|
|
116
126
|
"""POST ``/v1/runs`` at *url*. Prefer :class:`~tokenops.control.client.ControlPlaneClient`."""
|
|
117
127
|
base = url.rstrip("/")
|
|
118
128
|
async with httpx.AsyncClient(timeout=timeout) as client:
|
|
119
|
-
response = await client.post(f"{base}/v1/runs", json=payload)
|
|
129
|
+
response = await client.post(f"{base}/v1/runs", json=payload, headers=_plane_auth_headers())
|
|
120
130
|
_raise_for_response(response)
|
|
121
131
|
return response.json()
|
|
122
132
|
|
|
@@ -130,6 +140,6 @@ def post_run_sync(
|
|
|
130
140
|
"""Sync POST ``/v1/runs`` at *url*. Prefer :class:`~tokenops.control.client.ControlPlaneClient`."""
|
|
131
141
|
base = url.rstrip("/")
|
|
132
142
|
with httpx.Client(timeout=timeout) as client:
|
|
133
|
-
response = client.post(f"{base}/v1/runs", json=payload)
|
|
143
|
+
response = client.post(f"{base}/v1/runs", json=payload, headers=_plane_auth_headers())
|
|
134
144
|
_raise_for_response(response)
|
|
135
145
|
return response.json()
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
"""HTTP-backed Store used when TOKENOPS_URL / CONTROL_PLANE_URL is set.
|
|
2
|
+
|
|
3
|
+
Does not open SQLite. All ledger, governance, and run-history traffic goes to
|
|
4
|
+
the control plane process.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
import httpx
|
|
12
|
+
|
|
13
|
+
from tokenops.control.models import (
|
|
14
|
+
BudgetSpec,
|
|
15
|
+
PolicyInstance,
|
|
16
|
+
RunAlreadyRegisteredError,
|
|
17
|
+
RunNotRegisteredError,
|
|
18
|
+
RunRecord,
|
|
19
|
+
RunRegistration,
|
|
20
|
+
Segment,
|
|
21
|
+
parse_governance_mode,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class HttpStore:
|
|
26
|
+
def __init__(self, base_url: str, *, api_key: str | None = None, timeout: float = 30.0) -> None:
|
|
27
|
+
self.path = base_url.rstrip("/")
|
|
28
|
+
headers = {}
|
|
29
|
+
if api_key:
|
|
30
|
+
headers["Authorization"] = f"Bearer {api_key}"
|
|
31
|
+
self._client = httpx.Client(base_url=self.path, timeout=timeout, headers=headers)
|
|
32
|
+
|
|
33
|
+
def close(self) -> None:
|
|
34
|
+
self._client.close()
|
|
35
|
+
|
|
36
|
+
def _request(self, method: str, path: str, **kwargs: Any) -> httpx.Response:
|
|
37
|
+
response = self._client.request(method, path, **kwargs)
|
|
38
|
+
if response.status_code == 409:
|
|
39
|
+
body = response.json()
|
|
40
|
+
raise RunAlreadyRegisteredError(body.get("error", "run already registered"))
|
|
41
|
+
if response.status_code == 404:
|
|
42
|
+
return response
|
|
43
|
+
response.raise_for_status()
|
|
44
|
+
return response
|
|
45
|
+
|
|
46
|
+
def register_run(self, reg: RunRegistration) -> RunRegistration:
|
|
47
|
+
r = self._request(
|
|
48
|
+
"POST",
|
|
49
|
+
"/v1/runs",
|
|
50
|
+
json={
|
|
51
|
+
"run_id": reg.run_id,
|
|
52
|
+
"intent": reg.intent,
|
|
53
|
+
"user_dims": dict(reg.user_dims),
|
|
54
|
+
"mode": reg.mode.value,
|
|
55
|
+
},
|
|
56
|
+
)
|
|
57
|
+
body = r.json()
|
|
58
|
+
return RunRegistration(
|
|
59
|
+
run_id=str(body["run_id"]),
|
|
60
|
+
intent=str(body.get("intent") or reg.intent),
|
|
61
|
+
user_dims={str(k): str(v) for k, v in (body.get("user_dims") or reg.user_dims).items()},
|
|
62
|
+
mode=parse_governance_mode(body.get("mode") or reg.mode),
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
def resolve_run(self, run_id: str) -> RunRegistration:
|
|
66
|
+
r = self._client.get(f"/v1/runs/{run_id}/registration")
|
|
67
|
+
if r.status_code == 404:
|
|
68
|
+
raise RunNotRegisteredError(f"run {run_id!r} is not registered")
|
|
69
|
+
r.raise_for_status()
|
|
70
|
+
body = r.json()
|
|
71
|
+
return RunRegistration(
|
|
72
|
+
run_id=str(body["run_id"]),
|
|
73
|
+
intent=str(body.get("intent", "")),
|
|
74
|
+
user_dims={str(k): str(v) for k, v in (body.get("user_dims") or {}).items()},
|
|
75
|
+
mode=parse_governance_mode(body.get("mode")),
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
def get_run_registration(self, run_id: str) -> RunRegistration | None:
|
|
79
|
+
try:
|
|
80
|
+
return self.resolve_run(run_id)
|
|
81
|
+
except RunNotRegisteredError:
|
|
82
|
+
return None
|
|
83
|
+
|
|
84
|
+
def governance_config_for(self, agent: str) -> dict:
|
|
85
|
+
return self._request("GET", f"/v1/governance/{agent}").json()
|
|
86
|
+
|
|
87
|
+
def create_run(self, rec: RunRecord) -> RunRecord:
|
|
88
|
+
payload = {
|
|
89
|
+
"run_id": rec.run_id,
|
|
90
|
+
"agent": rec.agent,
|
|
91
|
+
"status": rec.status,
|
|
92
|
+
"parent_run": rec.parent_run,
|
|
93
|
+
"parent_span": rec.parent_span,
|
|
94
|
+
"halt_reason": rec.halt_reason,
|
|
95
|
+
"detector": rec.detector,
|
|
96
|
+
"cost_micros": rec.cost_micros,
|
|
97
|
+
"steps": rec.steps,
|
|
98
|
+
"started_at": rec.started_at,
|
|
99
|
+
"ended_at": rec.ended_at,
|
|
100
|
+
"task": rec.task,
|
|
101
|
+
"dims": dict(rec.dims),
|
|
102
|
+
"governance_events": list(rec.governance_events or []),
|
|
103
|
+
}
|
|
104
|
+
self._request("PUT", "/v1/run-records", json=payload)
|
|
105
|
+
return rec
|
|
106
|
+
|
|
107
|
+
def update_run(self, run_id: str, **fields: Any) -> None:
|
|
108
|
+
self._request("PATCH", f"/v1/run-records/{run_id}", json=fields)
|
|
109
|
+
|
|
110
|
+
def get_run(self, run_id: str) -> RunRecord | None:
|
|
111
|
+
r = self._client.get(f"/v1/run-records/{run_id}")
|
|
112
|
+
if r.status_code == 404:
|
|
113
|
+
return None
|
|
114
|
+
r.raise_for_status()
|
|
115
|
+
return _run(r.json())
|
|
116
|
+
|
|
117
|
+
def list_runs(self, *, problematic_only: bool = False, limit: int = 200) -> list[RunRecord]:
|
|
118
|
+
r = self._request(
|
|
119
|
+
"GET",
|
|
120
|
+
"/v1/run-records",
|
|
121
|
+
params={"problematic_only": problematic_only, "limit": limit},
|
|
122
|
+
)
|
|
123
|
+
return [_run(x) for x in r.json()]
|
|
124
|
+
|
|
125
|
+
def ledger_add_spent(self, budget_id: str, segment_key: str, period: str, delta: int) -> int:
|
|
126
|
+
r = self._request(
|
|
127
|
+
"POST",
|
|
128
|
+
"/v1/ledger/spent/add",
|
|
129
|
+
json={
|
|
130
|
+
"budget_id": budget_id,
|
|
131
|
+
"segment_key": segment_key,
|
|
132
|
+
"period": period,
|
|
133
|
+
"delta": delta,
|
|
134
|
+
},
|
|
135
|
+
)
|
|
136
|
+
return int(r.json()["spent_micros"])
|
|
137
|
+
|
|
138
|
+
def ledger_get_spent(self, budget_id: str, segment_key: str, period: str) -> int:
|
|
139
|
+
r = self._request(
|
|
140
|
+
"GET",
|
|
141
|
+
"/v1/ledger/spent",
|
|
142
|
+
params={"budget_id": budget_id, "segment_key": segment_key, "period": period},
|
|
143
|
+
)
|
|
144
|
+
return int(r.json()["spent_micros"])
|
|
145
|
+
|
|
146
|
+
def ledger_admit(self, segment_key: str) -> int:
|
|
147
|
+
r = self._request("POST", "/v1/ledger/inflight/admit", json={"segment_key": segment_key})
|
|
148
|
+
return int(r.json()["count"])
|
|
149
|
+
|
|
150
|
+
def ledger_complete(self, segment_key: str) -> int:
|
|
151
|
+
r = self._request("POST", "/v1/ledger/inflight/complete", json={"segment_key": segment_key})
|
|
152
|
+
return int(r.json()["count"])
|
|
153
|
+
|
|
154
|
+
def ledger_inflight(self, segment_key: str) -> int:
|
|
155
|
+
r = self._request("GET", "/v1/ledger/inflight", params={"segment_key": segment_key})
|
|
156
|
+
return int(r.json()["count"])
|
|
157
|
+
|
|
158
|
+
def ledger_mark_halted(self, run_id: str, reason: str = "") -> None:
|
|
159
|
+
self._request("POST", "/v1/ledger/halt/mark", json={"run_id": run_id, "reason": reason})
|
|
160
|
+
|
|
161
|
+
def ledger_is_halted(self, run_id: str) -> bool:
|
|
162
|
+
r = self._request("GET", f"/v1/ledger/halt/{run_id}")
|
|
163
|
+
return bool(r.json().get("halted"))
|
|
164
|
+
|
|
165
|
+
def ledger_halt_reason(self, run_id: str) -> str | None:
|
|
166
|
+
r = self._client.get(f"/v1/ledger/halt/{run_id}")
|
|
167
|
+
r.raise_for_status()
|
|
168
|
+
return r.json().get("halt_reason")
|
|
169
|
+
|
|
170
|
+
def ledger_clear_halt(self, run_id: str) -> None:
|
|
171
|
+
self._request("POST", "/v1/ledger/halt/clear", json={"run_id": run_id})
|
|
172
|
+
|
|
173
|
+
# Trajectory index is plane-local for now; no-ops keep policies from crashing.
|
|
174
|
+
def save_trajectory_snapshot(self, *args: Any, **kwargs: Any) -> None:
|
|
175
|
+
return None
|
|
176
|
+
|
|
177
|
+
def enqueue_trajectory_build(self, *args: Any, **kwargs: Any) -> None:
|
|
178
|
+
return None
|
|
179
|
+
|
|
180
|
+
def lookup_trajectory_index(self, **kwargs: Any) -> dict[str, Any] | None:
|
|
181
|
+
return None
|
|
182
|
+
|
|
183
|
+
def upsert_segment(self, seg: Segment) -> Segment:
|
|
184
|
+
self._request("PUT", "/v1/segments", json=seg.__dict__)
|
|
185
|
+
return seg
|
|
186
|
+
|
|
187
|
+
def list_segments(self) -> list[Segment]:
|
|
188
|
+
return [Segment(**x) for x in self._request("GET", "/v1/segments").json()]
|
|
189
|
+
|
|
190
|
+
def upsert_budget(self, b: BudgetSpec) -> BudgetSpec:
|
|
191
|
+
self._request("PUT", "/v1/budgets", json=b.__dict__)
|
|
192
|
+
return b
|
|
193
|
+
|
|
194
|
+
def list_budgets(self) -> list[BudgetSpec]:
|
|
195
|
+
return [BudgetSpec(**x) for x in self._request("GET", "/v1/budgets").json()]
|
|
196
|
+
|
|
197
|
+
def list_policy_instances(self) -> list[PolicyInstance]:
|
|
198
|
+
return [PolicyInstance(**x) for x in self._request("GET", "/v1/policies").json()]
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def _run(data: dict[str, Any]) -> RunRecord:
|
|
202
|
+
return RunRecord(
|
|
203
|
+
run_id=str(data["run_id"]),
|
|
204
|
+
agent=str(data["agent"]),
|
|
205
|
+
status=data.get("status", "running"),
|
|
206
|
+
parent_run=data.get("parent_run"),
|
|
207
|
+
parent_span=data.get("parent_span"),
|
|
208
|
+
halt_reason=data.get("halt_reason"),
|
|
209
|
+
detector=data.get("detector"),
|
|
210
|
+
cost_micros=int(data.get("cost_micros", 0)),
|
|
211
|
+
steps=int(data.get("steps", 0)),
|
|
212
|
+
started_at=float(data.get("started_at", 0.0)),
|
|
213
|
+
ended_at=data.get("ended_at"),
|
|
214
|
+
task=data.get("task"),
|
|
215
|
+
dims={str(k): str(v) for k, v in (data.get("dims") or {}).items()},
|
|
216
|
+
governance_events=list(data.get("governance_events") or []),
|
|
217
|
+
)
|
|
@@ -32,6 +32,7 @@ import uuid
|
|
|
32
32
|
from collections.abc import Callable
|
|
33
33
|
from typing import Any, TypeVar
|
|
34
34
|
|
|
35
|
+
from tokenops.control.ledger import LIFETIME, RUN_TOTAL_BUDGET
|
|
35
36
|
from tokenops.control.models import (
|
|
36
37
|
BudgetSpec,
|
|
37
38
|
PolicyInstance,
|
|
@@ -377,6 +378,18 @@ class Store:
|
|
|
377
378
|
(reg.run_id, reg.intent, json.dumps(reg.user_dims), reg.mode.value, time.time()),
|
|
378
379
|
)
|
|
379
380
|
self._db.commit()
|
|
381
|
+
# Dashboard reads ``runs``; registration alone must make the run visible.
|
|
382
|
+
# Explicit create_run/update_run remain for richer status/cost/steps (REPLACE-safe).
|
|
383
|
+
agent = reg.intent or "agent"
|
|
384
|
+
self.create_run(
|
|
385
|
+
RunRecord(
|
|
386
|
+
run_id=reg.run_id,
|
|
387
|
+
agent=agent,
|
|
388
|
+
status="running",
|
|
389
|
+
dims=dict(reg.user_dims),
|
|
390
|
+
task=reg.intent or None,
|
|
391
|
+
)
|
|
392
|
+
)
|
|
380
393
|
return reg
|
|
381
394
|
|
|
382
395
|
@_locked
|
|
@@ -484,7 +497,7 @@ class Store:
|
|
|
484
497
|
@_locked
|
|
485
498
|
def get_run(self, run_id: str) -> RunRecord | None:
|
|
486
499
|
row = self._db.execute("SELECT * FROM runs WHERE run_id=?", (run_id,)).fetchone()
|
|
487
|
-
return
|
|
500
|
+
return self._run_with_ledger_cost(row) if row else None
|
|
488
501
|
|
|
489
502
|
@_locked
|
|
490
503
|
def list_runs(self, *, problematic_only: bool = False, limit: int = 200) -> list[RunRecord]:
|
|
@@ -492,7 +505,19 @@ class Store:
|
|
|
492
505
|
if problematic_only:
|
|
493
506
|
sql += " WHERE status IN ('halted','throttled','error')"
|
|
494
507
|
sql += " ORDER BY started_at DESC LIMIT ?"
|
|
495
|
-
return [
|
|
508
|
+
return [self._run_with_ledger_cost(r) for r in self._db.execute(sql, (limit,))]
|
|
509
|
+
|
|
510
|
+
def _run_with_ledger_cost(self, row: sqlite3.Row) -> RunRecord:
|
|
511
|
+
"""Build a RunRecord; prefer ``__run_total__`` ledger spend when present."""
|
|
512
|
+
rec = _run(row)
|
|
513
|
+
spent_row = self._db.execute(
|
|
514
|
+
"SELECT spent_micros FROM ledger_spent "
|
|
515
|
+
"WHERE budget_id=? AND segment_key=? AND period=?",
|
|
516
|
+
(RUN_TOTAL_BUDGET.budget_id, f"run:{rec.run_id}", LIFETIME),
|
|
517
|
+
).fetchone()
|
|
518
|
+
if spent_row is not None:
|
|
519
|
+
rec.cost_micros = int(spent_row[0])
|
|
520
|
+
return rec
|
|
496
521
|
|
|
497
522
|
@_locked
|
|
498
523
|
def run_tag_keys(self, *, limit: int = 500) -> list[str]:
|
|
@@ -581,6 +606,11 @@ class Store:
|
|
|
581
606
|
"halt_reason=COALESCE(excluded.halt_reason, ledger_halt.halt_reason)",
|
|
582
607
|
(run_id, reason or None),
|
|
583
608
|
)
|
|
609
|
+
# Keep dashboard row in sync when present; ignore missing runs rows.
|
|
610
|
+
self._db.execute(
|
|
611
|
+
"UPDATE runs SET status='halted', halt_reason=? WHERE run_id=?",
|
|
612
|
+
(reason or None, run_id),
|
|
613
|
+
)
|
|
584
614
|
self._db.commit()
|
|
585
615
|
|
|
586
616
|
@_locked
|
|
@@ -606,6 +636,11 @@ class Store:
|
|
|
606
636
|
"ON CONFLICT(run_id) DO UPDATE SET halted=0, halt_reason=NULL",
|
|
607
637
|
(run_id,),
|
|
608
638
|
)
|
|
639
|
+
# Clear halt_reason only — do not invent a completed/running status.
|
|
640
|
+
self._db.execute(
|
|
641
|
+
"UPDATE runs SET halt_reason=NULL WHERE run_id=?",
|
|
642
|
+
(run_id,),
|
|
643
|
+
)
|
|
609
644
|
self._db.commit()
|
|
610
645
|
|
|
611
646
|
# ---- trajectory hint index -------------------------------------------- #
|
|
@@ -6,7 +6,14 @@ import pytest
|
|
|
6
6
|
|
|
7
7
|
from conftest import toy_price
|
|
8
8
|
from tokenops.control import build_governor
|
|
9
|
-
from tokenops.control.
|
|
9
|
+
from tokenops.control.ledger import LIFETIME, RUN_TOTAL_BUDGET
|
|
10
|
+
from tokenops.control.models import (
|
|
11
|
+
BudgetSpec,
|
|
12
|
+
PolicyInstance,
|
|
13
|
+
RunRecord,
|
|
14
|
+
RunRegistration,
|
|
15
|
+
Segment,
|
|
16
|
+
)
|
|
10
17
|
from tokenops.control.store import Store
|
|
11
18
|
|
|
12
19
|
|
|
@@ -77,6 +84,59 @@ def test_run_records_and_problematic_filter(store):
|
|
|
77
84
|
assert problematic[0].halt_reason == "budget exhausted"
|
|
78
85
|
|
|
79
86
|
|
|
87
|
+
def test_register_run_creates_dashboard_row(store):
|
|
88
|
+
"""Registration alone must surface on the Admin Dashboard (list_runs / get_run)."""
|
|
89
|
+
store.register_run(
|
|
90
|
+
RunRegistration(
|
|
91
|
+
run_id="reg-only",
|
|
92
|
+
intent="summarize",
|
|
93
|
+
user_dims={"team": "growth"},
|
|
94
|
+
)
|
|
95
|
+
)
|
|
96
|
+
got = store.get_run("reg-only")
|
|
97
|
+
assert got is not None
|
|
98
|
+
assert got.agent == "summarize"
|
|
99
|
+
assert got.status == "running"
|
|
100
|
+
assert got.task == "summarize"
|
|
101
|
+
assert got.dims == {"team": "growth"}
|
|
102
|
+
assert got.cost_micros == 0
|
|
103
|
+
listed = store.list_runs()
|
|
104
|
+
assert [r.run_id for r in listed] == ["reg-only"]
|
|
105
|
+
|
|
106
|
+
store.ledger_add_spent(RUN_TOTAL_BUDGET.budget_id, "run:reg-only", LIFETIME, 12_345)
|
|
107
|
+
assert store.get_run("reg-only").cost_micros == 12_345
|
|
108
|
+
assert store.list_runs()[0].cost_micros == 12_345
|
|
109
|
+
|
|
110
|
+
# Explicit create_run after register remains REPLACE-safe.
|
|
111
|
+
store.create_run(
|
|
112
|
+
RunRecord(
|
|
113
|
+
run_id="reg-only",
|
|
114
|
+
agent="summarize",
|
|
115
|
+
status="completed",
|
|
116
|
+
cost_micros=99,
|
|
117
|
+
dims={"team": "growth"},
|
|
118
|
+
task="summarize",
|
|
119
|
+
)
|
|
120
|
+
)
|
|
121
|
+
after = store.get_run("reg-only")
|
|
122
|
+
assert after.status == "completed"
|
|
123
|
+
assert after.cost_micros == 12_345 # ledger still overlays
|
|
124
|
+
|
|
125
|
+
store.ledger_mark_halted("reg-only", reason="budget exhausted")
|
|
126
|
+
halted = store.get_run("reg-only")
|
|
127
|
+
assert halted.status == "halted"
|
|
128
|
+
assert halted.halt_reason == "budget exhausted"
|
|
129
|
+
|
|
130
|
+
store.ledger_clear_halt("reg-only")
|
|
131
|
+
cleared = store.get_run("reg-only")
|
|
132
|
+
assert cleared.halt_reason is None
|
|
133
|
+
assert cleared.status == "halted" # clear does not invent a new status
|
|
134
|
+
|
|
135
|
+
store.register_run(RunRegistration(run_id="no-intent"))
|
|
136
|
+
assert store.get_run("no-intent").agent == "agent"
|
|
137
|
+
assert store.get_run("no-intent").task is None
|
|
138
|
+
|
|
139
|
+
|
|
80
140
|
def test_seed_default_governance_if_empty(tmp_path, monkeypatch):
|
|
81
141
|
monkeypatch.delenv("TOKENOPS_SKIP_GOVERNANCE_SEED", raising=False)
|
|
82
142
|
s = Store(str(tmp_path / "seed.db"), auto_seed=False)
|
|
File without changes
|
|
File without changes
|
{agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/agent_tokenops.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/control/policies/concurrency_cap.py
RENAMED
|
File without changes
|
{agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/control/policies/context_compaction.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/control/policies/output_runaway.py
RENAMED
|
File without changes
|
{agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/control/policies/pre_call_worst_case.py
RENAMED
|
File without changes
|
{agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/control/policies/progress_guard.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/control/policies/tool_output_cap.py
RENAMED
|
File without changes
|
{agent_tokenops-0.1.2 → agent_tokenops-0.2.0}/src/tokenops/control/policies/trajectory_hint.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|