context-compiler-example-integrations 0.1.0__py3-none-any.whl

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 (65) hide show
  1. context_compiler_example_integrations/README.md +71 -0
  2. context_compiler_example_integrations/__init__.py +1 -0
  3. context_compiler_example_integrations/examples/__init__.py +12 -0
  4. context_compiler_example_integrations/examples/_shared/__init__.py +1 -0
  5. context_compiler_example_integrations/examples/_shared/litellm_request.py +61 -0
  6. context_compiler_example_integrations/examples/_shared/provider_mode.py +91 -0
  7. context_compiler_example_integrations/examples/checkpoint_continuation/README.md +77 -0
  8. context_compiler_example_integrations/examples/checkpoint_continuation/example.py +169 -0
  9. context_compiler_example_integrations/examples/checkpoint_continuation/fastapi/README.md +85 -0
  10. context_compiler_example_integrations/examples/checkpoint_continuation/fastapi/app.py +205 -0
  11. context_compiler_example_integrations/examples/execution_authorization/README.md +31 -0
  12. context_compiler_example_integrations/examples/execution_authorization/expense_approval/README.md +77 -0
  13. context_compiler_example_integrations/examples/execution_authorization/expense_approval/__init__.py +1 -0
  14. context_compiler_example_integrations/examples/execution_authorization/expense_approval/example.py +168 -0
  15. context_compiler_example_integrations/examples/execution_authorization/expense_approval/fastapi/README.md +97 -0
  16. context_compiler_example_integrations/examples/execution_authorization/expense_approval/fastapi/__init__.py +1 -0
  17. context_compiler_example_integrations/examples/execution_authorization/expense_approval/fastapi/app.py +312 -0
  18. context_compiler_example_integrations/examples/execution_authorization/expense_approval/fastapi/model_approval.py +98 -0
  19. context_compiler_example_integrations/examples/gateway_middleware/README.md +49 -0
  20. context_compiler_example_integrations/examples/gateway_middleware/customer_support_routing/README.md +40 -0
  21. context_compiler_example_integrations/examples/gateway_middleware/customer_support_routing/__init__.py +1 -0
  22. context_compiler_example_integrations/examples/gateway_middleware/customer_support_routing/example.py +220 -0
  23. context_compiler_example_integrations/examples/prompt_construction/README.md +27 -0
  24. context_compiler_example_integrations/examples/prompt_construction/litellm/README.md +197 -0
  25. context_compiler_example_integrations/examples/prompt_construction/litellm/basic.py +392 -0
  26. context_compiler_example_integrations/examples/prompt_construction/litellm/confirmation_helper.py +91 -0
  27. context_compiler_example_integrations/examples/prompt_construction/litellm/with_directive_drafter.py +524 -0
  28. context_compiler_example_integrations/examples/prompt_construction/writing_assistant/README.md +92 -0
  29. context_compiler_example_integrations/examples/prompt_construction/writing_assistant/__init__.py +1 -0
  30. context_compiler_example_integrations/examples/prompt_construction/writing_assistant/example.py +205 -0
  31. context_compiler_example_integrations/examples/retrieval_filtering/README.md +65 -0
  32. context_compiler_example_integrations/examples/retrieval_filtering/chromadb_hr_policy_lookup/README.md +120 -0
  33. context_compiler_example_integrations/examples/retrieval_filtering/chromadb_hr_policy_lookup/__init__.py +1 -0
  34. context_compiler_example_integrations/examples/retrieval_filtering/chromadb_hr_policy_lookup/example.py +320 -0
  35. context_compiler_example_integrations/examples/retrieval_filtering/hr_policy_lookup/README.md +123 -0
  36. context_compiler_example_integrations/examples/retrieval_filtering/hr_policy_lookup/__init__.py +1 -0
  37. context_compiler_example_integrations/examples/retrieval_filtering/hr_policy_lookup/example.py +300 -0
  38. context_compiler_example_integrations/examples/schema_selection/README.md +41 -0
  39. context_compiler_example_integrations/examples/schema_selection/litellm_response_format/response_format.py +218 -0
  40. context_compiler_example_integrations/examples/schema_selection/ollama_structured_output/README.md +60 -0
  41. context_compiler_example_integrations/examples/schema_selection/ollama_structured_output/example.py +171 -0
  42. context_compiler_example_integrations/examples/schema_selection/refund_intake/README.md +95 -0
  43. context_compiler_example_integrations/examples/schema_selection/refund_intake/__init__.py +1 -0
  44. context_compiler_example_integrations/examples/schema_selection/refund_intake/example.py +180 -0
  45. context_compiler_example_integrations/examples/tool_gating/README.md +55 -0
  46. context_compiler_example_integrations/examples/tool_gating/calendar_admin/README.md +34 -0
  47. context_compiler_example_integrations/examples/tool_gating/calendar_admin/__init__.py +1 -0
  48. context_compiler_example_integrations/examples/tool_gating/calendar_admin/example.py +195 -0
  49. context_compiler_example_integrations/examples/tool_gating/mcp_calendar_admin/README.md +93 -0
  50. context_compiler_example_integrations/examples/tool_gating/mcp_calendar_admin/__init__.py +1 -0
  51. context_compiler_example_integrations/examples/tool_gating/mcp_calendar_admin/example.py +250 -0
  52. context_compiler_example_integrations/examples/tool_gating/mcp_calendar_admin/live_model.py +369 -0
  53. context_compiler_example_integrations/reference_integrations/__init__.py +12 -0
  54. context_compiler_example_integrations/reference_integrations/litellm_proxy/README.md +251 -0
  55. context_compiler_example_integrations/reference_integrations/litellm_proxy/_checkpoint_support.py +146 -0
  56. context_compiler_example_integrations/reference_integrations/litellm_proxy/config.example.yaml +23 -0
  57. context_compiler_example_integrations/reference_integrations/litellm_proxy/context_compiler_precall_hook.py +163 -0
  58. context_compiler_example_integrations/reference_integrations/litellm_proxy/context_compiler_precall_hook_with_directive_drafter.py +289 -0
  59. context_compiler_example_integrations/reference_integrations/openwebui_pipe/README.md +233 -0
  60. context_compiler_example_integrations/reference_integrations/openwebui_pipe/open_webui_pipe.py +750 -0
  61. context_compiler_example_integrations/reference_integrations/openwebui_pipe/open_webui_pipe_with_directive_drafter.py +1045 -0
  62. context_compiler_example_integrations-0.1.0.dist-info/METADATA +110 -0
  63. context_compiler_example_integrations-0.1.0.dist-info/RECORD +65 -0
  64. context_compiler_example_integrations-0.1.0.dist-info/WHEEL +4 -0
  65. context_compiler_example_integrations-0.1.0.dist-info/licenses/LICENSE +173 -0
@@ -0,0 +1,312 @@
1
+ """FastAPI comparison demo for execution authorization with expense approval."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from collections.abc import Callable
7
+ from dataclasses import dataclass
8
+ from pathlib import Path
9
+ from typing import Literal, cast
10
+
11
+ from context_compiler import State, create_engine, get_decision_state, is_clarify
12
+ from fastapi import FastAPI, HTTPException
13
+ from pydantic import BaseModel
14
+ from typing_extensions import TypedDict
15
+
16
+ from context_compiler_example_integrations.examples.execution_authorization.expense_approval.example import (
17
+ expense_execution_is_authorized,
18
+ )
19
+
20
+ from .model_approval import ModelApproval, get_model_approval_claim
21
+
22
+
23
+ class ExpenseRequest(BaseModel):
24
+ expense_id: str
25
+ employee_id: str
26
+ amount_usd: int
27
+ note: str
28
+ authoritative_state: dict[str, object] | None = None
29
+ compiler_input: str = ""
30
+ agent_claim: str | None = None
31
+
32
+
33
+ class SideEffectRecord(TypedDict):
34
+ expense_id: str
35
+ employee_id: str
36
+ amount_usd: int
37
+ note: str
38
+ path: Literal["baseline", "compiler"]
39
+ model_decision: str
40
+ authorization_source: Literal["model_claim", "context_compiler_state"]
41
+
42
+
43
+ class ExpenseMutationResponse(TypedDict):
44
+ path: Literal["baseline", "compiler"]
45
+ decision_kind: Literal["clarify", "update", "passthrough"] | None
46
+ model_decision: str
47
+ model_message: str
48
+ agent_claim: str | None
49
+ authorization_state: Literal["authorized", "blocked"]
50
+ executed: bool
51
+ blocked_reason: str | None
52
+ prompt_to_user: str | None
53
+ submission: dict[str, str | int] | None
54
+ side_effect_path: str
55
+ side_effect_count: int
56
+
57
+
58
+ def _decision_kind_name(
59
+ decision: object,
60
+ ) -> Literal["clarify", "update", "passthrough"]:
61
+ if not isinstance(decision, dict):
62
+ raise ValueError("unexpected decision shape")
63
+
64
+ kind = decision.get("kind")
65
+ kind_name = getattr(kind, "value", None)
66
+ if kind_name not in {"clarify", "update", "passthrough"}:
67
+ raise ValueError(f"unexpected decision kind: {kind_name}")
68
+
69
+ return kind_name
70
+
71
+
72
+ def _state_for_request(authoritative_state: dict[str, object] | None) -> State | None:
73
+ if authoritative_state is None:
74
+ return None
75
+ return cast(State, authoritative_state)
76
+
77
+
78
+ def _expense_summary(request: ExpenseRequest) -> str:
79
+ return (
80
+ f"expense_id={request.expense_id}; "
81
+ f"employee_id={request.employee_id}; "
82
+ f"amount_usd={request.amount_usd}; "
83
+ f"note={request.note}"
84
+ )
85
+
86
+
87
+ @dataclass
88
+ class ExpenseSideEffectStore:
89
+ """Host-owned append-only side-effect artifact for the comparison demo."""
90
+
91
+ artifact_path: Path
92
+
93
+ def append(
94
+ self,
95
+ *,
96
+ request: ExpenseRequest,
97
+ path_name: Literal["baseline", "compiler"],
98
+ model_decision: str,
99
+ authorization_source: Literal["model_claim", "context_compiler_state"],
100
+ ) -> dict[str, str | int]:
101
+ record: SideEffectRecord = {
102
+ "expense_id": request.expense_id,
103
+ "employee_id": request.employee_id,
104
+ "amount_usd": request.amount_usd,
105
+ "note": request.note,
106
+ "path": path_name,
107
+ "model_decision": model_decision,
108
+ "authorization_source": authorization_source,
109
+ }
110
+ self.artifact_path.parent.mkdir(parents=True, exist_ok=True)
111
+ with self.artifact_path.open("a", encoding="utf-8") as artifact:
112
+ artifact.write(json.dumps(record, sort_keys=True) + "\n")
113
+ return {
114
+ "expense_id": request.expense_id,
115
+ "employee_id": request.employee_id,
116
+ "amount_usd": request.amount_usd,
117
+ "note": request.note,
118
+ }
119
+
120
+ def count(self) -> int:
121
+ if not self.artifact_path.exists():
122
+ return 0
123
+ with self.artifact_path.open(encoding="utf-8") as artifact:
124
+ return sum(1 for _ in artifact)
125
+
126
+
127
+ def _blocked_response(
128
+ *,
129
+ path_name: Literal["baseline", "compiler"],
130
+ model_claim: ModelApproval,
131
+ side_effect_store: ExpenseSideEffectStore,
132
+ blocked_reason: str,
133
+ prompt_to_user: str | None,
134
+ decision_kind: Literal["clarify", "update", "passthrough"] | None,
135
+ request_agent_claim: str | None,
136
+ ) -> ExpenseMutationResponse:
137
+ return {
138
+ "path": path_name,
139
+ "decision_kind": decision_kind,
140
+ "model_decision": model_claim.decision,
141
+ "model_message": model_claim.message,
142
+ "agent_claim": request_agent_claim,
143
+ "authorization_state": "blocked",
144
+ "executed": False,
145
+ "blocked_reason": blocked_reason,
146
+ "prompt_to_user": prompt_to_user,
147
+ "submission": None,
148
+ "side_effect_path": str(side_effect_store.artifact_path),
149
+ "side_effect_count": side_effect_store.count(),
150
+ }
151
+
152
+
153
+ def _authorized_response(
154
+ *,
155
+ path_name: Literal["baseline", "compiler"],
156
+ model_claim: ModelApproval,
157
+ side_effect_store: ExpenseSideEffectStore,
158
+ submission: dict[str, str | int],
159
+ decision_kind: Literal["clarify", "update", "passthrough"] | None,
160
+ request_agent_claim: str | None,
161
+ ) -> ExpenseMutationResponse:
162
+ return {
163
+ "path": path_name,
164
+ "decision_kind": decision_kind,
165
+ "model_decision": model_claim.decision,
166
+ "model_message": model_claim.message,
167
+ "agent_claim": request_agent_claim,
168
+ "authorization_state": "authorized",
169
+ "executed": True,
170
+ "blocked_reason": None,
171
+ "prompt_to_user": None,
172
+ "submission": submission,
173
+ "side_effect_path": str(side_effect_store.artifact_path),
174
+ "side_effect_count": side_effect_store.count(),
175
+ }
176
+
177
+
178
+ def create_app(
179
+ *,
180
+ artifact_path: Path | None = None,
181
+ model_approval_getter: Callable[[str], ModelApproval] = get_model_approval_claim,
182
+ ) -> FastAPI:
183
+ if artifact_path is None:
184
+ artifact_path = Path(
185
+ "/tmp/context_compiler_expense_approval/submitted_expenses.jsonl"
186
+ )
187
+
188
+ side_effect_store = ExpenseSideEffectStore(artifact_path=artifact_path)
189
+ app = FastAPI(title="expense-approval-fastapi-comparison")
190
+ app.state.side_effect_store = side_effect_store
191
+
192
+ @app.post("/baseline/expenses")
193
+ def submit_baseline_expense(request: ExpenseRequest) -> ExpenseMutationResponse:
194
+ model_claim = model_approval_getter(_expense_summary(request))
195
+ if not model_claim.is_approved:
196
+ raise HTTPException(
197
+ status_code=403,
198
+ detail=_blocked_response(
199
+ path_name="baseline",
200
+ model_claim=model_claim,
201
+ side_effect_store=side_effect_store,
202
+ blocked_reason="model claim did not approve expense",
203
+ prompt_to_user=None,
204
+ decision_kind=None,
205
+ request_agent_claim=request.agent_claim,
206
+ ),
207
+ )
208
+
209
+ submission = side_effect_store.append(
210
+ request=request,
211
+ path_name="baseline",
212
+ model_decision=model_claim.decision,
213
+ authorization_source="model_claim",
214
+ )
215
+ return _authorized_response(
216
+ path_name="baseline",
217
+ model_claim=model_claim,
218
+ side_effect_store=side_effect_store,
219
+ submission=submission,
220
+ decision_kind=None,
221
+ request_agent_claim=request.agent_claim,
222
+ )
223
+
224
+ @app.post("/compiler/expenses")
225
+ def submit_compiler_mediated_expense(
226
+ request: ExpenseRequest,
227
+ ) -> ExpenseMutationResponse:
228
+ model_claim = model_approval_getter(_expense_summary(request))
229
+ if not model_claim.is_approved:
230
+ raise HTTPException(
231
+ status_code=403,
232
+ detail=_blocked_response(
233
+ path_name="compiler",
234
+ model_claim=model_claim,
235
+ side_effect_store=side_effect_store,
236
+ blocked_reason="model claim did not approve expense",
237
+ prompt_to_user=None,
238
+ decision_kind=None,
239
+ request_agent_claim=request.agent_claim,
240
+ ),
241
+ )
242
+
243
+ engine = create_engine(state=_state_for_request(request.authoritative_state))
244
+ decision_kind: Literal["clarify", "update", "passthrough"] | None = None
245
+ prompt_to_user: str | None = None
246
+ authoritative_state = engine.state
247
+
248
+ if request.compiler_input:
249
+ decision = engine.step(request.compiler_input)
250
+ decision_kind = _decision_kind_name(decision)
251
+ prompt_to_user = decision.get("prompt_to_user")
252
+ if is_clarify(decision):
253
+ raise HTTPException(
254
+ status_code=409,
255
+ detail=_blocked_response(
256
+ path_name="compiler",
257
+ model_claim=model_claim,
258
+ side_effect_store=side_effect_store,
259
+ blocked_reason=(
260
+ "clarification required before expense execution"
261
+ ),
262
+ prompt_to_user=prompt_to_user,
263
+ decision_kind=decision_kind,
264
+ request_agent_claim=request.agent_claim,
265
+ ),
266
+ )
267
+
268
+ decision_state = get_decision_state(decision)
269
+ authoritative_state = (
270
+ decision_state if decision_state is not None else engine.state
271
+ )
272
+
273
+ if not expense_execution_is_authorized(authoritative_state):
274
+ raise HTTPException(
275
+ status_code=403,
276
+ detail=_blocked_response(
277
+ path_name="compiler",
278
+ model_claim=model_claim,
279
+ side_effect_store=side_effect_store,
280
+ blocked_reason="expense_approval state not authorized",
281
+ prompt_to_user=prompt_to_user,
282
+ decision_kind=decision_kind,
283
+ request_agent_claim=request.agent_claim,
284
+ ),
285
+ )
286
+
287
+ submission = side_effect_store.append(
288
+ request=request,
289
+ path_name="compiler",
290
+ model_decision=model_claim.decision,
291
+ authorization_source="context_compiler_state",
292
+ )
293
+ return _authorized_response(
294
+ path_name="compiler",
295
+ model_claim=model_claim,
296
+ side_effect_store=side_effect_store,
297
+ submission=submission,
298
+ decision_kind=decision_kind,
299
+ request_agent_claim=request.agent_claim,
300
+ )
301
+
302
+ return app
303
+
304
+
305
+ app = create_app()
306
+
307
+
308
+ if __name__ == "__main__":
309
+ print(
310
+ "Run with: uv run fastapi dev "
311
+ "python/examples/execution_authorization/expense_approval/fastapi/app.py"
312
+ )
@@ -0,0 +1,98 @@
1
+ """Minimal provider-backed approval claim helper for the FastAPI demo."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Callable, Mapping
6
+ from dataclasses import dataclass
7
+ from importlib import import_module
8
+ from typing import cast
9
+
10
+ from context_compiler_example_integrations.examples._shared.litellm_request import (
11
+ build_litellm_provider_kwargs,
12
+ )
13
+ from context_compiler_example_integrations.examples._shared.provider_mode import (
14
+ print_startup_config,
15
+ resolve_provider_config,
16
+ )
17
+
18
+
19
+ @dataclass(frozen=True)
20
+ class ModelApproval:
21
+ decision: str
22
+ message: str
23
+
24
+ @property
25
+ def is_approved(self) -> bool:
26
+ return self.decision == "approved"
27
+
28
+
29
+ def _get_litellm_completion() -> Callable[..., object]:
30
+ litellm_module = import_module("litellm")
31
+ return cast(Callable[..., object], litellm_module.completion)
32
+
33
+
34
+ def _extract_response_content(response: object) -> str | None:
35
+ if isinstance(response, Mapping):
36
+ choices = response.get("choices")
37
+ if isinstance(choices, list) and choices:
38
+ first = choices[0]
39
+ if isinstance(first, Mapping):
40
+ message = first.get("message")
41
+ if isinstance(message, Mapping):
42
+ content = message.get("content")
43
+ if isinstance(content, str):
44
+ return content
45
+
46
+ choices_attr = getattr(response, "choices", None)
47
+ if isinstance(choices_attr, list) and choices_attr:
48
+ first = choices_attr[0]
49
+ message_attr = getattr(first, "message", None)
50
+ content_attr = getattr(message_attr, "content", None)
51
+ if isinstance(content_attr, str):
52
+ return content_attr
53
+
54
+ return None
55
+
56
+
57
+ def classify_model_approval(message: str) -> str:
58
+ normalized = message.strip().lower()
59
+ if normalized.startswith("approved"):
60
+ return "approved"
61
+ if " approved" in f" {normalized} ":
62
+ return "approved"
63
+ return "not_approved"
64
+
65
+
66
+ def get_model_approval_claim(expense_summary: str) -> ModelApproval:
67
+ """Return an approval-class claim from a live provider."""
68
+
69
+ try:
70
+ completion = _get_litellm_completion()
71
+ except ModuleNotFoundError as exc:
72
+ raise RuntimeError(
73
+ "litellm is required. Install with: pip install litellm"
74
+ ) from exc
75
+
76
+ config = resolve_provider_config(default_model="openai/gpt-4o-mini")
77
+ print_startup_config(config)
78
+
79
+ prompt = (
80
+ "You are an expense approver.\n"
81
+ "Reply with a short decision line that starts with APPROVED or DENIED.\n"
82
+ "Expense:\n"
83
+ f"{expense_summary}"
84
+ )
85
+ kwargs: dict[str, object] = {
86
+ **build_litellm_provider_kwargs(config),
87
+ "messages": [{"role": "user", "content": prompt}],
88
+ }
89
+
90
+ response = completion(**kwargs)
91
+ message = _extract_response_content(response)
92
+ if message is None:
93
+ raise RuntimeError("LiteLLM response missing choices[0].message.content")
94
+
95
+ return ModelApproval(
96
+ decision=classify_model_approval(message),
97
+ message=message,
98
+ )
@@ -0,0 +1,49 @@
1
+ # Gateway middleware
2
+
3
+ These examples show a host-owned gateway making an allow / block / route
4
+ decision before any downstream service call happens.
5
+
6
+ Context Compiler owns the authoritative policy state.
7
+
8
+ The host owns the gateway boundary, the default route, and the downstream
9
+ handler invocation.
10
+
11
+ Adversarial wording does not bypass the gateway or mutate authoritative state.
12
+
13
+ ## Examples
14
+
15
+ ### `customer_support_routing`
16
+
17
+ Routes a host-owned customer support request to `billing_support` only when
18
+ authoritative state contains:
19
+
20
+ ```text
21
+ use billing_support
22
+ ```
23
+
24
+ If state is absent, the gateway blocks billing-routed requests and still allows
25
+ non-billing requests to follow the documented default path,
26
+ `general_support`.
27
+
28
+ If state contains:
29
+
30
+ ```text
31
+ prohibit billing_support
32
+ ```
33
+
34
+ the gateway blocks billing-routed requests before the downstream handler is
35
+ called.
36
+
37
+ The tests cover default-path behavior, authorized routing, blocked routing,
38
+ adversarial text, downstream non-invocation when blocked, and contradiction /
39
+ clarification behavior.
40
+
41
+ ## Related integrations
42
+
43
+ The generic example teaches the gateway-middleware enforcement point in a small
44
+ host-owned flow.
45
+
46
+ For a concrete proxy runtime surface, see the LiteLLM Proxy reference
47
+ integration:
48
+
49
+ - [python/reference_integrations/litellm_proxy/README.md](../../reference_integrations/litellm_proxy/README.md)
@@ -0,0 +1,40 @@
1
+ # Customer support routing
2
+
3
+ Authoritative state changes whether the gateway lets a billing request reach
4
+ the downstream support handler. This example shows that gateway behavior in a
5
+ host-owned customer support routing flow.
6
+
7
+ The gateway calls the downstream support service only after it allows the
8
+ request.
9
+
10
+ ## Policy mapping
11
+
12
+ The gateway routes a billing request to `billing_support` only when state
13
+ contains:
14
+
15
+ ```text
16
+ use billing_support
17
+ ```
18
+
19
+ If state is absent, the gateway blocks billing requests.
20
+
21
+ If state contains:
22
+
23
+ ```text
24
+ prohibit billing_support
25
+ ```
26
+
27
+ the gateway also blocks billing requests.
28
+
29
+ Requests that are not asking for billing support follow the host's documented
30
+ default path, `general_support`.
31
+
32
+ ## What this example demonstrates
33
+
34
+ - Context Compiler owns authoritative policy state.
35
+ - The host owns the gateway middleware boundary and the downstream call.
36
+ - Adversarial request text does not bypass the gateway decision.
37
+ - Contradictory `use billing_support` and `prohibit billing_support` inputs
38
+ produce clarification behavior instead of a silent overwrite.
39
+ - The example does not call an LLM, does not use directive drafter, and does
40
+ not derive state from model output.
@@ -0,0 +1 @@
1
+ """Generic gateway middleware example for customer support routing."""