Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 8 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
[![License](https://img.shields.io/pypi/l/context-compiler)](https://pypi.org/project/context-compiler/)
[![codecov](https://codecov.io/gh/rlippmann/context-compiler/branch/main/graph/badge.svg)](https://codecov.io/gh/rlippmann/context-compiler)

Context Compiler helps LLM applications keep explicit premise and policy rules
stable across turns. It blocks invalid or conflicting changes and returns
structured decisions.
Context Compiler maintains host-side working memory consisting of a premise and
policies, separate from the model's conversational context. The Engine applies
directives to that working memory so applications can maintain explicit state
across model interactions without relying on the model to remember or infer it.
It blocks invalid or conflicting changes and returns structured decisions.

Use it when saved context and policy rules need to shape what an application
does, not just what the model sees or says.
Expand Down Expand Up @@ -90,7 +92,7 @@ repair behavior.

## State Model

The engine stores explicit user commitments as saved state:
Working memory contains explicit user commitments:

| State | Meaning |
| --- | --- |
Expand Down Expand Up @@ -120,8 +122,8 @@ To replace an existing `use` policy:
use podman instead of docker
```

If `docker` is absent from saved state, the replacement fails and leaves state
unchanged. It does not become plain `use podman`.
If `docker` is absent from working memory, the replacement fails and leaves
working memory unchanged. It does not become plain `use podman`.

To remove a policy or clear state:

Expand Down
30 changes: 17 additions & 13 deletions src/context_compiler/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@
PolicyValue = Literal["use", "prohibit"]


class _State(TypedDict):
"""Versioned authoritative state."""
class _WorkingMemory(TypedDict):
"""Versioned internal working memory."""

premise: str | None
policies: dict[str, PolicyValue]
Expand All @@ -37,7 +37,7 @@ class _State(TypedDict):

class _EvaluatedTransition(TypedDict):
decision: UpdateDecision | SemanticErrorDecision
next_state: _State
next_state: _WorkingMemory


_NO_DIRECTIVE = NoDirectiveDecision()
Expand All @@ -49,7 +49,7 @@ class Engine:
__slots__ = ("_state",)

def __init__(self) -> None:
self._state: _State
self._state: _WorkingMemory
self._replace_state(_initial_state())

@property
Expand Down Expand Up @@ -105,7 +105,7 @@ def apply_directive(
return evaluated["decision"]

def _evaluate_directive_transition(
self, state: _State, directive: CanonicalDirective
self, state: _WorkingMemory, directive: CanonicalDirective
) -> _EvaluatedTransition:
error_decision = self._pre_mutation_error(directive, state=state)
if error_decision is not None:
Expand All @@ -117,11 +117,11 @@ def _evaluate_directive_transition(
"next_state": next_state,
}

def _replace_state(self, state: _State) -> None:
def _replace_state(self, state: _WorkingMemory) -> None:
self._state = state

def _pre_mutation_error(
self, directive: CanonicalDirective, *, state: _State | None = None
self, directive: CanonicalDirective, *, state: _WorkingMemory | None = None
) -> SemanticErrorDecision | None:
candidate_state = self._state if state is None else state
# Single error path: all error outcomes are detected before any mutation.
Expand Down Expand Up @@ -203,7 +203,9 @@ def _pre_mutation_error(

return None

def _apply_directive(self, directive: CanonicalDirective, *, state: _State) -> _State:
def _apply_directive(
self, directive: CanonicalDirective, *, state: _WorkingMemory
) -> _WorkingMemory:
next_state = deepcopy(state)

if directive.kind is DirectiveKind.SET_PREMISE:
Expand Down Expand Up @@ -249,7 +251,9 @@ def _apply_directive(self, directive: CanonicalDirective, *, state: _State) -> _

return _initial_state()

def _apply_replacement_explicit(self, state: _State, new_item: str, old_item: str) -> None:
def _apply_replacement_explicit(
self, state: _WorkingMemory, new_item: str, old_item: str
) -> None:
new_key = _normalize_item(new_item)
old_key = _normalize_item(old_item)

Expand All @@ -260,15 +264,15 @@ def _apply_replacement_explicit(self, state: _State, new_item: str, old_item: st
state[STATE_POLICIES][new_key] = POLICY_USE


def _initial_state() -> _State:
def _initial_state() -> _WorkingMemory:
return {
STATE_PREMISE: None,
STATE_POLICIES: {},
STATE_VERSION: SCHEMA_VERSION,
}


def _load_state_json(payload: str) -> _State:
def _load_state_json(payload: str) -> _WorkingMemory:
try:
raw = json.loads(payload)
except json.JSONDecodeError as exc:
Expand All @@ -277,7 +281,7 @@ def _load_state_json(payload: str) -> _State:
return _load_state_obj(raw)


def _load_state_obj(raw: object) -> _State:
def _load_state_obj(raw: object) -> _WorkingMemory:
if not isinstance(raw, dict):
raise ValueError("Invalid state payload.")

Expand Down Expand Up @@ -379,5 +383,5 @@ def _repair_set_premise(value: str) -> CanonicalDirective:
)


def _update_decision(previous_state: _State, next_state: _State) -> UpdateDecision:
def _update_decision(previous_state: _WorkingMemory, next_state: _WorkingMemory) -> UpdateDecision:
return UpdateDecision(changed=previous_state != next_state)
Loading