context-compiler 0.9.0.dev4__tar.gz → 0.9.0.dev6__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 (40) hide show
  1. {context_compiler-0.9.0.dev4 → context_compiler-0.9.0.dev6}/PKG-INFO +25 -44
  2. {context_compiler-0.9.0.dev4 → context_compiler-0.9.0.dev6}/README.md +24 -35
  3. {context_compiler-0.9.0.dev4 → context_compiler-0.9.0.dev6}/demos/01_llm_contradiction_error.py +18 -4
  4. {context_compiler-0.9.0.dev4 → context_compiler-0.9.0.dev6}/demos/02_llm_constraint_guardrail.py +14 -6
  5. {context_compiler-0.9.0.dev4 → context_compiler-0.9.0.dev6}/demos/03_llm_premise_guardrail.py +21 -3
  6. {context_compiler-0.9.0.dev4 → context_compiler-0.9.0.dev6}/demos/04_llm_tool_denylist_guardrail.py +14 -6
  7. {context_compiler-0.9.0.dev4 → context_compiler-0.9.0.dev6}/demos/05_llm_prompt_drift_vs_state.py +18 -6
  8. {context_compiler-0.9.0.dev4 → context_compiler-0.9.0.dev6}/demos/06_llm_context_compaction.py +3 -2
  9. {context_compiler-0.9.0.dev4 → context_compiler-0.9.0.dev6}/demos/07_llm_prompt_vs_state.py +37 -9
  10. {context_compiler-0.9.0.dev4 → context_compiler-0.9.0.dev6}/demos/08_llm_replacement_precondition.py +16 -6
  11. {context_compiler-0.9.0.dev4 → context_compiler-0.9.0.dev6}/demos/09_llm_confirmation_no_directive.py +22 -13
  12. {context_compiler-0.9.0.dev4 → context_compiler-0.9.0.dev6}/demos/common.py +56 -29
  13. {context_compiler-0.9.0.dev4 → context_compiler-0.9.0.dev6}/examples/01_persistent_guardrails.py +11 -3
  14. {context_compiler-0.9.0.dev4 → context_compiler-0.9.0.dev6}/examples/02_configuration_and_correction.py +7 -3
  15. {context_compiler-0.9.0.dev4 → context_compiler-0.9.0.dev6}/examples/03_ambiguity_with_error.py +6 -2
  16. {context_compiler-0.9.0.dev4 → context_compiler-0.9.0.dev6}/examples/04_tool_governance_denylist.py +6 -3
  17. {context_compiler-0.9.0.dev4 → context_compiler-0.9.0.dev6}/examples/05_llm_integration_pattern.py +7 -5
  18. {context_compiler-0.9.0.dev4 → context_compiler-0.9.0.dev6}/examples/06_step_sequence_and_state_restore.py +8 -3
  19. {context_compiler-0.9.0.dev4 → context_compiler-0.9.0.dev6}/examples/07_single_policy_correction.py +6 -2
  20. {context_compiler-0.9.0.dev4 → context_compiler-0.9.0.dev6}/examples/README.md +0 -7
  21. {context_compiler-0.9.0.dev4 → context_compiler-0.9.0.dev6}/examples/_util.py +19 -3
  22. {context_compiler-0.9.0.dev4 → context_compiler-0.9.0.dev6}/pyproject.toml +2 -1
  23. {context_compiler-0.9.0.dev4 → context_compiler-0.9.0.dev6}/src/context_compiler/__init__.py +2 -20
  24. {context_compiler-0.9.0.dev4 → context_compiler-0.9.0.dev6}/src/context_compiler/decision_helpers.py +4 -20
  25. {context_compiler-0.9.0.dev4 → context_compiler-0.9.0.dev6}/src/context_compiler/engine.py +34 -47
  26. {context_compiler-0.9.0.dev4 → context_compiler-0.9.0.dev6}/src/context_compiler/grammar.py +33 -27
  27. {context_compiler-0.9.0.dev4 → context_compiler-0.9.0.dev6}/src/context_compiler/repl.py +103 -140
  28. context_compiler-0.9.0.dev4/examples/08_controller_preview_diff.py +0 -44
  29. context_compiler-0.9.0.dev4/src/context_compiler/audit.py +0 -23
  30. context_compiler-0.9.0.dev4/src/context_compiler/controller.py +0 -177
  31. {context_compiler-0.9.0.dev4 → context_compiler-0.9.0.dev6}/.gitignore +0 -0
  32. {context_compiler-0.9.0.dev4 → context_compiler-0.9.0.dev6}/LICENSE +0 -0
  33. {context_compiler-0.9.0.dev4 → context_compiler-0.9.0.dev6}/demos/README.md +0 -0
  34. {context_compiler-0.9.0.dev4 → context_compiler-0.9.0.dev6}/demos/__init__.py +0 -0
  35. {context_compiler-0.9.0.dev4 → context_compiler-0.9.0.dev6}/demos/llm_client.py +0 -0
  36. {context_compiler-0.9.0.dev4 → context_compiler-0.9.0.dev6}/demos/run_demo.py +0 -0
  37. {context_compiler-0.9.0.dev4 → context_compiler-0.9.0.dev6}/host_support/__init__.py +0 -0
  38. {context_compiler-0.9.0.dev4 → context_compiler-0.9.0.dev6}/host_support/provider_mode.py +0 -0
  39. {context_compiler-0.9.0.dev4 → context_compiler-0.9.0.dev6}/src/context_compiler/const.py +0 -0
  40. {context_compiler-0.9.0.dev4 → context_compiler-0.9.0.dev6}/src/context_compiler/py.typed +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: context-compiler
3
- Version: 0.9.0.dev4
3
+ Version: 0.9.0.dev6
4
4
  Summary: Deterministic conversational state engine for LLM applications.
5
5
  Project-URL: Homepage, https://github.com/rlippmann/context-compiler
6
6
  Project-URL: Repository, https://github.com/rlippmann/context-compiler
@@ -18,14 +18,6 @@ Classifier: Topic :: Software Development :: Libraries
18
18
  Requires-Python: >=3.11
19
19
  Provides-Extra: demos
20
20
  Requires-Dist: litellm>=1.0.0; extra == 'demos'
21
- Provides-Extra: dev
22
- Requires-Dist: coverage; extra == 'dev'
23
- Requires-Dist: hypothesis; extra == 'dev'
24
- Requires-Dist: mypy<2.0,>=1.12; extra == 'dev'
25
- Requires-Dist: pre-commit; extra == 'dev'
26
- Requires-Dist: pytest; extra == 'dev'
27
- Requires-Dist: pytest-cov; extra == 'dev'
28
- Requires-Dist: ruff<1.0,>=0.12; extra == 'dev'
29
21
  Description-Content-Type: text/markdown
30
22
 
31
23
  # Context Compiler
@@ -179,7 +171,11 @@ decision = engine.step(user_input)
179
171
  if is_error(decision):
180
172
  show_to_user(decision["message"])
181
173
  elif is_update(decision):
182
- messages = build_messages(engine.state, user_input)
174
+ messages = build_messages(
175
+ premise=engine.premise,
176
+ policies=engine.policies,
177
+ user_input=user_input,
178
+ )
183
179
  render(call_llm(messages))
184
180
  else:
185
181
  render(call_llm(user_input))
@@ -223,10 +219,9 @@ Preload options load authoritative state:
223
219
  - `--initial-state-json` / `--initial-state-file` load saved state
224
220
  (via exported state JSON).
225
221
 
226
- REPL commands (controller layer, not engine directives):
222
+ REPL commands (not engine directives):
227
223
 
228
224
  - `state` shows current saved state.
229
- - `preview <input>` runs deterministic dry-run without mutating live state.
230
225
  - `step <input>` is an explicit alias of normal bare-input step behavior.
231
226
 
232
227
  Bare REPL input behavior remains unchanged.
@@ -267,7 +262,7 @@ Packaging notes:
267
262
  ### Development
268
263
 
269
264
  ```bash
270
- uv sync --group dev
265
+ uv sync --dev
271
266
  uv run pytest
272
267
  ```
273
268
 
@@ -276,20 +271,20 @@ uv run pytest
276
271
  Each user message produces a `Decision`.
277
272
 
278
273
  ```python
279
- class NoDirectiveDecision(TypedDict):
280
- kind: Literal["no_directive"]
281
-
282
- class UpdateDecision(TypedDict):
283
- kind: Literal["update"]
284
- state: State
285
-
286
- class ErrorDecision(TypedDict):
287
- kind: Literal["error"]
288
- message: str
289
-
290
- Decision = NoDirectiveDecision | UpdateDecision | ErrorDecision
274
+ class DecisionKind(StrEnum):
275
+ NO_DIRECTIVE = "no_directive"
276
+ UPDATE = "update"
277
+ ERROR = "error"
278
+
279
+ class Decision(TypedDict):
280
+ kind: DecisionKind
281
+ message: str | None
291
282
  ```
292
283
 
284
+ `message` is structurally present on every `Decision`, but it is semantically
285
+ meaningful only for `error`. For `no_directive` and `update`, `message` is
286
+ `None`.
287
+
293
288
  Meaning:
294
289
 
295
290
  | kind | host behavior |
@@ -299,7 +294,7 @@ Meaning:
299
294
  | error | show `message` and do not continue normal downstream processing yet |
300
295
 
301
296
  For normal app code, prefer the exported decision helpers (`is_error`,
302
- `is_update`, `is_no_directive`, `get_error_message`, `get_decision_state`)
297
+ `is_update`, `is_no_directive`, `get_error_message`)
303
298
  instead of direct key traversal.
304
299
 
305
300
  See [docs/api-reference.md](docs/api-reference.md) for the full public API
@@ -307,26 +302,12 @@ reference.
307
302
 
308
303
  Common API entry points:
309
304
 
310
- - engine lifecycle: `create_engine(...)`, `engine.step(...)`, `engine.state`,
305
+ - engine lifecycle: `create_engine()`, `engine.step(...)`,
311
306
  `engine.premise`, `engine.policies`, `engine.export_json(...)`,
312
307
  `engine.import_json(...)`
313
308
  - decision helpers: `is_error(...)`, `is_update(...)`, `is_no_directive(...)`,
314
- `get_error_message(...)`, `get_decision_state(...)`
309
+ `get_error_message(...)`
315
310
  - state transport: `engine.export_json(...)`, `engine.import_json(...)`
316
- - controller API: `step(...)`
317
- - audit APIs: `preview(...)`, `state_diff(...)`
318
-
319
- ### Controller And Audit APIs (Reusable Outside REPL)
320
-
321
- - `step(engine, user_input)` returns a reusable result envelope around one
322
- engine turn
323
- - `preview(engine, user_input)` performs a deterministic dry run and restores
324
- live engine state afterward
325
- - `state_diff(state_before, state_after)` summarizes structural state changes
326
-
327
- For examples and helper accessors such as `get_step_decision(...)`,
328
- `get_preview_state_after(...)`, `preview_would_mutate(...)`, and
329
- `diff_has_changes(...)`, see [docs/api-reference.md](docs/api-reference.md).
330
311
 
331
312
  ---
332
313
 
@@ -350,8 +331,8 @@ Identical input sequences always produce identical state.
350
331
  For live engine-owned reads, use `engine.premise` and `engine.policies`.
351
332
  `engine.policies` returns a caller-owned copy.
352
333
 
353
- `engine.state` remains the public snapshot/serialization boundary when host code
354
- needs the full authoritative state object.
334
+ Use `engine.export_json()` and `engine.import_json()` for persistence and
335
+ restoration.
355
336
 
356
337
  ---
357
338
 
@@ -149,7 +149,11 @@ decision = engine.step(user_input)
149
149
  if is_error(decision):
150
150
  show_to_user(decision["message"])
151
151
  elif is_update(decision):
152
- messages = build_messages(engine.state, user_input)
152
+ messages = build_messages(
153
+ premise=engine.premise,
154
+ policies=engine.policies,
155
+ user_input=user_input,
156
+ )
153
157
  render(call_llm(messages))
154
158
  else:
155
159
  render(call_llm(user_input))
@@ -193,10 +197,9 @@ Preload options load authoritative state:
193
197
  - `--initial-state-json` / `--initial-state-file` load saved state
194
198
  (via exported state JSON).
195
199
 
196
- REPL commands (controller layer, not engine directives):
200
+ REPL commands (not engine directives):
197
201
 
198
202
  - `state` shows current saved state.
199
- - `preview <input>` runs deterministic dry-run without mutating live state.
200
203
  - `step <input>` is an explicit alias of normal bare-input step behavior.
201
204
 
202
205
  Bare REPL input behavior remains unchanged.
@@ -237,7 +240,7 @@ Packaging notes:
237
240
  ### Development
238
241
 
239
242
  ```bash
240
- uv sync --group dev
243
+ uv sync --dev
241
244
  uv run pytest
242
245
  ```
243
246
 
@@ -246,20 +249,20 @@ uv run pytest
246
249
  Each user message produces a `Decision`.
247
250
 
248
251
  ```python
249
- class NoDirectiveDecision(TypedDict):
250
- kind: Literal["no_directive"]
251
-
252
- class UpdateDecision(TypedDict):
253
- kind: Literal["update"]
254
- state: State
255
-
256
- class ErrorDecision(TypedDict):
257
- kind: Literal["error"]
258
- message: str
259
-
260
- Decision = NoDirectiveDecision | UpdateDecision | ErrorDecision
252
+ class DecisionKind(StrEnum):
253
+ NO_DIRECTIVE = "no_directive"
254
+ UPDATE = "update"
255
+ ERROR = "error"
256
+
257
+ class Decision(TypedDict):
258
+ kind: DecisionKind
259
+ message: str | None
261
260
  ```
262
261
 
262
+ `message` is structurally present on every `Decision`, but it is semantically
263
+ meaningful only for `error`. For `no_directive` and `update`, `message` is
264
+ `None`.
265
+
263
266
  Meaning:
264
267
 
265
268
  | kind | host behavior |
@@ -269,7 +272,7 @@ Meaning:
269
272
  | error | show `message` and do not continue normal downstream processing yet |
270
273
 
271
274
  For normal app code, prefer the exported decision helpers (`is_error`,
272
- `is_update`, `is_no_directive`, `get_error_message`, `get_decision_state`)
275
+ `is_update`, `is_no_directive`, `get_error_message`)
273
276
  instead of direct key traversal.
274
277
 
275
278
  See [docs/api-reference.md](docs/api-reference.md) for the full public API
@@ -277,26 +280,12 @@ reference.
277
280
 
278
281
  Common API entry points:
279
282
 
280
- - engine lifecycle: `create_engine(...)`, `engine.step(...)`, `engine.state`,
283
+ - engine lifecycle: `create_engine()`, `engine.step(...)`,
281
284
  `engine.premise`, `engine.policies`, `engine.export_json(...)`,
282
285
  `engine.import_json(...)`
283
286
  - decision helpers: `is_error(...)`, `is_update(...)`, `is_no_directive(...)`,
284
- `get_error_message(...)`, `get_decision_state(...)`
287
+ `get_error_message(...)`
285
288
  - state transport: `engine.export_json(...)`, `engine.import_json(...)`
286
- - controller API: `step(...)`
287
- - audit APIs: `preview(...)`, `state_diff(...)`
288
-
289
- ### Controller And Audit APIs (Reusable Outside REPL)
290
-
291
- - `step(engine, user_input)` returns a reusable result envelope around one
292
- engine turn
293
- - `preview(engine, user_input)` performs a deterministic dry run and restores
294
- live engine state afterward
295
- - `state_diff(state_before, state_after)` summarizes structural state changes
296
-
297
- For examples and helper accessors such as `get_step_decision(...)`,
298
- `get_preview_state_after(...)`, `preview_would_mutate(...)`, and
299
- `diff_has_changes(...)`, see [docs/api-reference.md](docs/api-reference.md).
300
289
 
301
290
  ---
302
291
 
@@ -320,8 +309,8 @@ Identical input sequences always produce identical state.
320
309
  For live engine-owned reads, use `engine.premise` and `engine.policies`.
321
310
  `engine.policies` returns a caller-owned copy.
322
311
 
323
- `engine.state` remains the public snapshot/serialization boundary when host code
324
- needs the full authoritative state object.
312
+ Use `engine.export_json()` and `engine.import_json()` for persistence and
313
+ restoration.
325
314
 
326
315
  ---
327
316
 
@@ -7,6 +7,7 @@ from demos.common import (
7
7
  build_reinjected_messages,
8
8
  compact_user_turns,
9
9
  extract_tag_value,
10
+ observe_engine,
10
11
  print_decision,
11
12
  print_host_check,
12
13
  print_messages,
@@ -14,6 +15,7 @@ from demos.common import (
14
15
  print_spec_report,
15
16
  print_tag_comparison,
16
17
  print_user_inputs,
18
+ state_observations,
17
19
  yes_no,
18
20
  )
19
21
  from demos.llm_client import complete_messages
@@ -25,9 +27,11 @@ def main() -> None:
25
27
  print_user_inputs(user_inputs)
26
28
 
27
29
  first = engine.step(user_inputs[0])
28
- print_decision("turn 1", first, engine.state)
30
+ premise, policies = observe_engine(engine)
31
+ print_decision("turn 1", first, premise=premise, policies=policies)
29
32
  second = engine.step(user_inputs[1])
30
- print_decision("turn 2", second, engine.state)
33
+ premise, policies = observe_engine(engine)
34
+ print_decision("turn 2", second, premise=premise, policies=policies)
31
35
 
32
36
  baseline_messages = build_baseline_messages(
33
37
  [
@@ -67,7 +71,12 @@ def main() -> None:
67
71
  mediated_output = f"[no call] error required: {second['message']}\nACTION:error"
68
72
  print_model_output("Compiler-mediated (full)", mediated_output)
69
73
  else:
70
- mediated_messages = build_mediated_messages_from_transcript(engine.state, user_inputs)
74
+ premise, policies = observe_engine(engine)
75
+ mediated_messages = build_mediated_messages_from_transcript(
76
+ premise=premise,
77
+ policies=policies,
78
+ user_turns=user_inputs,
79
+ )
71
80
  print_messages("compiler-mediated (full)", mediated_messages)
72
81
  mediated_output = complete_messages(mediated_messages)
73
82
  print_model_output("Compiler-mediated (full)", mediated_output)
@@ -78,7 +87,12 @@ def main() -> None:
78
87
  compact_output = f"[no call] error required: {compacted_prompt}\nACTION:error"
79
88
  print_model_output("Compiler-mediated + compact", compact_output)
80
89
  else:
81
- compact_messages = build_mediated_messages_from_transcript(compacted_state, compacted_turns)
90
+ compacted_premise, compacted_policies = state_observations(compacted_state)
91
+ compact_messages = build_mediated_messages_from_transcript(
92
+ premise=compacted_premise,
93
+ policies=compacted_policies,
94
+ user_turns=compacted_turns,
95
+ )
82
96
  print_messages("compiler-mediated + compact", compact_messages)
83
97
  compact_output = complete_messages(compact_messages)
84
98
  print_model_output("Compiler-mediated + compact", compact_output)
@@ -8,12 +8,14 @@ from demos.common import (
8
8
  build_mediated_messages_from_transcript,
9
9
  build_reinjected_messages,
10
10
  compact_user_turns,
11
+ observe_engine,
11
12
  print_decision,
12
13
  print_host_check,
13
14
  print_messages,
14
15
  print_model_output,
15
16
  print_spec_report,
16
17
  print_user_inputs,
18
+ state_observations,
17
19
  yes_no,
18
20
  )
19
21
  from demos.llm_client import complete_messages
@@ -115,10 +117,12 @@ def main() -> None:
115
117
  print_user_inputs(user_inputs)
116
118
 
117
119
  first = engine.step(user_inputs[0])
118
- print_decision("turn 1", first, engine.state)
120
+ premise, policies = observe_engine(engine)
121
+ print_decision("turn 1", first, premise=premise, policies=policies)
119
122
 
120
123
  second = engine.step(user_inputs[1])
121
- print_decision("turn 2", second, engine.state)
124
+ premise, policies = observe_engine(engine)
125
+ print_decision("turn 2", second, premise=premise, policies=policies)
122
126
 
123
127
  baseline_messages = build_baseline_messages(
124
128
  [user_inputs[1]],
@@ -154,9 +158,11 @@ def main() -> None:
154
158
  reinjected_safe_alternative = safe_alternative_detected(reinjected_output)
155
159
  reinjected_violation = recipe_includes_prohibited_item(reinjected_output)
156
160
 
161
+ premise, policies = observe_engine(engine)
157
162
  mediated_messages = build_mediated_messages_from_transcript(
158
- engine.state,
159
- user_inputs,
163
+ premise=premise,
164
+ policies=policies,
165
+ user_turns=user_inputs,
160
166
  extra_system_prompt=(
161
167
  "If the user requests a prohibited item, refuse the literal request. "
162
168
  "State briefly that the request conflicts with compiled policy, then provide "
@@ -178,9 +184,11 @@ def main() -> None:
178
184
  compact_refusal = True
179
185
  compact_violation = False
180
186
  else:
187
+ compacted_premise, compacted_policies = state_observations(compacted_state)
181
188
  compact_messages = build_mediated_messages_from_transcript(
182
- compacted_state,
183
- compacted_turns,
189
+ premise=compacted_premise,
190
+ policies=compacted_policies,
191
+ user_turns=compacted_turns,
184
192
  extra_system_prompt=(
185
193
  "If the user requests a prohibited item, refuse the literal request. "
186
194
  "State briefly that the request conflicts with compiled policy, then provide "
@@ -9,6 +9,7 @@ from demos.common import (
9
9
  build_reinjected_messages,
10
10
  compact_user_turns,
11
11
  extract_tag_value,
12
+ observe_engine,
12
13
  print_decision,
13
14
  print_host_check,
14
15
  print_messages,
@@ -16,6 +17,7 @@ from demos.common import (
16
17
  print_spec_report,
17
18
  print_tag_comparison,
18
19
  print_user_inputs,
20
+ state_observations,
19
21
  yes_no,
20
22
  )
21
23
  from demos.llm_client import complete_messages
@@ -78,7 +80,13 @@ def main() -> None:
78
80
 
79
81
  for index, user_input in enumerate(user_inputs, start=1):
80
82
  decision = engine.step(user_input)
81
- print_decision(f"turn {index}", decision, engine.state)
83
+ premise, policies = observe_engine(engine)
84
+ print_decision(
85
+ f"turn {index}",
86
+ decision,
87
+ premise=premise,
88
+ policies=policies,
89
+ )
82
90
 
83
91
  baseline_messages = build_baseline_messages(
84
92
  [user_inputs[0], user_inputs[1], user_inputs[2]],
@@ -100,7 +108,12 @@ def main() -> None:
100
108
  reinjected_output = complete_messages(reinjected_messages)
101
109
  print_model_output("Reinjected-state", reinjected_output)
102
110
 
103
- mediated_messages = build_mediated_messages_from_transcript(engine.state, user_inputs)
111
+ premise, policies = observe_engine(engine)
112
+ mediated_messages = build_mediated_messages_from_transcript(
113
+ premise=premise,
114
+ policies=policies,
115
+ user_turns=user_inputs,
116
+ )
104
117
  print_messages("compiler-mediated (full)", mediated_messages)
105
118
  mediated_output = complete_messages(mediated_messages)
106
119
  print_model_output("Compiler-mediated (full)", mediated_output)
@@ -111,7 +124,12 @@ def main() -> None:
111
124
  compact_output = f"[no call] error required: {compacted_prompt}"
112
125
  print_model_output("Compiler-mediated + compact", compact_output)
113
126
  else:
114
- compact_messages = build_mediated_messages_from_transcript(compacted_state, compacted_turns)
127
+ compacted_premise, compacted_policies = state_observations(compacted_state)
128
+ compact_messages = build_mediated_messages_from_transcript(
129
+ premise=compacted_premise,
130
+ policies=compacted_policies,
131
+ user_turns=compacted_turns,
132
+ )
115
133
  print_messages("compiler-mediated + compact", compact_messages)
116
134
  compact_output = complete_messages(compact_messages)
117
135
  print_model_output("Compiler-mediated + compact", compact_output)
@@ -10,6 +10,7 @@ from demos.common import (
10
10
  compact_user_turns,
11
11
  extract_tag_value,
12
12
  is_verbose,
13
+ observe_engine,
13
14
  print_decision,
14
15
  print_host_check,
15
16
  print_messages,
@@ -17,6 +18,7 @@ from demos.common import (
17
18
  print_spec_report,
18
19
  print_tag_comparison,
19
20
  print_user_inputs,
21
+ state_observations,
20
22
  )
21
23
  from demos.llm_client import complete_messages
22
24
 
@@ -69,10 +71,12 @@ def main() -> None:
69
71
  print_user_inputs(user_inputs)
70
72
 
71
73
  first = engine.step(user_inputs[0])
72
- print_decision("turn 1", first, engine.state)
74
+ premise, policies = observe_engine(engine)
75
+ print_decision("turn 1", first, premise=premise, policies=policies)
73
76
 
74
77
  second = engine.step(user_inputs[1])
75
- print_decision("turn 2", second, engine.state)
78
+ premise, policies = observe_engine(engine)
79
+ print_decision("turn 2", second, premise=premise, policies=policies)
76
80
 
77
81
  baseline_messages = build_baseline_messages(
78
82
  [user_inputs[1]],
@@ -107,9 +111,11 @@ def main() -> None:
107
111
  reinjected_output = complete_messages(reinjected_messages)
108
112
  print_model_output("Reinjected-state", reinjected_output)
109
113
 
114
+ premise, policies = observe_engine(engine)
110
115
  mediated_messages = build_mediated_messages_from_transcript(
111
- engine.state,
112
- user_inputs,
116
+ premise=premise,
117
+ policies=policies,
118
+ user_turns=user_inputs,
113
119
  extra_system_prompt=(
114
120
  "Only choose tools that are not prohibited."
115
121
  + "\nCandidate tools: "
@@ -128,9 +134,11 @@ def main() -> None:
128
134
  print_model_output("Compiler-mediated + compact", compact_output)
129
135
  compact_tool = None
130
136
  else:
137
+ compacted_premise, compacted_policies = state_observations(compacted_state)
131
138
  compact_messages = build_mediated_messages_from_transcript(
132
- compacted_state,
133
- compacted_turns,
139
+ premise=compacted_premise,
140
+ policies=compacted_policies,
141
+ user_turns=compacted_turns,
134
142
  extra_system_prompt=(
135
143
  "Only choose tools that are not prohibited."
136
144
  + "\nCandidate tools: "
@@ -11,6 +11,7 @@ from demos.common import (
11
11
  build_reinjected_messages,
12
12
  compact_user_turns,
13
13
  extract_tag_value,
14
+ observe_engine,
14
15
  print_decision,
15
16
  print_host_check,
16
17
  print_messages,
@@ -18,6 +19,7 @@ from demos.common import (
18
19
  print_spec_report,
19
20
  print_tag_comparison,
20
21
  print_user_inputs,
22
+ state_observations,
21
23
  yes_no,
22
24
  )
23
25
  from demos.llm_client import complete_messages
@@ -218,7 +220,13 @@ def _run_demo(turns: int = _DEFAULT_TURNS) -> None:
218
220
 
219
221
  for index, user_input in enumerate(user_inputs, start=1):
220
222
  decision = engine.step(user_input)
221
- print_decision(f"turn {index}", decision, engine.state)
223
+ premise, policies = observe_engine(engine)
224
+ print_decision(
225
+ f"turn {index}",
226
+ decision,
227
+ premise=premise,
228
+ policies=policies,
229
+ )
222
230
 
223
231
  baseline_messages = build_baseline_messages(
224
232
  user_inputs,
@@ -242,9 +250,11 @@ def _run_demo(turns: int = _DEFAULT_TURNS) -> None:
242
250
  reinjected_output = complete_messages(reinjected_messages)
243
251
  print_model_output("Reinjected-state", reinjected_output)
244
252
 
253
+ premise, policies = observe_engine(engine)
245
254
  mediated_messages = build_mediated_messages_from_transcript(
246
- engine.state,
247
- user_inputs,
255
+ premise=premise,
256
+ policies=policies,
257
+ user_turns=user_inputs,
248
258
  extra_system_prompt=_FORMAT_CONTRACT_SYSTEM_PROMPT,
249
259
  )
250
260
  print_messages("compiler-mediated (full)", mediated_messages)
@@ -257,7 +267,8 @@ def _run_demo(turns: int = _DEFAULT_TURNS) -> None:
257
267
  compact_output = f"[no call] error required: {compacted_prompt}"
258
268
  print_model_output("Compiler-mediated + compact", compact_output)
259
269
  else:
260
- premise_value = compacted_state["premise"]
270
+ compacted_premise, compacted_policies = state_observations(compacted_state)
271
+ premise_value = compacted_premise
261
272
  if (
262
273
  premise_value is not None
263
274
  and _ORIGINAL_DIRECTIVE not in compacted_turns
@@ -265,8 +276,9 @@ def _run_demo(turns: int = _DEFAULT_TURNS) -> None:
265
276
  ):
266
277
  compacted_turns = [f"Premise reminder: {premise_value}", *compacted_turns]
267
278
  compact_messages = build_mediated_messages_from_transcript(
268
- compacted_state,
269
- compacted_turns,
279
+ premise=compacted_premise,
280
+ policies=compacted_policies,
281
+ user_turns=compacted_turns,
270
282
  extra_system_prompt=_FORMAT_CONTRACT_SYSTEM_PROMPT,
271
283
  )
272
284
  print_messages("compiler-mediated + compact", compact_messages)
@@ -1,7 +1,7 @@
1
1
  """Demo 6: host-side prompt replacement from authoritative step-derived state."""
2
2
 
3
3
  from context_compiler import DECISION_UPDATE, create_engine
4
- from demos.common import compact_user_turns, is_verbose, print_info_report
4
+ from demos.common import compact_user_turns, is_verbose, print_info_report, state_observations
5
5
 
6
6
  DEMO_NAME = "06_context_compaction — superseded directives eliminated"
7
7
  FINAL_PREMISE = "chickpea curry"
@@ -148,8 +148,9 @@ def main() -> None:
148
148
  baseline_context = "\n".join(f"User: {turn}" for turn in transcript_turns)
149
149
  compiled_context = f"- premise: {compiled_premise}"
150
150
  compacted_turns, compacted_state, compacted_prompt = compact_user_turns(transcript_turns)
151
+ compacted_premise, _compacted_policies = state_observations(compacted_state)
151
152
  assert compacted_prompt is None
152
- assert compacted_state["premise"] == FINAL_PREMISE
153
+ assert compacted_premise == FINAL_PREMISE
153
154
  compacted_context = "\n".join(f"User: {turn}" for turn in compacted_turns)
154
155
  baseline_prompt = _build_baseline_prompt(transcript_turns)
155
156
  compiled_prompt = _build_compiled_prompt(compiled_premise)
@@ -1,20 +1,24 @@
1
1
  """Demo 7: prompt engineering and compiled state are complementary."""
2
2
 
3
3
  import re
4
+ from collections.abc import Mapping
4
5
 
5
- from context_compiler import State, create_engine
6
+ from context_compiler import create_engine
7
+ from context_compiler.engine import PolicyValue
6
8
  from demos.common import (
7
9
  build_baseline_messages,
8
10
  build_compiled_system_prompt,
9
11
  build_reinjected_messages,
10
12
  compact_user_turns,
11
13
  extract_tag_value,
14
+ observe_engine,
12
15
  print_decision,
13
16
  print_host_check,
14
17
  print_messages,
15
18
  print_model_output,
16
19
  print_spec_report,
17
20
  print_user_inputs,
21
+ state_observations,
18
22
  yes_no,
19
23
  )
20
24
  from demos.llm_client import Message, complete_messages
@@ -74,14 +78,22 @@ def build_strong_messages(user_inputs: list[str]) -> list[Message]:
74
78
  )
75
79
 
76
80
 
77
- def build_compiler_messages(state: State, user_inputs: list[str]) -> list[Message]:
78
- compiled_prefix = build_compiled_system_prompt(state)
81
+ def build_compiler_messages(
82
+ *, premise: str | None, policies: Mapping[str, PolicyValue], user_inputs: list[str]
83
+ ) -> list[Message]:
84
+ compiled_prefix = build_compiled_system_prompt(premise=premise, policies=policies)
79
85
  compiler_system_prompt = f"{compiled_prefix}\n{STRONG_PROMPT_ENGINEERING_TEXT}"
80
86
  return build_baseline_messages(user_inputs, baseline_system_prompt=compiler_system_prompt)
81
87
 
82
88
 
83
- def build_compact_compiler_messages(state: State, compacted_inputs: list[str]) -> list[Message]:
84
- return build_compiler_messages(state, compacted_inputs)
89
+ def build_compact_compiler_messages(
90
+ *, premise: str | None, policies: Mapping[str, PolicyValue], compacted_inputs: list[str]
91
+ ) -> list[Message]:
92
+ return build_compiler_messages(
93
+ premise=premise,
94
+ policies=policies,
95
+ user_inputs=compacted_inputs,
96
+ )
85
97
 
86
98
 
87
99
  def _actual_summary(*, weak_pass: bool, strong_pass: bool, compiler_pass: bool) -> str:
@@ -108,7 +120,13 @@ def main() -> None:
108
120
 
109
121
  for index, user_input in enumerate(USER_INPUTS, start=1):
110
122
  decision = engine.step(user_input)
111
- print_decision(f"turn {index}", decision, engine.state)
123
+ premise, policies = observe_engine(engine)
124
+ print_decision(
125
+ f"turn {index}",
126
+ decision,
127
+ premise=premise,
128
+ policies=policies,
129
+ )
112
130
 
113
131
  weak_messages = build_weak_messages(USER_INPUTS)
114
132
  print_messages("weak-baseline", weak_messages)
@@ -131,7 +149,12 @@ def main() -> None:
131
149
  reinjected_output = complete_messages(reinjected_messages)
132
150
  print_model_output("Reinjected-state", reinjected_output)
133
151
 
134
- compiler_messages = build_compiler_messages(engine.state, USER_INPUTS)
152
+ premise, policies = observe_engine(engine)
153
+ compiler_messages = build_compiler_messages(
154
+ premise=premise,
155
+ policies=policies,
156
+ user_inputs=USER_INPUTS,
157
+ )
135
158
  print_messages("compiler-mediated (full)", compiler_messages)
136
159
  compiler_output = complete_messages(compiler_messages)
137
160
  print_model_output("Compiler-mediated (full)", compiler_output)
@@ -142,7 +165,12 @@ def main() -> None:
142
165
  compact_output = f"[no call] error required: {compacted_prompt}"
143
166
  print_model_output("Compiler-mediated + compact", compact_output)
144
167
  else:
145
- compact_messages = build_compact_compiler_messages(compacted_state, compacted_inputs)
168
+ compacted_premise, compacted_policies = state_observations(compacted_state)
169
+ compact_messages = build_compact_compiler_messages(
170
+ premise=compacted_premise,
171
+ policies=compacted_policies,
172
+ compacted_inputs=compacted_inputs,
173
+ )
146
174
  print_messages("compiler-mediated + compact", compact_messages)
147
175
  compact_output = complete_messages(compact_messages)
148
176
  print_model_output("Compiler-mediated + compact", compact_output)
@@ -158,7 +186,7 @@ def main() -> None:
158
186
  compiler_pass = premise_matches_expected(compiler_output)
159
187
  compact_pass = compacted_prompt is None and premise_matches_expected(compact_output)
160
188
 
161
- compiled_prefix = build_compiled_system_prompt(engine.state)
189
+ compiled_prefix = build_compiled_system_prompt(premise=premise, policies=policies)
162
190
  shared_prompt_text = compiler_messages[0]["content"].endswith(STRONG_PROMPT_ENGINEERING_TEXT)
163
191
  compiler_augmented_only = (
164
192
  compiler_messages[1:] == strong_messages[1:]