context-compiler 0.9.0.dev8__tar.gz → 0.9.0.dev9__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.dev8 → context_compiler-0.9.0.dev9}/PKG-INFO +1 -1
- {context_compiler-0.9.0.dev8 → context_compiler-0.9.0.dev9}/pyproject.toml +1 -1
- {context_compiler-0.9.0.dev8 → context_compiler-0.9.0.dev9}/src/context_compiler/engine.py +14 -49
- {context_compiler-0.9.0.dev8 → context_compiler-0.9.0.dev9}/src/context_compiler/grammar.py +162 -52
- {context_compiler-0.9.0.dev8 → context_compiler-0.9.0.dev9}/.gitignore +0 -0
- {context_compiler-0.9.0.dev8 → context_compiler-0.9.0.dev9}/LICENSE +0 -0
- {context_compiler-0.9.0.dev8 → context_compiler-0.9.0.dev9}/README.md +0 -0
- {context_compiler-0.9.0.dev8 → context_compiler-0.9.0.dev9}/demos/01_llm_contradiction_error.py +0 -0
- {context_compiler-0.9.0.dev8 → context_compiler-0.9.0.dev9}/demos/02_llm_constraint_guardrail.py +0 -0
- {context_compiler-0.9.0.dev8 → context_compiler-0.9.0.dev9}/demos/03_llm_premise_guardrail.py +0 -0
- {context_compiler-0.9.0.dev8 → context_compiler-0.9.0.dev9}/demos/04_llm_tool_denylist_guardrail.py +0 -0
- {context_compiler-0.9.0.dev8 → context_compiler-0.9.0.dev9}/demos/05_llm_prompt_drift_vs_state.py +0 -0
- {context_compiler-0.9.0.dev8 → context_compiler-0.9.0.dev9}/demos/06_llm_context_compaction.py +0 -0
- {context_compiler-0.9.0.dev8 → context_compiler-0.9.0.dev9}/demos/07_llm_prompt_vs_state.py +0 -0
- {context_compiler-0.9.0.dev8 → context_compiler-0.9.0.dev9}/demos/08_llm_replacement_precondition.py +0 -0
- {context_compiler-0.9.0.dev8 → context_compiler-0.9.0.dev9}/demos/09_llm_confirmation_no_directive.py +0 -0
- {context_compiler-0.9.0.dev8 → context_compiler-0.9.0.dev9}/demos/README.md +0 -0
- {context_compiler-0.9.0.dev8 → context_compiler-0.9.0.dev9}/demos/__init__.py +0 -0
- {context_compiler-0.9.0.dev8 → context_compiler-0.9.0.dev9}/demos/common.py +0 -0
- {context_compiler-0.9.0.dev8 → context_compiler-0.9.0.dev9}/demos/llm_client.py +0 -0
- {context_compiler-0.9.0.dev8 → context_compiler-0.9.0.dev9}/demos/run_demo.py +0 -0
- {context_compiler-0.9.0.dev8 → context_compiler-0.9.0.dev9}/examples/01_persistent_guardrails.py +0 -0
- {context_compiler-0.9.0.dev8 → context_compiler-0.9.0.dev9}/examples/02_configuration_and_correction.py +0 -0
- {context_compiler-0.9.0.dev8 → context_compiler-0.9.0.dev9}/examples/03_ambiguity_with_error.py +0 -0
- {context_compiler-0.9.0.dev8 → context_compiler-0.9.0.dev9}/examples/04_tool_governance_denylist.py +0 -0
- {context_compiler-0.9.0.dev8 → context_compiler-0.9.0.dev9}/examples/05_llm_integration_pattern.py +0 -0
- {context_compiler-0.9.0.dev8 → context_compiler-0.9.0.dev9}/examples/06_step_sequence_and_state_restore.py +0 -0
- {context_compiler-0.9.0.dev8 → context_compiler-0.9.0.dev9}/examples/07_single_policy_correction.py +0 -0
- {context_compiler-0.9.0.dev8 → context_compiler-0.9.0.dev9}/examples/README.md +0 -0
- {context_compiler-0.9.0.dev8 → context_compiler-0.9.0.dev9}/examples/_util.py +0 -0
- {context_compiler-0.9.0.dev8 → context_compiler-0.9.0.dev9}/host_support/__init__.py +0 -0
- {context_compiler-0.9.0.dev8 → context_compiler-0.9.0.dev9}/host_support/provider_mode.py +0 -0
- {context_compiler-0.9.0.dev8 → context_compiler-0.9.0.dev9}/src/context_compiler/__init__.py +0 -0
- {context_compiler-0.9.0.dev8 → context_compiler-0.9.0.dev9}/src/context_compiler/const.py +0 -0
- {context_compiler-0.9.0.dev8 → context_compiler-0.9.0.dev9}/src/context_compiler/decision_helpers.py +0 -0
- {context_compiler-0.9.0.dev8 → context_compiler-0.9.0.dev9}/src/context_compiler/py.typed +0 -0
- {context_compiler-0.9.0.dev8 → context_compiler-0.9.0.dev9}/src/context_compiler/repl.py +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.dev9
|
|
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
|
|
@@ -19,7 +19,7 @@ from .const import (
|
|
|
19
19
|
STATE_PREMISE,
|
|
20
20
|
STATE_VERSION,
|
|
21
21
|
)
|
|
22
|
-
from .grammar import CanonicalDirective,
|
|
22
|
+
from .grammar import CanonicalDirective, DirectiveKind, decompose_directive
|
|
23
23
|
|
|
24
24
|
PolicyValue = Literal["use", "prohibit"]
|
|
25
25
|
|
|
@@ -129,61 +129,26 @@ class Engine:
|
|
|
129
129
|
) -> Decision | None:
|
|
130
130
|
candidate_state = self._state if state is None else state
|
|
131
131
|
# Single error path: all error outcomes are detected before any mutation.
|
|
132
|
-
if directive.kind in {_DirectiveKind.SET_PREMISE, _DirectiveKind.CHANGE_PREMISE}:
|
|
133
|
-
value = directive.operands["value"]
|
|
134
|
-
if _sanitize_premise_value(value) == "":
|
|
135
|
-
if directive.kind is _DirectiveKind.SET_PREMISE:
|
|
136
|
-
return _error(
|
|
137
|
-
"Premise value cannot be empty.\n"
|
|
138
|
-
"Use 'set premise <value>' with a non-empty value."
|
|
139
|
-
)
|
|
140
|
-
return _error(
|
|
141
|
-
"Premise value cannot be empty.\n"
|
|
142
|
-
"Use 'change premise to <value>' with a non-empty value."
|
|
143
|
-
)
|
|
144
|
-
|
|
145
|
-
if (
|
|
146
|
-
directive.kind is _DirectiveKind.REMOVE_POLICY
|
|
147
|
-
and _normalize_item(directive.operands["item"]) == ""
|
|
148
|
-
):
|
|
149
|
-
return _error(
|
|
150
|
-
"Policy item cannot be empty.\nUse 'remove policy <item>' with a non-empty value."
|
|
151
|
-
)
|
|
152
|
-
|
|
153
|
-
if (
|
|
154
|
-
directive.kind is _DirectiveKind.USE_ITEM
|
|
155
|
-
and _normalize_item(directive.operands["item"]) == ""
|
|
156
|
-
):
|
|
157
|
-
return _error("Policy item cannot be empty.\nUse 'use <item>' with a non-empty value.")
|
|
158
|
-
|
|
159
|
-
if (
|
|
160
|
-
directive.kind is _DirectiveKind.PROHIBIT_ITEM
|
|
161
|
-
and _normalize_item(directive.operands["item"]) == ""
|
|
162
|
-
):
|
|
163
|
-
return _error(
|
|
164
|
-
"Policy item cannot be empty.\nUse 'prohibit <item>' with a non-empty value."
|
|
165
|
-
)
|
|
166
|
-
|
|
167
132
|
if (
|
|
168
|
-
directive.kind is
|
|
133
|
+
directive.kind is DirectiveKind.SET_PREMISE
|
|
169
134
|
and candidate_state[STATE_PREMISE] is not None
|
|
170
135
|
):
|
|
171
136
|
return _error("Premise already set.\nUse 'change premise to <value>' to modify it.")
|
|
172
137
|
|
|
173
138
|
if (
|
|
174
|
-
directive.kind is
|
|
139
|
+
directive.kind is DirectiveKind.CHANGE_PREMISE
|
|
175
140
|
and candidate_state[STATE_PREMISE] is None
|
|
176
141
|
):
|
|
177
142
|
return _error("No premise is set.\nUse 'set premise <value>' to define one.")
|
|
178
143
|
|
|
179
|
-
if directive.kind is
|
|
144
|
+
if directive.kind is DirectiveKind.USE_ITEM:
|
|
180
145
|
item_key = _normalize_item(directive.operands["item"])
|
|
181
146
|
if candidate_state[STATE_POLICIES].get(item_key) == POLICY_PROHIBIT:
|
|
182
147
|
return _error(
|
|
183
148
|
f'"{item_key}" is currently prohibited.\nRemove or replace it before using it.'
|
|
184
149
|
)
|
|
185
150
|
|
|
186
|
-
if directive.kind is
|
|
151
|
+
if directive.kind is DirectiveKind.PROHIBIT_ITEM:
|
|
187
152
|
item_key = _normalize_item(directive.operands["item"])
|
|
188
153
|
if candidate_state[STATE_POLICIES].get(item_key) == POLICY_USE:
|
|
189
154
|
return _error(
|
|
@@ -191,7 +156,7 @@ class Engine:
|
|
|
191
156
|
"Remove or replace it before prohibiting it."
|
|
192
157
|
)
|
|
193
158
|
|
|
194
|
-
if directive.kind is
|
|
159
|
+
if directive.kind is DirectiveKind.REPLACE_USE:
|
|
195
160
|
new_item = directive.operands["new_item"]
|
|
196
161
|
old_item = directive.operands["old_item"]
|
|
197
162
|
new_key = _normalize_item(new_item)
|
|
@@ -222,27 +187,27 @@ class Engine:
|
|
|
222
187
|
def _apply_directive(self, directive: CanonicalDirective, *, state: _State) -> _State:
|
|
223
188
|
next_state = deepcopy(state)
|
|
224
189
|
|
|
225
|
-
if directive.kind is
|
|
190
|
+
if directive.kind is DirectiveKind.SET_PREMISE:
|
|
226
191
|
next_state[STATE_PREMISE] = _sanitize_premise_value(directive.operands["value"])
|
|
227
192
|
return next_state
|
|
228
193
|
|
|
229
|
-
if directive.kind is
|
|
194
|
+
if directive.kind is DirectiveKind.CHANGE_PREMISE:
|
|
230
195
|
next_state[STATE_PREMISE] = _sanitize_premise_value(directive.operands["value"])
|
|
231
196
|
return next_state
|
|
232
197
|
|
|
233
|
-
if directive.kind is
|
|
198
|
+
if directive.kind is DirectiveKind.USE_ITEM:
|
|
234
199
|
item_key = _normalize_item(directive.operands["item"])
|
|
235
200
|
# Idempotent directives are updates even if state does not change.
|
|
236
201
|
next_state[STATE_POLICIES][item_key] = POLICY_USE
|
|
237
202
|
return next_state
|
|
238
203
|
|
|
239
|
-
if directive.kind is
|
|
204
|
+
if directive.kind is DirectiveKind.PROHIBIT_ITEM:
|
|
240
205
|
item_key = _normalize_item(directive.operands["item"])
|
|
241
206
|
# Idempotent directives are updates even if state does not change.
|
|
242
207
|
next_state[STATE_POLICIES][item_key] = POLICY_PROHIBIT
|
|
243
208
|
return next_state
|
|
244
209
|
|
|
245
|
-
if directive.kind is
|
|
210
|
+
if directive.kind is DirectiveKind.REPLACE_USE:
|
|
246
211
|
self._apply_replacement_explicit(
|
|
247
212
|
next_state,
|
|
248
213
|
directive.operands["new_item"],
|
|
@@ -250,16 +215,16 @@ class Engine:
|
|
|
250
215
|
)
|
|
251
216
|
return next_state
|
|
252
217
|
|
|
253
|
-
if directive.kind is
|
|
218
|
+
if directive.kind is DirectiveKind.REMOVE_POLICY:
|
|
254
219
|
item_key = _normalize_item(directive.operands["item"])
|
|
255
220
|
next_state[STATE_POLICIES].pop(item_key, None)
|
|
256
221
|
return next_state
|
|
257
222
|
|
|
258
|
-
if directive.kind is
|
|
223
|
+
if directive.kind is DirectiveKind.CLEAR_PREMISE:
|
|
259
224
|
next_state[STATE_PREMISE] = None
|
|
260
225
|
return next_state
|
|
261
226
|
|
|
262
|
-
if directive.kind is
|
|
227
|
+
if directive.kind is DirectiveKind.RESET_POLICIES:
|
|
263
228
|
next_state[STATE_POLICIES] = {}
|
|
264
229
|
return next_state
|
|
265
230
|
|
|
@@ -7,7 +7,7 @@ from enum import StrEnum
|
|
|
7
7
|
from types import MappingProxyType
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
class
|
|
10
|
+
class DirectiveKind(StrEnum):
|
|
11
11
|
"""Enumerate the supported canonical directive families."""
|
|
12
12
|
|
|
13
13
|
SET_PREMISE = "set_premise"
|
|
@@ -21,6 +21,14 @@ class _DirectiveKind(StrEnum):
|
|
|
21
21
|
CLEAR_STATE = "clear_state"
|
|
22
22
|
|
|
23
23
|
|
|
24
|
+
class DirectiveSyntaxFailure(StrEnum):
|
|
25
|
+
"""Enumerate minimal grammar failure categories for directive-shaped input."""
|
|
26
|
+
|
|
27
|
+
COMPOUND_DIRECTIVE = "compound_directive"
|
|
28
|
+
MISSING_REQUIRED_OPERAND = "missing_required_operand"
|
|
29
|
+
MALFORMED_DIRECTIVE = "malformed_directive"
|
|
30
|
+
|
|
31
|
+
|
|
24
32
|
@dataclass(frozen=True, slots=True)
|
|
25
33
|
class CanonicalDirective:
|
|
26
34
|
"""Represent one parsed canonical directive and its named operands.
|
|
@@ -30,7 +38,7 @@ class CanonicalDirective:
|
|
|
30
38
|
"""
|
|
31
39
|
|
|
32
40
|
text: str
|
|
33
|
-
kind:
|
|
41
|
+
kind: DirectiveKind
|
|
34
42
|
operands: MappingProxyType[str, str]
|
|
35
43
|
|
|
36
44
|
|
|
@@ -38,10 +46,14 @@ class CanonicalDirective:
|
|
|
38
46
|
class InvalidDirectiveSyntax:
|
|
39
47
|
"""Represent directive-shaped input that fails canonical syntax parsing."""
|
|
40
48
|
|
|
49
|
+
failure: DirectiveSyntaxFailure = DirectiveSyntaxFailure.MALFORMED_DIRECTIVE
|
|
50
|
+
directive_kind: DirectiveKind | None = None
|
|
51
|
+
missing_operand: str | None = None
|
|
52
|
+
|
|
41
53
|
|
|
42
54
|
@dataclass(frozen=True, slots=True)
|
|
43
55
|
class _DirectiveSpec:
|
|
44
|
-
kind:
|
|
56
|
+
kind: DirectiveKind
|
|
45
57
|
operand_names: tuple[str, ...]
|
|
46
58
|
exact_text: str | None
|
|
47
59
|
renderer: Callable[[MappingProxyType[str, str]], str]
|
|
@@ -126,56 +138,56 @@ def _render_exact(text: str) -> Callable[[MappingProxyType[str, str]], str]:
|
|
|
126
138
|
|
|
127
139
|
_DIRECTIVE_SPECS = MappingProxyType(
|
|
128
140
|
{
|
|
129
|
-
|
|
130
|
-
kind=
|
|
141
|
+
DirectiveKind.SET_PREMISE: _DirectiveSpec(
|
|
142
|
+
kind=DirectiveKind.SET_PREMISE,
|
|
131
143
|
operand_names=("value",),
|
|
132
144
|
exact_text=None,
|
|
133
145
|
renderer=_render_with_prefix(_SET_PREMISE_PREFIX, "value"),
|
|
134
146
|
),
|
|
135
|
-
|
|
136
|
-
kind=
|
|
147
|
+
DirectiveKind.CHANGE_PREMISE: _DirectiveSpec(
|
|
148
|
+
kind=DirectiveKind.CHANGE_PREMISE,
|
|
137
149
|
operand_names=("value",),
|
|
138
150
|
exact_text=None,
|
|
139
151
|
renderer=_render_with_prefix(_CHANGE_PREMISE_PREFIX, "value"),
|
|
140
152
|
),
|
|
141
|
-
|
|
142
|
-
kind=
|
|
153
|
+
DirectiveKind.USE_ITEM: _DirectiveSpec(
|
|
154
|
+
kind=DirectiveKind.USE_ITEM,
|
|
143
155
|
operand_names=("item",),
|
|
144
156
|
exact_text=None,
|
|
145
157
|
renderer=_render_with_prefix(_USE_PREFIX, "item"),
|
|
146
158
|
),
|
|
147
|
-
|
|
148
|
-
kind=
|
|
159
|
+
DirectiveKind.PROHIBIT_ITEM: _DirectiveSpec(
|
|
160
|
+
kind=DirectiveKind.PROHIBIT_ITEM,
|
|
149
161
|
operand_names=("item",),
|
|
150
162
|
exact_text=None,
|
|
151
163
|
renderer=_render_with_prefix(_PROHIBIT_PREFIX, "item"),
|
|
152
164
|
),
|
|
153
|
-
|
|
154
|
-
kind=
|
|
165
|
+
DirectiveKind.REMOVE_POLICY: _DirectiveSpec(
|
|
166
|
+
kind=DirectiveKind.REMOVE_POLICY,
|
|
155
167
|
operand_names=("item",),
|
|
156
168
|
exact_text=None,
|
|
157
169
|
renderer=_render_with_prefix(_REMOVE_POLICY_PREFIX, "item"),
|
|
158
170
|
),
|
|
159
|
-
|
|
160
|
-
kind=
|
|
171
|
+
DirectiveKind.REPLACE_USE: _DirectiveSpec(
|
|
172
|
+
kind=DirectiveKind.REPLACE_USE,
|
|
161
173
|
operand_names=("new_item", "old_item"),
|
|
162
174
|
exact_text=None,
|
|
163
175
|
renderer=_render_replace_use,
|
|
164
176
|
),
|
|
165
|
-
|
|
166
|
-
kind=
|
|
177
|
+
DirectiveKind.CLEAR_PREMISE: _DirectiveSpec(
|
|
178
|
+
kind=DirectiveKind.CLEAR_PREMISE,
|
|
167
179
|
operand_names=(),
|
|
168
180
|
exact_text=_CLEAR_PREMISE_TEXT,
|
|
169
181
|
renderer=_render_exact(_CLEAR_PREMISE_TEXT),
|
|
170
182
|
),
|
|
171
|
-
|
|
172
|
-
kind=
|
|
183
|
+
DirectiveKind.RESET_POLICIES: _DirectiveSpec(
|
|
184
|
+
kind=DirectiveKind.RESET_POLICIES,
|
|
173
185
|
operand_names=(),
|
|
174
186
|
exact_text=_RESET_POLICIES_TEXT,
|
|
175
187
|
renderer=_render_exact(_RESET_POLICIES_TEXT),
|
|
176
188
|
),
|
|
177
|
-
|
|
178
|
-
kind=
|
|
189
|
+
DirectiveKind.CLEAR_STATE: _DirectiveSpec(
|
|
190
|
+
kind=DirectiveKind.CLEAR_STATE,
|
|
179
191
|
operand_names=(),
|
|
180
192
|
exact_text=_CLEAR_STATE_TEXT,
|
|
181
193
|
renderer=_render_exact(_CLEAR_STATE_TEXT),
|
|
@@ -305,11 +317,24 @@ def _parse_replace_use(trimmed_text: str) -> CanonicalDirective | None:
|
|
|
305
317
|
return None
|
|
306
318
|
return CanonicalDirective(
|
|
307
319
|
text=trimmed_text,
|
|
308
|
-
kind=
|
|
320
|
+
kind=DirectiveKind.REPLACE_USE,
|
|
309
321
|
operands=MappingProxyType({"new_item": new_item, "old_item": old_item}),
|
|
310
322
|
)
|
|
311
323
|
|
|
312
324
|
|
|
325
|
+
def _invalid_directive_syntax(
|
|
326
|
+
failure: DirectiveSyntaxFailure,
|
|
327
|
+
*,
|
|
328
|
+
directive_kind: DirectiveKind | None = None,
|
|
329
|
+
missing_operand: str | None = None,
|
|
330
|
+
) -> InvalidDirectiveSyntax:
|
|
331
|
+
return InvalidDirectiveSyntax(
|
|
332
|
+
failure=failure,
|
|
333
|
+
directive_kind=directive_kind,
|
|
334
|
+
missing_operand=missing_operand,
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
|
|
313
338
|
def decompose_directive(text: str) -> CanonicalDirective | InvalidDirectiveSyntax | None:
|
|
314
339
|
"""Parse one canonical directive into its semantic kind and operands.
|
|
315
340
|
|
|
@@ -326,50 +351,76 @@ def decompose_directive(text: str) -> CanonicalDirective | InvalidDirectiveSynta
|
|
|
326
351
|
if not _starts_with_directive_family(trimmed_text):
|
|
327
352
|
return None
|
|
328
353
|
if _contains_multiple_canonical_directives(trimmed_text):
|
|
329
|
-
return
|
|
330
|
-
|
|
331
|
-
invalid_result = InvalidDirectiveSyntax()
|
|
354
|
+
return _invalid_directive_syntax(DirectiveSyntaxFailure.COMPOUND_DIRECTIVE)
|
|
332
355
|
|
|
333
356
|
normalized = _normalized_for_matching(trimmed_text)
|
|
334
357
|
|
|
335
358
|
if normalized == _CLEAR_PREMISE_TEXT:
|
|
336
359
|
return CanonicalDirective(
|
|
337
|
-
text=text, kind=
|
|
360
|
+
text=text, kind=DirectiveKind.CLEAR_PREMISE, operands=MappingProxyType({})
|
|
338
361
|
)
|
|
339
362
|
if normalized == _RESET_POLICIES_TEXT:
|
|
340
363
|
return CanonicalDirective(
|
|
341
364
|
text=text,
|
|
342
|
-
kind=
|
|
365
|
+
kind=DirectiveKind.RESET_POLICIES,
|
|
343
366
|
operands=MappingProxyType({}),
|
|
344
367
|
)
|
|
345
368
|
if normalized == _CLEAR_STATE_TEXT:
|
|
346
369
|
return CanonicalDirective(
|
|
347
|
-
text=text, kind=
|
|
370
|
+
text=text, kind=DirectiveKind.CLEAR_STATE, operands=MappingProxyType({})
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
if normalized == "set premise":
|
|
374
|
+
return _invalid_directive_syntax(
|
|
375
|
+
DirectiveSyntaxFailure.MISSING_REQUIRED_OPERAND,
|
|
376
|
+
directive_kind=DirectiveKind.SET_PREMISE,
|
|
377
|
+
missing_operand="value",
|
|
348
378
|
)
|
|
349
379
|
|
|
350
380
|
if normalized.startswith("set premise "):
|
|
351
381
|
match = _SET_PREMISE_RE.fullmatch(trimmed_text)
|
|
352
382
|
if match is None:
|
|
353
|
-
return
|
|
383
|
+
return _invalid_directive_syntax(
|
|
384
|
+
DirectiveSyntaxFailure.MALFORMED_DIRECTIVE,
|
|
385
|
+
directive_kind=DirectiveKind.SET_PREMISE,
|
|
386
|
+
)
|
|
354
387
|
value = match.group("value")
|
|
355
388
|
if not _operand_has_content(value) or _operand_starts_with_token(value, "to"):
|
|
356
|
-
return
|
|
389
|
+
return _invalid_directive_syntax(
|
|
390
|
+
DirectiveSyntaxFailure.MALFORMED_DIRECTIVE,
|
|
391
|
+
directive_kind=DirectiveKind.SET_PREMISE,
|
|
392
|
+
)
|
|
357
393
|
return CanonicalDirective(
|
|
358
394
|
text=text,
|
|
359
|
-
kind=
|
|
395
|
+
kind=DirectiveKind.SET_PREMISE,
|
|
360
396
|
operands=MappingProxyType({"value": value}),
|
|
361
397
|
)
|
|
362
398
|
|
|
399
|
+
if normalized == "change premise to":
|
|
400
|
+
return _invalid_directive_syntax(
|
|
401
|
+
DirectiveSyntaxFailure.MISSING_REQUIRED_OPERAND,
|
|
402
|
+
directive_kind=DirectiveKind.CHANGE_PREMISE,
|
|
403
|
+
missing_operand="value",
|
|
404
|
+
)
|
|
405
|
+
|
|
363
406
|
if normalized.startswith("change premise to "):
|
|
364
407
|
match = _CHANGE_PREMISE_RE.fullmatch(trimmed_text)
|
|
365
408
|
if match is None:
|
|
366
|
-
return
|
|
409
|
+
return _invalid_directive_syntax(
|
|
410
|
+
DirectiveSyntaxFailure.MISSING_REQUIRED_OPERAND,
|
|
411
|
+
directive_kind=DirectiveKind.CHANGE_PREMISE,
|
|
412
|
+
missing_operand="value",
|
|
413
|
+
)
|
|
367
414
|
value = match.group("value")
|
|
368
415
|
if not _operand_has_content(value):
|
|
369
|
-
return
|
|
416
|
+
return _invalid_directive_syntax(
|
|
417
|
+
DirectiveSyntaxFailure.MISSING_REQUIRED_OPERAND,
|
|
418
|
+
directive_kind=DirectiveKind.CHANGE_PREMISE,
|
|
419
|
+
missing_operand="value",
|
|
420
|
+
)
|
|
370
421
|
return CanonicalDirective(
|
|
371
422
|
text=text,
|
|
372
|
-
kind=
|
|
423
|
+
kind=DirectiveKind.CHANGE_PREMISE,
|
|
373
424
|
operands=MappingProxyType({"value": value}),
|
|
374
425
|
)
|
|
375
426
|
|
|
@@ -377,58 +428,115 @@ def decompose_directive(text: str) -> CanonicalDirective | InvalidDirectiveSynta
|
|
|
377
428
|
if replacement is not None:
|
|
378
429
|
return replacement
|
|
379
430
|
|
|
431
|
+
if normalized == "use":
|
|
432
|
+
return _invalid_directive_syntax(
|
|
433
|
+
DirectiveSyntaxFailure.MISSING_REQUIRED_OPERAND,
|
|
434
|
+
directive_kind=DirectiveKind.USE_ITEM,
|
|
435
|
+
missing_operand="item",
|
|
436
|
+
)
|
|
437
|
+
|
|
380
438
|
if normalized.startswith("use "):
|
|
381
439
|
match = _USE_RE.fullmatch(trimmed_text)
|
|
382
440
|
if match is None:
|
|
383
|
-
return
|
|
441
|
+
return _invalid_directive_syntax(
|
|
442
|
+
DirectiveSyntaxFailure.MISSING_REQUIRED_OPERAND,
|
|
443
|
+
directive_kind=DirectiveKind.USE_ITEM,
|
|
444
|
+
missing_operand="item",
|
|
445
|
+
)
|
|
384
446
|
item = match.group("item")
|
|
385
447
|
normalized_item = _normalized_for_matching(item)
|
|
386
|
-
if (
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
448
|
+
if not _operand_has_content(item):
|
|
449
|
+
return _invalid_directive_syntax(
|
|
450
|
+
DirectiveSyntaxFailure.MISSING_REQUIRED_OPERAND,
|
|
451
|
+
directive_kind=DirectiveKind.USE_ITEM,
|
|
452
|
+
missing_operand="item",
|
|
453
|
+
)
|
|
454
|
+
if normalized_item == "instead of" or normalized_item.startswith("instead of "):
|
|
455
|
+
return _invalid_directive_syntax(
|
|
456
|
+
DirectiveSyntaxFailure.MISSING_REQUIRED_OPERAND,
|
|
457
|
+
directive_kind=DirectiveKind.REPLACE_USE,
|
|
458
|
+
missing_operand="new_item",
|
|
459
|
+
)
|
|
460
|
+
if normalized_item.endswith(" instead of"):
|
|
461
|
+
return _invalid_directive_syntax(
|
|
462
|
+
DirectiveSyntaxFailure.MISSING_REQUIRED_OPERAND,
|
|
463
|
+
directive_kind=DirectiveKind.REPLACE_USE,
|
|
464
|
+
missing_operand="old_item",
|
|
465
|
+
)
|
|
466
|
+
if _INSTEAD_OF_DELIMITER in normalized_item:
|
|
467
|
+
return _invalid_directive_syntax(
|
|
468
|
+
DirectiveSyntaxFailure.MALFORMED_DIRECTIVE,
|
|
469
|
+
directive_kind=DirectiveKind.USE_ITEM,
|
|
470
|
+
)
|
|
393
471
|
return CanonicalDirective(
|
|
394
472
|
text=text,
|
|
395
|
-
kind=
|
|
473
|
+
kind=DirectiveKind.USE_ITEM,
|
|
396
474
|
operands=MappingProxyType({"item": item}),
|
|
397
475
|
)
|
|
398
476
|
|
|
477
|
+
if normalized == "prohibit":
|
|
478
|
+
return _invalid_directive_syntax(
|
|
479
|
+
DirectiveSyntaxFailure.MISSING_REQUIRED_OPERAND,
|
|
480
|
+
directive_kind=DirectiveKind.PROHIBIT_ITEM,
|
|
481
|
+
missing_operand="item",
|
|
482
|
+
)
|
|
483
|
+
|
|
399
484
|
if normalized.startswith("prohibit "):
|
|
400
485
|
match = _PROHIBIT_RE.fullmatch(trimmed_text)
|
|
401
486
|
if match is None:
|
|
402
|
-
return
|
|
487
|
+
return _invalid_directive_syntax(
|
|
488
|
+
DirectiveSyntaxFailure.MISSING_REQUIRED_OPERAND,
|
|
489
|
+
directive_kind=DirectiveKind.PROHIBIT_ITEM,
|
|
490
|
+
missing_operand="item",
|
|
491
|
+
)
|
|
403
492
|
item = match.group("item")
|
|
404
493
|
if not _operand_has_content(item):
|
|
405
|
-
return
|
|
494
|
+
return _invalid_directive_syntax(
|
|
495
|
+
DirectiveSyntaxFailure.MISSING_REQUIRED_OPERAND,
|
|
496
|
+
directive_kind=DirectiveKind.PROHIBIT_ITEM,
|
|
497
|
+
missing_operand="item",
|
|
498
|
+
)
|
|
406
499
|
return CanonicalDirective(
|
|
407
500
|
text=text,
|
|
408
|
-
kind=
|
|
501
|
+
kind=DirectiveKind.PROHIBIT_ITEM,
|
|
409
502
|
operands=MappingProxyType({"item": item}),
|
|
410
503
|
)
|
|
411
504
|
|
|
505
|
+
if normalized == "remove policy":
|
|
506
|
+
return _invalid_directive_syntax(
|
|
507
|
+
DirectiveSyntaxFailure.MISSING_REQUIRED_OPERAND,
|
|
508
|
+
directive_kind=DirectiveKind.REMOVE_POLICY,
|
|
509
|
+
missing_operand="item",
|
|
510
|
+
)
|
|
511
|
+
|
|
412
512
|
if normalized.startswith("remove policy "):
|
|
413
513
|
match = _REMOVE_POLICY_RE.fullmatch(trimmed_text)
|
|
414
514
|
if match is None:
|
|
415
|
-
return
|
|
515
|
+
return _invalid_directive_syntax(
|
|
516
|
+
DirectiveSyntaxFailure.MISSING_REQUIRED_OPERAND,
|
|
517
|
+
directive_kind=DirectiveKind.REMOVE_POLICY,
|
|
518
|
+
missing_operand="item",
|
|
519
|
+
)
|
|
416
520
|
item = match.group("item")
|
|
417
521
|
if not _operand_has_content(item):
|
|
418
|
-
return
|
|
522
|
+
return _invalid_directive_syntax(
|
|
523
|
+
DirectiveSyntaxFailure.MISSING_REQUIRED_OPERAND,
|
|
524
|
+
directive_kind=DirectiveKind.REMOVE_POLICY,
|
|
525
|
+
missing_operand="item",
|
|
526
|
+
)
|
|
419
527
|
return CanonicalDirective(
|
|
420
528
|
text=text,
|
|
421
|
-
kind=
|
|
529
|
+
kind=DirectiveKind.REMOVE_POLICY,
|
|
422
530
|
operands=MappingProxyType({"item": item}),
|
|
423
531
|
)
|
|
424
532
|
|
|
425
|
-
return
|
|
533
|
+
return _invalid_directive_syntax(DirectiveSyntaxFailure.MALFORMED_DIRECTIVE)
|
|
426
534
|
|
|
427
535
|
|
|
428
|
-
def _render_directive(kind:
|
|
536
|
+
def _render_directive(kind: DirectiveKind | str, /, **operands: str) -> str:
|
|
429
537
|
"""Produce canonical directive text from a semantic kind and operands."""
|
|
430
538
|
try:
|
|
431
|
-
normalized_kind = kind if isinstance(kind,
|
|
539
|
+
normalized_kind = kind if isinstance(kind, DirectiveKind) else DirectiveKind(kind)
|
|
432
540
|
spec = _DIRECTIVE_SPECS[normalized_kind]
|
|
433
541
|
except (KeyError, ValueError) as exc:
|
|
434
542
|
raise ValueError(f"Unsupported directive kind: {kind!r}") from exc
|
|
@@ -462,6 +570,8 @@ def _render_directive(kind: _DirectiveKind | str, /, **operands: str) -> str:
|
|
|
462
570
|
|
|
463
571
|
|
|
464
572
|
__all__ = [
|
|
573
|
+
"DirectiveKind",
|
|
574
|
+
"DirectiveSyntaxFailure",
|
|
465
575
|
"CanonicalDirective",
|
|
466
576
|
"InvalidDirectiveSyntax",
|
|
467
577
|
"decompose_directive",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{context_compiler-0.9.0.dev8 → context_compiler-0.9.0.dev9}/demos/01_llm_contradiction_error.py
RENAMED
|
File without changes
|
{context_compiler-0.9.0.dev8 → context_compiler-0.9.0.dev9}/demos/02_llm_constraint_guardrail.py
RENAMED
|
File without changes
|
{context_compiler-0.9.0.dev8 → context_compiler-0.9.0.dev9}/demos/03_llm_premise_guardrail.py
RENAMED
|
File without changes
|
{context_compiler-0.9.0.dev8 → context_compiler-0.9.0.dev9}/demos/04_llm_tool_denylist_guardrail.py
RENAMED
|
File without changes
|
{context_compiler-0.9.0.dev8 → context_compiler-0.9.0.dev9}/demos/05_llm_prompt_drift_vs_state.py
RENAMED
|
File without changes
|
{context_compiler-0.9.0.dev8 → context_compiler-0.9.0.dev9}/demos/06_llm_context_compaction.py
RENAMED
|
File without changes
|
|
File without changes
|
{context_compiler-0.9.0.dev8 → context_compiler-0.9.0.dev9}/demos/08_llm_replacement_precondition.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.9.0.dev8 → context_compiler-0.9.0.dev9}/examples/01_persistent_guardrails.py
RENAMED
|
File without changes
|
|
File without changes
|
{context_compiler-0.9.0.dev8 → context_compiler-0.9.0.dev9}/examples/03_ambiguity_with_error.py
RENAMED
|
File without changes
|
{context_compiler-0.9.0.dev8 → context_compiler-0.9.0.dev9}/examples/04_tool_governance_denylist.py
RENAMED
|
File without changes
|
{context_compiler-0.9.0.dev8 → context_compiler-0.9.0.dev9}/examples/05_llm_integration_pattern.py
RENAMED
|
File without changes
|
|
File without changes
|
{context_compiler-0.9.0.dev8 → context_compiler-0.9.0.dev9}/examples/07_single_policy_correction.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{context_compiler-0.9.0.dev8 → context_compiler-0.9.0.dev9}/src/context_compiler/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{context_compiler-0.9.0.dev8 → context_compiler-0.9.0.dev9}/src/context_compiler/decision_helpers.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|