context-engineering-core 0.12.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 (84) hide show
  1. context_engine/__init__.py +15 -0
  2. context_engine/__main__.py +3 -0
  3. context_engine/_sqlite.py +19 -0
  4. context_engine/_windows_security.py +125 -0
  5. context_engine/_windows_storage.py +110 -0
  6. context_engine/adapters.py +148 -0
  7. context_engine/answers.py +90 -0
  8. context_engine/budget.py +93 -0
  9. context_engine/cli.py +222 -0
  10. context_engine/config.py +193 -0
  11. context_engine/context.py +116 -0
  12. context_engine/demo.py +98 -0
  13. context_engine/errors.py +91 -0
  14. context_engine/evaluation/__init__.py +1 -0
  15. context_engine/evaluation/commands.py +90 -0
  16. context_engine/evaluation/corpus.py +247 -0
  17. context_engine/evaluation/data/freeze.json +26 -0
  18. context_engine/evaluation/data/ground_truth.json +287 -0
  19. context_engine/evaluation/data/pin_retention.json +8 -0
  20. context_engine/evaluation/data/protocol.json +122 -0
  21. context_engine/evaluation/data/scenario.json +1226 -0
  22. context_engine/evaluation/data/summary_fixture.json +7 -0
  23. context_engine/evaluation/execution.py +538 -0
  24. context_engine/evaluation/fixtures.py +32 -0
  25. context_engine/evaluation/grading.py +62 -0
  26. context_engine/evaluation/harness.py +165 -0
  27. context_engine/evaluation/heldout/__init__.py +260 -0
  28. context_engine/evaluation/heldout/__main__.py +40 -0
  29. context_engine/evaluation/heldout/data/ground_truth.json +18 -0
  30. context_engine/evaluation/heldout/data/manifest.json +9 -0
  31. context_engine/evaluation/heldout/data/protocol.json +14 -0
  32. context_engine/evaluation/heldout/data/scenarios.json +18 -0
  33. context_engine/evaluation/heldout/data/scoring_manifest.json +4 -0
  34. context_engine/evaluation/heldout/data/scoring_protocol.json +12 -0
  35. context_engine/evaluation/heldout/scoring.py +176 -0
  36. context_engine/evaluation/protocol.py +264 -0
  37. context_engine/evaluation/reporting.py +294 -0
  38. context_engine/evaluation/results.py +331 -0
  39. context_engine/evaluation/run_commands.py +146 -0
  40. context_engine/evaluation/strategies.py +261 -0
  41. context_engine/inspection.py +266 -0
  42. context_engine/layers/__init__.py +18 -0
  43. context_engine/layers/cap.py +99 -0
  44. context_engine/layers/common.py +162 -0
  45. context_engine/layers/pin.py +68 -0
  46. context_engine/layers/retrieve.py +233 -0
  47. context_engine/layers/summarize.py +202 -0
  48. context_engine/layers/window.py +29 -0
  49. context_engine/mcp_server.py +267 -0
  50. context_engine/memory/__init__.py +5 -0
  51. context_engine/memory/codec.py +91 -0
  52. context_engine/memory/demo.py +90 -0
  53. context_engine/memory/store.py +770 -0
  54. context_engine/models.py +384 -0
  55. context_engine/pipeline.py +383 -0
  56. context_engine/providers/__init__.py +1 -0
  57. context_engine/providers/client.py +301 -0
  58. context_engine/providers/commands.py +138 -0
  59. context_engine/providers/contracts.py +324 -0
  60. context_engine/providers/credentials.py +66 -0
  61. context_engine/providers/memory.py +112 -0
  62. context_engine/providers/store.py +408 -0
  63. context_engine/providers/transport.py +195 -0
  64. context_engine/providers/windows_credentials.py +23 -0
  65. context_engine/py.typed +0 -0
  66. context_engine/service/__init__.py +1 -0
  67. context_engine/service/__main__.py +68 -0
  68. context_engine/service/app.py +404 -0
  69. context_engine/service/auth.py +132 -0
  70. context_engine/service/client.py +52 -0
  71. context_engine/service/control.py +134 -0
  72. context_engine/service/demo.py +151 -0
  73. context_engine/service/integrations.py +28 -0
  74. context_engine/service/schemas.py +59 -0
  75. context_engine/service/worker.py +59 -0
  76. context_engine/service/workers.py +156 -0
  77. context_engine/summaries.py +196 -0
  78. context_engine/tokens.py +171 -0
  79. context_engine/work.py +46 -0
  80. context_engineering_core-0.12.0.dist-info/METADATA +203 -0
  81. context_engineering_core-0.12.0.dist-info/RECORD +84 -0
  82. context_engineering_core-0.12.0.dist-info/WHEEL +4 -0
  83. context_engineering_core-0.12.0.dist-info/entry_points.txt +3 -0
  84. context_engineering_core-0.12.0.dist-info/licenses/LICENSE +202 -0
@@ -0,0 +1,384 @@
1
+ """Immutable source and chat contracts. Scope is a label, never authorization."""
2
+
3
+ import hashlib
4
+ import json
5
+ from dataclasses import dataclass, field
6
+ from datetime import UTC, datetime
7
+ from enum import StrEnum
8
+ from typing import Any
9
+
10
+ from .errors import ContractError
11
+
12
+
13
+ def text_value(name: str, value: str, *, nonempty: bool = True) -> None:
14
+ if not isinstance(value, str) or (nonempty and not value.strip()):
15
+ raise ContractError("Expected text", field=name)
16
+ try:
17
+ value.encode("utf-8")
18
+ except UnicodeError:
19
+ raise ContractError("Text contains invalid Unicode", field=name) from None
20
+
21
+
22
+ def integer_value(name: str, value: int, *, minimum: int = 0) -> None:
23
+ if type(value) is not int or value < minimum:
24
+ raise ContractError("Expected an integer within range", field=name, minimum=minimum)
25
+
26
+
27
+ def aware_time(name: str, value: datetime) -> None:
28
+ if not isinstance(value, datetime) or value.tzinfo is None or value.utcoffset() is None:
29
+ raise ContractError("Timestamp must have a timezone", field=name)
30
+
31
+
32
+ def canonical_json(value: Any) -> str:
33
+ try:
34
+ result = json.dumps(
35
+ value, ensure_ascii=False, sort_keys=True, separators=(",", ":"), allow_nan=False
36
+ )
37
+ result.encode("utf-8")
38
+ return result
39
+ except (TypeError, ValueError, UnicodeError, RecursionError):
40
+ raise ContractError("Value must be valid JSON with valid Unicode") from None
41
+
42
+
43
+ def object_json(value: str) -> str:
44
+ text_value("json", value)
45
+
46
+ def unique_object(pairs: list[tuple[str, Any]]) -> dict:
47
+ result = {}
48
+ for key, item in pairs:
49
+ if key in result:
50
+ raise ContractError("JSON object keys must be unique")
51
+ result[key] = item
52
+ return result
53
+
54
+ try:
55
+ parsed = json.loads(value, object_pairs_hook=unique_object)
56
+ except (ValueError, RecursionError):
57
+ raise ContractError("Expected a JSON object") from None
58
+ if not isinstance(parsed, dict):
59
+ raise ContractError("Expected a JSON object")
60
+ return canonical_json(parsed)
61
+
62
+
63
+ def freeze_sequence(instance: Any, name: str, item_type: type) -> tuple:
64
+ raw = getattr(instance, name)
65
+ if not isinstance(raw, (tuple, list)) or any(not isinstance(item, item_type) for item in raw):
66
+ raise ContractError("Invalid sequence contents", field=name)
67
+ frozen = tuple(raw)
68
+ object.__setattr__(instance, name, frozen)
69
+ return frozen
70
+
71
+
72
+ class Role(StrEnum):
73
+ SYSTEM = "system"
74
+ USER = "user"
75
+ ASSISTANT = "assistant"
76
+ TOOL = "tool"
77
+
78
+
79
+ class BlockKind(StrEnum):
80
+ SYSTEM = "system"
81
+ PINNED = "pinned"
82
+ SUMMARY = "summary"
83
+ WINDOW = "window"
84
+ RETRIEVED = "retrieved"
85
+ QUESTION = "question"
86
+
87
+
88
+ @dataclass(frozen=True, slots=True)
89
+ class Scope:
90
+ tenant_id: str
91
+ session_id: str
92
+
93
+ def __post_init__(self) -> None:
94
+ text_value("tenant_id", self.tenant_id)
95
+ text_value("session_id", self.session_id)
96
+
97
+
98
+ @dataclass(frozen=True, slots=True)
99
+ class ToolCall:
100
+ call_id: str
101
+ name: str
102
+ arguments_json: str
103
+
104
+ def __post_init__(self) -> None:
105
+ text_value("call_id", self.call_id)
106
+ text_value("name", self.name)
107
+ object_json(self.arguments_json) # Validate without rewriting original call bytes.
108
+
109
+ def to_wire(self) -> dict:
110
+ return {
111
+ "id": self.call_id,
112
+ "type": "function",
113
+ "function": {"name": self.name, "arguments": self.arguments_json},
114
+ }
115
+
116
+
117
+ @dataclass(frozen=True, slots=True)
118
+ class ToolDefinition:
119
+ name: str
120
+ description: str
121
+ parameters_json: str
122
+
123
+ def __post_init__(self) -> None:
124
+ text_value("name", self.name)
125
+ text_value("description", self.description, nonempty=False)
126
+ object.__setattr__(self, "parameters_json", object_json(self.parameters_json))
127
+
128
+ def to_wire(self) -> dict:
129
+ return {
130
+ "type": "function",
131
+ "function": {
132
+ "name": self.name,
133
+ "description": self.description,
134
+ "parameters": json.loads(self.parameters_json),
135
+ },
136
+ }
137
+
138
+
139
+ @dataclass(frozen=True, slots=True)
140
+ class Message:
141
+ message_id: str
142
+ role: Role
143
+ content: str
144
+ tool_call_id: str | None = None
145
+ tool_calls: tuple[ToolCall, ...] = ()
146
+
147
+ def __post_init__(self) -> None:
148
+ text_value("message_id", self.message_id)
149
+ text_value("content", self.content, nonempty=False)
150
+ try:
151
+ object.__setattr__(self, "role", Role(self.role))
152
+ except (ValueError, TypeError):
153
+ raise ContractError("Unsupported message role") from None
154
+ calls = freeze_sequence(self, "tool_calls", ToolCall)
155
+ if len({call.call_id for call in calls}) != len(calls):
156
+ raise ContractError("Duplicate tool call IDs")
157
+ if calls and self.role != Role.ASSISTANT:
158
+ raise ContractError("Only assistant messages can contain tool calls")
159
+ if self.role == Role.TOOL:
160
+ text_value("tool_call_id", self.tool_call_id)
161
+ elif self.tool_call_id is not None:
162
+ raise ContractError("Only tool results can carry tool_call_id")
163
+
164
+ @property
165
+ def content_hash(self) -> str:
166
+ return hashlib.sha256(self.content.encode("utf-8")).hexdigest()
167
+
168
+ def to_wire(self) -> dict:
169
+ result: dict = {"role": self.role.value, "content": self.content}
170
+ if self.tool_calls:
171
+ result["tool_calls"] = [call.to_wire() for call in self.tool_calls]
172
+ if self.tool_call_id is not None:
173
+ result["tool_call_id"] = self.tool_call_id
174
+ return result
175
+
176
+
177
+ @dataclass(frozen=True, slots=True)
178
+ class Turn:
179
+ turn_id: str
180
+ scope: Scope
181
+ messages: tuple[Message, ...]
182
+ revision: int = 1
183
+ timestamp: datetime = field(default_factory=lambda: datetime.now(UTC))
184
+
185
+ def __post_init__(self) -> None:
186
+ text_value("turn_id", self.turn_id)
187
+ if not isinstance(self.scope, Scope):
188
+ raise ContractError("Turn requires a scope")
189
+ messages = freeze_sequence(self, "messages", Message)
190
+ if not messages or len({m.message_id for m in messages}) != len(messages):
191
+ raise ContractError("A turn requires messages with unique IDs")
192
+ integer_value("revision", self.revision, minimum=1)
193
+ aware_time("timestamp", self.timestamp)
194
+
195
+ @property
196
+ def content_hash(self) -> str:
197
+ payload = [{"message_id": m.message_id, **m.to_wire()} for m in self.messages]
198
+ return hashlib.sha256(canonical_json(payload).encode("utf-8")).hexdigest()
199
+
200
+
201
+ @dataclass(frozen=True, slots=True)
202
+ class SourceRef:
203
+ scope: Scope
204
+ turn_id: str
205
+ message_id: str
206
+ revision: int
207
+ start: int
208
+ end: int
209
+ message_hash: str
210
+
211
+ def __post_init__(self) -> None:
212
+ if not isinstance(self.scope, Scope):
213
+ raise ContractError("Source requires a scope")
214
+ text_value("turn_id", self.turn_id)
215
+ text_value("message_id", self.message_id)
216
+ integer_value("revision", self.revision, minimum=1)
217
+ integer_value("start", self.start)
218
+ integer_value("end", self.end)
219
+ if self.end <= self.start:
220
+ raise ContractError("Source range must be nonempty and increasing")
221
+ if (
222
+ not isinstance(self.message_hash, str)
223
+ or len(self.message_hash) != 64
224
+ or any(c not in "0123456789abcdef" for c in self.message_hash)
225
+ ):
226
+ raise ContractError("Source hash must be a lowercase SHA-256 digest")
227
+
228
+ def extract(self, turn: Turn) -> str:
229
+ """Offsets are Unicode code points, validated against the original revision."""
230
+ if (turn.scope, turn.turn_id, turn.revision) != (self.scope, self.turn_id, self.revision):
231
+ raise ContractError("Source scope or revision mismatch")
232
+ message = next((m for m in turn.messages if m.message_id == self.message_id), None)
233
+ if (
234
+ message is None
235
+ or message.content_hash != self.message_hash
236
+ or self.end > len(message.content)
237
+ ):
238
+ raise ContractError("Source message, hash or range mismatch")
239
+ return message.content[self.start : self.end]
240
+
241
+
242
+ @dataclass(frozen=True, slots=True)
243
+ class Chunk:
244
+ chunk_id: str
245
+ source: SourceRef
246
+ content: str
247
+ chunker_version: str
248
+
249
+ def __post_init__(self) -> None:
250
+ text_value("chunk_id", self.chunk_id)
251
+ text_value("content", self.content)
252
+ text_value("chunker_version", self.chunker_version)
253
+ if not isinstance(self.source, SourceRef):
254
+ raise ContractError("Chunk requires a source reference")
255
+ if len(self.content) != self.source.end - self.source.start:
256
+ raise ContractError("Chunk length does not match source range")
257
+
258
+ def validate_source(self, turn: Turn) -> None:
259
+ if self.source.extract(turn) != self.content:
260
+ raise ContractError("Chunk content does not match original source")
261
+
262
+
263
+ @dataclass(frozen=True, slots=True)
264
+ class Pin:
265
+ scope: Scope
266
+ key: str
267
+ value: str
268
+ origin: str
269
+ revision: int = 1
270
+ effective_at: datetime = field(default_factory=lambda: datetime.now(UTC))
271
+ expires_at: datetime | None = None
272
+ source: SourceRef | None = None
273
+
274
+ def __post_init__(self) -> None:
275
+ if not isinstance(self.scope, Scope):
276
+ raise ContractError("Pin requires a scope")
277
+ for name in ("key", "value", "origin"):
278
+ text_value(name, getattr(self, name))
279
+ integer_value("revision", self.revision, minimum=1)
280
+ aware_time("effective_at", self.effective_at)
281
+ if self.expires_at is not None:
282
+ aware_time("expires_at", self.expires_at)
283
+ if self.expires_at <= self.effective_at:
284
+ raise ContractError("Pin expiry must follow its effective time")
285
+ if self.source is not None and (
286
+ not isinstance(self.source, SourceRef) or self.source.scope != self.scope
287
+ ):
288
+ raise ContractError("Pin source scope mismatch")
289
+
290
+ def is_active(self, at: datetime) -> bool:
291
+ aware_time("at", at)
292
+ return self.effective_at <= at and (self.expires_at is None or at < self.expires_at)
293
+
294
+
295
+ @dataclass(frozen=True, slots=True)
296
+ class KeyedPins:
297
+ scope: Scope
298
+ pins: tuple[Pin, ...] = ()
299
+
300
+ def __post_init__(self) -> None:
301
+ if not isinstance(self.scope, Scope):
302
+ raise ContractError("Pins require a scope")
303
+ pins = freeze_sequence(self, "pins", Pin)
304
+ if any(pin.scope != self.scope for pin in pins):
305
+ raise ContractError("Pin scope mismatch")
306
+ if len({pin.key for pin in pins}) != len(pins):
307
+ raise ContractError("Pin keys must be unique; supersede old values before assembly")
308
+ object.__setattr__(self, "pins", tuple(sorted(pins, key=lambda pin: pin.key)))
309
+
310
+ def upsert(self, pin: Pin, *, expected_revision: int | None = None) -> "KeyedPins":
311
+ """Immutable application-side update; transactional persistence remains C07."""
312
+ if not isinstance(pin, Pin) or pin.scope != self.scope:
313
+ raise ContractError("Pin scope mismatch")
314
+ existing = next((item for item in self.pins if item.key == pin.key), None)
315
+ if expected_revision is not None:
316
+ integer_value("expected_revision", expected_revision, minimum=1)
317
+ if existing is None:
318
+ if expected_revision is not None or pin.revision != 1:
319
+ raise ContractError("New pins must start at revision one")
320
+ elif expected_revision != existing.revision or pin.revision != existing.revision + 1:
321
+ raise ContractError("Pin revision conflict")
322
+ return KeyedPins(self.scope, tuple(p for p in self.pins if p.key != pin.key) + (pin,))
323
+
324
+
325
+ @dataclass(frozen=True, slots=True)
326
+ class ContextBlock:
327
+ kind: BlockKind
328
+ content: str
329
+ sources: tuple[SourceRef, ...] = ()
330
+ prefix_stable: bool = False
331
+ text_tokens: int | None = None
332
+
333
+ def __post_init__(self) -> None:
334
+ try:
335
+ object.__setattr__(self, "kind", BlockKind(self.kind))
336
+ except (ValueError, TypeError):
337
+ raise ContractError("Unsupported block kind") from None
338
+ text_value("content", self.content, nonempty=False)
339
+ freeze_sequence(self, "sources", SourceRef)
340
+ if type(self.prefix_stable) is not bool:
341
+ raise ContractError("prefix_stable must be a boolean")
342
+ if self.text_tokens is not None:
343
+ integer_value("text_tokens", self.text_tokens)
344
+
345
+ @property
346
+ def required(self) -> bool:
347
+ return self.kind in (BlockKind.SYSTEM, BlockKind.PINNED, BlockKind.QUESTION)
348
+
349
+
350
+ @dataclass(frozen=True, slots=True)
351
+ class ChatRequest:
352
+ messages: tuple[Message, ...]
353
+ tools: tuple[ToolDefinition, ...] = ()
354
+
355
+ def __post_init__(self) -> None:
356
+ messages = freeze_sequence(self, "messages", Message)
357
+ tools = freeze_sequence(self, "tools", ToolDefinition)
358
+ if not messages or len({m.message_id for m in messages}) != len(messages):
359
+ raise ContractError("Request requires messages with unique IDs")
360
+ if len({tool.name for tool in tools}) != len(tools):
361
+ raise ContractError("Tool definitions must have unique names")
362
+ pending: set[str] = set()
363
+ seen: set[str] = set()
364
+ for message in messages:
365
+ if message.role == Role.TOOL:
366
+ if message.tool_call_id not in pending:
367
+ raise ContractError("Tool result has no pending call")
368
+ pending.remove(message.tool_call_id)
369
+ else:
370
+ if pending:
371
+ raise ContractError("Tool results must complete before subsequent messages")
372
+ for call in message.tool_calls:
373
+ if call.call_id in seen:
374
+ raise ContractError("Tool call IDs must be unique across a request")
375
+ seen.add(call.call_id)
376
+ pending.add(call.call_id)
377
+ if pending:
378
+ raise ContractError("Request contains incomplete tool calls")
379
+
380
+ def to_wire(self) -> dict:
381
+ result = {"messages": [message.to_wire() for message in self.messages]}
382
+ if self.tools:
383
+ result["tools"] = [tool.to_wire() for tool in self.tools]
384
+ return result