agent-tokenops 1.0.1__tar.gz → 2.0.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.
Files changed (120) hide show
  1. {agent_tokenops-1.0.1/src/agent_tokenops.egg-info → agent_tokenops-2.0.0}/PKG-INFO +6 -3
  2. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/README.md +5 -2
  3. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/pyproject.toml +25 -0
  4. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0/src/agent_tokenops.egg-info}/PKG-INFO +6 -3
  5. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/src/agent_tokenops.egg-info/SOURCES.txt +44 -34
  6. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/src/tokenops/__init__.py +18 -18
  7. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/src/tokenops/_version.py +1 -1
  8. agent_tokenops-2.0.0/src/tokenops/capabilities/__init__.py +32 -0
  9. {agent_tokenops-1.0.1/src/tokenops/control → agent_tokenops-2.0.0/src/tokenops/capabilities}/chronicle_trace_utils.py +1 -1
  10. agent_tokenops-2.0.0/src/tokenops/capabilities/complete_wrapper.py +131 -0
  11. {agent_tokenops-1.0.1/src/tokenops/control → agent_tokenops-2.0.0/src/tokenops/capabilities}/crossing.py +6 -6
  12. {agent_tokenops-1.0.1/src/tokenops/control → agent_tokenops-2.0.0/src/tokenops/capabilities}/ledger_writers.py +5 -5
  13. {agent_tokenops-1.0.1/src/tokenops/control → agent_tokenops-2.0.0/src/tokenops/capabilities}/run.py +14 -19
  14. agent_tokenops-2.0.0/src/tokenops/capabilities/step_observation.py +107 -0
  15. agent_tokenops-2.0.0/src/tokenops/capabilities/stream_wrapper.py +174 -0
  16. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/src/tokenops/config/default.yaml +1 -1
  17. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/src/tokenops/demo.py +4 -3
  18. agent_tokenops-2.0.0/src/tokenops/framework/__init__.py +114 -0
  19. {agent_tokenops-1.0.1/src/tokenops/control → agent_tokenops-2.0.0/src/tokenops/framework}/attribution.py +4 -9
  20. {agent_tokenops-1.0.1/src/tokenops/control → agent_tokenops-2.0.0/src/tokenops/framework}/boundary.py +9 -30
  21. agent_tokenops-2.0.0/src/tokenops/framework/budget.py +112 -0
  22. {agent_tokenops-1.0.1/src/tokenops/control → agent_tokenops-2.0.0/src/tokenops/framework}/config.py +13 -13
  23. {agent_tokenops-1.0.1/src/tokenops/control → agent_tokenops-2.0.0/src/tokenops/framework}/context.py +3 -13
  24. {agent_tokenops-1.0.1/src/tokenops/control → agent_tokenops-2.0.0/src/tokenops/framework}/core.py +18 -9
  25. {agent_tokenops-1.0.1/src/tokenops/control → agent_tokenops-2.0.0/src/tokenops/framework}/engine.py +7 -5
  26. {agent_tokenops-1.0.1/src/tokenops/control → agent_tokenops-2.0.0/src/tokenops/framework}/ledger.py +42 -141
  27. agent_tokenops-2.0.0/src/tokenops/framework/ledger_backend.py +40 -0
  28. agent_tokenops-2.0.0/src/tokenops/framework/ledger_events.py +37 -0
  29. agent_tokenops-2.0.0/src/tokenops/framework/message_steering.py +87 -0
  30. {agent_tokenops-1.0.1/src/tokenops/control → agent_tokenops-2.0.0/src/tokenops/framework}/models.py +0 -5
  31. agent_tokenops-2.0.0/src/tokenops/framework/observation_utils.py +34 -0
  32. {agent_tokenops-1.0.1/src/tokenops/control → agent_tokenops-2.0.0/src/tokenops/framework}/policies/__init__.py +1 -1
  33. {agent_tokenops-1.0.1/src/tokenops/control → agent_tokenops-2.0.0/src/tokenops/framework}/policies/_util.py +2 -1
  34. {agent_tokenops-1.0.1/src/tokenops/control → agent_tokenops-2.0.0/src/tokenops/framework}/policies/concurrency_cap.py +2 -2
  35. {agent_tokenops-1.0.1/src/tokenops/control → agent_tokenops-2.0.0/src/tokenops/framework}/policies/context_compaction.py +2 -2
  36. {agent_tokenops-1.0.1/src/tokenops/control → agent_tokenops-2.0.0/src/tokenops/framework}/policies/cost_budget.py +2 -2
  37. {agent_tokenops-1.0.1/src/tokenops/control → agent_tokenops-2.0.0/src/tokenops/framework}/policies/cost_guard.py +2 -2
  38. {agent_tokenops-1.0.1/src/tokenops/control → agent_tokenops-2.0.0/src/tokenops/framework}/policies/output_runaway.py +2 -2
  39. {agent_tokenops-1.0.1/src/tokenops/control → agent_tokenops-2.0.0/src/tokenops/framework}/policies/pre_call_worst_case.py +2 -2
  40. {agent_tokenops-1.0.1/src/tokenops/control → agent_tokenops-2.0.0/src/tokenops/framework}/policies/progress_guard.py +2 -2
  41. {agent_tokenops-1.0.1/src/tokenops/control → agent_tokenops-2.0.0/src/tokenops/framework}/policies/step_cap.py +1 -1
  42. {agent_tokenops-1.0.1/src/tokenops/control → agent_tokenops-2.0.0/src/tokenops/framework}/policies/time_budget.py +1 -1
  43. {agent_tokenops-1.0.1/src/tokenops/control → agent_tokenops-2.0.0/src/tokenops/framework}/policies/tool_fix.py +14 -5
  44. {agent_tokenops-1.0.1/src/tokenops/control → agent_tokenops-2.0.0/src/tokenops/framework}/policies/tool_output_cap.py +2 -2
  45. {agent_tokenops-1.0.1/src/tokenops/control → agent_tokenops-2.0.0/src/tokenops/framework}/policy_labels.py +1 -1
  46. {agent_tokenops-1.0.1/src/tokenops/control → agent_tokenops-2.0.0/src/tokenops/framework}/pricing.py +2 -2
  47. {agent_tokenops-1.0.1/src/tokenops/control → agent_tokenops-2.0.0/src/tokenops/framework}/request_context.py +2 -2
  48. {agent_tokenops-1.0.1/src/tokenops/control → agent_tokenops-2.0.0/src/tokenops/framework}/usage.py +1 -1
  49. agent_tokenops-2.0.0/src/tokenops/interfaces/__init__.py +6 -0
  50. {agent_tokenops-1.0.1/src/tokenops/control → agent_tokenops-2.0.0/src/tokenops/interfaces}/http.py +2 -2
  51. {agent_tokenops-1.0.1/src/tokenops/control → agent_tokenops-2.0.0/src/tokenops/interfaces}/instrument.py +4 -4
  52. agent_tokenops-2.0.0/src/tokenops/repositories/control_plane_types.py +23 -0
  53. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/tests/test_actuators.py +8 -7
  54. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/tests/test_apply.py +6 -14
  55. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/tests/test_async_ledger.py +3 -2
  56. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/tests/test_attribution.py +4 -4
  57. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/tests/test_attribution_ledger_policies_e2e.py +9 -6
  58. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/tests/test_boundary.py +6 -4
  59. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/tests/test_chronicle_boundary.py +6 -4
  60. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/tests/test_compaction_metadata.py +7 -5
  61. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/tests/test_concurrency_cap.py +2 -2
  62. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/tests/test_config.py +2 -1
  63. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/tests/test_context_compaction.py +10 -10
  64. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/tests/test_cost_budget.py +4 -2
  65. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/tests/test_cost_guard.py +3 -2
  66. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/tests/test_cross_process_budget_gating.py +3 -2
  67. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/tests/test_crossing_hook.py +6 -9
  68. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/tests/test_demo.py +2 -1
  69. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/tests/test_governance_event_names.py +17 -12
  70. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/tests/test_integration.py +6 -3
  71. agent_tokenops-2.0.0/tests/test_layering.py +58 -0
  72. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/tests/test_ledger.py +8 -2
  73. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/tests/test_llm_boundary_precall.py +7 -4
  74. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/tests/test_output_runaway.py +2 -2
  75. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/tests/test_policies_wrap_integration.py +8 -15
  76. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/tests/test_policy_coordination.py +2 -2
  77. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/tests/test_policy_names.py +7 -7
  78. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/tests/test_pre_call_worst_case.py +4 -11
  79. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/tests/test_preview_mode.py +9 -12
  80. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/tests/test_pricing.py +2 -2
  81. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/tests/test_progress_guard.py +2 -2
  82. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/tests/test_provider_usage.py +8 -8
  83. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/tests/test_reason_encoding.py +1 -1
  84. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/tests/test_step_cap.py +4 -2
  85. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/tests/test_thread_safety.py +2 -2
  86. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/tests/test_time_budget.py +4 -2
  87. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/tests/test_tokenops_run.py +3 -3
  88. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/tests/test_tool_fix.py +2 -2
  89. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/tests/test_tool_output_cap.py +3 -3
  90. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/tests/test_wrap_adapters.py +7 -10
  91. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/tests/test_wrap_complete_chronicle.py +7 -10
  92. agent_tokenops-1.0.1/src/tokenops/control/__init__.py +0 -162
  93. agent_tokenops-1.0.1/src/tokenops/control/integration.py +0 -463
  94. agent_tokenops-1.0.1/tests/test_deprecated_names.py +0 -21
  95. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/LICENSE.txt +0 -0
  96. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/setup.cfg +0 -0
  97. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/src/agent_tokenops.egg-info/dependency_links.txt +0 -0
  98. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/src/agent_tokenops.egg-info/requires.txt +0 -0
  99. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/src/agent_tokenops.egg-info/top_level.txt +0 -0
  100. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/src/tokenops/adapters/__init__.py +0 -0
  101. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/src/tokenops/adapters/langchain.py +0 -0
  102. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/src/tokenops/config/__init__.py +0 -0
  103. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/src/tokenops/config/loader.py +0 -0
  104. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/src/tokenops/env.py +0 -0
  105. /agent_tokenops-1.0.1/src/tokenops/repositories/control_plane_types.py → /agent_tokenops-2.0.0/src/tokenops/framework/ledger_types.py +0 -0
  106. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/src/tokenops/providers/__init__.py +0 -0
  107. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/src/tokenops/providers/anthropic.py +0 -0
  108. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/src/tokenops/providers/factory.py +0 -0
  109. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/src/tokenops/providers/openai.py +0 -0
  110. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/src/tokenops/providers/types.py +0 -0
  111. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/src/tokenops/py.typed +0 -0
  112. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/src/tokenops/repositories/__init__.py +0 -0
  113. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/src/tokenops/repositories/control_plane_client.py +0 -0
  114. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/src/tokenops/repositories/control_plane_errors.py +0 -0
  115. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/src/tokenops/repositories/ledger_event_writer.py +0 -0
  116. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/tests/test_a2a_health.py +0 -0
  117. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/tests/test_a2a_server_errors.py +0 -0
  118. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/tests/test_httpx_instrumentation.py +0 -0
  119. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/tests/test_repositories_control_plane_client.py +0 -0
  120. {agent_tokenops-1.0.1 → agent_tokenops-2.0.0}/tests/test_repositories_ledger_event_writer.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agent-tokenops
3
- Version: 1.0.1
3
+ Version: 2.0.0
4
4
  Summary: Run-aware token governance for agents: budgets, policies and halts on Chronicle and the AgentPlane control plane
5
5
  Author: Susheem Koul, Tisha Chawla
6
6
  License-Expression: MIT
@@ -124,7 +124,8 @@ Wrap your model call once, then hand the wrapped version to your agent.
124
124
 
125
125
  ```python
126
126
  from tokenops import tokenops_run
127
- from tokenops.control import Halt, wrap_complete
127
+ from tokenops.capabilities import wrap_complete
128
+ from tokenops.framework import Halt
128
129
  from tokenops.providers import complete
129
130
 
130
131
  GOVERNANCE = {"governance": {
@@ -286,7 +287,9 @@ Only `src/tokenops/` is the installable package. Demos and benches stay under `e
286
287
 
287
288
  ```
288
289
  src/tokenops/ # installable package
289
- ├── control/ # the governors: ledger, policies, wrap_complete, Chronicle hook
290
+ ├── framework/ # in-process machinery: ledger, policies, pricing, governor (no network)
291
+ ├── capabilities/ # tokenops_run, wrap_complete, the Chronicle hook
292
+ ├── interfaces/ # instrument_app, HTTP error mapping
290
293
  ├── repositories/ # the control-plane client and the background ledger writer
291
294
  ├── providers/ # OpenAI / Anthropic complete dispatch
292
295
  └── config/ # packaged default governance
@@ -83,7 +83,8 @@ Wrap your model call once, then hand the wrapped version to your agent.
83
83
 
84
84
  ```python
85
85
  from tokenops import tokenops_run
86
- from tokenops.control import Halt, wrap_complete
86
+ from tokenops.capabilities import wrap_complete
87
+ from tokenops.framework import Halt
87
88
  from tokenops.providers import complete
88
89
 
89
90
  GOVERNANCE = {"governance": {
@@ -245,7 +246,9 @@ Only `src/tokenops/` is the installable package. Demos and benches stay under `e
245
246
 
246
247
  ```
247
248
  src/tokenops/ # installable package
248
- ├── control/ # the governors: ledger, policies, wrap_complete, Chronicle hook
249
+ ├── framework/ # in-process machinery: ledger, policies, pricing, governor (no network)
250
+ ├── capabilities/ # tokenops_run, wrap_complete, the Chronicle hook
251
+ ├── interfaces/ # instrument_app, HTTP error mapping
249
252
  ├── repositories/ # the control-plane client and the background ledger writer
250
253
  ├── providers/ # OpenAI / Anthropic complete dispatch
251
254
  └── config/ # packaged default governance
@@ -116,6 +116,7 @@ mypy_path = "src"
116
116
  packages = ["tokenops"]
117
117
  ignore_missing_imports = true
118
118
  warn_unused_configs = true
119
+ warn_redundant_casts = true
119
120
  # Gradual typing: adapters are LangChain-heavy; tighten later.
120
121
  exclude = "(?x)(tokenops/adapters/)"
121
122
 
@@ -128,6 +129,30 @@ module = [
128
129
  ]
129
130
  follow_imports = "skip"
130
131
 
132
+ # The layered packages are held to `mypy --strict`.
133
+ [[tool.mypy.overrides]]
134
+ module = [
135
+ "tokenops.framework",
136
+ "tokenops.framework.*",
137
+ "tokenops.capabilities",
138
+ "tokenops.capabilities.*",
139
+ "tokenops.interfaces",
140
+ "tokenops.interfaces.*",
141
+ "tokenops.repositories",
142
+ "tokenops.repositories.*",
143
+ ]
144
+ # `strict = true` is not accepted per module, so the --strict flags are listed.
145
+ disallow_untyped_defs = true
146
+ disallow_incomplete_defs = true
147
+ disallow_untyped_calls = true
148
+ disallow_untyped_decorators = true
149
+ disallow_any_generics = true
150
+ check_untyped_defs = true
151
+ no_implicit_optional = true
152
+ warn_return_any = true
153
+ warn_unused_ignores = true
154
+ strict_equality = true
155
+
131
156
  [tool.setuptools.package-dir]
132
157
  "" = "src"
133
158
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agent-tokenops
3
- Version: 1.0.1
3
+ Version: 2.0.0
4
4
  Summary: Run-aware token governance for agents: budgets, policies and halts on Chronicle and the AgentPlane control plane
5
5
  Author: Susheem Koul, Tisha Chawla
6
6
  License-Expression: MIT
@@ -124,7 +124,8 @@ Wrap your model call once, then hand the wrapped version to your agent.
124
124
 
125
125
  ```python
126
126
  from tokenops import tokenops_run
127
- from tokenops.control import Halt, wrap_complete
127
+ from tokenops.capabilities import wrap_complete
128
+ from tokenops.framework import Halt
128
129
  from tokenops.providers import complete
129
130
 
130
131
  GOVERNANCE = {"governance": {
@@ -286,7 +287,9 @@ Only `src/tokenops/` is the installable package. Demos and benches stay under `e
286
287
 
287
288
  ```
288
289
  src/tokenops/ # installable package
289
- ├── control/ # the governors: ledger, policies, wrap_complete, Chronicle hook
290
+ ├── framework/ # in-process machinery: ledger, policies, pricing, governor (no network)
291
+ ├── capabilities/ # tokenops_run, wrap_complete, the Chronicle hook
292
+ ├── interfaces/ # instrument_app, HTTP error mapping
290
293
  ├── repositories/ # the control-plane client and the background ledger writer
291
294
  ├── providers/ # OpenAI / Anthropic complete dispatch
292
295
  └── config/ # packaged default governance
@@ -13,42 +13,52 @@ src/tokenops/env.py
13
13
  src/tokenops/py.typed
14
14
  src/tokenops/adapters/__init__.py
15
15
  src/tokenops/adapters/langchain.py
16
+ src/tokenops/capabilities/__init__.py
17
+ src/tokenops/capabilities/chronicle_trace_utils.py
18
+ src/tokenops/capabilities/complete_wrapper.py
19
+ src/tokenops/capabilities/crossing.py
20
+ src/tokenops/capabilities/ledger_writers.py
21
+ src/tokenops/capabilities/run.py
22
+ src/tokenops/capabilities/step_observation.py
23
+ src/tokenops/capabilities/stream_wrapper.py
16
24
  src/tokenops/config/__init__.py
17
25
  src/tokenops/config/default.yaml
18
26
  src/tokenops/config/loader.py
19
- src/tokenops/control/__init__.py
20
- src/tokenops/control/attribution.py
21
- src/tokenops/control/boundary.py
22
- src/tokenops/control/chronicle_trace_utils.py
23
- src/tokenops/control/config.py
24
- src/tokenops/control/context.py
25
- src/tokenops/control/core.py
26
- src/tokenops/control/crossing.py
27
- src/tokenops/control/engine.py
28
- src/tokenops/control/http.py
29
- src/tokenops/control/instrument.py
30
- src/tokenops/control/integration.py
31
- src/tokenops/control/ledger.py
32
- src/tokenops/control/ledger_writers.py
33
- src/tokenops/control/models.py
34
- src/tokenops/control/policy_labels.py
35
- src/tokenops/control/pricing.py
36
- src/tokenops/control/request_context.py
37
- src/tokenops/control/run.py
38
- src/tokenops/control/usage.py
39
- src/tokenops/control/policies/__init__.py
40
- src/tokenops/control/policies/_util.py
41
- src/tokenops/control/policies/concurrency_cap.py
42
- src/tokenops/control/policies/context_compaction.py
43
- src/tokenops/control/policies/cost_budget.py
44
- src/tokenops/control/policies/cost_guard.py
45
- src/tokenops/control/policies/output_runaway.py
46
- src/tokenops/control/policies/pre_call_worst_case.py
47
- src/tokenops/control/policies/progress_guard.py
48
- src/tokenops/control/policies/step_cap.py
49
- src/tokenops/control/policies/time_budget.py
50
- src/tokenops/control/policies/tool_fix.py
51
- src/tokenops/control/policies/tool_output_cap.py
27
+ src/tokenops/framework/__init__.py
28
+ src/tokenops/framework/attribution.py
29
+ src/tokenops/framework/boundary.py
30
+ src/tokenops/framework/budget.py
31
+ src/tokenops/framework/config.py
32
+ src/tokenops/framework/context.py
33
+ src/tokenops/framework/core.py
34
+ src/tokenops/framework/engine.py
35
+ src/tokenops/framework/ledger.py
36
+ src/tokenops/framework/ledger_backend.py
37
+ src/tokenops/framework/ledger_events.py
38
+ src/tokenops/framework/ledger_types.py
39
+ src/tokenops/framework/message_steering.py
40
+ src/tokenops/framework/models.py
41
+ src/tokenops/framework/observation_utils.py
42
+ src/tokenops/framework/policy_labels.py
43
+ src/tokenops/framework/pricing.py
44
+ src/tokenops/framework/request_context.py
45
+ src/tokenops/framework/usage.py
46
+ src/tokenops/framework/policies/__init__.py
47
+ src/tokenops/framework/policies/_util.py
48
+ src/tokenops/framework/policies/concurrency_cap.py
49
+ src/tokenops/framework/policies/context_compaction.py
50
+ src/tokenops/framework/policies/cost_budget.py
51
+ src/tokenops/framework/policies/cost_guard.py
52
+ src/tokenops/framework/policies/output_runaway.py
53
+ src/tokenops/framework/policies/pre_call_worst_case.py
54
+ src/tokenops/framework/policies/progress_guard.py
55
+ src/tokenops/framework/policies/step_cap.py
56
+ src/tokenops/framework/policies/time_budget.py
57
+ src/tokenops/framework/policies/tool_fix.py
58
+ src/tokenops/framework/policies/tool_output_cap.py
59
+ src/tokenops/interfaces/__init__.py
60
+ src/tokenops/interfaces/http.py
61
+ src/tokenops/interfaces/instrument.py
52
62
  src/tokenops/providers/__init__.py
53
63
  src/tokenops/providers/anthropic.py
54
64
  src/tokenops/providers/factory.py
@@ -77,10 +87,10 @@ tests/test_cost_guard.py
77
87
  tests/test_cross_process_budget_gating.py
78
88
  tests/test_crossing_hook.py
79
89
  tests/test_demo.py
80
- tests/test_deprecated_names.py
81
90
  tests/test_governance_event_names.py
82
91
  tests/test_httpx_instrumentation.py
83
92
  tests/test_integration.py
93
+ tests/test_layering.py
84
94
  tests/test_ledger.py
85
95
  tests/test_llm_boundary_precall.py
86
96
  tests/test_output_runaway.py
@@ -11,38 +11,38 @@ def init() -> None:
11
11
  """Process bootstrap: install the Chronicle crossing hook (idempotent).
12
12
 
13
13
  Prefer calling this (or :meth:`ControlPlaneClient.from_env` /
14
- :func:`~tokenops.control.instrument.instrument_app`) on agent startup so
15
- callers need not remember :func:`~tokenops.control.crossing.install_crossing_hook`.
14
+ :func:`~tokenops.interfaces.instrument.instrument_app`) on agent startup so
15
+ callers need not remember :func:`~tokenops.capabilities.crossing.install_crossing_hook`.
16
16
  """
17
- from tokenops.control.crossing import install_crossing_hook
17
+ from tokenops.capabilities.crossing import install_crossing_hook
18
18
 
19
19
  install_crossing_hook()
20
20
 
21
21
 
22
22
  # First-class SDK surface for agents / UIs talking to the plane.
23
- from tokenops.control.instrument import instrument_app as instrument_app
24
- from tokenops.control.ledger_writers import flush_ledger_writes as flush_ledger_writes
25
- from tokenops.control.request_context import (
23
+ from tokenops.capabilities.ledger_writers import flush_ledger_writes as flush_ledger_writes
24
+ from tokenops.capabilities.run import (
25
+ TokenOpsBound as TokenOpsBound,
26
+ )
27
+ from tokenops.capabilities.run import (
28
+ agentplane_run_scope as agentplane_run_scope,
29
+ )
30
+ from tokenops.capabilities.run import (
31
+ tokenops_run as tokenops_run,
32
+ )
33
+ from tokenops.framework.request_context import (
26
34
  RequestContext as RequestContext,
27
35
  )
28
- from tokenops.control.request_context import (
36
+ from tokenops.framework.request_context import (
29
37
  bind_request_context as bind_request_context,
30
38
  )
31
- from tokenops.control.request_context import (
39
+ from tokenops.framework.request_context import (
32
40
  clear_request_context as clear_request_context,
33
41
  )
34
- from tokenops.control.request_context import (
42
+ from tokenops.framework.request_context import (
35
43
  current_request_context as current_request_context,
36
44
  )
37
- from tokenops.control.run import (
38
- TokenOpsBound as TokenOpsBound,
39
- )
40
- from tokenops.control.run import (
41
- agentplane_run_scope as agentplane_run_scope,
42
- )
43
- from tokenops.control.run import (
44
- tokenops_run as tokenops_run,
45
- )
45
+ from tokenops.interfaces.instrument import instrument_app as instrument_app
46
46
  from tokenops.repositories import ControlPlaneClient as ControlPlaneClient
47
47
 
48
48
  __all__ = [
@@ -1,3 +1,3 @@
1
1
  """Single source of the package version (read by the build backend and at runtime)."""
2
2
 
3
- __version__ = "1.0.1"
3
+ __version__ = "2.0.0"
@@ -0,0 +1,32 @@
1
+ """The capabilities layer: governed-run workflows.
2
+
3
+ Opens runs (:func:`tokenops_run`), wraps model calls (:func:`wrap_complete`), and installs the
4
+ Chronicle crossing hook that feeds the governor. Importing this package registers the hook on
5
+ ``chronicle.default_hooks`` for the whole process.
6
+ """
7
+
8
+ from tokenops.capabilities.complete_wrapper import wrap_complete
9
+ from tokenops.capabilities.crossing import install_crossing_hook, on_crossing
10
+ from tokenops.capabilities.ledger_writers import flush_ledger_writes
11
+ from tokenops.capabilities.run import TokenOpsBound, agentplane_run_scope, tokenops_run
12
+ from tokenops.capabilities.step_observation import make_on_step, step_to_observation
13
+ from tokenops.capabilities.stream_wrapper import wrap_stream
14
+ from tokenops.framework.message_steering import apply_carry_to_messages, consume_carry
15
+
16
+ # Process-wide: register the governor hook on chronicle.default_hooks (inherited by every session).
17
+ install_crossing_hook()
18
+
19
+ __all__ = [
20
+ "TokenOpsBound",
21
+ "agentplane_run_scope",
22
+ "apply_carry_to_messages",
23
+ "consume_carry",
24
+ "flush_ledger_writes",
25
+ "install_crossing_hook",
26
+ "make_on_step",
27
+ "on_crossing",
28
+ "step_to_observation",
29
+ "tokenops_run",
30
+ "wrap_complete",
31
+ "wrap_stream",
32
+ ]
@@ -2,7 +2,7 @@
2
2
 
3
3
  A run is a Chronicle trace: its only identifier is ``trace_id``. A hop arrives with the
4
4
  caller's ``traceparent`` and joins that trace; the entry hop starts a new one. This module is
5
- the single place that reads the active Chronicle span, so the rest of ``tokenops.control``
5
+ the single place that reads the active Chronicle span, so the rest of TokenOps
6
6
  never touches Chronicle's session.
7
7
 
8
8
  ``chronicle.current_session`` (Chronicle 1.1) reads the active session without opening one;
@@ -0,0 +1,131 @@
1
+ """``wrap_complete``: govern a ``complete(provider, model, messages)`` entry point.
2
+
3
+ Pre-call governance (MUTATE / HALT / RETRY) happens here; the dispatch itself goes through
4
+ Chronicle ``wrap_llm`` so the LIVE record and ``on_crossing`` fire. The TokenOps crossing hook
5
+ projects crossings into ``Governor.observe``: do not also ``emit_observation`` here, or the
6
+ call is billed twice.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from collections.abc import Callable
12
+ from typing import Any
13
+
14
+ from chronicle import wrap_llm
15
+
16
+ from tokenops.capabilities.crossing import (
17
+ install_crossing_hook,
18
+ reset_wrap_owns_precall,
19
+ wrap_owns_precall,
20
+ )
21
+ from tokenops.framework.core import Attribution, CallRequest
22
+ from tokenops.framework.engine import ApplyControls, Governor
23
+ from tokenops.framework.message_steering import (
24
+ compact_messages,
25
+ consume_carry,
26
+ estimate_input_tokens,
27
+ )
28
+
29
+ __all__ = ["DispatchFn", "policy_metadata", "tighten_cap", "wrap_complete"]
30
+
31
+ #: A dispatch callable: (provider, model, messages, max_output_tokens) -> response.
32
+ DispatchFn = Callable[..., object]
33
+
34
+ _RETRY_BASE_CAP = 512
35
+
36
+
37
+ def tighten_cap(cap: int | None) -> int:
38
+ """RETRY tightens the output cap each attempt (halve, floor 64)."""
39
+ return max(64, (cap or _RETRY_BASE_CAP) // 2)
40
+
41
+
42
+ def policy_metadata(controls: ApplyControls) -> Callable[[object], dict[str, int]]:
43
+ """Envelope metadata for what a policy did to this call.
44
+
45
+ The plane stores no per-policy statistics (control-plane ADR 0009), so what a policy
46
+ achieved is recorded on the envelope instead: ``tokenops.compaction_tokens_before``,
47
+ ``..._tokens_after`` and ``..._tokens_saved`` when ``context_compaction`` rewrote the
48
+ prompt. Read after the dispatch returns, so ``controls.call`` is this call's.
49
+ """
50
+
51
+ def extract(_result: object) -> dict[str, int]:
52
+ call = getattr(controls, "call", None)
53
+ compaction = getattr(call, "compaction", None) if call is not None else None
54
+ if not compaction:
55
+ return {}
56
+ return {f"tokenops.compaction_{key}": int(value) for key, value in compaction.items()}
57
+
58
+ return extract
59
+
60
+
61
+ def wrap_complete(
62
+ governor: Governor,
63
+ controls: ApplyControls,
64
+ attr: Attribution,
65
+ *,
66
+ provider: str,
67
+ model: str,
68
+ dispatch: DispatchFn,
69
+ est_input_fn: Callable[[object], int] | None = None,
70
+ service: str = "",
71
+ max_call_retries: int = 3,
72
+ ) -> Callable[[str, str, Any], object]:
73
+ """Wrap a ``complete(provider, model, messages)`` entry point for **pre_call** governance
74
+ and the **RETRY** actuator.
75
+
76
+ Dispatch is traced via Chronicle ``wrap_llm`` (LIVE envelope + ``on_crossing``).
77
+ TokenOps observes through the crossing hook, not a second ``emit_observation``.
78
+
79
+ After each traced dispatch, ``output_runaway`` may set ``controls.retry``; the call is
80
+ re-issued with a tighter output cap and raised frequency/presence penalties, bounded
81
+ by ``max_call_retries`` and by the policy's own retry budget (INJECT once exhausted).
82
+ """
83
+ install_crossing_hook()
84
+ estimate = est_input_fn or estimate_input_tokens
85
+ seg = f"trace:{attr.trace_id}"
86
+ boundary_id = f"{service or attr.agent}.chat"
87
+ traced = wrap_llm(
88
+ boundary_id, dispatch, provider=provider, extract_metadata=policy_metadata(controls)
89
+ )
90
+ controls.compaction_supported = True # wrap_complete IS the prompt-assembly hook
91
+
92
+ def governed(p: str, m: str, messages: Any) -> object:
93
+ owns = wrap_owns_precall(True)
94
+ try:
95
+ controls.begin_call()
96
+ request = CallRequest(
97
+ attr=attr,
98
+ provider=provider,
99
+ model=m,
100
+ estimated_input_tokens=estimate(messages),
101
+ max_output_tokens=controls.call.max_output_tokens,
102
+ )
103
+ governor.pre_call(request)
104
+
105
+ use_model = controls.call.model_override or m
106
+ messages = consume_carry(controls, messages)
107
+ if controls.call.compact: # deep prompt compaction
108
+ messages, compaction = compact_messages(messages, estimate=estimate)
109
+ controls.call.compaction = compaction
110
+
111
+ governor.ledger.admit(seg)
112
+ try:
113
+ cap = controls.call.max_output_tokens
114
+ penalties: dict[str, float] = {}
115
+ attempt = 0
116
+ while True:
117
+ controls.retry = False
118
+ # Chronicle records + on_crossing → Governor.observe (when bound)
119
+ response = traced(p, use_model, messages, max_output_tokens=cap, **penalties)
120
+ if controls.retry and attempt < max_call_retries:
121
+ attempt += 1
122
+ cap = tighten_cap(cap)
123
+ penalties = {"frequency_penalty": 1.0, "presence_penalty": 0.6}
124
+ continue
125
+ return response
126
+ finally:
127
+ governor.ledger.complete(seg)
128
+ finally:
129
+ reset_wrap_owns_precall(owns)
130
+
131
+ return governed
@@ -35,7 +35,7 @@ def reset_wrap_owns_precall(token: Any) -> None:
35
35
 
36
36
 
37
37
  def _service_name() -> str:
38
- from tokenops.control.context import current_span
38
+ from tokenops.framework.context import current_span
39
39
 
40
40
  span = current_span()
41
41
  return span.service if span else "unknown"
@@ -57,8 +57,8 @@ def on_enter(info: CallInfo) -> dict[str, Any] | None:
57
57
  if _kind(info) != "llm" or _wrap_owns_precall.get():
58
58
  return None
59
59
 
60
- from tokenops.control.context import current_governance, current_hop
61
- from tokenops.control.core import CallRequest
60
+ from tokenops.framework.context import current_governance, current_hop
61
+ from tokenops.framework.core import CallRequest
62
62
 
63
63
  gov_ctx = current_governance()
64
64
  if gov_ctx is None or current_hop() is None:
@@ -111,7 +111,7 @@ def on_enter(info: CallInfo) -> dict[str, Any] | None:
111
111
  def on_leave(info: CallInfo) -> None:
112
112
  """Release inflight admit from :func:`on_enter` (success or failure)."""
113
113
  del info
114
- from tokenops.control.context import current_governance
114
+ from tokenops.framework.context import current_governance
115
115
 
116
116
  seg = _inflight_seg.get()
117
117
  if not seg:
@@ -126,8 +126,8 @@ def on_leave(info: CallInfo) -> None:
126
126
 
127
127
  def on_crossing(info: CallInfo, result: Any) -> None:
128
128
  """Project crossing → Observation when a hop attribution + governance are bound."""
129
- from tokenops.control.boundary import emit_observation, observation_from_crossing
130
- from tokenops.control.context import current_governance, current_hop
129
+ from tokenops.framework.boundary import emit_observation, observation_from_crossing
130
+ from tokenops.framework.context import current_governance, current_hop
131
131
 
132
132
  if current_governance() is None or current_hop() is None:
133
133
  return
@@ -15,20 +15,15 @@ from tokenops.repositories import ControlPlaneClient, LedgerEventWriter
15
15
  _LOCK = threading.Lock()
16
16
  # Keeps the client referenced so its id() cannot be reused while its writer is alive.
17
17
  _WRITERS: dict[int, tuple[ControlPlaneClient, LedgerEventWriter]] = {}
18
- _ATEXIT_REGISTERED = False
19
18
 
20
19
 
21
20
  def writer_for(client: ControlPlaneClient) -> LedgerEventWriter:
22
21
  """The shared writer for ``client`` (created on first use)."""
23
- global _ATEXIT_REGISTERED
24
22
  with _LOCK:
25
23
  entry = _WRITERS.get(id(client))
26
24
  if entry is None:
27
25
  entry = (client, LedgerEventWriter(client))
28
26
  _WRITERS[id(client)] = entry
29
- if not _ATEXIT_REGISTERED:
30
- atexit.register(flush_ledger_writes)
31
- _ATEXIT_REGISTERED = True
32
27
  return entry[1]
33
28
 
34
29
 
@@ -37,3 +32,8 @@ def flush_ledger_writes(timeout: float = 5.0) -> bool:
37
32
  with _LOCK:
38
33
  writers = [w for _, w in _WRITERS.values()]
39
34
  return all([w.flush(timeout) for w in writers])
35
+
36
+
37
+ # Registered once at import; a no-op until a writer exists, so a short script does not lose
38
+ # its last spend.
39
+ atexit.register(flush_ledger_writes)
@@ -24,27 +24,27 @@ from contextlib import contextmanager
24
24
  from dataclasses import dataclass
25
25
  from typing import Any
26
26
 
27
- from tokenops.control.attribution import _build_attribution, merge_hop_dims
28
- from tokenops.control.chronicle_trace_utils import chronicle_span
29
- from tokenops.control.config import build_governance_stack, build_governor
30
- from tokenops.control.context import (
27
+ from tokenops.capabilities.chronicle_trace_utils import chronicle_span
28
+ from tokenops.capabilities.crossing import install_crossing_hook
29
+ from tokenops.capabilities.ledger_writers import writer_for
30
+ from tokenops.framework.attribution import _build_attribution, merge_hop_dims
31
+ from tokenops.framework.config import build_governance_stack, build_governor
32
+ from tokenops.framework.context import (
31
33
  SpanContext,
32
34
  _governance_scope,
33
35
  bind_hop,
34
36
  bind_span,
35
37
  run_scope,
36
38
  )
37
- from tokenops.control.context import (
39
+ from tokenops.framework.context import (
38
40
  clear as clear_run_context,
39
41
  )
40
- from tokenops.control.core import Attribution
41
- from tokenops.control.crossing import install_crossing_hook
42
- from tokenops.control.engine import ApplyControls, Governor, PreviewControls
43
- from tokenops.control.ledger import PriceFn
44
- from tokenops.control.ledger_writers import writer_for
45
- from tokenops.control.models import GovernanceMode, HopAttribution, parse_governance_mode
46
- from tokenops.control.pricing import build_price_book
47
- from tokenops.control.request_context import current_request_context
42
+ from tokenops.framework.core import Attribution
43
+ from tokenops.framework.engine import ApplyControls, Governor, PreviewControls
44
+ from tokenops.framework.ledger import PriceFn
45
+ from tokenops.framework.models import GovernanceMode, HopAttribution, parse_governance_mode
46
+ from tokenops.framework.pricing import build_price_book
47
+ from tokenops.framework.request_context import current_request_context
48
48
  from tokenops.repositories import ControlPlaneClient
49
49
 
50
50
  logger = logging.getLogger("tokenops.run")
@@ -61,11 +61,6 @@ class TokenOpsBound:
61
61
  controls: ApplyControls | PreviewControls
62
62
  client: ControlPlaneClient | None
63
63
 
64
- @property
65
- def registration(self) -> HopAttribution:
66
- """Deprecated alias of ``hop``."""
67
- return self.hop
68
-
69
64
 
70
65
  def _resolve_ambient(
71
66
  *,
@@ -162,7 +157,7 @@ def tokenops_run(
162
157
  ) -> Iterator[TokenOpsBound]:
163
158
  """One hop under a governed trace: join the trace, bind span + governance.
164
159
 
165
- Happy path (with :func:`~tokenops.control.instrument.instrument_app`)::
160
+ Happy path (with :func:`~tokenops.interfaces.instrument.instrument_app`)::
166
161
 
167
162
  with tokenops_run():
168
163
  ...