context-compiler-example-integrations 0.1.1__tar.gz → 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/PKG-INFO +15 -15
  2. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/pyproject.toml +5 -5
  3. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/python/README.md +10 -10
  4. context_compiler_example_integrations-0.2.0/python/examples/checkpoint_continuation/README.md +65 -0
  5. context_compiler_example_integrations-0.2.0/python/examples/checkpoint_continuation/example.py +163 -0
  6. context_compiler_example_integrations-0.2.0/python/examples/checkpoint_continuation/fastapi/README.md +65 -0
  7. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/python/examples/checkpoint_continuation/fastapi/app.py +65 -69
  8. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/python/examples/execution_authorization/expense_approval/README.md +4 -4
  9. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/python/examples/execution_authorization/expense_approval/example.py +39 -40
  10. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/python/examples/execution_authorization/expense_approval/fastapi/README.md +3 -3
  11. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/python/examples/execution_authorization/expense_approval/fastapi/app.py +58 -35
  12. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/python/examples/gateway_middleware/customer_support_routing/example.py +35 -40
  13. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/python/examples/prompt_construction/litellm/README.md +32 -23
  14. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/python/examples/prompt_construction/litellm/basic.py +65 -120
  15. context_compiler_example_integrations-0.2.0/python/examples/prompt_construction/litellm/with_directive_drafter.py +371 -0
  16. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/python/examples/prompt_construction/writing_assistant/README.md +1 -1
  17. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/python/examples/prompt_construction/writing_assistant/example.py +39 -43
  18. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/python/examples/retrieval_filtering/chromadb_hr_policy_lookup/README.md +2 -3
  19. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/python/examples/retrieval_filtering/chromadb_hr_policy_lookup/example.py +41 -45
  20. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/python/examples/retrieval_filtering/hr_policy_lookup/README.md +1 -1
  21. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/python/examples/retrieval_filtering/hr_policy_lookup/example.py +47 -48
  22. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/python/examples/schema_selection/litellm_response_format/response_format.py +15 -23
  23. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/python/examples/schema_selection/ollama_structured_output/README.md +1 -1
  24. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/python/examples/schema_selection/ollama_structured_output/example.py +15 -23
  25. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/python/examples/schema_selection/refund_intake/example.py +13 -12
  26. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/python/examples/tool_gating/calendar_admin/example.py +40 -43
  27. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/python/examples/tool_gating/mcp_calendar_admin/README.md +1 -1
  28. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/python/examples/tool_gating/mcp_calendar_admin/example.py +49 -54
  29. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/python/examples/tool_gating/mcp_calendar_admin/live_model.py +65 -33
  30. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/python/reference_integrations/litellm_proxy/README.md +17 -16
  31. context_compiler_example_integrations-0.2.0/python/reference_integrations/litellm_proxy/_litellm_support.py +34 -0
  32. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/python/reference_integrations/litellm_proxy/context_compiler_precall_hook.py +20 -56
  33. context_compiler_example_integrations-0.2.0/python/reference_integrations/litellm_proxy/context_compiler_precall_hook_with_directive_drafter.py +184 -0
  34. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/python/reference_integrations/openwebui_pipe/README.md +57 -38
  35. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/python/reference_integrations/openwebui_pipe/open_webui_pipe.py +115 -136
  36. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/python/reference_integrations/openwebui_pipe/open_webui_pipe_with_directive_drafter.py +276 -368
  37. context_compiler_example_integrations-0.1.1/python/examples/checkpoint_continuation/README.md +0 -77
  38. context_compiler_example_integrations-0.1.1/python/examples/checkpoint_continuation/example.py +0 -169
  39. context_compiler_example_integrations-0.1.1/python/examples/checkpoint_continuation/fastapi/README.md +0 -85
  40. context_compiler_example_integrations-0.1.1/python/examples/prompt_construction/litellm/confirmation_helper.py +0 -91
  41. context_compiler_example_integrations-0.1.1/python/examples/prompt_construction/litellm/with_directive_drafter.py +0 -524
  42. context_compiler_example_integrations-0.1.1/python/reference_integrations/litellm_proxy/context_compiler_precall_hook_with_directive_drafter.py +0 -289
  43. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/.gitignore +0 -0
  44. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/LICENSE +0 -0
  45. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/context_compiler_example_integrations/__init__.py +0 -0
  46. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/context_compiler_example_integrations/examples/__init__.py +0 -0
  47. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/context_compiler_example_integrations/reference_integrations/__init__.py +0 -0
  48. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/python/examples/_shared/__init__.py +0 -0
  49. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/python/examples/_shared/litellm_request.py +0 -0
  50. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/python/examples/_shared/provider_mode.py +0 -0
  51. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/python/examples/execution_authorization/README.md +0 -0
  52. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/python/examples/execution_authorization/expense_approval/__init__.py +0 -0
  53. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/python/examples/execution_authorization/expense_approval/fastapi/__init__.py +0 -0
  54. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/python/examples/execution_authorization/expense_approval/fastapi/model_approval.py +0 -0
  55. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/python/examples/gateway_middleware/README.md +0 -0
  56. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/python/examples/gateway_middleware/customer_support_routing/README.md +0 -0
  57. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/python/examples/gateway_middleware/customer_support_routing/__init__.py +0 -0
  58. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/python/examples/prompt_construction/README.md +0 -0
  59. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/python/examples/prompt_construction/writing_assistant/__init__.py +0 -0
  60. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/python/examples/retrieval_filtering/README.md +0 -0
  61. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/python/examples/retrieval_filtering/chromadb_hr_policy_lookup/__init__.py +0 -0
  62. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/python/examples/retrieval_filtering/hr_policy_lookup/__init__.py +0 -0
  63. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/python/examples/schema_selection/README.md +0 -0
  64. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/python/examples/schema_selection/refund_intake/README.md +0 -0
  65. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/python/examples/schema_selection/refund_intake/__init__.py +0 -0
  66. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/python/examples/tool_gating/README.md +0 -0
  67. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/python/examples/tool_gating/calendar_admin/README.md +0 -0
  68. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/python/examples/tool_gating/calendar_admin/__init__.py +0 -0
  69. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/python/examples/tool_gating/mcp_calendar_admin/__init__.py +0 -0
  70. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/python/reference_integrations/litellm_proxy/_checkpoint_support.py +0 -0
  71. {context_compiler_example_integrations-0.1.1 → context_compiler_example_integrations-0.2.0}/python/reference_integrations/litellm_proxy/config.example.yaml +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: context-compiler-example-integrations
3
- Version: 0.1.1
3
+ Version: 0.2.0
4
4
  Summary: Example integrations and enforcement-point demonstrations for Context Compiler.
5
5
  Project-URL: Homepage, https://github.com/rlippmann/context-compiler-example-integrations
6
6
  Project-URL: Repository, https://github.com/rlippmann/context-compiler-example-integrations
@@ -21,14 +21,14 @@ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
21
21
  Classifier: Topic :: Software Development :: Libraries
22
22
  Classifier: Topic :: Software Development :: Testing
23
23
  Requires-Python: >=3.11
24
- Requires-Dist: context-compiler>=0.8.3
24
+ Requires-Dist: context-compiler<0.10,>=0.9.1
25
25
  Provides-Extra: all
26
26
  Requires-Dist: chromadb; extra == 'all'
27
- Requires-Dist: context-compiler-directive-drafter>=0.1.2; extra == 'all'
27
+ Requires-Dist: context-compiler-directive-drafter<0.3,>=0.2.0; extra == 'all'
28
28
  Requires-Dist: fastapi; extra == 'all'
29
29
  Requires-Dist: litellm; extra == 'all'
30
30
  Provides-Extra: drafter
31
- Requires-Dist: context-compiler-directive-drafter>=0.1.2; extra == 'drafter'
31
+ Requires-Dist: context-compiler-directive-drafter<0.3,>=0.2.0; extra == 'drafter'
32
32
  Provides-Extra: fastapi
33
33
  Requires-Dist: fastapi; extra == 'fastapi'
34
34
  Provides-Extra: litellm
@@ -61,7 +61,7 @@ pip install "context-compiler-example-integrations"
61
61
 
62
62
  That installs the shared core dependency only:
63
63
 
64
- - `context-compiler>=0.8.3`
64
+ - `context-compiler>=0.9.1,<0.10`
65
65
 
66
66
  Add extras only for the examples you want to inspect locally:
67
67
 
@@ -77,13 +77,13 @@ runtime.
77
77
 
78
78
  ## Generic examples
79
79
 
80
- - [Checkpoint continuation](examples/checkpoint_continuation/README.md): persisted confirmation and resume flows change host behavior across turns or requests
81
- - [Execution authorization](examples/execution_authorization/README.md): protected host actions execute only when authoritative state allows them
82
- - [Gateway middleware](examples/gateway_middleware/README.md): the host allows, blocks, or routes requests before downstream work runs
83
- - [Prompt construction](examples/prompt_construction/README.md): the host builds different request or prompt payloads from authoritative state
84
- - [Retrieval filtering](examples/retrieval_filtering/README.md): the host changes which documents are eligible or relevant before returning results
85
- - [Schema selection](examples/schema_selection/README.md): the host picks different workflow or response schemas from authoritative state
86
- - [Tool gating](examples/tool_gating/README.md): the host changes which tools are visible or executable at runtime
80
+ - [Checkpoint continuation](https://github.com/rlippmann/context-compiler-example-integrations/blob/main/python/examples/checkpoint_continuation/README.md): persisted authoritative state changes host behavior across turns or requests
81
+ - [Execution authorization](https://github.com/rlippmann/context-compiler-example-integrations/blob/main/python/examples/execution_authorization/README.md): protected host actions execute only when authoritative state allows them
82
+ - [Gateway middleware](https://github.com/rlippmann/context-compiler-example-integrations/blob/main/python/examples/gateway_middleware/README.md): the host allows, blocks, or routes requests before downstream work runs
83
+ - [Prompt construction](https://github.com/rlippmann/context-compiler-example-integrations/blob/main/python/examples/prompt_construction/README.md): the host builds different request or prompt payloads from authoritative state
84
+ - [Retrieval filtering](https://github.com/rlippmann/context-compiler-example-integrations/blob/main/python/examples/retrieval_filtering/README.md): the host changes which documents are eligible or relevant before returning results
85
+ - [Schema selection](https://github.com/rlippmann/context-compiler-example-integrations/blob/main/python/examples/schema_selection/README.md): the host picks different workflow or response schemas from authoritative state
86
+ - [Tool gating](https://github.com/rlippmann/context-compiler-example-integrations/blob/main/python/examples/tool_gating/README.md): the host changes which tools are visible or executable at runtime
87
87
 
88
88
  ## Reference integrations
89
89
 
@@ -96,8 +96,8 @@ behavior on a specific host or framework surface.
96
96
  Start with the generic example first, then use the Python reference
97
97
  integrations to inspect a runtime-specific path:
98
98
 
99
- - [python/reference_integrations/litellm_proxy/README.md](reference_integrations/litellm_proxy/README.md)
100
- - [python/reference_integrations/openwebui_pipe/README.md](reference_integrations/openwebui_pipe/README.md)
99
+ - [LiteLLM Proxy reference integration](https://github.com/rlippmann/context-compiler-example-integrations/blob/main/python/reference_integrations/litellm_proxy/README.md)
100
+ - [Open WebUI pipe reference integration](https://github.com/rlippmann/context-compiler-example-integrations/blob/main/python/reference_integrations/openwebui_pipe/README.md)
101
101
 
102
102
  ## Run an example
103
103
 
@@ -4,14 +4,14 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "context-compiler-example-integrations"
7
- version = "0.1.1"
7
+ version = "0.2.0"
8
8
  description = "Example integrations and enforcement-point demonstrations for Context Compiler."
9
9
  readme = "python/README.md"
10
10
  requires-python = ">=3.11"
11
11
  license = { text = "Apache-2.0" }
12
12
  authors = [{ name = "Robert Lippmann" }]
13
13
  dependencies = [
14
- "context-compiler>=0.8.3",
14
+ "context-compiler>=0.9.1,<0.10",
15
15
  ]
16
16
  keywords = [
17
17
  "context-compiler",
@@ -47,12 +47,12 @@ Issues = "https://github.com/rlippmann/context-compiler-example-integrations/iss
47
47
  [project.optional-dependencies]
48
48
  all = [
49
49
  "chromadb",
50
- "context-compiler-directive-drafter>=0.1.2",
50
+ "context-compiler-directive-drafter>=0.2.0,<0.3",
51
51
  "fastapi",
52
52
  "litellm",
53
53
  ]
54
54
  drafter = [
55
- "context-compiler-directive-drafter>=0.1.2",
55
+ "context-compiler-directive-drafter>=0.2.0,<0.3",
56
56
  ]
57
57
  fastapi = [
58
58
  "fastapi",
@@ -67,7 +67,7 @@ retrieval = [
67
67
  [dependency-groups]
68
68
  dev = [
69
69
  "chromadb",
70
- "context-compiler-directive-drafter>=0.1.2",
70
+ "context-compiler-directive-drafter>=0.2.0,<0.3",
71
71
  "fastapi",
72
72
  "httpx2>=2.5.0",
73
73
  "httpx>=0.28.1",
@@ -22,7 +22,7 @@ pip install "context-compiler-example-integrations"
22
22
 
23
23
  That installs the shared core dependency only:
24
24
 
25
- - `context-compiler>=0.8.3`
25
+ - `context-compiler>=0.9.1,<0.10`
26
26
 
27
27
  Add extras only for the examples you want to inspect locally:
28
28
 
@@ -38,13 +38,13 @@ runtime.
38
38
 
39
39
  ## Generic examples
40
40
 
41
- - [Checkpoint continuation](examples/checkpoint_continuation/README.md): persisted confirmation and resume flows change host behavior across turns or requests
42
- - [Execution authorization](examples/execution_authorization/README.md): protected host actions execute only when authoritative state allows them
43
- - [Gateway middleware](examples/gateway_middleware/README.md): the host allows, blocks, or routes requests before downstream work runs
44
- - [Prompt construction](examples/prompt_construction/README.md): the host builds different request or prompt payloads from authoritative state
45
- - [Retrieval filtering](examples/retrieval_filtering/README.md): the host changes which documents are eligible or relevant before returning results
46
- - [Schema selection](examples/schema_selection/README.md): the host picks different workflow or response schemas from authoritative state
47
- - [Tool gating](examples/tool_gating/README.md): the host changes which tools are visible or executable at runtime
41
+ - [Checkpoint continuation](https://github.com/rlippmann/context-compiler-example-integrations/blob/main/python/examples/checkpoint_continuation/README.md): persisted authoritative state changes host behavior across turns or requests
42
+ - [Execution authorization](https://github.com/rlippmann/context-compiler-example-integrations/blob/main/python/examples/execution_authorization/README.md): protected host actions execute only when authoritative state allows them
43
+ - [Gateway middleware](https://github.com/rlippmann/context-compiler-example-integrations/blob/main/python/examples/gateway_middleware/README.md): the host allows, blocks, or routes requests before downstream work runs
44
+ - [Prompt construction](https://github.com/rlippmann/context-compiler-example-integrations/blob/main/python/examples/prompt_construction/README.md): the host builds different request or prompt payloads from authoritative state
45
+ - [Retrieval filtering](https://github.com/rlippmann/context-compiler-example-integrations/blob/main/python/examples/retrieval_filtering/README.md): the host changes which documents are eligible or relevant before returning results
46
+ - [Schema selection](https://github.com/rlippmann/context-compiler-example-integrations/blob/main/python/examples/schema_selection/README.md): the host picks different workflow or response schemas from authoritative state
47
+ - [Tool gating](https://github.com/rlippmann/context-compiler-example-integrations/blob/main/python/examples/tool_gating/README.md): the host changes which tools are visible or executable at runtime
48
48
 
49
49
  ## Reference integrations
50
50
 
@@ -57,8 +57,8 @@ behavior on a specific host or framework surface.
57
57
  Start with the generic example first, then use the Python reference
58
58
  integrations to inspect a runtime-specific path:
59
59
 
60
- - [python/reference_integrations/litellm_proxy/README.md](reference_integrations/litellm_proxy/README.md)
61
- - [python/reference_integrations/openwebui_pipe/README.md](reference_integrations/openwebui_pipe/README.md)
60
+ - [LiteLLM Proxy reference integration](https://github.com/rlippmann/context-compiler-example-integrations/blob/main/python/reference_integrations/litellm_proxy/README.md)
61
+ - [Open WebUI pipe reference integration](https://github.com/rlippmann/context-compiler-example-integrations/blob/main/python/reference_integrations/openwebui_pipe/README.md)
62
62
 
63
63
  ## Run an example
64
64
 
@@ -0,0 +1,65 @@
1
+ # State Persistence
2
+
3
+ Persisting authoritative compiler state lets a fresh host process recover the
4
+ same premise and policy decisions without recreating them from model output or
5
+ conversation history. This example shows state persistence in a generic Python
6
+ travel-booking flow.
7
+
8
+ ## Domain
9
+
10
+ The domain is a small travel-booking change flow.
11
+
12
+ The user selects a new itinerary, the compiler records that selection in
13
+ authoritative state, and the host later applies the booking change from a
14
+ restored engine.
15
+
16
+ ## Runtime
17
+
18
+ This is a generic Python example.
19
+
20
+ It does not call an LLM.
21
+
22
+ It does not use directive drafter.
23
+
24
+ ## What Context Compiler owns
25
+
26
+ Context Compiler owns:
27
+
28
+ - authoritative policy state
29
+ - serialization of that state through `export_json()`
30
+ - restoration of that state through `import_json()`
31
+
32
+ ## What the host owns
33
+
34
+ The host owns:
35
+
36
+ - the booking record
37
+ - persisted state storage
38
+ - process boundaries
39
+ - the runtime behavior that actually applies the itinerary change
40
+
41
+ The host reads restored authoritative Context Compiler state and decides whether
42
+ to apply the booking change.
43
+
44
+ ## Example behavior
45
+
46
+ 1. The host starts with a booking on `boston_trip`.
47
+ 2. The user selects `chicago_trip`.
48
+ 3. Context Compiler updates authoritative state.
49
+ 4. The host persists that state JSON.
50
+ 5. A fresh host process restores the saved state into a new engine.
51
+ 6. The host applies the booking change from the restored authoritative state.
52
+
53
+ ## Run
54
+
55
+ From the repository root:
56
+
57
+ ```bash
58
+ uv run python python/examples/checkpoint_continuation/example.py
59
+ uv run pytest python/tests/test_checkpoint_continuation_example.py
60
+ ```
61
+
62
+ ## FastAPI variant
63
+
64
+ For a request-boundary example, see
65
+ [python/examples/checkpoint_continuation/fastapi/README.md](fastapi/README.md).
@@ -0,0 +1,163 @@
1
+ """Minimal persistence example for a travel booking change."""
2
+
3
+ from collections.abc import Mapping
4
+ from dataclasses import dataclass, field
5
+ from typing import Literal, TypedDict
6
+
7
+ from context_compiler import Decision, DecisionKind, Engine, POLICY_USE, PolicyValue
8
+
9
+
10
+ class BookingRecord(TypedDict):
11
+ booking_id: str
12
+ active_itinerary: str
13
+
14
+
15
+ class BookingChangeRuntimeResult(TypedDict):
16
+ compiler_input: str
17
+ decision_kind: Literal["error", "update", "passthrough"]
18
+ message_to_user: str | None
19
+ persisted_state_json: str
20
+ selected_itinerary: str | None
21
+ host_applied_change: bool
22
+ active_itinerary: str
23
+
24
+
25
+ @dataclass
26
+ class EnginePersistenceStore:
27
+ """Host-owned persistence for serialized authoritative compiler state."""
28
+
29
+ saved_state_json: str | None = None
30
+
31
+ def save(self, state_json: str) -> None:
32
+ self.saved_state_json = state_json
33
+
34
+ def load(self) -> str:
35
+ if self.saved_state_json is None:
36
+ raise ValueError("no saved state")
37
+ return self.saved_state_json
38
+
39
+
40
+ @dataclass
41
+ class BookingHost:
42
+ """Host-owned runtime behavior for the booking example."""
43
+
44
+ booking: BookingRecord
45
+ applied_changes: list[str] = field(default_factory=list)
46
+
47
+ def apply_selected_itinerary(self, policies: Mapping[str, PolicyValue]) -> bool:
48
+ selected_itinerary = select_itinerary_from_policies(policies)
49
+ if selected_itinerary is None:
50
+ return False
51
+
52
+ self.booking["active_itinerary"] = selected_itinerary
53
+ self.applied_changes.append(selected_itinerary)
54
+ return True
55
+
56
+
57
+ def select_itinerary_from_policies(policies: Mapping[str, PolicyValue]) -> str | None:
58
+ """Select the host-visible itinerary from authoritative state."""
59
+
60
+ for item, kind in policies.items():
61
+ if kind == POLICY_USE:
62
+ return item
63
+ return None
64
+
65
+
66
+ def _decision_kind_name(
67
+ decision: Decision,
68
+ ) -> Literal["error", "update", "passthrough"]:
69
+ kind = decision.kind
70
+ if kind == DecisionKind.ERROR:
71
+ return "error"
72
+ if kind == DecisionKind.UPDATE:
73
+ return "update"
74
+ if kind == DecisionKind.NO_DIRECTIVE:
75
+ return "passthrough"
76
+ raise ValueError(f"unexpected decision kind: {kind}")
77
+
78
+
79
+ def persist_itinerary_selection(
80
+ engine: Engine,
81
+ *,
82
+ requested_itinerary: str,
83
+ ) -> BookingChangeRuntimeResult:
84
+ """Persist authoritative state after selecting an itinerary."""
85
+
86
+ compiler_input = f"use {requested_itinerary}"
87
+ decision = engine.step(compiler_input)
88
+ persisted_state_json = engine.export_json()
89
+ selected_itinerary = select_itinerary_from_policies(engine.policies)
90
+
91
+ return {
92
+ "compiler_input": compiler_input,
93
+ "decision_kind": _decision_kind_name(decision),
94
+ "message_to_user": decision.message
95
+ if decision.kind == DecisionKind.ERROR
96
+ else None,
97
+ "persisted_state_json": persisted_state_json,
98
+ "selected_itinerary": selected_itinerary,
99
+ "host_applied_change": False,
100
+ "active_itinerary": requested_itinerary
101
+ if selected_itinerary is not None
102
+ else "boston_trip",
103
+ }
104
+
105
+
106
+ def restore_engine_from_persisted_state(state_json: str) -> Engine:
107
+ """Restore authoritative compiler state into a fresh engine."""
108
+
109
+ engine = Engine()
110
+ engine.import_json(state_json)
111
+ return engine
112
+
113
+
114
+ def apply_restored_itinerary(
115
+ engine: Engine, host: BookingHost
116
+ ) -> BookingChangeRuntimeResult:
117
+ """Apply host behavior from restored authoritative compiler state."""
118
+
119
+ host_applied_change = host.apply_selected_itinerary(engine.policies)
120
+ selected_itinerary = select_itinerary_from_policies(engine.policies)
121
+
122
+ return {
123
+ "compiler_input": "",
124
+ "decision_kind": "update" if host_applied_change else "passthrough",
125
+ "message_to_user": None,
126
+ "persisted_state_json": engine.export_json(),
127
+ "selected_itinerary": selected_itinerary,
128
+ "host_applied_change": host_applied_change,
129
+ "active_itinerary": host.booking["active_itinerary"],
130
+ }
131
+
132
+
133
+ def run_demo() -> dict[str, BookingChangeRuntimeResult | str]:
134
+ """Run a deterministic persistence demonstration."""
135
+
136
+ initial_booking: BookingRecord = {
137
+ "booking_id": "booking-100",
138
+ "active_itinerary": "boston_trip",
139
+ }
140
+ first_engine = Engine()
141
+ engine_persistence_store = EnginePersistenceStore()
142
+
143
+ persisted_result = persist_itinerary_selection(
144
+ first_engine,
145
+ requested_itinerary="chicago_trip",
146
+ )
147
+ engine_persistence_store.save(persisted_result["persisted_state_json"])
148
+
149
+ restored_engine = restore_engine_from_persisted_state(
150
+ engine_persistence_store.load()
151
+ )
152
+ restored_host = BookingHost(booking=initial_booking.copy())
153
+ applied_result = apply_restored_itinerary(restored_engine, restored_host)
154
+
155
+ return {
156
+ "persisted_result": persisted_result,
157
+ "applied_result": applied_result,
158
+ "saved_state_json": engine_persistence_store.load(),
159
+ }
160
+
161
+
162
+ if __name__ == "__main__":
163
+ print(run_demo())
@@ -0,0 +1,65 @@
1
+ # State Persistence with FastAPI
2
+
3
+ Saved authoritative compiler state lets later HTTP requests recover the same
4
+ policy decisions instead of starting over. This example shows state
5
+ persistence across stateless HTTP request boundaries.
6
+
7
+ ## Enforcement Point
8
+
9
+ Authoritative state persistence
10
+
11
+ ## Domain
12
+
13
+ The domain is a small travel-booking change flow.
14
+
15
+ The first request selects `chicago_trip` in compiler state. A later request
16
+ restores that saved state and lets the host apply the booking change.
17
+
18
+ ## Runtime
19
+
20
+ This is a small FastAPI example.
21
+
22
+ FastAPI is secondary to the enforcement point.
23
+
24
+ It exists to show that the host can persist authoritative state between
25
+ separate HTTP requests and restore it later into a fresh engine.
26
+
27
+ ## Ownership Boundary
28
+
29
+ Context Compiler owns:
30
+
31
+ - authoritative policy state
32
+ - state export through `export_json()`
33
+ - state restore through `import_json()`
34
+
35
+ The host owns:
36
+
37
+ - persisted state storage
38
+ - request routing
39
+ - booking mutation
40
+
41
+ In this example, the host creates a fresh engine per request.
42
+
43
+ The second request applies the saved itinerary only because the host restores
44
+ the persisted authoritative state, not because the process remembered a
45
+ conversation.
46
+
47
+ ## Endpoints
48
+
49
+ - `POST /change-trip`
50
+ - updates authoritative state with `use chicago_trip`
51
+ - persists the resulting state JSON in the host store
52
+ - `POST /apply-trip`
53
+ - restores the saved state JSON into a fresh engine
54
+ - applies the booking change from restored policy state
55
+ - `GET /booking`
56
+ - returns the host-owned booking state
57
+
58
+ ## Validate
59
+
60
+ From the repository root:
61
+
62
+ ```bash
63
+ uv run pytest python/tests/test_fastapi_checkpoint_continuation_example.py
64
+ uv run pre-commit run --all-files
65
+ ```
@@ -1,11 +1,10 @@
1
- """Small FastAPI checkpoint-continuation example for travel booking."""
1
+ """Small FastAPI persistence example for travel booking."""
2
2
 
3
- from collections.abc import Callable
3
+ from collections.abc import Callable, Mapping
4
4
  from dataclasses import dataclass, field
5
5
  from typing import Literal
6
6
 
7
- from context_compiler import POLICY_USE, State, create_engine, get_policy_items
8
- from context_compiler.engine import Checkpoint, Engine
7
+ from context_compiler import DecisionKind, Engine, POLICY_USE, PolicyValue
9
8
  from fastapi import FastAPI, HTTPException
10
9
  from pydantic import BaseModel
11
10
  from typing_extensions import TypedDict
@@ -22,17 +21,16 @@ class BookingResponse(TypedDict):
22
21
 
23
22
 
24
23
  class ChangeTripResponse(TypedDict):
25
- decision_kind: Literal["clarify"]
26
- prompt_to_user: str | None
27
- checkpoint_pending: bool
24
+ decision_kind: Literal["error", "update", "passthrough"]
25
+ message_to_user: str | None
26
+ persisted_state_json: str
27
+ selected_itinerary: str | None
28
28
  booking: BookingResponse
29
29
 
30
30
 
31
- class ConfirmResponse(TypedDict):
32
- decision_kind: Literal["clarify", "update", "passthrough"]
33
- prompt_to_user: str | None
34
- checkpoint_pending: bool
31
+ class ApplyTripResponse(TypedDict):
35
32
  host_applied_change: bool
33
+ selected_itinerary: str | None
36
34
  booking: BookingResponse
37
35
 
38
36
 
@@ -40,28 +38,23 @@ class ChangeTripRequest(BaseModel):
40
38
  booking_id: str
41
39
 
42
40
 
43
- class ConfirmRequest(BaseModel):
44
- booking_id: str
45
- user_input: str
46
-
47
-
48
41
  @dataclass
49
- class CheckpointStore:
50
- """Host-owned checkpoint persistence for stateless HTTP requests."""
42
+ class EnginePersistenceStore:
43
+ """Host-owned authoritative state persistence for stateless HTTP requests."""
51
44
 
52
- checkpoints_by_booking_id: dict[str, Checkpoint] = field(default_factory=dict)
45
+ states_by_booking_id: dict[str, str] = field(default_factory=dict)
53
46
 
54
- def save(self, booking_id: str, checkpoint: Checkpoint) -> None:
55
- self.checkpoints_by_booking_id[booking_id] = checkpoint
47
+ def save(self, booking_id: str, state_json: str) -> None:
48
+ self.states_by_booking_id[booking_id] = state_json
56
49
 
57
- def load(self, booking_id: str) -> Checkpoint:
58
- checkpoint = self.checkpoints_by_booking_id.get(booking_id)
59
- if checkpoint is None:
50
+ def load(self, booking_id: str) -> str:
51
+ state_json = self.states_by_booking_id.get(booking_id)
52
+ if state_json is None:
60
53
  raise KeyError(booking_id)
61
- return checkpoint
54
+ return state_json
62
55
 
63
56
  def has(self, booking_id: str) -> bool:
64
- return booking_id in self.checkpoints_by_booking_id
57
+ return booking_id in self.states_by_booking_id
65
58
 
66
59
 
67
60
  @dataclass
@@ -85,8 +78,10 @@ class BookingHost:
85
78
  booking_store: BookingStore
86
79
  applied_changes: list[str] = field(default_factory=list)
87
80
 
88
- def apply_selected_itinerary(self, booking_id: str, state: State) -> bool:
89
- selected_itinerary = select_itinerary_from_state(state)
81
+ def apply_selected_itinerary(
82
+ self, booking_id: str, policies: Mapping[str, PolicyValue]
83
+ ) -> bool:
84
+ selected_itinerary = select_itinerary_from_policies(policies)
90
85
  if selected_itinerary is None:
91
86
  return False
92
87
 
@@ -96,42 +91,35 @@ class BookingHost:
96
91
  return True
97
92
 
98
93
 
99
- def select_itinerary_from_state(state: State) -> str | None:
100
- use_items = list(get_policy_items(state, POLICY_USE))
101
- if not use_items:
102
- return None
103
- return use_items[0]
94
+ def select_itinerary_from_policies(policies: Mapping[str, PolicyValue]) -> str | None:
95
+ for item, kind in policies.items():
96
+ if kind == POLICY_USE:
97
+ return item
98
+ return None
104
99
 
105
100
 
106
- def restore_engine_from_checkpoint(checkpoint: Checkpoint) -> Engine:
107
- engine = create_engine()
108
- engine.import_checkpoint(checkpoint)
101
+ def restore_engine_from_persisted_state(state_json: str) -> Engine:
102
+ engine = Engine()
103
+ engine.import_json(state_json)
109
104
  return engine
110
105
 
111
106
 
112
- def restore_engine_from_authoritative_state_only(checkpoint: Checkpoint) -> Engine:
113
- authoritative_state = checkpoint["authoritative_state"]
114
- return create_engine(state=authoritative_state)
115
-
116
-
117
107
  def _fresh_engine() -> Engine:
118
- """Create a fresh engine per request to demonstrate stateless boundaries."""
119
-
120
- return create_engine()
108
+ return Engine()
121
109
 
122
110
 
123
111
  def create_app(
124
112
  *,
125
- checkpoint_store: CheckpointStore | None = None,
113
+ engine_persistence_store: EnginePersistenceStore | None = None,
126
114
  booking_store: BookingStore | None = None,
127
115
  engine_factory: Callable[[], Engine] = _fresh_engine,
128
116
  ) -> FastAPI:
129
- checkpoint_store = checkpoint_store or CheckpointStore()
117
+ engine_persistence_store = engine_persistence_store or EnginePersistenceStore()
130
118
  booking_store = booking_store or BookingStore()
131
119
  booking_host = BookingHost(booking_store=booking_store)
132
120
 
133
- app = FastAPI(title="checkpoint-continuation-fastapi-example")
134
- app.state.checkpoint_store = checkpoint_store
121
+ app = FastAPI(title="state-persistence-fastapi-example")
122
+ app.state.engine_persistence_store = engine_persistence_store
135
123
  app.state.booking_store = booking_store
136
124
  app.state.booking_host = booking_host
137
125
  app.state.engine_factory = engine_factory
@@ -141,43 +129,51 @@ def create_app(
141
129
  booking = booking_store.get_or_create(request.booking_id)
142
130
  engine = engine_factory()
143
131
 
144
- compiler_input = f"use chicago_trip instead of {booking['active_itinerary']}"
132
+ compiler_input = "use chicago_trip"
145
133
  decision = engine.step(compiler_input)
146
- checkpoint_store.save(request.booking_id, engine.export_checkpoint())
134
+ state_json = engine.export_json()
135
+ engine_persistence_store.save(request.booking_id, state_json)
136
+
137
+ selected_itinerary = select_itinerary_from_policies(engine.policies)
138
+ decision_kind: Literal["error", "update", "passthrough"]
139
+ if decision.kind == DecisionKind.ERROR:
140
+ decision_kind = "error"
141
+ elif decision.kind == DecisionKind.UPDATE:
142
+ decision_kind = "update"
143
+ else:
144
+ decision_kind = "passthrough"
147
145
 
148
146
  return {
149
- "decision_kind": "clarify",
150
- "prompt_to_user": decision.get("prompt_to_user"),
151
- "checkpoint_pending": engine.has_pending_clarification(),
147
+ "decision_kind": decision_kind,
148
+ "message_to_user": decision.message
149
+ if decision.kind == DecisionKind.ERROR
150
+ else None,
151
+ "persisted_state_json": state_json,
152
+ "selected_itinerary": selected_itinerary,
152
153
  "booking": {
153
154
  "booking_id": booking["booking_id"],
154
155
  "active_itinerary": booking["active_itinerary"],
155
156
  },
156
157
  }
157
158
 
158
- @app.post("/confirm")
159
- def confirm(request: ConfirmRequest) -> ConfirmResponse:
159
+ @app.post("/apply-trip")
160
+ def apply_trip(request: ChangeTripRequest) -> ApplyTripResponse:
160
161
  booking = booking_store.get_or_create(request.booking_id)
161
162
  try:
162
- checkpoint = checkpoint_store.load(request.booking_id)
163
+ state_json = engine_persistence_store.load(request.booking_id)
163
164
  except KeyError as exc:
164
- raise HTTPException(status_code=404, detail="checkpoint not found") from exc
165
-
166
- engine = restore_engine_from_checkpoint(checkpoint)
167
- decision = engine.step(request.user_input)
168
- checkpoint_store.save(request.booking_id, engine.export_checkpoint())
165
+ raise HTTPException(
166
+ status_code=404, detail="saved state not found"
167
+ ) from exc
169
168
 
170
- host_applied_change = False
171
- if decision["kind"].value == "update":
172
- host_applied_change = booking_host.apply_selected_itinerary(
173
- request.booking_id, engine.state
174
- )
169
+ engine = restore_engine_from_persisted_state(state_json)
170
+ host_applied_change = booking_host.apply_selected_itinerary(
171
+ request.booking_id, engine.policies
172
+ )
175
173
 
176
174
  return {
177
- "decision_kind": decision["kind"].value,
178
- "prompt_to_user": decision.get("prompt_to_user"),
179
- "checkpoint_pending": engine.has_pending_clarification(),
180
175
  "host_applied_change": host_applied_change,
176
+ "selected_itinerary": select_itinerary_from_policies(engine.policies),
181
177
  "booking": {
182
178
  "booking_id": booking["booking_id"],
183
179
  "active_itinerary": booking["active_itinerary"],