context-compiler 0.9.0.dev12__tar.gz → 0.9.0.dev13__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.
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev13}/PKG-INFO +99 -42
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev13}/README.md +98 -41
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev13}/demos/01_llm_contradiction_error.py +4 -4
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev13}/demos/06_llm_context_compaction.py +2 -2
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev13}/demos/08_llm_replacement_precondition.py +4 -4
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev13}/demos/README.md +6 -7
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev13}/demos/common.py +8 -10
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev13}/demos/run_demo.py +1 -2
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev13}/examples/03_ambiguity_with_error.py +3 -3
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev13}/examples/05_llm_integration_pattern.py +7 -7
- context_compiler-0.9.0.dev13/examples/08_apply_directive_decisions.py +95 -0
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev13}/examples/README.md +10 -0
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev13}/examples/_util.py +7 -5
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev13}/pyproject.toml +1 -1
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev13}/src/context_compiler/__init__.py +10 -13
- context_compiler-0.9.0.dev13/src/context_compiler/decision.py +107 -0
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev13}/src/context_compiler/engine.py +101 -45
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev13}/src/context_compiler/repl.py +49 -14
- context_compiler-0.9.0.dev12/demos/09_llm_confirmation_no_directive.py +0 -173
- context_compiler-0.9.0.dev12/src/context_compiler/decision_helpers.py +0 -30
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev13}/.gitignore +0 -0
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev13}/LICENSE +0 -0
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev13}/demos/02_llm_constraint_guardrail.py +0 -0
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev13}/demos/03_llm_premise_guardrail.py +0 -0
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev13}/demos/04_llm_tool_denylist_guardrail.py +0 -0
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev13}/demos/05_llm_prompt_drift_vs_state.py +0 -0
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev13}/demos/07_llm_prompt_vs_state.py +0 -0
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev13}/demos/__init__.py +0 -0
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev13}/demos/llm_client.py +0 -0
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev13}/examples/01_persistent_guardrails.py +0 -0
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev13}/examples/02_configuration_and_correction.py +0 -0
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev13}/examples/04_tool_governance_denylist.py +0 -0
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev13}/examples/06_step_sequence_and_state_restore.py +0 -0
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev13}/examples/07_single_policy_correction.py +0 -0
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev13}/host_support/__init__.py +0 -0
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev13}/host_support/provider_mode.py +0 -0
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev13}/src/context_compiler/const.py +0 -0
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev13}/src/context_compiler/grammar.py +0 -0
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev13}/src/context_compiler/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: context-compiler
|
|
3
|
-
Version: 0.9.0.
|
|
3
|
+
Version: 0.9.0.dev13
|
|
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
|
|
@@ -27,9 +27,8 @@ Description-Content-Type: text/markdown
|
|
|
27
27
|
[](https://pypi.org/project/context-compiler/)
|
|
28
28
|
|
|
29
29
|
Context Compiler is a deterministic conversational state authority for LLM applications.
|
|
30
|
-
It handles canonical directive execution, semantic validation,
|
|
31
|
-
|
|
32
|
-
the host.
|
|
30
|
+
It handles canonical directive execution, semantic validation, terminal error
|
|
31
|
+
decisions, advisory repairs, and structured authoritative state for the host.
|
|
33
32
|
|
|
34
33
|
## What Context Compiler provides
|
|
35
34
|
|
|
@@ -37,7 +36,7 @@ Context Compiler gives hosts fixed state rules:
|
|
|
37
36
|
|
|
38
37
|
- handle canonical explicit state changes with deterministic rules
|
|
39
38
|
- error instead of silent overwrite for blocked/ambiguous changes
|
|
40
|
-
-
|
|
39
|
+
- return structured terminal errors with explicitly selectable advisory repairs
|
|
41
40
|
- export and import authoritative state for host-managed persistence
|
|
42
41
|
- produce structured authoritative state for downstream host decisions
|
|
43
42
|
|
|
@@ -84,8 +83,8 @@ The architecture has three layers:
|
|
|
84
83
|
directive-shaped syntax, or ordinary no_directive
|
|
85
84
|
- semantic evaluation decides whether a canonical directive updates state,
|
|
86
85
|
clarifies, or no-ops
|
|
87
|
-
- semantic
|
|
88
|
-
|
|
86
|
+
- semantic evaluation returns an update or a terminal error with structured
|
|
87
|
+
advisory repairs when a canonical directive conflicts with state
|
|
89
88
|
|
|
90
89
|
### Explicit directive
|
|
91
90
|
|
|
@@ -103,7 +102,7 @@ use docker and prohibit peanuts
|
|
|
103
102
|
```
|
|
104
103
|
|
|
105
104
|
- Without an authority layer: host/model behavior varies
|
|
106
|
-
- Context Compiler: treats this as invalid directive-shaped syntax
|
|
105
|
+
- Context Compiler: treats this as invalid directive-shaped syntax and keeps authoritative state unchanged
|
|
107
106
|
|
|
108
107
|
### State-dependent operation
|
|
109
108
|
|
|
@@ -159,8 +158,9 @@ Use Context Compiler in your host application first:
|
|
|
159
158
|
```python
|
|
160
159
|
from context_compiler import (
|
|
161
160
|
Engine,
|
|
162
|
-
|
|
163
|
-
|
|
161
|
+
NoDirectiveDecision,
|
|
162
|
+
SemanticErrorDecision,
|
|
163
|
+
UpdateDecision,
|
|
164
164
|
)
|
|
165
165
|
|
|
166
166
|
engine = Engine()
|
|
@@ -168,16 +168,16 @@ engine = Engine()
|
|
|
168
168
|
user_input = "set premise current project uses uv"
|
|
169
169
|
decision = engine.step(user_input)
|
|
170
170
|
|
|
171
|
-
if
|
|
172
|
-
show_to_user(decision
|
|
173
|
-
elif
|
|
171
|
+
if isinstance(decision, SemanticErrorDecision):
|
|
172
|
+
show_to_user(decision.message)
|
|
173
|
+
elif isinstance(decision, UpdateDecision):
|
|
174
174
|
messages = build_messages(
|
|
175
175
|
premise=engine.premise,
|
|
176
176
|
policies=engine.policies,
|
|
177
177
|
user_input=user_input,
|
|
178
178
|
)
|
|
179
179
|
render(call_llm(messages))
|
|
180
|
-
|
|
180
|
+
elif isinstance(decision, NoDirectiveDecision):
|
|
181
181
|
render(call_llm(user_input))
|
|
182
182
|
```
|
|
183
183
|
|
|
@@ -188,7 +188,7 @@ For runnable application-layer examples, see
|
|
|
188
188
|
[`context-compiler-example-integrations`](https://github.com/rlippmann/context-compiler-example-integrations).
|
|
189
189
|
That companion repository shows enforcement points built on compiler state,
|
|
190
190
|
including retrieval filtering, schema selection, tool gating, execution
|
|
191
|
-
authorization, gateway middleware, runtime
|
|
191
|
+
authorization, gateway middleware, runtime state handling, and prompt
|
|
192
192
|
construction.
|
|
193
193
|
|
|
194
194
|
## Does it Work?
|
|
@@ -235,6 +235,12 @@ for non-interactive usage.
|
|
|
235
235
|
context-compiler --json < input.txt
|
|
236
236
|
```
|
|
237
237
|
|
|
238
|
+
The JSON output uses `output_version: 2`. Decision payloads expose structured
|
|
239
|
+
fields: updates include `changed`; semantic errors include `failure`, the
|
|
240
|
+
failed canonical `directive`, ordered advisory `repairs`, and `message`.
|
|
241
|
+
These fields are a CLI projection of ephemeral Decisions, not Decision object
|
|
242
|
+
serialization. Repairs are never applied automatically.
|
|
243
|
+
|
|
238
244
|
Preload options load authoritative state:
|
|
239
245
|
|
|
240
246
|
- `--initial-state-json` / `--initial-state-file` load saved state
|
|
@@ -268,7 +274,9 @@ uv run pytest
|
|
|
268
274
|
|
|
269
275
|
## Decision API
|
|
270
276
|
|
|
271
|
-
Each user message produces
|
|
277
|
+
Each user message produces one immutable `Decision` variant. Use concrete
|
|
278
|
+
variants with `isinstance` or pattern matching; use `kind` when generic code
|
|
279
|
+
needs the stable discriminator.
|
|
272
280
|
|
|
273
281
|
```python
|
|
274
282
|
class DecisionKind(StrEnum):
|
|
@@ -276,26 +284,72 @@ class DecisionKind(StrEnum):
|
|
|
276
284
|
UPDATE = "update"
|
|
277
285
|
ERROR = "error"
|
|
278
286
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
287
|
+
Decision = NoDirectiveDecision | UpdateDecision | SemanticErrorDecision
|
|
288
|
+
|
|
289
|
+
@dataclass(frozen=True, slots=True)
|
|
290
|
+
class NoDirectiveDecision:
|
|
291
|
+
kind = DecisionKind.NO_DIRECTIVE
|
|
292
|
+
|
|
293
|
+
@dataclass(frozen=True, slots=True)
|
|
294
|
+
class UpdateDecision:
|
|
295
|
+
kind = DecisionKind.UPDATE
|
|
296
|
+
changed: bool
|
|
297
|
+
|
|
298
|
+
@dataclass(frozen=True, slots=True)
|
|
299
|
+
class SemanticErrorDecision:
|
|
300
|
+
kind = DecisionKind.ERROR
|
|
301
|
+
failure: SemanticFailure
|
|
302
|
+
directive: CanonicalDirective
|
|
303
|
+
repairs: tuple[CanonicalDirective, ...]
|
|
304
|
+
message: str
|
|
282
305
|
```
|
|
283
306
|
|
|
284
|
-
`
|
|
285
|
-
|
|
286
|
-
`
|
|
307
|
+
`UpdateDecision.changed` reports whether authoritative state actually changed.
|
|
308
|
+
An accepted idempotent directive is still an `update` with
|
|
309
|
+
`changed=False`.
|
|
310
|
+
|
|
311
|
+
`SemanticErrorDecision.message` is derived human-readable text. Callers should
|
|
312
|
+
use `failure` for machine decisions rather than parsing the message.
|
|
313
|
+
|
|
314
|
+
`directive` is the canonical directive that failed semantic evaluation.
|
|
315
|
+
`repairs` is an ordered tuple of advisory canonical directives. Repairs are
|
|
316
|
+
never applied automatically; a host must explicitly submit a selected repair
|
|
317
|
+
through `engine.apply_directive(...)`.
|
|
318
|
+
|
|
319
|
+
The normative repair mapping is:
|
|
320
|
+
|
|
321
|
+
| Failure | Ordered advisory repairs |
|
|
322
|
+
| --- | --- |
|
|
323
|
+
| `PREMISE_ALREADY_SET` | `change premise to <requested value>` |
|
|
324
|
+
| `PREMISE_NOT_SET` | `set premise <requested value>` |
|
|
325
|
+
| `ITEM_PROHIBITED` | `remove policy <item>`; `use <item>` |
|
|
326
|
+
| `ITEM_ALREADY_IN_USE` | `remove policy <item>`; `prohibit <item>` |
|
|
327
|
+
| `REPLACEMENT_TARGET_PROHIBITED` | `remove policy <target>`; retry the original replacement directive |
|
|
328
|
+
| `REPLACEMENT_SOURCE_PROHIBITED` | no repair (`()`) |
|
|
329
|
+
| `REPLACEMENT_SOURCE_MISSING` | no repair (`()`) |
|
|
330
|
+
|
|
331
|
+
Repairs are canonical, ordered, and advisory only. The engine never applies
|
|
332
|
+
them automatically; hosts explicitly select and submit any repair they want to
|
|
333
|
+
use. `message` is presentation data, so control flow must use `failure` and
|
|
334
|
+
the structured directives rather than parse message text.
|
|
287
335
|
|
|
288
336
|
Meaning:
|
|
289
337
|
|
|
290
338
|
| kind | host behavior |
|
|
291
339
|
| --- | --- |
|
|
292
340
|
| no_directive | no canonical directive recognized; no authoritative state change; host decides what to do next |
|
|
293
|
-
| update |
|
|
294
|
-
| error | show `message
|
|
341
|
+
| update | canonical directive was accepted; inspect `changed` and use updated state downstream |
|
|
342
|
+
| error | canonical directive was rejected semantically; inspect `failure`, show `message`, and optionally offer `repairs` |
|
|
343
|
+
|
|
344
|
+
`engine.step(...)` is the raw input boundary: it parses user input, may return
|
|
345
|
+
`NoDirectiveDecision` when no canonical directive is produced, and performs
|
|
346
|
+
semantic evaluation only after canonical parsing succeeds. The canonical
|
|
347
|
+
execution boundary is `engine.apply_directive(...)`, which accepts only a
|
|
348
|
+
`CanonicalDirective` and returns only `UpdateDecision` or
|
|
349
|
+
`SemanticErrorDecision`.
|
|
295
350
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
instead of direct key traversal.
|
|
351
|
+
Grammar failures do not produce semantic errors. A semantic error is possible
|
|
352
|
+
only after a canonical directive has parsed successfully.
|
|
299
353
|
|
|
300
354
|
See [docs/api-reference.md](docs/api-reference.md) for the full public API
|
|
301
355
|
reference.
|
|
@@ -305,8 +359,8 @@ Common API entry points:
|
|
|
305
359
|
- engine lifecycle: `Engine()`, `engine.step(...)`,
|
|
306
360
|
`engine.premise`, `engine.policies`, `engine.export_json(...)`,
|
|
307
361
|
`engine.import_json(...)`
|
|
308
|
-
- decision
|
|
309
|
-
`
|
|
362
|
+
- decision variants: `NoDirectiveDecision`, `UpdateDecision`,
|
|
363
|
+
`SemanticErrorDecision`, `SemanticFailure`
|
|
310
364
|
- state transport: `engine.export_json(...)`, `engine.import_json(...)`
|
|
311
365
|
|
|
312
366
|
---
|
|
@@ -387,8 +441,8 @@ the compiler.
|
|
|
387
441
|
|
|
388
442
|
- They transport **authoritative state only**
|
|
389
443
|
- Hosts own any broader interaction or session workflow around that state
|
|
390
|
-
-
|
|
391
|
-
|
|
444
|
+
- Decision objects and advisory repairs are not persisted; persistence carries
|
|
445
|
+
authoritative state only
|
|
392
446
|
|
|
393
447
|
---
|
|
394
448
|
|
|
@@ -431,11 +485,14 @@ Directive-shaped invalid input is outside the canonical language, and
|
|
|
431
485
|
`error` is reserved for canonical directives that later fail semantic
|
|
432
486
|
evaluation against authoritative state.
|
|
433
487
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
488
|
+
A semantic error is a terminal result for the current input. It leaves
|
|
489
|
+
authoritative state unchanged and returns the failure classification, failed
|
|
490
|
+
canonical directive, ordered advisory repairs, and presentation message.
|
|
491
|
+
Repairs are canonical directives. They are advisory only, are never applied
|
|
492
|
+
automatically, and require explicit host selection and submission through
|
|
493
|
+
`engine.apply_directive(...)`. An absent source item in a canonical replacement
|
|
494
|
+
directive is a semantic `error` and does not authorize degradation to plain
|
|
495
|
+
`use`.
|
|
439
496
|
|
|
440
497
|
Examples:
|
|
441
498
|
|
|
@@ -483,8 +540,8 @@ boundary, see [DirectiveGrammarSpec.md](docs/DirectiveGrammarSpec.md).
|
|
|
483
540
|
**Isn't this just prompt reinjection?**
|
|
484
541
|
No. Prompt construction is one downstream use of authoritative state.
|
|
485
542
|
Context Compiler is the authority layer that decides when state changes are
|
|
486
|
-
allowed, when error is required, and
|
|
487
|
-
|
|
543
|
+
allowed, when a terminal error is required, and which advisory repairs are
|
|
544
|
+
available. For runnable application-layer examples, see
|
|
488
545
|
[`context-compiler-example-integrations`](https://github.com/rlippmann/context-compiler-example-integrations).
|
|
489
546
|
|
|
490
547
|
Human-facing interpretation is a separate concern. If you want to recognize
|
|
@@ -505,8 +562,8 @@ User: prohibit python_script
|
|
|
505
562
|
```
|
|
506
563
|
|
|
507
564
|
Without an authority layer, the application must invent conflict-resolution and
|
|
508
|
-
|
|
509
|
-
|
|
565
|
+
repair rules itself. Context Compiler applies deterministic state-transition
|
|
566
|
+
rules and can return a terminal error instead of silently
|
|
510
567
|
overwriting state.
|
|
511
568
|
|
|
512
569
|
---
|
|
@@ -515,11 +572,11 @@ overwriting state.
|
|
|
515
572
|
|
|
516
573
|
### Guarantees
|
|
517
574
|
|
|
518
|
-
- State changes only through
|
|
575
|
+
- State changes only through canonical directives that pass semantic evaluation.
|
|
519
576
|
- Identical input sequences produce identical compiler state.
|
|
520
577
|
- Model responses never modify compiler state.
|
|
521
578
|
- Ambiguous directives trigger error instead of changing state.
|
|
522
|
-
- Syntax errors never
|
|
579
|
+
- Syntax errors never produce semantic errors or state changes.
|
|
523
580
|
|
|
524
581
|
Behavioral tests and Hypothesis-based property tests verify these invariants.
|
|
525
582
|
|
|
@@ -5,9 +5,8 @@
|
|
|
5
5
|
[](https://pypi.org/project/context-compiler/)
|
|
6
6
|
|
|
7
7
|
Context Compiler is a deterministic conversational state authority for LLM applications.
|
|
8
|
-
It handles canonical directive execution, semantic validation,
|
|
9
|
-
|
|
10
|
-
the host.
|
|
8
|
+
It handles canonical directive execution, semantic validation, terminal error
|
|
9
|
+
decisions, advisory repairs, and structured authoritative state for the host.
|
|
11
10
|
|
|
12
11
|
## What Context Compiler provides
|
|
13
12
|
|
|
@@ -15,7 +14,7 @@ Context Compiler gives hosts fixed state rules:
|
|
|
15
14
|
|
|
16
15
|
- handle canonical explicit state changes with deterministic rules
|
|
17
16
|
- error instead of silent overwrite for blocked/ambiguous changes
|
|
18
|
-
-
|
|
17
|
+
- return structured terminal errors with explicitly selectable advisory repairs
|
|
19
18
|
- export and import authoritative state for host-managed persistence
|
|
20
19
|
- produce structured authoritative state for downstream host decisions
|
|
21
20
|
|
|
@@ -62,8 +61,8 @@ The architecture has three layers:
|
|
|
62
61
|
directive-shaped syntax, or ordinary no_directive
|
|
63
62
|
- semantic evaluation decides whether a canonical directive updates state,
|
|
64
63
|
clarifies, or no-ops
|
|
65
|
-
- semantic
|
|
66
|
-
|
|
64
|
+
- semantic evaluation returns an update or a terminal error with structured
|
|
65
|
+
advisory repairs when a canonical directive conflicts with state
|
|
67
66
|
|
|
68
67
|
### Explicit directive
|
|
69
68
|
|
|
@@ -81,7 +80,7 @@ use docker and prohibit peanuts
|
|
|
81
80
|
```
|
|
82
81
|
|
|
83
82
|
- Without an authority layer: host/model behavior varies
|
|
84
|
-
- Context Compiler: treats this as invalid directive-shaped syntax
|
|
83
|
+
- Context Compiler: treats this as invalid directive-shaped syntax and keeps authoritative state unchanged
|
|
85
84
|
|
|
86
85
|
### State-dependent operation
|
|
87
86
|
|
|
@@ -137,8 +136,9 @@ Use Context Compiler in your host application first:
|
|
|
137
136
|
```python
|
|
138
137
|
from context_compiler import (
|
|
139
138
|
Engine,
|
|
140
|
-
|
|
141
|
-
|
|
139
|
+
NoDirectiveDecision,
|
|
140
|
+
SemanticErrorDecision,
|
|
141
|
+
UpdateDecision,
|
|
142
142
|
)
|
|
143
143
|
|
|
144
144
|
engine = Engine()
|
|
@@ -146,16 +146,16 @@ engine = Engine()
|
|
|
146
146
|
user_input = "set premise current project uses uv"
|
|
147
147
|
decision = engine.step(user_input)
|
|
148
148
|
|
|
149
|
-
if
|
|
150
|
-
show_to_user(decision
|
|
151
|
-
elif
|
|
149
|
+
if isinstance(decision, SemanticErrorDecision):
|
|
150
|
+
show_to_user(decision.message)
|
|
151
|
+
elif isinstance(decision, UpdateDecision):
|
|
152
152
|
messages = build_messages(
|
|
153
153
|
premise=engine.premise,
|
|
154
154
|
policies=engine.policies,
|
|
155
155
|
user_input=user_input,
|
|
156
156
|
)
|
|
157
157
|
render(call_llm(messages))
|
|
158
|
-
|
|
158
|
+
elif isinstance(decision, NoDirectiveDecision):
|
|
159
159
|
render(call_llm(user_input))
|
|
160
160
|
```
|
|
161
161
|
|
|
@@ -166,7 +166,7 @@ For runnable application-layer examples, see
|
|
|
166
166
|
[`context-compiler-example-integrations`](https://github.com/rlippmann/context-compiler-example-integrations).
|
|
167
167
|
That companion repository shows enforcement points built on compiler state,
|
|
168
168
|
including retrieval filtering, schema selection, tool gating, execution
|
|
169
|
-
authorization, gateway middleware, runtime
|
|
169
|
+
authorization, gateway middleware, runtime state handling, and prompt
|
|
170
170
|
construction.
|
|
171
171
|
|
|
172
172
|
## Does it Work?
|
|
@@ -213,6 +213,12 @@ for non-interactive usage.
|
|
|
213
213
|
context-compiler --json < input.txt
|
|
214
214
|
```
|
|
215
215
|
|
|
216
|
+
The JSON output uses `output_version: 2`. Decision payloads expose structured
|
|
217
|
+
fields: updates include `changed`; semantic errors include `failure`, the
|
|
218
|
+
failed canonical `directive`, ordered advisory `repairs`, and `message`.
|
|
219
|
+
These fields are a CLI projection of ephemeral Decisions, not Decision object
|
|
220
|
+
serialization. Repairs are never applied automatically.
|
|
221
|
+
|
|
216
222
|
Preload options load authoritative state:
|
|
217
223
|
|
|
218
224
|
- `--initial-state-json` / `--initial-state-file` load saved state
|
|
@@ -246,7 +252,9 @@ uv run pytest
|
|
|
246
252
|
|
|
247
253
|
## Decision API
|
|
248
254
|
|
|
249
|
-
Each user message produces
|
|
255
|
+
Each user message produces one immutable `Decision` variant. Use concrete
|
|
256
|
+
variants with `isinstance` or pattern matching; use `kind` when generic code
|
|
257
|
+
needs the stable discriminator.
|
|
250
258
|
|
|
251
259
|
```python
|
|
252
260
|
class DecisionKind(StrEnum):
|
|
@@ -254,26 +262,72 @@ class DecisionKind(StrEnum):
|
|
|
254
262
|
UPDATE = "update"
|
|
255
263
|
ERROR = "error"
|
|
256
264
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
265
|
+
Decision = NoDirectiveDecision | UpdateDecision | SemanticErrorDecision
|
|
266
|
+
|
|
267
|
+
@dataclass(frozen=True, slots=True)
|
|
268
|
+
class NoDirectiveDecision:
|
|
269
|
+
kind = DecisionKind.NO_DIRECTIVE
|
|
270
|
+
|
|
271
|
+
@dataclass(frozen=True, slots=True)
|
|
272
|
+
class UpdateDecision:
|
|
273
|
+
kind = DecisionKind.UPDATE
|
|
274
|
+
changed: bool
|
|
275
|
+
|
|
276
|
+
@dataclass(frozen=True, slots=True)
|
|
277
|
+
class SemanticErrorDecision:
|
|
278
|
+
kind = DecisionKind.ERROR
|
|
279
|
+
failure: SemanticFailure
|
|
280
|
+
directive: CanonicalDirective
|
|
281
|
+
repairs: tuple[CanonicalDirective, ...]
|
|
282
|
+
message: str
|
|
260
283
|
```
|
|
261
284
|
|
|
262
|
-
`
|
|
263
|
-
|
|
264
|
-
`
|
|
285
|
+
`UpdateDecision.changed` reports whether authoritative state actually changed.
|
|
286
|
+
An accepted idempotent directive is still an `update` with
|
|
287
|
+
`changed=False`.
|
|
288
|
+
|
|
289
|
+
`SemanticErrorDecision.message` is derived human-readable text. Callers should
|
|
290
|
+
use `failure` for machine decisions rather than parsing the message.
|
|
291
|
+
|
|
292
|
+
`directive` is the canonical directive that failed semantic evaluation.
|
|
293
|
+
`repairs` is an ordered tuple of advisory canonical directives. Repairs are
|
|
294
|
+
never applied automatically; a host must explicitly submit a selected repair
|
|
295
|
+
through `engine.apply_directive(...)`.
|
|
296
|
+
|
|
297
|
+
The normative repair mapping is:
|
|
298
|
+
|
|
299
|
+
| Failure | Ordered advisory repairs |
|
|
300
|
+
| --- | --- |
|
|
301
|
+
| `PREMISE_ALREADY_SET` | `change premise to <requested value>` |
|
|
302
|
+
| `PREMISE_NOT_SET` | `set premise <requested value>` |
|
|
303
|
+
| `ITEM_PROHIBITED` | `remove policy <item>`; `use <item>` |
|
|
304
|
+
| `ITEM_ALREADY_IN_USE` | `remove policy <item>`; `prohibit <item>` |
|
|
305
|
+
| `REPLACEMENT_TARGET_PROHIBITED` | `remove policy <target>`; retry the original replacement directive |
|
|
306
|
+
| `REPLACEMENT_SOURCE_PROHIBITED` | no repair (`()`) |
|
|
307
|
+
| `REPLACEMENT_SOURCE_MISSING` | no repair (`()`) |
|
|
308
|
+
|
|
309
|
+
Repairs are canonical, ordered, and advisory only. The engine never applies
|
|
310
|
+
them automatically; hosts explicitly select and submit any repair they want to
|
|
311
|
+
use. `message` is presentation data, so control flow must use `failure` and
|
|
312
|
+
the structured directives rather than parse message text.
|
|
265
313
|
|
|
266
314
|
Meaning:
|
|
267
315
|
|
|
268
316
|
| kind | host behavior |
|
|
269
317
|
| --- | --- |
|
|
270
318
|
| no_directive | no canonical directive recognized; no authoritative state change; host decides what to do next |
|
|
271
|
-
| update |
|
|
272
|
-
| error | show `message
|
|
319
|
+
| update | canonical directive was accepted; inspect `changed` and use updated state downstream |
|
|
320
|
+
| error | canonical directive was rejected semantically; inspect `failure`, show `message`, and optionally offer `repairs` |
|
|
321
|
+
|
|
322
|
+
`engine.step(...)` is the raw input boundary: it parses user input, may return
|
|
323
|
+
`NoDirectiveDecision` when no canonical directive is produced, and performs
|
|
324
|
+
semantic evaluation only after canonical parsing succeeds. The canonical
|
|
325
|
+
execution boundary is `engine.apply_directive(...)`, which accepts only a
|
|
326
|
+
`CanonicalDirective` and returns only `UpdateDecision` or
|
|
327
|
+
`SemanticErrorDecision`.
|
|
273
328
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
instead of direct key traversal.
|
|
329
|
+
Grammar failures do not produce semantic errors. A semantic error is possible
|
|
330
|
+
only after a canonical directive has parsed successfully.
|
|
277
331
|
|
|
278
332
|
See [docs/api-reference.md](docs/api-reference.md) for the full public API
|
|
279
333
|
reference.
|
|
@@ -283,8 +337,8 @@ Common API entry points:
|
|
|
283
337
|
- engine lifecycle: `Engine()`, `engine.step(...)`,
|
|
284
338
|
`engine.premise`, `engine.policies`, `engine.export_json(...)`,
|
|
285
339
|
`engine.import_json(...)`
|
|
286
|
-
- decision
|
|
287
|
-
`
|
|
340
|
+
- decision variants: `NoDirectiveDecision`, `UpdateDecision`,
|
|
341
|
+
`SemanticErrorDecision`, `SemanticFailure`
|
|
288
342
|
- state transport: `engine.export_json(...)`, `engine.import_json(...)`
|
|
289
343
|
|
|
290
344
|
---
|
|
@@ -365,8 +419,8 @@ the compiler.
|
|
|
365
419
|
|
|
366
420
|
- They transport **authoritative state only**
|
|
367
421
|
- Hosts own any broader interaction or session workflow around that state
|
|
368
|
-
-
|
|
369
|
-
|
|
422
|
+
- Decision objects and advisory repairs are not persisted; persistence carries
|
|
423
|
+
authoritative state only
|
|
370
424
|
|
|
371
425
|
---
|
|
372
426
|
|
|
@@ -409,11 +463,14 @@ Directive-shaped invalid input is outside the canonical language, and
|
|
|
409
463
|
`error` is reserved for canonical directives that later fail semantic
|
|
410
464
|
evaluation against authoritative state.
|
|
411
465
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
466
|
+
A semantic error is a terminal result for the current input. It leaves
|
|
467
|
+
authoritative state unchanged and returns the failure classification, failed
|
|
468
|
+
canonical directive, ordered advisory repairs, and presentation message.
|
|
469
|
+
Repairs are canonical directives. They are advisory only, are never applied
|
|
470
|
+
automatically, and require explicit host selection and submission through
|
|
471
|
+
`engine.apply_directive(...)`. An absent source item in a canonical replacement
|
|
472
|
+
directive is a semantic `error` and does not authorize degradation to plain
|
|
473
|
+
`use`.
|
|
417
474
|
|
|
418
475
|
Examples:
|
|
419
476
|
|
|
@@ -461,8 +518,8 @@ boundary, see [DirectiveGrammarSpec.md](docs/DirectiveGrammarSpec.md).
|
|
|
461
518
|
**Isn't this just prompt reinjection?**
|
|
462
519
|
No. Prompt construction is one downstream use of authoritative state.
|
|
463
520
|
Context Compiler is the authority layer that decides when state changes are
|
|
464
|
-
allowed, when error is required, and
|
|
465
|
-
|
|
521
|
+
allowed, when a terminal error is required, and which advisory repairs are
|
|
522
|
+
available. For runnable application-layer examples, see
|
|
466
523
|
[`context-compiler-example-integrations`](https://github.com/rlippmann/context-compiler-example-integrations).
|
|
467
524
|
|
|
468
525
|
Human-facing interpretation is a separate concern. If you want to recognize
|
|
@@ -483,8 +540,8 @@ User: prohibit python_script
|
|
|
483
540
|
```
|
|
484
541
|
|
|
485
542
|
Without an authority layer, the application must invent conflict-resolution and
|
|
486
|
-
|
|
487
|
-
|
|
543
|
+
repair rules itself. Context Compiler applies deterministic state-transition
|
|
544
|
+
rules and can return a terminal error instead of silently
|
|
488
545
|
overwriting state.
|
|
489
546
|
|
|
490
547
|
---
|
|
@@ -493,11 +550,11 @@ overwriting state.
|
|
|
493
550
|
|
|
494
551
|
### Guarantees
|
|
495
552
|
|
|
496
|
-
- State changes only through
|
|
553
|
+
- State changes only through canonical directives that pass semantic evaluation.
|
|
497
554
|
- Identical input sequences produce identical compiler state.
|
|
498
555
|
- Model responses never modify compiler state.
|
|
499
556
|
- Ambiguous directives trigger error instead of changing state.
|
|
500
|
-
- Syntax errors never
|
|
557
|
+
- Syntax errors never produce semantic errors or state changes.
|
|
501
558
|
|
|
502
559
|
Behavioral tests and Hypothesis-based property tests verify these invariants.
|
|
503
560
|
|
{context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev13}/demos/01_llm_contradiction_error.py
RENAMED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from context_compiler import (
|
|
4
4
|
Engine,
|
|
5
|
-
|
|
5
|
+
SemanticErrorDecision,
|
|
6
6
|
)
|
|
7
7
|
from demos.common import (
|
|
8
8
|
build_baseline_messages,
|
|
@@ -69,9 +69,9 @@ def main() -> None:
|
|
|
69
69
|
reinjected_output = complete_messages(reinjected_messages)
|
|
70
70
|
print_model_output("Reinjected-state", reinjected_output)
|
|
71
71
|
|
|
72
|
-
if
|
|
72
|
+
if isinstance(second, SemanticErrorDecision):
|
|
73
73
|
print_messages("compiler-mediated (full)", [])
|
|
74
|
-
mediated_output = f"[no call] error required: {second
|
|
74
|
+
mediated_output = f"[no call] error required: {second.message}\nACTION:error"
|
|
75
75
|
print_model_output("Compiler-mediated (full)", mediated_output)
|
|
76
76
|
else:
|
|
77
77
|
premise, policies = observe_engine(engine)
|
|
@@ -106,7 +106,7 @@ def main() -> None:
|
|
|
106
106
|
compact_action = extract_tag_value(compact_output, "ACTION")
|
|
107
107
|
baseline_respects = baseline_action is not None and baseline_action.lower() == "error"
|
|
108
108
|
reinjected_respects = reinjected_action is not None and reinjected_action.lower() == "error"
|
|
109
|
-
compiler_host_blocked =
|
|
109
|
+
compiler_host_blocked = isinstance(second, SemanticErrorDecision)
|
|
110
110
|
mediated_respects = compiler_host_blocked
|
|
111
111
|
compact_respects = compacted_prompt is not None or (
|
|
112
112
|
compact_action is not None and compact_action.lower() == "error"
|
{context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev13}/demos/06_llm_context_compaction.py
RENAMED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"""Demo 6: host-side prompt replacement from authoritative step-derived state."""
|
|
2
2
|
|
|
3
3
|
from context_compiler import (
|
|
4
|
-
DECISION_UPDATE,
|
|
5
4
|
Engine,
|
|
5
|
+
UpdateDecision,
|
|
6
6
|
)
|
|
7
7
|
from demos.common import compact_user_turns, is_verbose, print_info_report, state_observations
|
|
8
8
|
|
|
@@ -46,7 +46,7 @@ def _compile_premise(turns: list[str]) -> str:
|
|
|
46
46
|
engine = Engine()
|
|
47
47
|
for turn in turns:
|
|
48
48
|
decision = engine.step(turn)
|
|
49
|
-
assert decision
|
|
49
|
+
assert isinstance(decision, UpdateDecision)
|
|
50
50
|
compiled_premise = engine.premise
|
|
51
51
|
assert compiled_premise is not None
|
|
52
52
|
return compiled_premise
|
|
@@ -4,7 +4,7 @@ from collections.abc import Mapping
|
|
|
4
4
|
|
|
5
5
|
from context_compiler import (
|
|
6
6
|
Engine,
|
|
7
|
-
|
|
7
|
+
SemanticErrorDecision,
|
|
8
8
|
)
|
|
9
9
|
from demos.common import (
|
|
10
10
|
build_baseline_messages,
|
|
@@ -69,7 +69,7 @@ def main() -> None:
|
|
|
69
69
|
reinjected_output = complete_messages(reinjected_messages)
|
|
70
70
|
print_model_output("Reinjected-state", reinjected_output)
|
|
71
71
|
|
|
72
|
-
if
|
|
72
|
+
if isinstance(decision, SemanticErrorDecision):
|
|
73
73
|
print_messages("compiler-mediated (full)", [])
|
|
74
74
|
mediated_output = "[no call] authoritative state blocked replacement without source use"
|
|
75
75
|
print_model_output("Compiler-mediated (full)", mediated_output)
|
|
@@ -99,7 +99,7 @@ def main() -> None:
|
|
|
99
99
|
|
|
100
100
|
baseline_has_authoritative_precondition = False
|
|
101
101
|
reinjected_has_authoritative_precondition = False
|
|
102
|
-
compiler_pass =
|
|
102
|
+
compiler_pass = isinstance(decision, SemanticErrorDecision) and state_preserved
|
|
103
103
|
compact_pass = compact_error_preserved and compact_state_preserved
|
|
104
104
|
|
|
105
105
|
print_host_check(
|
|
@@ -114,7 +114,7 @@ def main() -> None:
|
|
|
114
114
|
)
|
|
115
115
|
print_host_check(
|
|
116
116
|
"COMPILER_BLOCKED_INVALID_REPLACEMENT",
|
|
117
|
-
yes_no(
|
|
117
|
+
yes_no(isinstance(decision, SemanticErrorDecision)),
|
|
118
118
|
context="compiler-mediated",
|
|
119
119
|
)
|
|
120
120
|
print_host_check(
|
|
@@ -35,8 +35,7 @@ Runnable application-layer enforcement-point integrations live in
|
|
|
35
35
|
| [05](./05_llm_prompt_drift_vs_state.py) | Prompt drift | long transcript failure | weaker long-context models ([see Demo 05 example](#demo-05-example-prompt-drift-under-longer-context)) |
|
|
36
36
|
| [06](./06_llm_context_compaction.py) | Context compaction | saved compiler state replacing transcript context | small or local models |
|
|
37
37
|
| [07](./07_llm_prompt_vs_state.py) | Prompt engineering comparison | prompting vs saved compiler state | any model with long transcript sensitivity |
|
|
38
|
-
| [08](./08_llm_replacement_precondition.py) | Replacement precondition | missing-source replacement
|
|
39
|
-
| [09](./09_llm_confirmation_no_directive.py) | Confirmation no_directive | invalid replacement does not create an engine-owned confirmation state; later confirmation-style input remains ordinary no_directive | any model |
|
|
38
|
+
| [08](./08_llm_replacement_precondition.py) | Replacement precondition | missing-source replacement is terminal; state remains unchanged | any model |
|
|
40
39
|
|
|
41
40
|
Stronger frontier models may show these behaviors less often, but the same
|
|
42
41
|
patterns still appear in real applications.
|
|
@@ -167,18 +166,18 @@ includes:
|
|
|
167
166
|
|
|
168
167
|
Notes:
|
|
169
168
|
|
|
170
|
-
- There are **
|
|
169
|
+
- There are **7 scored demos** (`01`–`05`, `07`, `08`). `06_context_compaction` is informational and excluded from PASS/FAIL totals.
|
|
171
170
|
- Anthropic runs in this repo are executed through the `openai_compatible` provider path.
|
|
172
171
|
- `PASS` means the demo-specific expected-behavior check for that path succeeded; `FAIL` means it did not.
|
|
173
172
|
- `reinjected-state` can be enough for some persistence cases; in this demo set it is intentionally used as a prompt-only comparison baseline.
|
|
174
|
-
- Scored checks focus on app-side authority rules (for example blocked mutation and
|
|
173
|
+
- Scored checks focus on app-side authority rules (for example blocked mutation and terminal invalid replacement handling), not model prose quality. `reinjected-state` remains plain text injection only.
|
|
175
174
|
- Interpretation:
|
|
176
175
|
|
|
177
176
|
- Demos `01`-`05` and `07` mostly test persistence and policy-following behavior across turns.
|
|
178
|
-
-
|
|
179
|
-
-
|
|
177
|
+
- Demo `08` tests rules for when state is allowed to change.
|
|
178
|
+
- Demo `08` covers authority semantics prompt text does not implement by itself, such as replacement preconditions, blocked mutations, and terminal semantic errors before saving changes.
|
|
180
179
|
- Plain prompt reinjection can produce reasonable answers, but it does not run these authority checks by itself and is not the only or preferred production integration pattern.
|
|
181
|
-
- Similar outcomes across models in `08
|
|
180
|
+
- Similar outcomes across models in `08` reflect app behavior limits, not model leaderboard ranking.
|
|
182
181
|
|
|
183
182
|
### Demo 05 example (prompt drift under longer context)
|
|
184
183
|
|