context-compiler 0.4.2__tar.gz → 0.4.4__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.4.2 → context_compiler-0.4.4}/AGENTS.md +1 -0
- {context_compiler-0.4.2 → context_compiler-0.4.4}/PKG-INFO +29 -80
- {context_compiler-0.4.2 → context_compiler-0.4.4}/README.md +28 -79
- {context_compiler-0.4.2 → context_compiler-0.4.4}/demos/04_llm_tool_governance.py +2 -3
- {context_compiler-0.4.2 → context_compiler-0.4.4}/demos/06_context_compaction.py +2 -3
- {context_compiler-0.4.2 → context_compiler-0.4.4}/demos/common.py +3 -4
- {context_compiler-0.4.2 → context_compiler-0.4.4}/docs/DirectiveGrammarSpec.md +13 -3
- {context_compiler-0.4.2 → context_compiler-0.4.4}/examples/01_persistent_guardrails.py +2 -3
- {context_compiler-0.4.2 → context_compiler-0.4.4}/examples/04_tool_governance_denylist.py +2 -3
- {context_compiler-0.4.2 → context_compiler-0.4.4}/pyproject.toml +1 -1
- context_compiler-0.4.4/src/context_compiler/__init__.py +25 -0
- {context_compiler-0.4.2 → context_compiler-0.4.4}/src/context_compiler/engine.py +11 -10
- {context_compiler-0.4.2 → context_compiler-0.4.4}/tests/test_engine.py +64 -19
- {context_compiler-0.4.2 → context_compiler-0.4.4}/uv.lock +1 -1
- context_compiler-0.4.2/src/context_compiler/__init__.py +0 -7
- {context_compiler-0.4.2 → context_compiler-0.4.4}/.github/workflows/ci.yml +0 -0
- {context_compiler-0.4.2 → context_compiler-0.4.4}/.github/workflows/publish-pypi.yml +0 -0
- {context_compiler-0.4.2 → context_compiler-0.4.4}/.gitignore +0 -0
- {context_compiler-0.4.2 → context_compiler-0.4.4}/.pre-commit-config.yaml +0 -0
- {context_compiler-0.4.2 → context_compiler-0.4.4}/CONTRIBUTING.md +0 -0
- {context_compiler-0.4.2 → context_compiler-0.4.4}/LICENSE +0 -0
- {context_compiler-0.4.2 → context_compiler-0.4.4}/demos/01_llm_ambiguity_block.py +0 -0
- {context_compiler-0.4.2 → context_compiler-0.4.4}/demos/02_llm_constraint_drift.py +0 -0
- {context_compiler-0.4.2 → context_compiler-0.4.4}/demos/03_llm_correction_replacement.py +0 -0
- {context_compiler-0.4.2 → context_compiler-0.4.4}/demos/05_llm_prompt_drift.py +0 -0
- {context_compiler-0.4.2 → context_compiler-0.4.4}/demos/07_llm_prompt_engineering_comparison.py +0 -0
- {context_compiler-0.4.2 → context_compiler-0.4.4}/demos/README.md +0 -0
- {context_compiler-0.4.2 → context_compiler-0.4.4}/demos/__init__.py +0 -0
- {context_compiler-0.4.2 → context_compiler-0.4.4}/demos/llm_client.py +0 -0
- {context_compiler-0.4.2 → context_compiler-0.4.4}/demos/run_demo.py +0 -0
- {context_compiler-0.4.2 → context_compiler-0.4.4}/docs/DescriptionAndMilestones.md +0 -0
- {context_compiler-0.4.2 → context_compiler-0.4.4}/docs/README.md +0 -0
- {context_compiler-0.4.2 → context_compiler-0.4.4}/examples/02_configuration_and_correction.py +0 -0
- {context_compiler-0.4.2 → context_compiler-0.4.4}/examples/03_ambiguity_with_clarification.py +0 -0
- {context_compiler-0.4.2 → context_compiler-0.4.4}/examples/05_llm_integration_pattern.py +0 -0
- {context_compiler-0.4.2 → context_compiler-0.4.4}/examples/06_transcript_replay.py +0 -0
- {context_compiler-0.4.2 → context_compiler-0.4.4}/examples/README.md +0 -0
- {context_compiler-0.4.2 → context_compiler-0.4.4}/examples/_util.py +0 -0
- {context_compiler-0.4.2 → context_compiler-0.4.4}/src/context_compiler/const.py +0 -0
- {context_compiler-0.4.2 → context_compiler-0.4.4}/src/context_compiler/repl.py +0 -0
- {context_compiler-0.4.2 → context_compiler-0.4.4}/tests/test_04_grammar_edge_cases.py +0 -0
- {context_compiler-0.4.2 → context_compiler-0.4.4}/tests/test_04_llm_tool_governance.py +0 -0
- {context_compiler-0.4.2 → context_compiler-0.4.4}/tests/test_07_llm_prompt_engineering_comparison.py +0 -0
- {context_compiler-0.4.2 → context_compiler-0.4.4}/tests/test_examples.py +0 -0
- {context_compiler-0.4.2 → context_compiler-0.4.4}/tests/test_llm_client.py +0 -0
- {context_compiler-0.4.2 → context_compiler-0.4.4}/tests/test_llm_demos.py +0 -0
- {context_compiler-0.4.2 → context_compiler-0.4.4}/tests/test_properties.py +0 -0
- {context_compiler-0.4.2 → context_compiler-0.4.4}/tests/test_repl.py +0 -0
- {context_compiler-0.4.2 → context_compiler-0.4.4}/tests/test_repl_properties.py +0 -0
- {context_compiler-0.4.2 → context_compiler-0.4.4}/tests/test_run_demo.py +0 -0
- {context_compiler-0.4.2 → context_compiler-0.4.4}/tests/test_smoke.py +0 -0
- {context_compiler-0.4.2 → context_compiler-0.4.4}/tests/test_transcript_replay.py +0 -0
|
@@ -61,6 +61,7 @@ Prefer modern typing syntax:
|
|
|
61
61
|
- PR descriptions should include:
|
|
62
62
|
- what changed
|
|
63
63
|
- why the change was needed
|
|
64
|
+
- A dedicated "Validation" section in PR text is optional and not required.
|
|
64
65
|
- Keep PR scope aligned to the requested task; if scope grows, ask for guidance before expanding.
|
|
65
66
|
|
|
66
67
|
## CI
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: context-compiler
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.4
|
|
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
|
|
@@ -73,19 +73,7 @@ User sets a constraint once:
|
|
|
73
73
|
User: don't use peanuts
|
|
74
74
|
```
|
|
75
75
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
```json
|
|
79
|
-
{
|
|
80
|
-
"facts": {
|
|
81
|
-
"focus.primary": null
|
|
82
|
-
},
|
|
83
|
-
"policies": {
|
|
84
|
-
"prohibit": ["peanuts"]
|
|
85
|
-
},
|
|
86
|
-
"version": 1
|
|
87
|
-
}
|
|
88
|
-
```
|
|
76
|
+
Outcome: prohibited items now include `"peanuts"`.
|
|
89
77
|
|
|
90
78
|
Later in the conversation:
|
|
91
79
|
|
|
@@ -157,35 +145,35 @@ else:
|
|
|
157
145
|
|
|
158
146
|
| API | Description |
|
|
159
147
|
|---|---|
|
|
160
|
-
| `create_engine(
|
|
148
|
+
| `create_engine(state=None)` | Create a new compiler engine; optional `state` provides initial authoritative state (validated/canonicalized). |
|
|
161
149
|
| `step(user_input)` | Parse one user turn and return a deterministic `Decision`. |
|
|
162
150
|
| `compile_transcript(messages)` | Replay a transcript from a fresh engine and return either final state or a confirmation prompt. |
|
|
163
151
|
| `engine.apply_transcript(messages)` | Replay a transcript onto the current engine state and return either final state or a confirmation prompt. |
|
|
164
|
-
| `engine.state` | Read
|
|
152
|
+
| `engine.state` | Read current authoritative in-memory state snapshot. |
|
|
153
|
+
| `get_focus_value(state)` | Read the current focus value from a state snapshot. |
|
|
154
|
+
| `get_prohibited_items(state)` | Read prohibited items from a state snapshot. |
|
|
165
155
|
| `export_json()` | Export current state as JSON for persistence/transport. |
|
|
166
|
-
| `import_json(payload)` | Load state from exported JSON payload. |
|
|
156
|
+
| `import_json(payload)` | Load/restore state from exported JSON payload. |
|
|
167
157
|
|
|
168
158
|
---
|
|
169
159
|
|
|
170
160
|
## State Model
|
|
171
161
|
|
|
172
|
-
The compiler maintains an authoritative state
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
{
|
|
176
|
-
"facts": {
|
|
177
|
-
"focus.primary": null
|
|
178
|
-
},
|
|
179
|
-
"policies": {
|
|
180
|
-
"prohibit": []
|
|
181
|
-
},
|
|
182
|
-
"version": 1
|
|
183
|
-
}
|
|
184
|
-
```
|
|
162
|
+
The compiler maintains an authoritative state snapshot.
|
|
163
|
+
Hosts should treat this state as structured application data and avoid coupling
|
|
164
|
+
to internal field names or nested layout.
|
|
185
165
|
|
|
186
166
|
## State Access and Persistence
|
|
187
167
|
|
|
188
|
-
Hosts
|
|
168
|
+
Hosts can provide initial state at engine creation (`create_engine(state=...)`),
|
|
169
|
+
read current in-memory state via `engine.state`, and persist/restore via
|
|
170
|
+
`export_json()` and `import_json()`. Semantic state mutations occur through
|
|
171
|
+
directives processed by `step()`. Storage is managed by the host application.
|
|
172
|
+
|
|
173
|
+
Use the returned state snapshot as structured host input for prompt
|
|
174
|
+
construction, policy enforcement, or replay/storage workflows.
|
|
175
|
+
For host code that needs typed reads without direct nested key lookups, use
|
|
176
|
+
`get_focus_value(state)` and `get_prohibited_items(state)`.
|
|
189
177
|
|
|
190
178
|
### Transcript Replay
|
|
191
179
|
|
|
@@ -200,7 +188,7 @@ Transcript replay compiles conversational history by reusing the same determinis
|
|
|
200
188
|
|
|
201
189
|
### Fact Schema
|
|
202
190
|
|
|
203
|
-
The current
|
|
191
|
+
The current behavior includes one exclusive focus value.
|
|
204
192
|
This demonstrates deterministic fact replacement and correction behavior.
|
|
205
193
|
Richer schemas may be introduced in future releases.
|
|
206
194
|
|
|
@@ -221,9 +209,9 @@ User: use corn oil
|
|
|
221
209
|
```
|
|
222
210
|
|
|
223
211
|
Result:
|
|
224
|
-
|
|
212
|
+
the current focus value becomes `"corn oil"`
|
|
225
213
|
|
|
226
|
-
Because
|
|
214
|
+
Because the focus value is exclusive (last write wins), later `use ...` directives replace earlier values.
|
|
227
215
|
|
|
228
216
|
This may differ from human expectations, where the intent may be interpreted as additive (e.g., ingredient + cooking medium). The current schema models a single focus value. See [issue #45](https://github.com/rlippmann/context-compiler/issues/45) for discussion.
|
|
229
217
|
|
|
@@ -238,14 +226,7 @@ User: don't use peanuts
|
|
|
238
226
|
```
|
|
239
227
|
|
|
240
228
|
Result:
|
|
241
|
-
|
|
242
|
-
```json
|
|
243
|
-
{
|
|
244
|
-
"policies": {
|
|
245
|
-
"prohibit": ["peanuts"]
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
```
|
|
229
|
+
prohibited items include `"peanuts"`.
|
|
249
230
|
|
|
250
231
|
Fact configuration:
|
|
251
232
|
|
|
@@ -254,10 +235,7 @@ User: use vegetarian curry
|
|
|
254
235
|
```
|
|
255
236
|
|
|
256
237
|
State update:
|
|
257
|
-
|
|
258
|
-
```text
|
|
259
|
-
facts.focus.primary = "vegetarian curry"
|
|
260
|
-
```
|
|
238
|
+
the current focus value becomes `"vegetarian curry"`
|
|
261
239
|
|
|
262
240
|
Correction:
|
|
263
241
|
|
|
@@ -266,10 +244,7 @@ User: actually vegan curry
|
|
|
266
244
|
```
|
|
267
245
|
|
|
268
246
|
Result:
|
|
269
|
-
|
|
270
|
-
```text
|
|
271
|
-
facts.focus.primary = "vegan curry"
|
|
272
|
-
```
|
|
247
|
+
the current focus value becomes `"vegan curry"`
|
|
273
248
|
|
|
274
249
|
Ambiguous mutation:
|
|
275
250
|
|
|
@@ -289,40 +264,14 @@ No state mutation occurs until confirmation.
|
|
|
289
264
|
|
|
290
265
|
Two explicit reset commands are supported:
|
|
291
266
|
|
|
292
|
-
- `reset policies` clears
|
|
267
|
+
- `reset policies` clears prohibited items but preserves the current focus value
|
|
293
268
|
- `clear state` resets the full state to initial values
|
|
294
269
|
|
|
295
270
|
Example:
|
|
296
271
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
{
|
|
301
|
-
"facts": {"focus.primary": "vegetarian curry"},
|
|
302
|
-
"policies": {"prohibit": ["peanuts"]},
|
|
303
|
-
"version": 1
|
|
304
|
-
}
|
|
305
|
-
```
|
|
306
|
-
|
|
307
|
-
After `reset policies`:
|
|
308
|
-
|
|
309
|
-
```json
|
|
310
|
-
{
|
|
311
|
-
"facts": {"focus.primary": "vegetarian curry"},
|
|
312
|
-
"policies": {"prohibit": []},
|
|
313
|
-
"version": 1
|
|
314
|
-
}
|
|
315
|
-
```
|
|
316
|
-
|
|
317
|
-
After `clear state`:
|
|
318
|
-
|
|
319
|
-
```json
|
|
320
|
-
{
|
|
321
|
-
"facts": {"focus.primary": null},
|
|
322
|
-
"policies": {"prohibit": []},
|
|
323
|
-
"version": 1
|
|
324
|
-
}
|
|
325
|
-
```
|
|
272
|
+
- If current focus is `"vegetarian curry"` and prohibited items include `"peanuts"`:
|
|
273
|
+
- after `reset policies`, prohibited items are empty and focus remains `"vegetarian curry"`.
|
|
274
|
+
- after `clear state`, both focus and prohibited items return to initial defaults.
|
|
326
275
|
|
|
327
276
|
---
|
|
328
277
|
|
|
@@ -44,19 +44,7 @@ User sets a constraint once:
|
|
|
44
44
|
User: don't use peanuts
|
|
45
45
|
```
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
```json
|
|
50
|
-
{
|
|
51
|
-
"facts": {
|
|
52
|
-
"focus.primary": null
|
|
53
|
-
},
|
|
54
|
-
"policies": {
|
|
55
|
-
"prohibit": ["peanuts"]
|
|
56
|
-
},
|
|
57
|
-
"version": 1
|
|
58
|
-
}
|
|
59
|
-
```
|
|
47
|
+
Outcome: prohibited items now include `"peanuts"`.
|
|
60
48
|
|
|
61
49
|
Later in the conversation:
|
|
62
50
|
|
|
@@ -128,35 +116,35 @@ else:
|
|
|
128
116
|
|
|
129
117
|
| API | Description |
|
|
130
118
|
|---|---|
|
|
131
|
-
| `create_engine(
|
|
119
|
+
| `create_engine(state=None)` | Create a new compiler engine; optional `state` provides initial authoritative state (validated/canonicalized). |
|
|
132
120
|
| `step(user_input)` | Parse one user turn and return a deterministic `Decision`. |
|
|
133
121
|
| `compile_transcript(messages)` | Replay a transcript from a fresh engine and return either final state or a confirmation prompt. |
|
|
134
122
|
| `engine.apply_transcript(messages)` | Replay a transcript onto the current engine state and return either final state or a confirmation prompt. |
|
|
135
|
-
| `engine.state` | Read
|
|
123
|
+
| `engine.state` | Read current authoritative in-memory state snapshot. |
|
|
124
|
+
| `get_focus_value(state)` | Read the current focus value from a state snapshot. |
|
|
125
|
+
| `get_prohibited_items(state)` | Read prohibited items from a state snapshot. |
|
|
136
126
|
| `export_json()` | Export current state as JSON for persistence/transport. |
|
|
137
|
-
| `import_json(payload)` | Load state from exported JSON payload. |
|
|
127
|
+
| `import_json(payload)` | Load/restore state from exported JSON payload. |
|
|
138
128
|
|
|
139
129
|
---
|
|
140
130
|
|
|
141
131
|
## State Model
|
|
142
132
|
|
|
143
|
-
The compiler maintains an authoritative state
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
{
|
|
147
|
-
"facts": {
|
|
148
|
-
"focus.primary": null
|
|
149
|
-
},
|
|
150
|
-
"policies": {
|
|
151
|
-
"prohibit": []
|
|
152
|
-
},
|
|
153
|
-
"version": 1
|
|
154
|
-
}
|
|
155
|
-
```
|
|
133
|
+
The compiler maintains an authoritative state snapshot.
|
|
134
|
+
Hosts should treat this state as structured application data and avoid coupling
|
|
135
|
+
to internal field names or nested layout.
|
|
156
136
|
|
|
157
137
|
## State Access and Persistence
|
|
158
138
|
|
|
159
|
-
Hosts
|
|
139
|
+
Hosts can provide initial state at engine creation (`create_engine(state=...)`),
|
|
140
|
+
read current in-memory state via `engine.state`, and persist/restore via
|
|
141
|
+
`export_json()` and `import_json()`. Semantic state mutations occur through
|
|
142
|
+
directives processed by `step()`. Storage is managed by the host application.
|
|
143
|
+
|
|
144
|
+
Use the returned state snapshot as structured host input for prompt
|
|
145
|
+
construction, policy enforcement, or replay/storage workflows.
|
|
146
|
+
For host code that needs typed reads without direct nested key lookups, use
|
|
147
|
+
`get_focus_value(state)` and `get_prohibited_items(state)`.
|
|
160
148
|
|
|
161
149
|
### Transcript Replay
|
|
162
150
|
|
|
@@ -171,7 +159,7 @@ Transcript replay compiles conversational history by reusing the same determinis
|
|
|
171
159
|
|
|
172
160
|
### Fact Schema
|
|
173
161
|
|
|
174
|
-
The current
|
|
162
|
+
The current behavior includes one exclusive focus value.
|
|
175
163
|
This demonstrates deterministic fact replacement and correction behavior.
|
|
176
164
|
Richer schemas may be introduced in future releases.
|
|
177
165
|
|
|
@@ -192,9 +180,9 @@ User: use corn oil
|
|
|
192
180
|
```
|
|
193
181
|
|
|
194
182
|
Result:
|
|
195
|
-
|
|
183
|
+
the current focus value becomes `"corn oil"`
|
|
196
184
|
|
|
197
|
-
Because
|
|
185
|
+
Because the focus value is exclusive (last write wins), later `use ...` directives replace earlier values.
|
|
198
186
|
|
|
199
187
|
This may differ from human expectations, where the intent may be interpreted as additive (e.g., ingredient + cooking medium). The current schema models a single focus value. See [issue #45](https://github.com/rlippmann/context-compiler/issues/45) for discussion.
|
|
200
188
|
|
|
@@ -209,14 +197,7 @@ User: don't use peanuts
|
|
|
209
197
|
```
|
|
210
198
|
|
|
211
199
|
Result:
|
|
212
|
-
|
|
213
|
-
```json
|
|
214
|
-
{
|
|
215
|
-
"policies": {
|
|
216
|
-
"prohibit": ["peanuts"]
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
```
|
|
200
|
+
prohibited items include `"peanuts"`.
|
|
220
201
|
|
|
221
202
|
Fact configuration:
|
|
222
203
|
|
|
@@ -225,10 +206,7 @@ User: use vegetarian curry
|
|
|
225
206
|
```
|
|
226
207
|
|
|
227
208
|
State update:
|
|
228
|
-
|
|
229
|
-
```text
|
|
230
|
-
facts.focus.primary = "vegetarian curry"
|
|
231
|
-
```
|
|
209
|
+
the current focus value becomes `"vegetarian curry"`
|
|
232
210
|
|
|
233
211
|
Correction:
|
|
234
212
|
|
|
@@ -237,10 +215,7 @@ User: actually vegan curry
|
|
|
237
215
|
```
|
|
238
216
|
|
|
239
217
|
Result:
|
|
240
|
-
|
|
241
|
-
```text
|
|
242
|
-
facts.focus.primary = "vegan curry"
|
|
243
|
-
```
|
|
218
|
+
the current focus value becomes `"vegan curry"`
|
|
244
219
|
|
|
245
220
|
Ambiguous mutation:
|
|
246
221
|
|
|
@@ -260,40 +235,14 @@ No state mutation occurs until confirmation.
|
|
|
260
235
|
|
|
261
236
|
Two explicit reset commands are supported:
|
|
262
237
|
|
|
263
|
-
- `reset policies` clears
|
|
238
|
+
- `reset policies` clears prohibited items but preserves the current focus value
|
|
264
239
|
- `clear state` resets the full state to initial values
|
|
265
240
|
|
|
266
241
|
Example:
|
|
267
242
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
{
|
|
272
|
-
"facts": {"focus.primary": "vegetarian curry"},
|
|
273
|
-
"policies": {"prohibit": ["peanuts"]},
|
|
274
|
-
"version": 1
|
|
275
|
-
}
|
|
276
|
-
```
|
|
277
|
-
|
|
278
|
-
After `reset policies`:
|
|
279
|
-
|
|
280
|
-
```json
|
|
281
|
-
{
|
|
282
|
-
"facts": {"focus.primary": "vegetarian curry"},
|
|
283
|
-
"policies": {"prohibit": []},
|
|
284
|
-
"version": 1
|
|
285
|
-
}
|
|
286
|
-
```
|
|
287
|
-
|
|
288
|
-
After `clear state`:
|
|
289
|
-
|
|
290
|
-
```json
|
|
291
|
-
{
|
|
292
|
-
"facts": {"focus.primary": null},
|
|
293
|
-
"policies": {"prohibit": []},
|
|
294
|
-
"version": 1
|
|
295
|
-
}
|
|
296
|
-
```
|
|
243
|
+
- If current focus is `"vegetarian curry"` and prohibited items include `"peanuts"`:
|
|
244
|
+
- after `reset policies`, prohibited items are empty and focus remains `"vegetarian curry"`.
|
|
245
|
+
- after `clear state`, both focus and prohibited items return to initial defaults.
|
|
297
246
|
|
|
298
247
|
---
|
|
299
248
|
|
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import re
|
|
4
4
|
|
|
5
|
-
from context_compiler import create_engine
|
|
6
|
-
from context_compiler.const import POLICY_PROHIBIT, STATE_POLICIES
|
|
5
|
+
from context_compiler import create_engine, get_prohibited_items
|
|
7
6
|
from demos.common import (
|
|
8
7
|
build_baseline_messages,
|
|
9
8
|
build_mediated_messages,
|
|
@@ -72,7 +71,7 @@ def main() -> None:
|
|
|
72
71
|
baseline_output = complete_messages(baseline_messages)
|
|
73
72
|
print_model_output("Baseline", baseline_output)
|
|
74
73
|
|
|
75
|
-
prohibited = engine.state
|
|
74
|
+
prohibited = get_prohibited_items(engine.state)
|
|
76
75
|
candidate_tools = ["docker", "kubectl"]
|
|
77
76
|
filtered_tools = [tool for tool in candidate_tools if tool not in prohibited]
|
|
78
77
|
if is_verbose():
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"""Demo 6: host-side prompt replacement from authoritative compiled state."""
|
|
2
2
|
|
|
3
|
-
from context_compiler import compile_transcript
|
|
4
|
-
from context_compiler.const import FOCUS_PRIMARY, STATE_FACTS
|
|
3
|
+
from context_compiler import compile_transcript, get_focus_value
|
|
5
4
|
from demos.common import is_verbose, print_info_report
|
|
6
5
|
|
|
7
6
|
DEMO_NAME = "06_context_compaction — superseded directives eliminated"
|
|
@@ -44,7 +43,7 @@ def _compile_focus(turns: list[str]) -> str:
|
|
|
44
43
|
messages: list[dict[str, object]] = [{"role": "user", "content": turn} for turn in turns]
|
|
45
44
|
result = compile_transcript(messages)
|
|
46
45
|
assert result["kind"] == "state"
|
|
47
|
-
compiled_focus = result["state"]
|
|
46
|
+
compiled_focus = get_focus_value(result["state"])
|
|
48
47
|
assert compiled_focus is not None
|
|
49
48
|
return compiled_focus
|
|
50
49
|
|
|
@@ -5,8 +5,7 @@ import os
|
|
|
5
5
|
import re
|
|
6
6
|
from typing import Any, TypedDict
|
|
7
7
|
|
|
8
|
-
from context_compiler import Decision, State
|
|
9
|
-
from context_compiler.const import FOCUS_PRIMARY, POLICY_PROHIBIT, STATE_FACTS, STATE_POLICIES
|
|
8
|
+
from context_compiler import Decision, State, get_focus_value, get_prohibited_items
|
|
10
9
|
from demos.llm_client import Message
|
|
11
10
|
|
|
12
11
|
VERBOSE_ENV_VAR = "CONTEXT_COMPILER_DEMO_VERBOSE"
|
|
@@ -179,8 +178,8 @@ def consume_last_info_report() -> InfoReport | None:
|
|
|
179
178
|
|
|
180
179
|
|
|
181
180
|
def build_compiled_system_prompt(state: State) -> str:
|
|
182
|
-
focus_value = state
|
|
183
|
-
prohibit = state
|
|
181
|
+
focus_value = get_focus_value(state)
|
|
182
|
+
prohibit = get_prohibited_items(state)
|
|
184
183
|
prohibit_text = ", ".join(prohibit) if prohibit else "(none)"
|
|
185
184
|
focus_text = focus_value if focus_value is not None else "(unset)"
|
|
186
185
|
return (
|
|
@@ -43,6 +43,15 @@ The host:
|
|
|
43
43
|
- Displays clarification prompts
|
|
44
44
|
- Calls the LLM when allowed
|
|
45
45
|
- Formats prompts using provided state
|
|
46
|
+
- May read state snapshots directly, but should prefer public helper accessors where available.
|
|
47
|
+
|
|
48
|
+
Current helpers:
|
|
49
|
+
- `get_focus_value(state)`
|
|
50
|
+
- `get_prohibited_items(state)`
|
|
51
|
+
|
|
52
|
+
These helpers are read-only conveniences for state snapshots to reduce direct
|
|
53
|
+
coupling to nested layout. They do not modify compiler state and are not
|
|
54
|
+
semantic/compiler primitives.
|
|
46
55
|
|
|
47
56
|
### 4. Decision API Contract
|
|
48
57
|
|
|
@@ -291,11 +300,12 @@ directive parsing, and does not mutate state.
|
|
|
291
300
|
Adding duplicate policy is a no-op.
|
|
292
301
|
Policies stored in sorted lexical order.
|
|
293
302
|
|
|
294
|
-
Administrative state replacement is
|
|
295
|
-
- `
|
|
303
|
+
Administrative state initialization/replacement is supported through:
|
|
304
|
+
- constructor input (`create_engine(state=...)` / `Engine(state=...)`) for initial load
|
|
296
305
|
- `engine.import_json(payload)` (JSON replacement)
|
|
297
306
|
|
|
298
|
-
|
|
307
|
+
Import-based replacement clears pending clarification state and must behave like
|
|
308
|
+
live state for subsequent `step()` calls.
|
|
299
309
|
|
|
300
310
|
### 10. Context Serialization Contract
|
|
301
311
|
|
|
@@ -2,12 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
from _util import print_json
|
|
4
4
|
|
|
5
|
-
from context_compiler import State, create_engine
|
|
6
|
-
from context_compiler.const import POLICY_PROHIBIT, STATE_POLICIES
|
|
5
|
+
from context_compiler import State, create_engine, get_prohibited_items
|
|
7
6
|
|
|
8
7
|
|
|
9
8
|
def build_prompt(state: State, user_input: str) -> str:
|
|
10
|
-
prohibit = state
|
|
9
|
+
prohibit = get_prohibited_items(state)
|
|
11
10
|
prohibit_text = ", ".join(prohibit) if prohibit else "(none)"
|
|
12
11
|
return (
|
|
13
12
|
"System: Follow authoritative conversation state.\n"
|
|
@@ -4,8 +4,7 @@ from dataclasses import dataclass
|
|
|
4
4
|
|
|
5
5
|
from _util import print_json
|
|
6
6
|
|
|
7
|
-
from context_compiler import create_engine
|
|
8
|
-
from context_compiler.const import POLICY_PROHIBIT, STATE_POLICIES
|
|
7
|
+
from context_compiler import create_engine, get_prohibited_items
|
|
9
8
|
|
|
10
9
|
|
|
11
10
|
@dataclass
|
|
@@ -35,7 +34,7 @@ def main() -> None:
|
|
|
35
34
|
print()
|
|
36
35
|
|
|
37
36
|
print("Host-side tool denylist behavior:")
|
|
38
|
-
prohibit = state
|
|
37
|
+
prohibit = get_prohibited_items(state)
|
|
39
38
|
tools = [Tool("docker"), Tool("kubectl")]
|
|
40
39
|
for tool in tools:
|
|
41
40
|
if tool.name in prohibit:
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from importlib.metadata import version
|
|
2
|
+
|
|
3
|
+
from .engine import (
|
|
4
|
+
ApplyResult,
|
|
5
|
+
Decision,
|
|
6
|
+
Engine,
|
|
7
|
+
State,
|
|
8
|
+
compile_transcript,
|
|
9
|
+
create_engine,
|
|
10
|
+
get_focus_value,
|
|
11
|
+
get_prohibited_items,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
__version__ = version("context-compiler")
|
|
15
|
+
|
|
16
|
+
__all__ = [
|
|
17
|
+
"ApplyResult",
|
|
18
|
+
"Decision",
|
|
19
|
+
"Engine",
|
|
20
|
+
"State",
|
|
21
|
+
"compile_transcript",
|
|
22
|
+
"create_engine",
|
|
23
|
+
"get_focus_value",
|
|
24
|
+
"get_prohibited_items",
|
|
25
|
+
]
|
|
@@ -123,6 +123,16 @@ def compile_transcript(messages: list[dict[str, object]]) -> ApplyResult:
|
|
|
123
123
|
return engine.apply_transcript(messages)
|
|
124
124
|
|
|
125
125
|
|
|
126
|
+
def get_focus_value(state: State) -> str | None:
|
|
127
|
+
"""Return the current exclusive focus value from a state snapshot."""
|
|
128
|
+
return state[STATE_FACTS][FOCUS_PRIMARY]
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def get_prohibited_items(state: State) -> list[str]:
|
|
132
|
+
"""Return prohibited items from a state snapshot as a defensive list copy."""
|
|
133
|
+
return list(state[STATE_POLICIES][POLICY_PROHIBIT])
|
|
134
|
+
|
|
135
|
+
|
|
126
136
|
class Engine:
|
|
127
137
|
"""Deterministic state engine implementing directive semantics.
|
|
128
138
|
|
|
@@ -132,7 +142,7 @@ class Engine:
|
|
|
132
142
|
directive input to ``step()``.
|
|
133
143
|
- Host code should not rely on imperative helpers such as
|
|
134
144
|
``reset_policies()`` or ``clear_state()``.
|
|
135
|
-
- State may be administratively replaced via
|
|
145
|
+
- State may be administratively replaced via constructor input and
|
|
136
146
|
``engine.import_json(...)``.
|
|
137
147
|
"""
|
|
138
148
|
|
|
@@ -153,15 +163,6 @@ class Engine:
|
|
|
153
163
|
"""Return a defensive copy of the current authoritative in-memory state."""
|
|
154
164
|
return deepcopy(self._state)
|
|
155
165
|
|
|
156
|
-
@state.setter
|
|
157
|
-
def state(self, value: State) -> None:
|
|
158
|
-
"""Replace authoritative in-memory state from a supplied object.
|
|
159
|
-
|
|
160
|
-
The supplied value is validated and canonicalized. Replacement is full,
|
|
161
|
-
and pending clarification state is cleared.
|
|
162
|
-
"""
|
|
163
|
-
self._replace_state(_load_state_obj(value))
|
|
164
|
-
|
|
165
166
|
def export_json(self) -> str:
|
|
166
167
|
"""Serialize authoritative state for persistence or transport."""
|
|
167
168
|
return json.dumps(self._state, sort_keys=True, separators=(",", ":"))
|
|
@@ -2,7 +2,7 @@ import json
|
|
|
2
2
|
|
|
3
3
|
import pytest
|
|
4
4
|
|
|
5
|
-
from context_compiler import create_engine
|
|
5
|
+
from context_compiler import create_engine, get_focus_value, get_prohibited_items
|
|
6
6
|
from context_compiler.engine import DecisionKind, Engine
|
|
7
7
|
|
|
8
8
|
|
|
@@ -20,6 +20,23 @@ def test_state_getter_returns_defensive_copy() -> None:
|
|
|
20
20
|
assert engine.state["facts"]["focus.primary"] is None
|
|
21
21
|
|
|
22
22
|
|
|
23
|
+
def test_get_focus_value_reads_current_focus_from_state_snapshot() -> None:
|
|
24
|
+
engine = create_engine()
|
|
25
|
+
engine.step("use Nord Stage 4")
|
|
26
|
+
|
|
27
|
+
assert get_focus_value(engine.state) == "Nord Stage 4"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def test_get_prohibited_items_returns_defensive_list_copy() -> None:
|
|
31
|
+
engine = create_engine()
|
|
32
|
+
engine.step("don't use docker")
|
|
33
|
+
|
|
34
|
+
prohibited = get_prohibited_items(engine.state)
|
|
35
|
+
prohibited.append("kubernetes")
|
|
36
|
+
|
|
37
|
+
assert get_prohibited_items(engine.state) == ["docker"]
|
|
38
|
+
|
|
39
|
+
|
|
23
40
|
def test_export_json_returns_complete_representation_of_state() -> None:
|
|
24
41
|
engine = create_engine()
|
|
25
42
|
engine.step("use Nord Stage 4")
|
|
@@ -195,7 +212,7 @@ def test_create_engine_with_state_initializes_from_normalized_state() -> None:
|
|
|
195
212
|
},
|
|
196
213
|
),
|
|
197
214
|
(
|
|
198
|
-
"
|
|
215
|
+
"import_json",
|
|
199
216
|
{
|
|
200
217
|
"facts": {"focus.primary": None},
|
|
201
218
|
"policies": {"prohibit": "docker"},
|
|
@@ -212,22 +229,36 @@ def test_object_state_replacement_paths_reject_invalid_state(path: str, bad_stat
|
|
|
212
229
|
|
|
213
230
|
engine = create_engine()
|
|
214
231
|
with pytest.raises(ValueError):
|
|
215
|
-
engine.
|
|
232
|
+
engine.import_json(json.dumps(bad_state))
|
|
216
233
|
|
|
217
234
|
|
|
218
|
-
def
|
|
235
|
+
def test_state_property_is_read_only() -> None:
|
|
236
|
+
engine = create_engine()
|
|
237
|
+
with pytest.raises(AttributeError):
|
|
238
|
+
engine.state = {
|
|
239
|
+
"facts": {"focus.primary": None},
|
|
240
|
+
"policies": {"prohibit": []},
|
|
241
|
+
"version": 1,
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def test_import_json_replaces_state_and_clears_pending_clarification() -> None:
|
|
219
246
|
engine = create_engine()
|
|
220
247
|
decision = engine.step("no use docker")
|
|
221
248
|
assert decision["kind"] == "clarify"
|
|
222
249
|
|
|
223
|
-
engine.
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
250
|
+
engine.import_json(
|
|
251
|
+
json.dumps(
|
|
252
|
+
{
|
|
253
|
+
"facts": {"focus.primary": "Nord Stage 4"},
|
|
254
|
+
"policies": {"prohibit": ["kubernetes", "docker", "docker"]},
|
|
255
|
+
"version": 1,
|
|
256
|
+
}
|
|
257
|
+
)
|
|
258
|
+
)
|
|
228
259
|
|
|
229
|
-
|
|
230
|
-
assert
|
|
260
|
+
decision_after_import = engine.step("yes")
|
|
261
|
+
assert decision_after_import["kind"] == "passthrough"
|
|
231
262
|
assert engine.state == {
|
|
232
263
|
"facts": {"focus.primary": "Nord Stage 4"},
|
|
233
264
|
"policies": {"prohibit": ["docker", "kubernetes"]},
|
|
@@ -235,7 +266,7 @@ def test_state_setter_replaces_state_and_clears_pending_clarification() -> None:
|
|
|
235
266
|
}
|
|
236
267
|
|
|
237
268
|
|
|
238
|
-
def
|
|
269
|
+
def test_constructor_and_import_json_share_normalization_behavior() -> None:
|
|
239
270
|
raw_state = {
|
|
240
271
|
"facts": {"focus.primary": " MacBook M3` "},
|
|
241
272
|
"policies": {"prohibit": ["kubernetes", "docker", "docker"]},
|
|
@@ -244,13 +275,10 @@ def test_constructor_setter_and_import_json_share_normalization_behavior() -> No
|
|
|
244
275
|
|
|
245
276
|
from_ctor = Engine(state=json.loads(json.dumps(raw_state)))
|
|
246
277
|
|
|
247
|
-
from_setter = create_engine()
|
|
248
|
-
from_setter.state = json.loads(json.dumps(raw_state))
|
|
249
|
-
|
|
250
278
|
from_import = create_engine()
|
|
251
279
|
from_import.import_json(json.dumps(raw_state))
|
|
252
280
|
|
|
253
|
-
assert from_ctor.state ==
|
|
281
|
+
assert from_ctor.state == from_import.state
|
|
254
282
|
|
|
255
283
|
|
|
256
284
|
def test_import_json_clears_pending_clarification_state() -> None:
|
|
@@ -881,9 +909,9 @@ def test_pending_clarification_rejected_by_explicit_no_is_passthrough() -> None:
|
|
|
881
909
|
("path", "bad_state"),
|
|
882
910
|
[
|
|
883
911
|
("constructor", []),
|
|
884
|
-
("
|
|
912
|
+
("import_json", "not-a-dict"),
|
|
885
913
|
("constructor", {"facts": [], "policies": {"prohibit": []}, "version": 1}),
|
|
886
|
-
("
|
|
914
|
+
("import_json", {"facts": {"focus.primary": None}, "policies": [], "version": 1}),
|
|
887
915
|
(
|
|
888
916
|
"constructor",
|
|
889
917
|
{"facts": {"focus.primary": 123}, "policies": {"prohibit": []}, "version": 1},
|
|
@@ -898,7 +926,7 @@ def test_object_state_paths_reject_malformed_state_inputs(path: str, bad_state:
|
|
|
898
926
|
|
|
899
927
|
engine = create_engine()
|
|
900
928
|
with pytest.raises(ValueError):
|
|
901
|
-
engine.
|
|
929
|
+
engine.import_json(json.dumps(bad_state))
|
|
902
930
|
|
|
903
931
|
|
|
904
932
|
def test_allow_suffix_removes_existing_prohibition() -> None:
|
|
@@ -909,3 +937,20 @@ def test_allow_suffix_removes_existing_prohibition() -> None:
|
|
|
909
937
|
|
|
910
938
|
assert decision["kind"] == "update"
|
|
911
939
|
assert engine.state["policies"]["prohibit"] == []
|
|
940
|
+
|
|
941
|
+
|
|
942
|
+
def test_correction_payload_that_invokes_other_directive_family_is_clarified_without_mutation() -> (
|
|
943
|
+
None
|
|
944
|
+
):
|
|
945
|
+
engine = create_engine()
|
|
946
|
+
engine.step("use Nord Stage 4")
|
|
947
|
+
before = engine.state
|
|
948
|
+
|
|
949
|
+
decision = engine.step("actually don't use docker")
|
|
950
|
+
|
|
951
|
+
assert decision["kind"] == "clarify"
|
|
952
|
+
assert (
|
|
953
|
+
decision["prompt_to_user"]
|
|
954
|
+
== "Your directive mixes multiple directive types. Please provide one."
|
|
955
|
+
)
|
|
956
|
+
assert engine.state == before
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
from importlib.metadata import version
|
|
2
|
-
|
|
3
|
-
from .engine import ApplyResult, Decision, Engine, State, compile_transcript, create_engine
|
|
4
|
-
|
|
5
|
-
__version__ = version("context-compiler")
|
|
6
|
-
|
|
7
|
-
__all__ = ["ApplyResult", "Decision", "Engine", "State", "compile_transcript", "create_engine"]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{context_compiler-0.4.2 → context_compiler-0.4.4}/demos/07_llm_prompt_engineering_comparison.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{context_compiler-0.4.2 → context_compiler-0.4.4}/examples/02_configuration_and_correction.py
RENAMED
|
File without changes
|
{context_compiler-0.4.2 → context_compiler-0.4.4}/examples/03_ambiguity_with_clarification.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{context_compiler-0.4.2 → context_compiler-0.4.4}/tests/test_07_llm_prompt_engineering_comparison.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|