crp.core¶
Auto-generated reference for the crp.core subpackage.
core¶
crp.core ¶
Core protocol - orchestrator, task intent, windows, sessions, config, errors.
BatchResult dataclass ¶
Result for one item in a batch operation.
ConfigurationResolver ¶
Resolve configuration through the 5-layer hierarchy.
resolve(**init_kwargs) ¶
Build a CRPConfig from layers 1-4.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
**init_kwargs | Any | Layer 4 overrides from Client() constructor. | {} |
Raises:
| Type | Description |
|---|---|
ValueError | If any resolved value is out of bounds. |
CRPConfig dataclass ¶
Resolved CRP configuration.
Constructed via :class:ConfigurationResolver.
enabled property ¶
Return the enabled.
max_continuations property ¶
Return the max continuations.
log_envelopes property ¶
Return the log envelopes.
max_dispatch_rate property ¶
Return the max dispatch rate.
session_timeout property ¶
Return the session timeout.
max_windows_per_session property ¶
Return the max windows per session.
max_total_input_tokens property ¶
Return the max total input tokens.
max_total_output_tokens property ¶
Return the max total output tokens.
get(key, default=None) ¶
Return the resolved config value for key, or default if absent.
lock() ¶
Lock immutable fields - called after session starts.
update(overrides) ¶
Apply mutable-only overrides (Layer 5: runtime configure).
Raises ValueError if attempting to change an immutable field after lock().
BudgetExhaustedError ¶
Bases: CRPError
Raised when a session-level budget cap is exceeded.
Covers windows-per-session, total input tokens, total output tokens, and rate-limit budgets. details typically contains limit, used, and optionally requested.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
message | str | Human-readable description. | 'Budget exhausted' |
**details | Any | Structured context for logging and headers. | {} |
CRPError ¶
Bases: Exception
Base exception for all CRP errors.
Each error carries a numeric code (1001-1031) per §6.8, a human-readable message, and an optional details dict for structured logging and downstream Gateway responses.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
code | int | ErrorCode | Numeric error code or | required |
message | str | Human-readable error description. | required |
details | dict[str, Any] | None | Optional structured context (limits, ids, reasons, etc.). | None |
SessionClosedError ¶
SessionExpiredError ¶
CachedResult dataclass ¶
Cached dispatch result for idempotency.
is_expired property ¶
Return whether this object is expired.
RequestDeduplicator ¶
Idempotency key manager with TTL-based cache.
Concurrency: session-level write lock for mutations, reads are lock-free.
stats property ¶
Return the stats.
cache_size property ¶
Return the current cache count.
compute_key(system_prompt, task_input, **kwargs) ¶
Compute idempotency key from request parameters.
check(key) ¶
Check if a non-expired result exists for this key.
Returns cached result or None.
store(key, output, report=None) ¶
Store a dispatch result in the cache.
invalidate(key) ¶
Remove a cached entry. Returns True if found and removed.
clear() ¶
Clear all cached entries. Returns count of entries cleared.
SessionLock ¶
Read-write lock with ordering for session operations.
Lock ordering (deadlock prevention): 1. Cold Storage Lock 2. Model Registry Lock 3. Session Write Lock
Write lock for: dispatch, ingest, configure, reset, close Read lock (no-op) for: session_status, estimate, preview
CRPOrchestrator ¶
Bases: DispatchMixin, ExtractionMixin
Central dispatcher managing window lifecycle and protocol operations.
Integrates ALL CRP subsystems: - ExtractionPipeline (graduated 6-stage extraction on outputs + ingestion) - WarmStateStore (persistent fact accumulation, ranking, aging) - CKF (ContextualKnowledgeFabric - 4-mode retrieval) - Envelope builder (6-phase budget-aware fact packing) - ContinuationManager (3-way termination, gap analysis, stitching) - SecurityManager (input validation, injection detection, RBAC, etc.)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
provider | LLMProvider | None | Primary LLM provider. | None |
config | CRPConfig | None | Optional | None |
llm | LLMProvider | None | Alias for | None |
model | str | None | Model name for auto-detection when no provider is given. | None |
**init_kwargs | Any | Extra configuration passed to | {} |
emitter property ¶
Protocol event bus for subscribing to events.
feedback property ¶
Access the feedback loop for fact confidence adjustments.
parallel property ¶
Parallel fan-out engine for multi-task dispatch.
session property ¶
Return the session.
dag property ¶
Return the dag.
config property ¶
Return the config.
warm_store property ¶
Access the WarmStateStore for direct fact operations.
ckf property ¶
Access the CKF for 4-mode retrieval queries.
compliance_audit property ¶
Access the compliance audit trail for inspection (§7.14).
pii_scanner property ¶
Access the PII scanner (§7.12).
consent_manager property ¶
Access the consent manager (§7.13).
processing_records property ¶
Access the processing record keeper - GDPR Art. 30 (§7.13).
retention_manager property ¶
Access the retention manager (§7.12).
lineage_tracker property ¶
Access the data lineage tracker (§7.12).
human_oversight property ¶
Access the human oversight controller - EU AI Act Art. 14 (§7.13).
compliance_reporter property ¶
Access the compliance reporter (§7.15).
risk_classifier property ¶
Access the risk classifier - EU AI Act Art. 6 (§7.15).
extraction_pipeline property ¶
Access the extraction pipeline for configuration.
dispatch_with_strategy(strategy, system_prompt, task_input, **kwargs) ¶
Route a dispatch to the named relay strategy.
Unknown strategies fall back to push-based dispatch with a warning.
session_status() ¶
Get live session metrics.
Returns:
| Type | Description |
|---|---|
SessionStatus | A |
Raises:
| Type | Description |
|---|---|
SessionClosedError | If the session is closed. |
SessionExpiredError | If the session has expired. |
RateLimitExceededError | If RBAC denies READ_STATE. |
estimate_session(system_prompt='', task_input='', *, planned_dispatches=1, avg_output_tokens=None) ¶
Pre-flight cost estimation WITHOUT executing LLM calls.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
system_prompt | str | Representative system prompt for token estimation. | '' |
task_input | str | Representative task input for token estimation. | '' |
planned_dispatches | int | Number of dispatches planned (default: 1). | 1 |
avg_output_tokens | int | None | Expected average output tokens per dispatch. If None, assumes model uses full generation reserve. | None |
Returns:
| Type | Description |
|---|---|
CostEstimate | CostEstimate with token estimates and USD cost (if pricing known). |
preview_envelope(system_prompt, task_input) ¶
Inspect envelope contents WITHOUT dispatching.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
system_prompt | str | System prompt for the dispatch. | required |
task_input | str | User task input. | required |
Returns:
| Type | Description |
|---|---|
EnvelopePreview | An |
Raises:
| Type | Description |
|---|---|
SessionClosedError | If the session is closed. |
SessionExpiredError | If the session has expired. |
RateLimitExceededError | If RBAC denies READ_ENVELOPE. |
configure(**kwargs) ¶
Apply runtime configuration changes (mutable fields only).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
**kwargs | Any | Key-value pairs to update in the resolved config. | {} |
Raises:
| Type | Description |
|---|---|
SessionClosedError | If the session is closed. |
SessionExpiredError | If the session has expired. |
RateLimitExceededError | If RBAC denies CONFIGURE. |
reset_session() ¶
Clear warm state, CKF, event log, DAG. Keeps session open.
Raises:
| Type | Description |
|---|---|
SessionClosedError | If the session is closed. |
SessionExpiredError | If the session has expired. |
RateLimitExceededError | If RBAC denies MANAGE_SESSIONS. |
on(event_type, listener) ¶
Subscribe to a protocol event (convenience wrapper).
Usage::
def on_dispatch(event):
print(f"Dispatch completed: {event.data}")
orch.on("dispatch.completed", on_dispatch)
boost_fact(fact_id, delta=0.1, reason='') ¶
Boost a fact's confidence (positive feedback).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fact_id | str | Fact identifier. | required |
delta | float | Amount to increase confidence. | 0.1 |
reason | str | Optional reason for the feedback. | '' |
penalize_fact(fact_id, delta=-0.2, reason='') ¶
Penalize a fact's confidence (negative feedback).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fact_id | str | Fact identifier. | required |
delta | float | Amount to decrease confidence. | -0.2 |
reason | str | Optional reason for the feedback. | '' |
reject_fact(fact_id, reason='') ¶
Reject a fact entirely (user override).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fact_id | str | Fact identifier. | required |
reason | str | Optional reason for rejection. | '' |
register_provider(provider) ¶
Register an additional LLM provider for fallback routing.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
provider | LLMProvider | Additional provider to register. | required |
close() ¶
Close session - flush warm→cold, persist CKF, zero keys (§2.5).
This method is thread-safe and idempotent.
resume(session_id, provider=None, *, llm=None, data_dir=None, **kwargs) classmethod ¶
Resume a previously closed session by restoring CKF state.
Loads persisted facts from cold storage into a new orchestrator instance, providing cross-session knowledge continuity.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
session_id | str | The session_id from the previous session. | required |
provider | LLMProvider | None | LLM provider to use for the resumed session. | None |
llm | LLMProvider | None | Alias for | None |
data_dir | str | None | Override for CRP_DATA_DIR (default: env or '.'). | None |
**kwargs | Any | Additional arguments forwarded to the orchestrator init. | {} |
Returns:
| Type | Description |
|---|---|
CRPOrchestrator | A new CRPOrchestrator with the previous session's facts loaded. |
Raises:
| Type | Description |
|---|---|
ValidationError | If |
export_state(fmt=None) ¶
Export session state as encrypted bytes (§2.5).
Includes warm store facts, critical state, structural state, and CKF health. Embeddings are NOT exported (§7.11) - recomputed on import.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fmt | str | None | Export format (reserved, currently only 'json' supported). | None |
Returns:
| Type | Description |
|---|---|
bytes | Encrypted bytes (AES-256-GCM with session-bound key). |
Raises:
| Type | Description |
|---|---|
SessionClosedError | If the session is closed. |
SessionExpiredError | If the session has expired. |
RateLimitExceededError | If RBAC denies EXPORT_STATE. |
async_dispatch(system_prompt, task_input, **kwargs) async ¶
Async version of dispatch() - runs in a configurable thread pool.
Uses the orchestrator's own ThreadPoolExecutor (sized via max_threads config) instead of the default asyncio executor, which prevents thread-pool saturation under concurrent load.
Use from async code (FastAPI, asyncio, etc.)::
output, report = await client.async_dispatch(
"You are helpful.", "Explain CRP."
)
async_ingest(raw_text, *, source_label='') async ¶
Async version of ingest() - runs in the orchestrator's thread pool.
async_close() async ¶
Async version of close() - runs in the orchestrator's thread pool.
async_dispatch_stream(system_prompt, task_input, **kwargs) async ¶
Async streaming dispatch - yields StreamEvent objects.
Usage::
async for event in client.async_dispatch_stream(
"You are helpful.", "Explain CRP."
):
if event.event_type == "token":
print(event.data, end="")
ExtractionResult dataclass ¶
Result of zero-LLM ingestion.
StreamEvent dataclass ¶
Events emitted during streaming dispatch (§6.10.5).
CostEstimate dataclass ¶
Pre-flight cost estimate returned by estimate_session().
Implements the cost-estimate.json schema.
Attributes:
| Name | Type | Description |
|---|---|---|
estimated_windows | int | Number of dispatches estimated. |
estimated_input_tokens | int | Total input tokens estimated. |
estimated_output_tokens | int | Total output tokens estimated. |
estimated_cost_usd | float | None | Estimated USD cost, or None if pricing unknown. |
confidence | str | "high", "medium", or "low" depending on input completeness. |
EnvelopePreview dataclass ¶
Envelope inspection result returned by preview_envelope().
Implements the envelope-preview.json schema.
Attributes:
| Name | Type | Description |
|---|---|---|
total_tokens | int | System + task + envelope tokens. |
envelope_tokens | int | Tokens consumed by the constructed envelope. |
generation_reserve | int | Tokens reserved for LLM generation. |
facts_included | int | Facts packed into the envelope. |
facts_available | int | Total facts available in the warm store. |
saturation | float | Envelope token utilisation ratio. |
QualityReport dataclass ¶
Governance summary returned by every dispatch() call.
Implements the quality-report.json schema. output is the COMPLETE, UNMODIFIED LLM output (Axiom 9).
Attributes:
| Name | Type | Description |
|---|---|---|
session_id | str | Session identifier. |
window_id | str | Window identifier for this dispatch. |
output | str | Raw LLM output. |
facts_extracted | int | Number of facts extracted from the output. |
security_flags | SecurityFlags | Security validation results. |
continuation_windows | int | Number of continuation windows triggered. |
envelope_saturation | float | Envelope token utilisation ratio. |
quality_tier | str | None | "S", "A", "B", "C", or "D". |
telemetry | dict[str, Any] | None | Optional per-window telemetry dict. |
RemainingBudget dataclass ¶
Remaining quota for a session.
Attributes:
| Name | Type | Description |
|---|---|---|
windows_remaining | int | None | Windows left before |
input_tokens_remaining | int | None | Input tokens left before |
output_tokens_remaining | int | None | Output tokens left before |
SecurityFlags dataclass ¶
Security validation results - always present in QualityReport (§7.4).
Attributes:
| Name | Type | Description |
|---|---|---|
injection_markers_detected | int | Count of injection patterns found. |
injection_marker_details | list[dict[str, Any]] | Details for each detected marker. |
unicode_normalized | bool | Whether unicode input was normalised. |
control_chars_stripped | int | Number of control characters removed. |
input_truncated | bool | Whether input was truncated to fit budget. |
integrity_violations | int | Count of provenance-chain violations. |
output_injection_facts_penalized | int | Output-side injection detections (§7.5.2). |
SessionHandle dataclass ¶
Session identity and lifetime metadata.
Implements the session-handle.json schema. The session_key is managed separately by SessionBindingManager and is never serialised to JSON or cold storage.
Attributes:
| Name | Type | Description |
|---|---|---|
session_id | str | Unique UUID for this session. |
protocol_version | str | CRP protocol version running. |
capabilities | list[str] | Operations the session supports. |
created_at | float | Unix timestamp of creation. |
expires_at | float | Unix timestamp after which the session is invalid. |
is_expired property ¶
Return True if the current time is past expires_at.
SessionStatus dataclass ¶
Live session metrics returned by session_status().
Implements the session-status.json schema.
Attributes:
| Name | Type | Description |
|---|---|---|
session_id | str | Session identifier. |
windows_completed | int | Number of primary + continuation windows completed. |
total_input_tokens | int | Cumulative input tokens across all windows. |
total_output_tokens | int | Cumulative output tokens across all windows. |
facts_in_warm_state | int | Current number of facts in the warm store. |
overhead_ratio | float | Ratio of continuation windows to total windows. |
remaining_budget | RemainingBudget | None | Remaining quota, or None if no caps configured. |
total_cost | float | None | Estimated USD cost, or None if provider pricing unknown. |
TaskIntent dataclass ¶
Structured task description per §2.3.
All fields are optional - CRP infers missing values from the raw task input. Matches the task-intent.json schema exactly.
Attributes:
| Name | Type | Description |
|---|---|---|
description | str | None | Optional human-readable summary of the task. |
system_prompt | str | None | Optional system prompt override for this dispatch. |
task_input | str | None | The user's actual request / question. |
expected_output_type | OutputType | str | None | Desired response format. |
max_windows | int | None | Maximum continuation windows allowed. |
max_output_tokens | int | None | Maximum tokens to generate in this dispatch. |
output_schema | dict[str, Any] | None | JSON Schema for structured output validation. |
metadata | dict[str, Any] | Arbitrary key-value context for routing or observability. |
TransferType ¶
Bases: str, Enum
What information flows from a parent window to a child (§8.1).
FULL_CONTEXT = 'FULL_CONTEXT' class-attribute instance-attribute ¶
Complete parent response plus envelope.
SUMMARY = 'SUMMARY' class-attribute instance-attribute ¶
Compressed summary (default).
FACTS_ONLY = 'FACTS_ONLY' class-attribute instance-attribute ¶
Deferred facts only, no response content.
RESULT_ONLY = 'RESULT_ONLY' class-attribute instance-attribute ¶
Final answer only (fan-in synthesis).
WindowDAG ¶
Tracks the provenance graph of all windows in a session (§2.4, Axiom 7).
nodes property ¶
Mapping from window ID to WindowNode.
edges property ¶
List of all context-flow edges.
add_node(node) ¶
Register a created window.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
node | WindowNode | Window node to add. | required |
Raises:
| Type | Description |
|---|---|
ValueError | If the window ID already exists. |
set_parent(child_id, parent_id) ¶
Declare a provenance edge: parent contributed facts to child.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
child_id | str | Child window ID. | required |
parent_id | str | Parent window ID. | required |
add_edge(edge) ¶
Record a context-flow edge with transfer metadata (§3.2).
Establishes the parent/child relationship and rejects edges that would introduce a cycle, preserving the acyclic invariant (§3.3).
descendants(window_id) ¶
Return all transitive descendant window IDs.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
window_id | str | Window to descend from. | required |
Returns:
| Type | Description |
|---|---|
set[str] | Set of all descendant window IDs (transitive children). |
get(window_id) ¶
Return the WindowNode for window_id.
roots() ¶
Return windows with no parents (initial windows).
leaves() ¶
Return windows with no children (terminal windows).
ancestors(window_id) ¶
Return all transitive ancestor window IDs.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
window_id | str | Window to ascend from. | required |
Returns:
| Type | Description |
|---|---|
set[str] | Set of all ancestor window IDs (transitive parents). |
window_count() ¶
Return the number of windows in the DAG.
lineage(window_id) ¶
Return the DAG path from a root to window_id (CRP-SPEC-004 §14.2).
Follows the first parent at each step; suitable for emitting CRP-Provenance-Window-Lineage (root -> ... -> current).
detect_cycle() ¶
Return True if the graph contains a cycle (violates §3.3).
Uses a three-colour DFS over all nodes.
clear() ¶
Remove all nodes and edges (session reset).
WindowEdge dataclass ¶
A directed context-flow edge from a parent to a child window (§3.2).
Attributes:
| Name | Type | Description |
|---|---|---|
source_id | str | Parent window ID. |
target_id | str | Child window ID. |
transfer_type | TransferType | Mode of information transfer. |
transferred_tokens | int | Tokens carried across this edge. |
summary_hash | str | Hash of the transferred summary for integrity checks. |
to_dict() ¶
Serialise the edge to a JSON-compatible dict.
WindowMetrics dataclass ¶
Recorded per window for telemetry.jsonl output.
to_dict() ¶
Serialise to a flat dict for JSONL telemetry output.
WindowNode dataclass ¶
Represents a single task window in the DAG (§2.4).
advance(to_state) ¶
Transition to a new state, enforcing the forward-only invariant.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
to_state | WindowState | Target state. | required |
Raises:
| Type | Description |
|---|---|
ValueError | If the transition is not allowed. |
WindowPattern ¶
Bases: str, Enum
How a window relates to its parent(s) in the DAG (§3.1).
LINEAR = 'LINEAR' class-attribute instance-attribute ¶
Single parent, single child chain.
FAN_OUT = 'FAN_OUT' class-attribute instance-attribute ¶
One parent spawns multiple parallel children.
FAN_IN = 'FAN_IN' class-attribute instance-attribute ¶
Multiple parents merge into one child.
BRANCH = 'BRANCH' class-attribute instance-attribute ¶
Exploratory branch that may later merge or terminate.
WindowState ¶
Bases: Enum
Lifecycle states - transitions are strictly forward-only.
dispatch_batch(intents, dispatch_fn=None) ¶
Batch dispatch with sequential execution (parallel fan-out ready).
Each intent should have keys: "system_prompt", "task_input".
ingest_batch(texts, extract_fn=None, task_intent='') ¶
Batch ingestion with per-item extraction.
Returns extraction results per text item.
partition_fan_in_budget(parent_budgets) ¶
Fan-in safety budget is the MINIMUM of all parents (§7.3.4, §6.4).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
parent_budgets | list[float] | Envelope budgets of each parent window. | required |
Returns:
| Type | Description |
|---|---|
float | The conservative minimum budget, or 0.0 if no parents exist. |
select_transfer_type(*, available_budget, parent_response_tokens, summary_tokens, deferred_facts_tokens, reserve_fraction=0.6) ¶
Select a transfer type from token-budget pressure (§8.2).
The reserve_fraction (default 0.60) leaves 40% of the budget for new facts specific to the child's query.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
available_budget | int | Tokens available in the child window. | required |
parent_response_tokens | int | Tokens of the full parent response. | required |
summary_tokens | int | Tokens of a compressed parent summary. | required |
deferred_facts_tokens | int | Tokens of deferred facts. | required |
reserve_fraction | float | Fraction of budget that may be spent on context relay. | 0.6 |
Returns:
| Type | Description |
|---|---|
TransferType | The most complete transfer type that fits within the reserved budget. |
core.app_profile¶
crp.core.app_profile ¶
Application capability contract and strategy discovery (CRP-SPEC-008 extension).
Most applications already have their own context-management and tool-calling strategy before CRP is introduced: a LangChain chain with a set of tools, a LlamaIndex RAG pipeline, a custom sliding-window summariser, etc. Rather than forcing the integrator to throw that away, :class:ApplicationProfile lets CRP discover or be told what the application already does, so CRP can:
- Avoid duplicating existing retrieval (don't build a second vector index if the app already has one).
- Delegate tool execution to the application's own tool registry when possible.
- Choose a compatible relay strategy (push vs pull vs reflexive) based on the app's actual provider and context budget.
- Record provenance accurately for audit (this fact came from the app's existing RAG, not from CRP's warm store).
The profile is intentionally a contract, not a config file. It can be:
- Declared by the integrator (authoritative).
- Derived from a message list and an optional tool list (heuristic).
- Detected from a framework object such as a LangChain
BaseCallbackHandleror LlamaIndexQueryEngine(best-effort).
FrameworkKind ¶
Bases: str, Enum
Known application/framework integrations.
ContextStrategy ¶
Bases: str, Enum
How the application manages long conversation context.
ProviderKind ¶
Bases: str, Enum
Broad provider families CRP knows how to govern.
ToolInfo dataclass ¶
A tool the application already exposes.
ApplicationProfile dataclass ¶
Capability contract for an existing application.
Fields are optional; missing fields mean "unknown / not provided" and CRP falls back to its own defaults.
detect_framework(messages) ¶
Heuristically detect the framework from message content/structure.
detect_context_strategy(messages) ¶
Heuristically detect how the app manages context.
This is best-effort: we look at message-count patterns, presence of summarising system prompts, and explicit metadata.
build_profile_from_messages(messages, *, tools=None, provider=None, context_window=None) ¶
Derive an application profile from observed messages and tools.
The derived profile is observed, not authoritative. Use it for negotiation and audit, but prefer an explicitly declared profile when accuracy matters.
core.batch¶
crp.core.batch ¶
Batch dispatch and ingest operations (§6.6).
dispatch_batch: Fan-out multiple intents in parallel. ingest_batch: Batch ingestion with boundary reconciliation.
BatchResult dataclass ¶
Result for one item in a batch operation.
dispatch_batch(intents, dispatch_fn=None) ¶
Batch dispatch with sequential execution (parallel fan-out ready).
Each intent should have keys: "system_prompt", "task_input".
ingest_batch(texts, extract_fn=None, task_intent='') ¶
Batch ingestion with per-item extraction.
Returns extraction results per text item.
core.circuit_breaker¶
crp.core.circuit_breaker ¶
core.config¶
crp.core.config ¶
ConfigurationResolver - 5-layer hierarchy, feature flags (§06 §6.12).
Resolution order (later overrides prior): Layer 1: Hardcoded defaults (this file) Layer 2: Environment variables (CRP_* prefix) Layer 3: Config file (YAML/.env) - future Layer 4: Client() init kwargs Layer 5: Runtime configure() call
CRPConfig dataclass ¶
Resolved CRP configuration.
Constructed via :class:ConfigurationResolver.
enabled property ¶
Return the enabled.
max_continuations property ¶
Return the max continuations.
log_envelopes property ¶
Return the log envelopes.
max_dispatch_rate property ¶
Return the max dispatch rate.
session_timeout property ¶
Return the session timeout.
max_windows_per_session property ¶
Return the max windows per session.
max_total_input_tokens property ¶
Return the max total input tokens.
max_total_output_tokens property ¶
Return the max total output tokens.
get(key, default=None) ¶
Return the resolved config value for key, or default if absent.
lock() ¶
Lock immutable fields - called after session starts.
update(overrides) ¶
Apply mutable-only overrides (Layer 5: runtime configure).
Raises ValueError if attempting to change an immutable field after lock().
ConfigurationResolver ¶
Resolve configuration through the 5-layer hierarchy.
resolve(**init_kwargs) ¶
Build a CRPConfig from layers 1-4.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
**init_kwargs | Any | Layer 4 overrides from Client() constructor. | {} |
Raises:
| Type | Description |
|---|---|
ValueError | If any resolved value is out of bounds. |
core.context_enforcer¶
crp.core.context_enforcer ¶
Context enforcement pipeline (§7.14.4, introduced in CRP 2.2).
CRP 2.1 defined the vocabulary (ContextSource, ContextManifest). CRP 2.2 defines the enforcement point - the single choke-point that every envelope assembly flows through when a manifest is attached.
Pipeline::
observed sources + manifest
├─ manifest-signature verify (fails → CONTEXT_MANIFEST_INVALID)
├─ manifest expiry check (fails → CONTEXT_MANIFEST_INVALID)
├─ attestation mismatch scan (mismatches → audit events)
├─ injection-signal scan (trusted) (hits → audit events)
└─ return EnforcementResult (never raises by default; policy decides)
The enforcer is deliberately non-raising for mismatches - in production the right behaviour varies (block, downgrade trust, alert, ignore). Raising is the integrator's decision via policy=.
every observed source is either
(a) declared in a signed, unexpired manifest, or (b) captured in an audit event emitted to the configured sink.
The "global application context" the pessimist asked for lives here: once this enforcer is wired into assemble_messages, no CRP request to an LLM can occur without the orchestrator having seen and classified every non-benign source.
EnforcementPolicy ¶
Bases: str, Enum
What the enforcer does when a violation is detected.
OBSERVE- record audit events, never raise. Default for dev.WARN- log a WARNING for every violation, still pass through.REJECT- raise :class:crp.core.errors.CRPErrorwith :attr:CONTEXT_ATTESTATION_MISMATCHor :attr:CONTEXT_MANIFEST_INVALID. Default for prod.
InjectionSignal dataclass ¶
A suspicious pattern found inside a source that was labelled TRUSTED.
Trust labels are declarative - a developer marking a SYSTEM_PROMPT as TRUSTED doesn't make its contents safe (they may have templated untrusted input into it). This record is emitted to the audit sink so the gap between declared and actual trust is observable.
to_audit_event() ¶
Convert this signal into a CONTEXT_TRUST_VIOLATION audit event.
AuditSink ¶
Protocol for audit-event consumers.
Implementations MUST be thread-safe. The enforcer calls emit() once per event with a JSON-ready dict. Events are never re-emitted.
emit(event) ¶
Emit an audit event to the sink.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
event | dict[str, Any] | JSON-ready audit event dict. | required |
Raises:
| Type | Description |
|---|---|
NotImplementedError | Subclasses must implement this method. |
LoggingAuditSink ¶
InMemoryAuditSink ¶
Bases: AuditSink
Thread-safe ring buffer for tests and cross-turn replay.
The most recent max_events events are retained; older events are dropped silently. Iterating the sink is snapshot-consistent.
EnforcementResult dataclass ¶
Outcome of a single enforcement pass.
Always non-None even if everything passed - consumers can inspect ok, mismatches, injection_signals to decide next steps.
has_violations property ¶
Return whether this object has violations.
ContextEnforcer ¶
Single enforcement choke-point for CRP 2.2.
Construct once per application / orchestrator and reuse across turns::
enforcer = ContextEnforcer(
policy=EnforcementPolicy.REJECT,
sink=LoggingAuditSink(),
manifest_secret=os.environ["CRP_MANIFEST_SECRET"].encode(),
)
result = enforcer.check(manifest, observed)
if not result.ok:
...
The same instance can be installed as the process-wide default via :func:set_default_enforcer so libraries (dispatch router, CKF, warm store) can find it without explicit plumbing.
with_sink(sink) ¶
Return a shallow copy with a different sink (ergonomic swap).
check(manifest, observed, *, emit=True) ¶
Run the full enforcement pipeline.
Accepts either bare :class:ContextSource objects (no content scanning) or :class:_ObservedContent bundles (source + text, which enables injection-signal detection for TRUSTED sources).
If emit is True (default), audit events are pushed to the sink. Pass emit=False for dry-run analysis.
check_messages(messages, manifest=None, *, session_id=None, derive_manifest=False, emit=True) ¶
Re-run the enforcement pipeline against a full chat history (CRP 2.3).
For every message the enforcer derives a :class:ContextSource (system/user/assistant/tool → kind+trust) and an observed-content bundle, so both attestation and injection scans run over the actual bytes that will be sent to the model. Tool-call loops benefit most: tool_result payloads are re-validated every turn instead of only at first assembly.
When derive_manifest=True and manifest is None, a fresh manifest is derived from the messages themselves (unsigned, one source per message). This is the "belt-and-braces" path for integrators who want a per-turn audit record even if they never authored a manifest.
check_tool_result(content, *, source=None, manifest=None, tool_call_id=None, tool_name=None, emit=True) ¶
Validate a single tool-call / function-call result (CRP 2.3).
Runs the same pipeline as :meth:check but bounded to one payload, so agent loops can re-validate each tool response individually. If source is not supplied, a derived FUNCTION_CALL source is constructed with an UNKNOWN trust level (tool outputs are never implicitly trusted).
detect_injection_signals(content, source, *, only_trusted=True) ¶
Scan content for patterns that contradict the source's declared trust.
By default only sources with trust_level == TRUSTED are scanned - untrusted content is expected to contain arbitrary text and scanning it would produce noise. Pass only_trusted=False to scan everything (useful for a red-team sweep).
Returns an empty list if the source is skipped or no signals match.
observed_content(source, content='') ¶
Build an _ObservedContent bundle for :meth:ContextEnforcer.check.
Kept as a function (rather than exporting the class) so the public surface area stays minimal - the bundle is an implementation detail.
default_enforcer() ¶
Return the installed process-wide enforcer, or None.
On first call, honours the CRP_DEFAULT_ENFORCEMENT environment variable (observe|warn|reject|off) as a safety net when :func:set_default_enforcer was never called. Explicit calls to :func:set_default_enforcer always take precedence - env auto-install only fires when no enforcer has been installed yet.
set_default_enforcer(enforcer) ¶
Install enforcer as the process-wide default. Returns the previous one.
Marks env auto-install as "done" so a later default_enforcer() call does not clobber an explicit None.
core.context_source¶
crp.core.context_source ¶
Context-source provenance primitives (§7.14.3, introduced in CRP 2.1).
Background¶
The Decision Provenance Engine (crp/provenance/) already classifies every LLM output claim as CONTEXT_GROUNDED | PARAMETRIC | MIXED | UNCERTAIN. That is output-side provenance - where did the answer come from, relative to the envelope?
This module adds the symmetric input-side primitive: where did the envelope content itself come from? Was a fact retrieved from a vector DB, pulled from a relational database, returned by an MCP tool, typed by the end user, or is it model-parametric knowledge?
Without this, an organisation running CRP cannot answer ISO/IEC 42001 §4.1 (Context of the organisation), GDPR Art. 30 (Records of Processing), or EU AI Act Art. 10 (Data governance) truthfully.
Design¶
Three types:
- :class:
SourceKind- a closed enumeration of upstream source categories. Closed intentionally: if new kinds appear in the wild, they land here via an RFC so auditors have a stable vocabulary. - :class:
ContextSource- an immutable record attached to facts and messages. Records what kind of source supplied the content, who declared it (a signed manifest, an observed channel, or a heuristic parser), and what we know about its sensitivity, region, and retrieval policy. - :class:
ContextManifest- a customer-authored declarative catalogue of the sources their application intends to use. Observed sources that do not appear in the manifest are flaggedUNATTESTEDand emitted to the audit log as :data:CONTEXT_ATTESTATION_MISMATCHevents.
This module is pure-data; no I/O, no network, no LLM calls. Consumers (the envelope builder, dispatch router, Decision Provenance Engine, and the crp-comply compliance-reporting layer) integrate it at their own pace - every new field is optional and defaults preserve v2.0 behaviour.
References¶
- ISO/IEC 42001:2023 §4.1–4.2 (Context of the organisation)
- EU AI Act Regulation (EU) 2024/1689, Art. 10 (Data and data governance)
- GDPR Art. 30 (Records of processing activities)
- NIST AI RMF 1.0, MAP 4 (Context mapping)
SourceKind ¶
Bases: str, Enum
Closed enumeration of upstream context-source categories.
Stable vocabulary - additions require an RFC so downstream auditors do not receive novel strings without warning.
USER_TURN = 'user_turn' class-attribute instance-attribute ¶
End-user's direct chat input.
SYSTEM_PROMPT = 'system_prompt' class-attribute instance-attribute ¶
Developer-authored system/instruction prompt.
DEVELOPER_PROMPT = 'developer_prompt' class-attribute instance-attribute ¶
Messages from a developer role (OpenAI-style role=developer).
RAG_RETRIEVAL = 'rag_retrieval' class-attribute instance-attribute ¶
Retrieval-augmented generation chunk (generic; use VECTOR_DB or DATABASE when the backend is known).
VECTOR_DB = 'vector_db' class-attribute instance-attribute ¶
Vector-database retrieval (Pinecone, Weaviate, pgvector, etc.).
DATABASE = 'database' class-attribute instance-attribute ¶
Relational/NoSQL database read.
KNOWLEDGE_GRAPH = 'knowledge_graph' class-attribute instance-attribute ¶
Structured knowledge graph (Neo4j, RDF, etc.).
MCP_TOOL = 'mcp_tool' class-attribute instance-attribute ¶
Model Context Protocol server tool invocation.
FUNCTION_CALL = 'function_call' class-attribute instance-attribute ¶
OpenAI/Anthropic function-calling result returned to the model.
WEB_SEARCH = 'web_search' class-attribute instance-attribute ¶
Live web-search result.
FILE_UPLOAD = 'file_upload' class-attribute instance-attribute ¶
User-uploaded document.
AGENT_MEMORY = 'agent_memory' class-attribute instance-attribute ¶
Agent-framework conversational memory store.
CKF_RETRIEVAL = 'ckf_retrieval' class-attribute instance-attribute ¶
CRP Contextual Knowledge Fabric retrieval (internal).
WARM_STORE = 'warm_store' class-attribute instance-attribute ¶
CRP warm-store fact (internal, recycled from an earlier window).
PARAMETRIC = 'parametric' class-attribute instance-attribute ¶
Model-internal knowledge - not a retrievable upstream source.
UNATTESTED = 'unattested' class-attribute instance-attribute ¶
Content whose kind could not be determined; flagged for audit.
SourceOrigin ¶
Bases: str, Enum
How this :class:ContextSource record came into existence.
DECLARED = 'declared' class-attribute instance-attribute ¶
Explicitly registered via :class:ContextManifest.
OBSERVED = 'observed' class-attribute instance-attribute ¶
Explicitly attached by the caller (e.g. tool-call plumbing that knows it is returning an MCP tool result).
HEURISTIC = 'heuristic' class-attribute instance-attribute ¶
Inferred by :func:detect_source_kind from marker patterns in the message content. Lowest trust - surfaced in audit reports for review.
TrustLevel ¶
Bases: str, Enum
Caller's trust posture for the source.
Not a security boundary - this is an audit signal. Enforcement (ACLs, redaction, refusal) is the integrator's responsibility.
ContextSource dataclass ¶
Immutable record describing where a piece of envelope content came from.
Attached optionally to :class:crp.Fact via its source field and to role=tool / role=user messages by the dispatch router.
Every field except kind and source_id is optional. Defaults preserve v2.0 behaviour - callers that do not opt in see no change.
Parameters¶
kind A :class:SourceKind value. Closed enumeration. source_id Stable handle for the source (e.g. "acme-hr-policies-vdb", "mcp://atlassian/jira-search"). Must be non-empty and ≤ 256 chars. origin :class:SourceOrigin - DECLARED (from a signed manifest), OBSERVED (plumbed in by the caller), or HEURISTIC (detected by pattern-match). trust_level Caller's trust posture. Default UNKNOWN. contains_pii Tri-state: True, False, or None (unknown). Used by GDPR deliverables and by the EU AI Act Art. 10 data-governance report. sensitivity Free-form sensitivity label (e.g. "confidential", "public", "restricted"). Not normalised - customer vocabulary passes through. region Geographic region of the source ("eu-west-1", "us-east-2", "on-prem-dc1"). Enables cross-border-transfer audits. retrieval_query For RAG/DB/search sources: the query string used. Stored verbatim for the Source Grounding Engine. Truncated to 512 chars. retrieved_at UTC timestamp of retrieval. upstream_uri Stable URI pointer back to the source record if available. declared_by_manifest_id Links the record back to a :class:ContextManifest when origin == DECLARED. metadata Integrator-defined extras. Size-limited below.
ManifestValidationError ¶
Bases: ValueError
Raised when a :class:ContextManifest cannot be loaded or verified.
ContextManifest dataclass ¶
Customer-authored declaration of upstream context sources.
Signed with HMAC-SHA256 over a canonical JSON representation. The signature is advisory - it proves the declaration has not been tampered with since signing, but CRP does not manage the signing key.
Minimal usage::
manifest = ContextManifest(system_id="resume-rank-v1", customer_id="acme")
manifest.add(ContextSource(
kind=SourceKind.VECTOR_DB,
source_id="acme-hr-policies-vdb",
origin=SourceOrigin.DECLARED,
trust_level=TrustLevel.TRUSTED,
contains_pii=True,
region="eu-west-1",
))
manifest.sign(secret=os.environ["CRP_MANIFEST_SECRET"].encode())
blob = manifest.to_json() # persist / ship to proxy
add(source) ¶
Append a :class:ContextSource to the manifest.
The source's origin is forced to DECLARED and the manifest id is stamped into declared_by_manifest_id. Invalidates any existing signature.
sign(secret) ¶
Sign the manifest with HMAC-SHA256 and return the hex digest.
verify(secret) ¶
Verify the HMAC signature in constant time.
Returns True iff signature is present and matches.
is_expired(now=None) ¶
Return True if the manifest has passed its expiry timestamp.
declared_kinds() ¶
Set of :class:SourceKind values that appear in the manifest.
declared_source_ids() ¶
Return the set of non-empty source IDs declared in the manifest.
find(source_id) ¶
Return the declared source with source_id, or None if absent.
to_json() ¶
Serialise to JSON (includes signature if present).
from_json(blob) classmethod ¶
Create a new instance from a JSON string or object.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
blob | str | bytes | The blob value. | required |
Returns:
| Type | Description |
|---|---|
ContextManifest |
|
AttestationMismatch dataclass ¶
One audit-log row describing a discrepancy between observed and declared sources.
Emitted whenever :func:check_attestation detects that an observed :class:ContextSource has no counterpart in the current :class:ContextManifest. Consumed by the dispatch router's audit pipeline (§7.14.2) and by the compliance layer's attestation report.
to_audit_event() ¶
Serialise into the §7.14.2 audit-event envelope shape.
detect_source_kind(content, *, role=None, default=SourceKind.UNATTESTED) ¶
Heuristically classify content into a :class:ContextSource.
The resulting :class:ContextSource always has origin=HEURISTIC and trust_level=UNKNOWN. Callers that know the true origin should construct the record directly rather than relying on this function.
Parameters¶
content Raw message text. role Optional OpenAI-style chat role hint ("user", "system", "developer", "tool", "function"). If provided, strong role-based signals win over content-based ones. default :class:SourceKind to fall back to if nothing matches.
Returns¶
ContextSource With source_id set to a stable fingerprint of the detected kind plus a short content hash.
check_attestation(observed, manifest, *, now=None) ¶
Return the set of mismatches between observed sources and manifest.
The matching rule is deliberately strict:
- No manifest → every observed source that is not obviously benign (
USER_TURN,SYSTEM_PROMPT,DEVELOPER_PROMPT,PARAMETRIC) yields ano_manifestmismatch. - Expired manifest → every observed source yields a
manifest_expiredmismatch. - Otherwise, observed
source_idmust match a declaredsource_idor observedkindmust be present indeclared_kinds(). A missingsource_idmatch falls back to a kind-only match and yields anunattested_source_idrow if thesource_idis non-empty; a missing kind match yieldsunattested_kind.
Heuristic and declared sources are both checked - customers who want to suppress heuristic noise should declare their sources.
iso8601(ts) ¶
UTC ISO-8601 string for an epoch timestamp (audit log convenience).
core.context_tools¶
crp.core.context_tools ¶
Tool-mediated context relay - pull-based architecture (§20).
Instead of pre-loading ALL context into the prompt (push model), CRP exposes its knowledge stores as callable tools. The LLM requests context on demand during generation, consuming only what it needs.
This is fundamentally different from RAG/prompt injection: - Push (old): Pre-compute all context → stuff into prompt → generate - Pull (new): Give LLM task + tools → LLM decides what it needs → CRP serves on demand
The module provides: 1. Tool definitions in OpenAI-compatible format 2. A ContextToolExecutor that routes tool calls to CKF/WarmStore 3. Integration with the iterative dispatch loop in the orchestrator
ToolCall dataclass ¶
A single tool call extracted from an LLM response.
ToolResult dataclass ¶
Result of executing a tool call.
ContextToolExecutor ¶
Routes tool calls to CRP subsystems and returns results.
Wired to: - WarmStateStore: ranked facts, structural state, fact lookup - CKF: semantic/graph retrieval, community queries - Embedding function: for semantic search - Continuation state: gap analysis, directives
calls_executed property ¶
Return the calls executed.
total_tokens_served property ¶
Return the total tokens served.
update_continuation_state(state) ¶
Update continuation state (called between windows).
execute(tool_call) ¶
Execute a single tool call and return the result.
execute_batch(tool_calls) ¶
Execute multiple tool calls and return results.
build_tool_system_prompt(original_system, fact_count) ¶
Augment the system prompt with context-tool usage guidance.
Tells the LLM that it has access to CRP context tools and should use them to retrieve verified information instead of relying on parametric knowledge alone.
tool_results_to_messages(assistant_message, results) ¶
Build the message sequence for a tool call round-trip.
Returns [assistant_msg_with_tool_calls, tool_result_1, tool_result_2, ...]. This is the OpenAI-compatible format for continuing generation after tool calls.
core.dispatch_router¶
crp.core.dispatch_router ¶
Dispatch router mixin - all CRP dispatch strategies (§2.5, §6.5).
Extracted from orchestrator.py for maintainability. This mixin provides all dispatch method implementations. The CRPOrchestrator inherits from this class.
StreamEvent dataclass ¶
Events emitted during streaming dispatch (§6.10.5).
ExtractionProgress dataclass ¶
Progress update during extraction pipeline.
ContinuationInfo dataclass ¶
Emitted when a streaming continuation window is triggered.
WindowSummary dataclass ¶
Summary of a completed window.
DispatchMixin ¶
Mixin providing all CRP dispatch strategies.
Methods access orchestrator state via self (multiple inheritance).
dispatch(system_prompt, task_input, **kwargs) ¶
Dispatch a task window to the LLM with full envelope + extraction.
Pipeline: 1. Advisory injection scan on task_input 2. Build envelope from WarmStore facts (6-phase) 3. Assemble messages (Axiom 4 - no modification) 4. Dispatch to LLM 5. Extract facts from output (graduated 6-stage pipeline) 6. Store facts in WarmStore + CKF 7. Continuation loop if wall hit + gap remaining + info flowing
Returns (stitched_output, QualityReport). Output is UNMODIFIED (Axiom 9).
dispatch_with_tools(system_prompt, task_input, *, max_tool_rounds=10, **kwargs) ¶
Dispatch with tool-mediated context relay (pull model).
Instead of pre-loading ALL context into the envelope (push model), this method: 1. Sends the task to the LLM with CRP context tools 2. The LLM requests context on demand via tool calls 3. CRP executes tool calls against WarmStore/CKF 4. Results are fed back, and the LLM continues 5. When the LLM finishes (stop/length), extraction proceeds normally
Falls back to push-based dispatch() if the provider doesn't support tool calling.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
system_prompt | str | System prompt (unmodified per Axiom 4). | required |
task_input | str | User task/prompt. | required |
max_tool_rounds | int | Maximum tool call round-trips (safety cap). | 10 |
**kwargs | Any | Provider-specific overrides. | {} |
Returns:
| Type | Description |
|---|---|
tuple[str, QualityReport] | (output_text, QualityReport) - same interface as dispatch(). |
dispatch_reflexive(system_prompt, task_input, *, max_refinement_passes=2, **kwargs) ¶
Reflexive dispatch - generate first, verify against KB, refine.
Unlike push (all context upfront) or pull (LLM asks for context), reflexive dispatch lets the model generate freely, then CRP fact-checks the output against the knowledge base and sends back targeted corrections. The model refines based on SPECIFIC evidence rather than wading through pre-loaded context.
Flow
Pass 1: Generate with NO envelope (pure parametric knowledge) CRP: Analyze output - find contradictions, unsupported claims Pass 2: Send model its own output + correction payload Model: Refines with surgical precision (Optional Pass 3+ if coverage remains low)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
system_prompt | str | System prompt (unmodified per Axiom 4). | required |
task_input | str | User task/prompt. | required |
max_refinement_passes | int | Max verify-refine cycles (safety cap). | 2 |
**kwargs | Any | Provider-specific overrides. | {} |
Returns:
| Type | Description |
|---|---|
tuple[str, QualityReport] | (output_text, QualityReport) - same interface as dispatch(). |
dispatch_progressive(system_prompt, task_input, **kwargs) ¶
Progressive disclosure - send context INDEX first, details on demand.
Instead of sending ALL facts (push) or none (pull/tools), progressive disclosure sends a compact INDEX of available facts - one-line summaries - so the model sees WHAT knowledge exists at ~10% of the token cost. After initial generation, CRP detects which indexed facts were actually referenced, expands them to full detail, and the model refines with targeted depth.
Flow
Step 1: Build compact context index from WarmStore Step 2: Send task + index to model (model sees all topics cheaply) Step 3: CRP detects which index entries were referenced Step 4: Expand referenced entries to full detail Step 5: Model refines with deep context on referenced items only
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
system_prompt | str | System prompt (unmodified per Axiom 4). | required |
task_input | str | User task/prompt. | required |
**kwargs | Any | Provider-specific overrides. | {} |
Returns:
| Type | Description |
|---|---|
tuple[str, QualityReport] | (output_text, QualityReport) - same interface as dispatch(). |
dispatch_stream_augmented(system_prompt, task_input, *, max_injections=5, **kwargs) ¶
Stream-augmented generation - inject context mid-generation.
The most novel strategy: CRP monitors the LLM's output stream in real-time, buffering sentences. After each sentence, CRP checks if relevant WarmStore facts exist for the topic being generated. When relevant facts are found, generation is PAUSED, the partial output + injected facts are sent as a continuation, and the model resumes - now informed.
The model receives context EXACTLY when it's generating about a relevant topic, not before (wasted) and not only when it asks (pull). This is point-of-need context delivery.
Flow
- Start streaming generation (no envelope)
- Buffer tokens into sentences
- After each sentence: CRP fact-matches against WarmStore
- If relevant NEW facts found → stop generation
- Send partial output + injected facts as continuation prompt
- Resume generation from where model left off
- Repeat until generation completes or max_injections reached
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
system_prompt | str | System prompt (unmodified per Axiom 4). | required |
task_input | str | User task/prompt. | required |
max_injections | int | Maximum mid-stream injections (safety cap). | 5 |
**kwargs | Any | Provider-specific overrides. | {} |
Returns:
| Type | Description |
|---|---|
tuple[str, QualityReport] | (output_text, QualityReport) - same interface as dispatch(). |
dispatch_agentic(system_prompt, task_input, *, max_revision_rounds=2, enable_curation=True, enable_planning=True, **kwargs) ¶
Agentic dispatch - CRP uses the LLM as its internal brain.
╔═══════════════════════════════════════════════════════════╗ ║ §22 AGENTIC COGNITIVE LOOP ║ ╠═══════════════════════════════════════════════════════════╣ ║ 1. ANALYZE - LLM analyzes task complexity & needs ║ ║ 2. PLAN - LLM decomposes complex tasks ║ ║ 3. SYNTHESIZE - LLM pre-processes KB facts ║ ║ 4. ROUTE - LLM picks optimal dispatch strategy ║ ║ 5. GENERATE - Execute chosen dispatch strategy ║ ║ 6. EVALUATE - LLM evaluates output quality ║ ║ 7. REVISE - If evaluation says revision needed ║ ║ 8. CURATE - LLM manages CRP's knowledge base ║ ╚═══════════════════════════════════════════════════════════╝
The LLM is INSIDE CRP. CRP orchestrates all reasoning. Every decision point uses the LLM instead of heuristics.
dispatch_intent(intent) ¶
Dispatch using an explicit TaskIntent object.
dispatch_hierarchical(system_prompt, large_input, task_intent='', **kwargs) ¶
Hierarchical map-reduce dispatch for oversized inputs (§14).
Segments the input, dispatches each segment through the LLM, then iteratively reduces the syntheses. All facts extracted from every segment are stored in the warm store + CKF.
Returns (final_syntheses, QualityReport).
dispatch_batch(intents) ¶
Batch dispatch multiple tasks sequentially (§6.6).
Each intent dict should have "system_prompt" and "task_input" keys. Returns list of (output, QualityReport) tuples.
dispatch_stream(system_prompt, task_input, **kwargs) ¶
Streaming dispatch - yields StreamEvent objects.
Stream contract: - Emits one or more "token" events in generation order. - Emits "extraction" events as facts are discovered. - Emits one "window_complete" event per stream segment. - Emits exactly one "done" event as the final event (or "error"). - Token concatenation MUST produce the same string as dispatch(). - No reordering of token events. - Continues automatically when the provider hits the token wall.
assemble_messages(system_prompt, envelope, task_input, *, manifest=None, observed_sources=None, enforcer=None) ¶
Build the chat message array sent to the LLM.
Structural Injection Defense (§7.5.1): Envelope and task_input are placed in SEPARATE user messages with provenance boundary markers.
Invariants (Axiom 4 - Model Ignorance): - system_prompt is NEVER modified - task_input content is NEVER modified - No CRP-internal protocol metadata is injected
Context enforcement (§7.14.4, CRP 2.2): If manifest or observed_sources is supplied, the configured :class:crp.core.context_enforcer.ContextEnforcer runs before the messages are returned. When none is passed explicitly, the process-wide default set via :func:crp.core.context_enforcer.set_default_enforcer is used. Applications with no enforcer installed pay zero cost - this code path short-circuits.
Parameters¶
manifest Optional :class:~crp.core.context_source.ContextManifest covering this turn. observed_sources Optional iterable of :class:~crp.core.context_source.ContextSource objects (or observed_content() bundles) describing what the envelope exposes. enforcer Optional :class:~crp.core.context_enforcer.ContextEnforcer to use for this call. Overrides the process-wide default.
core.errors¶
crp.core.errors ¶
CRP error taxonomy - codes 1001-1031 per §6.8.
This module defines the structured exception hierarchy used throughout the SDK. Every error carries a numeric code that can be serialised into the crp-error.json schema and propagated across Gateway / Comply boundaries.
ErrorCode ¶
Bases: IntEnum
All CRP error codes used in CRPError.code.
Codes are grouped by subsystem: - 1001-1005: budget and session lifecycle - 1010-1013: validation and security - 1020-1021: LLM provider failures - 1030-1031: state integrity and provenance chain - 1040-1041: context-source attestation (CRP 2.1+, §7.14.3)
CRPError ¶
Bases: Exception
Base exception for all CRP errors.
Each error carries a numeric code (1001-1031) per §6.8, a human-readable message, and an optional details dict for structured logging and downstream Gateway responses.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
code | int | ErrorCode | Numeric error code or | required |
message | str | Human-readable error description. | required |
details | dict[str, Any] | None | Optional structured context (limits, ids, reasons, etc.). | None |
BudgetExhaustedError ¶
Bases: CRPError
Raised when a session-level budget cap is exceeded.
Covers windows-per-session, total input tokens, total output tokens, and rate-limit budgets. details typically contains limit, used, and optionally requested.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
message | str | Human-readable description. | 'Budget exhausted' |
**details | Any | Structured context for logging and headers. | {} |
SessionExpiredError ¶
SessionClosedError ¶
ProviderTimeoutError ¶
ChainVerificationFailedError ¶
core.extraction_facade¶
crp.core.extraction_facade ¶
Extraction facade mixin - fact extraction and ingestion (§2.5).
Extracted from orchestrator.py for maintainability. This mixin provides extraction and ingestion method implementations. The CRPOrchestrator inherits from this class.
ExtractionResult dataclass ¶
Result of zero-LLM ingestion.
ExtractionMixin ¶
Mixin providing CRP extraction and ingestion methods.
Methods access orchestrator state via self (multiple inheritance).
ingest_batch(texts, task_intent='') ¶
Batch ingest multiple texts, returning facts extracted per text (§6.6).
ingest(raw_text, source_label=None) ¶
Ingest raw text without LLM invocation (§2.5).
Runs graduated extraction pipeline on raw_text, adds facts to WarmStateStore and CKF. No LLM call, no window creation, no envelope.
core.facilitator¶
crp.core.facilitator ¶
CRP Facilitator - LLM-in-the-Loop cognitive engine (§22).
╔═══════════════════════════════════════════════════════════════════╗ ║ PARADIGM INVERSION ║ ║ ║ ║ Traditional: User → LLM → Text ║ ║ RAG: User → Context + LLM → Text ║ ║ Agentic: LLM (controller) → Tools → LLM → Output ║ ║ ║ ║ CRP §22: CRP (orchestrator) → LLM (cognitive engine) ║ ║ → CRP (acts on reasoning) → LLM (generates) ║ ║ → CRP (evaluates via LLM) → CRP (curates) ║ ║ ║ ║ The LLM is INSIDE CRP. CRP is the system. ║ ║ The LLM provides reasoning. CRP provides structure. ║ ╚═══════════════════════════════════════════════════════════════════╝
The CRPFacilitator uses the LLM for six cognitive functions:
§22.1 TASK ANALYSIS - Parse task complexity, domain, knowledge needs §22.2 STRATEGY ROUTING - Choose optimal dispatch strategy §22.3 FACT SYNTHESIS - Merge/compress/derive new knowledge from facts §22.4 OUTPUT EVALUATION - Assess output quality against task intent §22.5 MEMORY CURATION - Decide what knowledge to keep/merge/discard §22.6 EXECUTION PLANNING - Decompose complex tasks into sub-tasks
The facilitator wraps each cognitive call in a structured prompt with constrained JSON output. CRP interprets the JSON and acts on it - the LLM never touches CRP's internal state directly.
Inspired by
- Xi et al., "The Rise and Potential of LLM-Based Agents" (2023): Agent = Brain(LLM) + Perception + Action
- Park et al., "Generative Agents" (2023): Observation → Planning → Reflection cognitive loop
- Packer et al., "MemGPT" (2023): LLM manages its own memory via structured interrupts
CRP differs from all three: the LLM is NOT the controller. CRP IS the controller. The LLM is the reasoning module.
TaskAnalysis dataclass ¶
LLM's analysis of a task - §22.1.
StrategyDecision dataclass ¶
LLM's routing decision - §22.2.
SynthesizedKnowledge dataclass ¶
LLM-synthesized knowledge from existing facts - §22.3.
OutputEvaluation dataclass ¶
LLM's evaluation of CRP's output - §22.4.
CurationDecision dataclass ¶
LLM's memory curation recommendations - §22.5.
ExecutionPlan dataclass ¶
LLM's execution plan for complex tasks - §22.6.
PlanStep dataclass ¶
One step in an execution plan.
FacilitatorMetrics dataclass ¶
Telemetry for facilitator cognitive calls.
total_cognitive_ms property ¶
Return the total cognitive ms.
CRPFacilitator ¶
LLM-in-the-Loop cognitive engine for CRP.
The facilitator uses the LLM for CRP's internal reasoning: - Task analysis (what kind of task is this?) - Strategy routing (which dispatch method?) - Fact synthesis (merge/compress knowledge) - Output evaluation (did we actually help?) - Memory curation (what to keep/discard?) - Execution planning (how to handle complex tasks?)
The LLM never touches CRP state directly. It returns structured JSON that CRP interprets and acts upon. This maintains CRP's sovereignty over its own subsystems while leveraging the LLM's reasoning capability at every decision point.
metrics property ¶
Return the metrics.
reset_metrics() ¶
Reset all facilitator timing and token counters to zero.
analyze_task(task_input, system_prompt, fact_count=0) ¶
Use the LLM to analyze what kind of task this is.
Instead of CRP guessing task complexity via regex, the LLM actually understands the task semantically. This feeds into every downstream decision: strategy routing, envelope budget, continuation thresholds, extraction depth.
route_strategy(analysis, fact_count, available_strategies=None) ¶
Use the LLM to choose the optimal dispatch strategy.
Given the task analysis and CRP's current state (fact count, available strategies), the LLM reasons about which approach will produce the best output.
This replaces the hardcoded "always use push" default.
synthesize_facts(facts, task_context='') ¶
Use the LLM to synthesize coherent knowledge from raw facts.
Instead of CRP dumping raw facts into an envelope, the LLM: - Merges overlapping facts into coherent statements - Identifies contradictions - Spots knowledge gaps - Ranks by actual relevance to the task
This produces BETTER context than raw fact packing.
evaluate_output(task_input, output, facts_used=0) ¶
Use the LLM to evaluate CRP's own output.
After generation, the LLM assesses whether the output actually addresses the task. This replaces the heuristic quality scoring (fact count × saturation × token count).
The evaluation feeds back into: - Whether to trigger continuation - Whether to try a different strategy - Quality tier classification - Memory curation decisions
curate_memory(facts, recent_task='') ¶
Use the LLM to curate CRP's knowledge base.
Instead of fixed-interval curation with hardcoded rules, the LLM reviews the knowledge base and makes intelligent decisions about what to keep, merge, or discard.
This is the LLM managing CRP's own memory - the deepest integration of LLM-as-cognitive-engine.
plan_execution(analysis, fact_count=0) ¶
Use the LLM to create an execution plan for complex tasks.
For multi-part or complex tasks, the LLM decomposes the task into steps, each with its own dispatch strategy and context needs. CRP then orchestrates the plan, running each step with the optimal configuration.
This replaces the flat "one dispatch" approach with intelligent multi-step orchestration.
core.idempotency¶
crp.core.idempotency ¶
Request deduplication and idempotency keys (§23).
Same key within TTL returns cached result - no re-dispatch. Concurrency model: session-level write lock for dispatch/ingest/configure, read lock for session_status/estimate/preview. Lock timeout: 30 seconds → CRPError(code=1003).
CachedResult dataclass ¶
Cached dispatch result for idempotency.
is_expired property ¶
Return whether this object is expired.
DeduplicationStats dataclass ¶
Statistics for request deduplication.
RequestDeduplicator ¶
Idempotency key manager with TTL-based cache.
Concurrency: session-level write lock for mutations, reads are lock-free.
stats property ¶
Return the stats.
cache_size property ¶
Return the current cache count.
compute_key(system_prompt, task_input, **kwargs) ¶
Compute idempotency key from request parameters.
check(key) ¶
Check if a non-expired result exists for this key.
Returns cached result or None.
store(key, output, report=None) ¶
Store a dispatch result in the cache.
invalidate(key) ¶
Remove a cached entry. Returns True if found and removed.
clear() ¶
Clear all cached entries. Returns count of entries cleared.
SessionLock ¶
Read-write lock with ordering for session operations.
Lock ordering (deadlock prevention): 1. Cold Storage Lock 2. Model Registry Lock 3. Session Write Lock
Write lock for: dispatch, ingest, configure, reset, close Read lock (no-op) for: session_status, estimate, preview
core.ledger_backends¶
crp.core.ledger_backends ¶
Ledger forwarding sinks (§7.14.5, CRP 2.3).
CRP 2.2's :class:~crp.core.manifest_ledger.ManifestLedger wrote JSONL files to the local filesystem. That is sufficient for a single node but fails in two real operational settings:
- Distributed workers - multiple replicas need their ledger entries aggregated in a central store (SIEM, SQL, data-lake) so auditors see a single timeline.
- Tamper-evidence in depth - the CRP 2.3 hash-chain catches edits on the local JSONL file, but a root-level attacker could still delete the file. Forwarding every record to an append-only external system closes that hole.
These sinks implement the existing :class:~crp.core.context_enforcer.AuditSink protocol (emit(event)) so the same wiring serves enforcement events and ledger writes. Pass the sinks in via ManifestLedger(forward_to=[...]).
All sinks here are pure-stdlib. Production deployments should use HTTPS with rotating tokens and rely on the SIEM's native replay/retention.
NullSink ¶
No-op sink. Useful when a configuration requires a sink object but nothing should be forwarded (e.g. in tests).
emit(event) ¶
Drop the event silently.
JSONLinesFileSink ¶
Append every event as one JSON object per line to a local file.
Thread-safe: all writes are serialised on an internal lock. Intended for replicating ledger records to a location monitored by Splunk Universal Forwarder, Vector, Fluentd, etc.
emit(event) ¶
Append event as one JSON line to the configured file.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
event | dict[str, Any] | Audit or ledger record to persist. | required |
HTTPForwardingSink ¶
POST every event as JSON to an HTTPS endpoint.
Intended for Splunk HEC, Elastic Common Schema ingest, Datadog Logs HTTP intake, Azure Monitor, AWS CloudWatch Logs (via signed gateway), or a bespoke SIEM collector.
Failures are logged at WARNING; the sink never raises out to the caller. Callers that need guaranteed delivery should wrap this in :class:AsyncBufferedSink and pair it with a retry layer.
emit(event) ¶
POST event as JSON to the configured HTTP endpoint.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
event | dict[str, Any] | Audit or ledger record to forward. | required |
AsyncBufferedSink ¶
Background-thread buffer wrapping a downstream sink.
Enqueues events in memory (bounded; default 10k) and drains them on a single worker thread. Keeps ledger writes off the hot path when the downstream sink is slow or flaky.
Call :meth:close on shutdown to flush. Events that cannot be enqueued (queue full) are dropped and logged - operators should size the queue for their write rate.
core.manifest_derive¶
crp.core.manifest_derive ¶
Derive context sources/manifests from raw message content (CRP 2.3).
Problem¶
CRP 2.1/2.2 manifests are declarative - the integrator must author them. A malicious or lazy integrator who never calls :meth:ContextManifest.add receives no enforcement benefit. The enforcer cannot distinguish "no manifest because this turn is genuinely ephemeral" from "no manifest because the caller is hostile".
Solution¶
Given any list of chat messages ([{"role": ..., "content": ...}]), derive a :class:ContextManifest by hashing the content of each message and emitting a :class:ContextSource with role-appropriate defaults:
===================== ========================== =============== role :class:SourceKind :class:TrustLevel ===================== ========================== =============== system SYSTEM_PROMPT TRUSTED developer DEVELOPER_PROMPT TRUSTED user USER_TURN UNKNOWN assistant PARAMETRIC UNKNOWN tool / function FUNCTION_CALL UNKNOWN other UNATTESTED UNKNOWN ===================== ========================== ===============
The resulting manifest is not signed by default - signing it would misrepresent the trust: derivation says "this is what came through the wire", not "a human declared this is safe". When the caller opts in via sign_with=, the signature covers exactly the content the derivation saw.
The content hash lives in each source's metadata["content_sha256"] so downstream audit systems can correlate a manifest entry back to the raw message it was produced from.
Every source derived here is stamped origin=OBSERVED so it is distinguishable from DECLARED sources in a human-authored manifest.
content_hash(text, *, algo='sha256') ¶
Return "{algo}:{hex}" for text. Stable across processes.
source_id_for_role(role, index, *, session_id=None) ¶
Return a stable, human-readable source_id for a derived message source.
derive_source_from_message(message, *, index=0, session_id=None, default_trust=None) ¶
Return (source, content_text) for one chat message.
Parameters¶
message A chat message dict with at least a role and content key. Follows OpenAI chat-completions convention; Anthropic messages (which omit role on the server's system parameter) should be normalised by the caller before invocation. index Position within the originating message list - used to build a stable source_id. session_id Optional session scope; included in the source_id. default_trust Overrides the role-default trust for all messages (useful when the caller already knows e.g. every user turn is untrusted).
derive_sources_from_messages(messages, *, session_id=None, default_trust=None) ¶
Derive one (source, content) pair per message.
derive_manifest_from_messages(messages, *, session_id=None, system_id='', customer_id='', default_trust=None, sign_with=None) ¶
Build a :class:ContextManifest from raw chat messages.
Each message becomes one declared source; the full content hash is captured in source.metadata["content_sha256"] so the manifest is replayable against the exact bytes that crossed the wire.
Passing sign_with= yields a signed manifest - the signature attests only that these specific bytes passed through derivation, not that a human reviewed them.
core.manifest_ledger¶
crp.core.manifest_ledger ¶
Manifest ledger - cross-turn continuity and replay (§7.14.5, CRP 2.2).
Motivation¶
In 2.1, each turn builds a fresh :class:ContextManifest. There is no persistent record answering questions like:
- "Did this fact in turn 9 originate from the web-search source declared in turn 3?"
- "Show me the full manifest history for session X from six months ago."
- "Which turns exposed content from
acme-hr-policies-vdb?"
The ledger persists a per-session sequence of signed manifests to the filesystem (crp_sessions/<session_id>.manifest.jsonl) and maintains an in-memory lineage index keyed by source_id.
Storage format¶
One JSON object per line (JSONL). Each line is a complete signed manifest plus a turn field and a recorded_at epoch stamp. Append-only; tamper-evident via the HMAC signature carried inside each record.
Replay¶
:meth:ManifestLedger.load rehydrates a session's history into memory. :meth:ManifestLedger.find_by_source_id walks the index and returns every (turn, manifest) pair where the source was declared.
This module deliberately stores on disk only; integrators who need a DB back-end can wrap ManifestLedger - the API is intentionally small.
LedgerChainError ¶
Bases: ManifestValidationError
Raised when the ledger hash-chain fails verification.
ManifestLedgerEntry dataclass ¶
One recorded manifest within a session's history.
CRP 2.3 adds prev_hash + entry_hash fields forming a tamper-evident hash chain. Older entries without these fields (written by CRP 2.2) load with empty strings and are flagged by :meth:ManifestLedger.verify_chain.
to_jsonl() ¶
Serialise the entry to a single JSONL line.
from_jsonl(line) classmethod ¶
Create a new instance from a JSON Lines record.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
line | str | The line value. | required |
Returns:
| Type | Description |
|---|---|
ManifestLedgerEntry |
|
ManifestLedger ¶
Append-only, per-session manifest store.
The ledger is cheap to construct (no I/O until record or load) and thread-safe for all public methods. One instance per session is the typical pattern; integrators may share a single ledger across sessions if they choose.
session_dir property ¶
Return the session dir.
record(session_id, manifest, *, turn=None) ¶
Append manifest to the ledger for session_id.
Returns the created :class:ManifestLedgerEntry. If turn is not supplied, it defaults to len(existing) + 1 - callers that sequence turns themselves should pass the explicit number.
load(session_id) ¶
Rehydrate a session's ledger from disk into memory.
Idempotent - calling repeatedly returns the same list. Missing files yield [].
history(session_id) ¶
Return all entries for session_id (loads from disk if needed).
latest(session_id) ¶
Return the most recent ledger entry for session_id.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
session_id | str | Session identifier to query. | required |
Returns:
| Type | Description |
|---|---|
ManifestLedgerEntry | None | The last recorded entry, or |
find_by_source_id(source_id, *, session_id=None) ¶
Every entry whose manifest declares source_id.
If session_id is provided, search is limited to that session - otherwise the currently-cached sessions are scanned. Disk-only sessions are NOT discovered automatically; call :meth:load first to include them.
find_by_kind(kind, *, session_id=None) ¶
Every entry whose manifest declares at least one source of kind.
verify_signatures(session_id, secret) ¶
Return entries whose signature does NOT verify under secret.
Useful for periodic ledger-integrity audits. An empty list means every entry verifies successfully.
clear_cache(session_id=None) ¶
Drop in-memory cache for session_id (or all if None).
verify_chain(session_id, *, strict=False) ¶
Re-compute every entry's hash chain for session_id.
Returns [(turn, reason), ...] for each broken entry. An empty list means the ledger is intact. When strict=True and an entry is missing its entry_hash (written by CRP 2.2 pre-chain) this is treated as a break; otherwise pre-2.3 rows are tolerated.
CRP 2.3 tamper-evidence: any post-write edit of a JSONL line changes that entry's recomputed hash and causes every subsequent entry to report prev_hash_mismatch.
scan_sessions() ¶
Enumerate every session with a ledger file on disk.
Returns a sorted list of session IDs. Useful before calling :meth:find_by_source_id or :meth:find_by_kind without a session_id argument - those methods only search in-memory sessions, so on-disk-only sessions are invisible unless scanned and loaded first.
load_all() ¶
Scan disk and rehydrate every session ledger.
Equivalent to calling :meth:scan_sessions and then :meth:load on each. Returns a dict keyed by session_id.
KeyProvider ¶
Minimal key-provider interface for manifest signing.
Keeps CRP out of the secret-management business while still offering enough structure for rotation, grace periods, and audit-trail binding. Integrators with HSMs, KMS, or Vault plug in by subclassing.
current() ¶
Return the active secret for signing new manifests.
candidates() ¶
Iterate over every secret that should verify against historical manifests - current first, then retired keys within the grace window. Used during rotation.
verify(manifest) ¶
Try each candidate key; return True if any verifies.
EnvVarKeyProvider ¶
Bases: KeyProvider
Read the active secret from an environment variable.
env_var (default CRP_MANIFEST_SECRET) must contain at least 32 bytes of entropy encoded as either raw UTF-8 or hex. Hex is detected automatically when the value is an even-length [0-9a-f]+ string.
This is the minimum viable provider - sufficient for single-node deployments and tests. Production deployments should use a rotating provider backed by KMS/Vault and inject RotatingKeyProvider.
RotatingKeyProvider ¶
Bases: KeyProvider
Key provider with an in-process rotation ring.
The caller owns actual key storage (KMS / Vault / file). This class is a coordination primitive: during rotation, both the new and the previous key verify, so in-flight manifests signed under the old key remain valid for a configurable grace window.
Usage::
kp = RotatingKeyProvider(initial=load_from_kms())
...
# Rotate when KMS emits a new version
kp.rotate(new_key=load_from_kms(), grace=timedelta(hours=24))
Thread-safe for rotate, current, and candidates.
rotate(new_key) ¶
Promote new_key to current and retire the previous key.
retire_all() ¶
Drop every retired key - active key continues to sign/verify.
current() ¶
Execute current and return the result.
Returns:
| Type | Description |
|---|---|
bytes |
|
candidates() ¶
Execute candidates and return the result.
Returns:
| Type | Description |
|---|---|
Iterable[bytes] |
|
core.orchestrator¶
crp.core.orchestrator ¶
CRP orchestrator - full-featured dispatch with all subsystems (§2.5, §6.5).
Capabilities
- Envelope-based dispatch with 6-phase fact packing
- Graduated 6-stage extraction pipeline on outputs + ingestion
- WarmStateStore with CriticalState / StructuralState
- CKF (Contextual Knowledge Fabric) 4-mode retrieval
- Multi-window continuation with 3-way termination
- Token measurement & budget validation
- Message assembly per Axiom 4 (Model Ignorance)
- QualityReport with real extraction & security stats
- Session status & cost estimation
- Budget cap enforcement
- Streaming dispatch (§6.10.5)
- Zero-LLM ingestion with graduated extraction (§2.5)
- State export (§2.5)
- Injection detection (advisory, §7.5)
CRPOrchestrator ¶
Bases: DispatchMixin, ExtractionMixin
Central dispatcher managing window lifecycle and protocol operations.
Integrates ALL CRP subsystems: - ExtractionPipeline (graduated 6-stage extraction on outputs + ingestion) - WarmStateStore (persistent fact accumulation, ranking, aging) - CKF (ContextualKnowledgeFabric - 4-mode retrieval) - Envelope builder (6-phase budget-aware fact packing) - ContinuationManager (3-way termination, gap analysis, stitching) - SecurityManager (input validation, injection detection, RBAC, etc.)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
provider | LLMProvider | None | Primary LLM provider. | None |
config | CRPConfig | None | Optional | None |
llm | LLMProvider | None | Alias for | None |
model | str | None | Model name for auto-detection when no provider is given. | None |
**init_kwargs | Any | Extra configuration passed to | {} |
emitter property ¶
Protocol event bus for subscribing to events.
feedback property ¶
Access the feedback loop for fact confidence adjustments.
parallel property ¶
Parallel fan-out engine for multi-task dispatch.
session property ¶
Return the session.
dag property ¶
Return the dag.
config property ¶
Return the config.
warm_store property ¶
Access the WarmStateStore for direct fact operations.
ckf property ¶
Access the CKF for 4-mode retrieval queries.
compliance_audit property ¶
Access the compliance audit trail for inspection (§7.14).
pii_scanner property ¶
Access the PII scanner (§7.12).
consent_manager property ¶
Access the consent manager (§7.13).
processing_records property ¶
Access the processing record keeper - GDPR Art. 30 (§7.13).
retention_manager property ¶
Access the retention manager (§7.12).
lineage_tracker property ¶
Access the data lineage tracker (§7.12).
human_oversight property ¶
Access the human oversight controller - EU AI Act Art. 14 (§7.13).
compliance_reporter property ¶
Access the compliance reporter (§7.15).
risk_classifier property ¶
Access the risk classifier - EU AI Act Art. 6 (§7.15).
extraction_pipeline property ¶
Access the extraction pipeline for configuration.
dispatch_with_strategy(strategy, system_prompt, task_input, **kwargs) ¶
Route a dispatch to the named relay strategy.
Unknown strategies fall back to push-based dispatch with a warning.
session_status() ¶
Get live session metrics.
Returns:
| Type | Description |
|---|---|
SessionStatus | A |
Raises:
| Type | Description |
|---|---|
SessionClosedError | If the session is closed. |
SessionExpiredError | If the session has expired. |
RateLimitExceededError | If RBAC denies READ_STATE. |
estimate_session(system_prompt='', task_input='', *, planned_dispatches=1, avg_output_tokens=None) ¶
Pre-flight cost estimation WITHOUT executing LLM calls.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
system_prompt | str | Representative system prompt for token estimation. | '' |
task_input | str | Representative task input for token estimation. | '' |
planned_dispatches | int | Number of dispatches planned (default: 1). | 1 |
avg_output_tokens | int | None | Expected average output tokens per dispatch. If None, assumes model uses full generation reserve. | None |
Returns:
| Type | Description |
|---|---|
CostEstimate | CostEstimate with token estimates and USD cost (if pricing known). |
preview_envelope(system_prompt, task_input) ¶
Inspect envelope contents WITHOUT dispatching.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
system_prompt | str | System prompt for the dispatch. | required |
task_input | str | User task input. | required |
Returns:
| Type | Description |
|---|---|
EnvelopePreview | An |
Raises:
| Type | Description |
|---|---|
SessionClosedError | If the session is closed. |
SessionExpiredError | If the session has expired. |
RateLimitExceededError | If RBAC denies READ_ENVELOPE. |
configure(**kwargs) ¶
Apply runtime configuration changes (mutable fields only).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
**kwargs | Any | Key-value pairs to update in the resolved config. | {} |
Raises:
| Type | Description |
|---|---|
SessionClosedError | If the session is closed. |
SessionExpiredError | If the session has expired. |
RateLimitExceededError | If RBAC denies CONFIGURE. |
reset_session() ¶
Clear warm state, CKF, event log, DAG. Keeps session open.
Raises:
| Type | Description |
|---|---|
SessionClosedError | If the session is closed. |
SessionExpiredError | If the session has expired. |
RateLimitExceededError | If RBAC denies MANAGE_SESSIONS. |
on(event_type, listener) ¶
Subscribe to a protocol event (convenience wrapper).
Usage::
def on_dispatch(event):
print(f"Dispatch completed: {event.data}")
orch.on("dispatch.completed", on_dispatch)
boost_fact(fact_id, delta=0.1, reason='') ¶
Boost a fact's confidence (positive feedback).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fact_id | str | Fact identifier. | required |
delta | float | Amount to increase confidence. | 0.1 |
reason | str | Optional reason for the feedback. | '' |
penalize_fact(fact_id, delta=-0.2, reason='') ¶
Penalize a fact's confidence (negative feedback).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fact_id | str | Fact identifier. | required |
delta | float | Amount to decrease confidence. | -0.2 |
reason | str | Optional reason for the feedback. | '' |
reject_fact(fact_id, reason='') ¶
Reject a fact entirely (user override).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fact_id | str | Fact identifier. | required |
reason | str | Optional reason for rejection. | '' |
register_provider(provider) ¶
Register an additional LLM provider for fallback routing.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
provider | LLMProvider | Additional provider to register. | required |
close() ¶
Close session - flush warm→cold, persist CKF, zero keys (§2.5).
This method is thread-safe and idempotent.
resume(session_id, provider=None, *, llm=None, data_dir=None, **kwargs) classmethod ¶
Resume a previously closed session by restoring CKF state.
Loads persisted facts from cold storage into a new orchestrator instance, providing cross-session knowledge continuity.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
session_id | str | The session_id from the previous session. | required |
provider | LLMProvider | None | LLM provider to use for the resumed session. | None |
llm | LLMProvider | None | Alias for | None |
data_dir | str | None | Override for CRP_DATA_DIR (default: env or '.'). | None |
**kwargs | Any | Additional arguments forwarded to the orchestrator init. | {} |
Returns:
| Type | Description |
|---|---|
CRPOrchestrator | A new CRPOrchestrator with the previous session's facts loaded. |
Raises:
| Type | Description |
|---|---|
ValidationError | If |
export_state(fmt=None) ¶
Export session state as encrypted bytes (§2.5).
Includes warm store facts, critical state, structural state, and CKF health. Embeddings are NOT exported (§7.11) - recomputed on import.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fmt | str | None | Export format (reserved, currently only 'json' supported). | None |
Returns:
| Type | Description |
|---|---|
bytes | Encrypted bytes (AES-256-GCM with session-bound key). |
Raises:
| Type | Description |
|---|---|
SessionClosedError | If the session is closed. |
SessionExpiredError | If the session has expired. |
RateLimitExceededError | If RBAC denies EXPORT_STATE. |
async_dispatch(system_prompt, task_input, **kwargs) async ¶
Async version of dispatch() - runs in a configurable thread pool.
Uses the orchestrator's own ThreadPoolExecutor (sized via max_threads config) instead of the default asyncio executor, which prevents thread-pool saturation under concurrent load.
Use from async code (FastAPI, asyncio, etc.)::
output, report = await client.async_dispatch(
"You are helpful.", "Explain CRP."
)
async_ingest(raw_text, *, source_label='') async ¶
Async version of ingest() - runs in the orchestrator's thread pool.
async_close() async ¶
Async version of close() - runs in the orchestrator's thread pool.
async_dispatch_stream(system_prompt, task_input, **kwargs) async ¶
Async streaming dispatch - yields StreamEvent objects.
Usage::
async for event in client.async_dispatch_stream(
"You are helpful.", "Explain CRP."
):
if event.event_type == "token":
print(event.data, end="")
core.relay_strategies¶
crp.core.relay_strategies ¶
Novel context relay strategies - beyond push and pull (§21).
Three fundamentally different approaches to providing context to LLMs:
§21.1 REFLEXIVE DISPATCH - Verify-then-Refine Generate first with zero context (pure parametric knowledge). CRP analyzes the output against its knowledge base, identifying factual errors, unsupported claims, and missing information. A targeted correction payload is assembled and sent back with the model's own output - the model refines with surgical precision instead of drowning in pre-loaded context.
Analogy: A fact-checker reviews your first draft and hands
you a marked-up copy with corrections and sources.
§21.2 PROGRESSIVE DISCLOSURE - Index → Detail on Demand Instead of full facts, send a compact CONTEXT INDEX - one-line summaries of every available fact, grouped by topic. The model sees WHAT knowledge exists without the full payload. It generates with awareness of available resources, and CRP detects which indexed items were referenced. A second pass provides full detail for only the referenced items.
Analogy: A library card catalog - you browse titles first,
then check out only the books you need.
§21.3 STREAM-AUGMENTED GENERATION - Real-time Context Injection Stream tokens from the LLM. Buffer into sentences. After each sentence, CRP runs real-time fact-matching against the knowledge base. When relevant facts are found, generation is paused, the partial output + injected context are sent as a new prompt, and the model continues from where it left off - now informed.
Analogy: A research assistant who watches you write and slides
relevant papers across the desk exactly when you need them.
Each strategy has fundamentally different characteristics
- Reflexive: Best for ACCURACY - catches and corrects hallucinations
- Progressive: Best for EFFICIENCY - minimal token usage, max coverage
- Stream-aug: Best for COHERENCE - context arrives at point-of-need
FactCorrection dataclass ¶
A correction identified by comparing output against knowledge base.
ReflexiveAnalysis dataclass ¶
Result of analyzing model output against CRP knowledge base.
needs_refinement property ¶
Whether the output needs a refinement pass.
ContextIndex dataclass ¶
A compact index of available knowledge - titles/summaries only.
to_text() ¶
Render the index as a compact text block for the LLM.
ContextIndexEntry dataclass ¶
One entry in the context index.
AugmentationEvent dataclass ¶
Record of a real-time context injection during streaming.
StreamAugmentationState dataclass ¶
Tracks state during stream-augmented generation.
should_check property ¶
Check after every N sentences to avoid excessive overhead.
analyze_output_against_kb(output, warm_store, count_tokens, embed_fn=None) ¶
Compare LLM output against CRP knowledge base.
Extracts claims/sentences from the output and cross-references each against the WarmStore facts. Identifies: - Supported claims (fact backing exists) - Contradicted claims (conflicting evidence) - Unsupported claims (no evidence either way) - Enrichment opportunities (related facts not mentioned)
build_refinement_prompt(original_output, analysis, count_tokens, max_correction_tokens=3000) ¶
Build the refinement prompt from reflexive analysis.
Returns a structured correction payload that tells the model exactly what to fix, with evidence.
build_context_index(warm_store, count_tokens, max_entries=50, max_index_tokens=1500) ¶
Build a compact context index from WarmStore facts.
Each entry is compressed to a one-line summary (~15 tokens) instead of the full fact text (~50-200 tokens). This gives the LLM awareness of ALL available knowledge at ~10% of the token cost of sending full facts.
detect_index_references(output, index) ¶
Detect which index entries were referenced in the output.
The LLM may reference entries by [F1], [F2] etc., or by mentioning key terms from the summary. Both are detected.
build_detail_injection(referenced_entries, count_tokens, max_tokens=3000) ¶
Build the detail payload for referenced index entries.
Only the entries the model actually referenced get expanded to full text. This is maximally efficient - no wasted context.
find_relevant_facts_for_sentence(sentence, warm_store, already_injected, count_tokens, max_facts=3, max_tokens=500) ¶
Find WarmStore facts relevant to a sentence that haven't been injected yet.
Returns [(fact_id, fact_text), ...] - only NEW facts not already served.
build_augmented_continuation(system_prompt, partial_output, injected_facts, task_input) ¶
Build the message sequence for resuming after a stream augmentation.
The model receives: 1. System prompt (unchanged) 2. Original task 3. Its own partial output so far (as assistant message) 4. Injected context (as system/user interjection) 5. Instruction to continue from where it left off
core.security_manager¶
crp.core.security_manager ¶
SecurityManager - facade for all CRP security subsystems (§audit4 CQ-C1).
Extracts the 18 security & compliance subsystem initializations from CRPOrchestrator.__init__() into a single cohesive class, reducing orchestrator init from ~360 lines to ~250 lines.
All subsystems remain accessible as attributes on the manager instance.
SecurityManager ¶
Groups all CRP security and compliance subsystems (§7).
Subsystems initialized: - InputValidator (§7.4) - Layer 1, cannot be disabled - InjectionDetector (§7.5) - advisory, never blocks - SessionBindingManager (§7.1) - HMAC-SHA256 key derivation - RBACEnforcer (§7.10) - role-based access + rate limiting - FactIntegrityChain (§7.2, §7.7) - BLAKE3/SHA-256 hash chain - StateEncryptor (§7.3) - AES-256-GCM - IngestQuarantine (§7.8) - 1-window anti-poisoning - EmbeddingDefense (§7.11) - SQ8 + XOR salting - PIIScanner (§7.12) - pattern-based PII detection - RetentionManager (§7.12) - auto-expiry per classification - ErasureManager (§7.12) - GDPR Art. 17 right to erasure - DataLineageTracker (§7.12) - provenance tracking - ConsentManager (§7.13) - purpose-based consent - ProcessingRecordKeeper (§7.13) - GDPR Art. 30 records - HumanOversightController (§7.13) - EU AI Act Art. 14 - ComplianceAuditTrail (§7.14) - tamper-evident HMAC chain - RiskClassifier (§7.15) - EU AI Act risk classification - ComplianceReporter (§7.15) - regulatory reporting
session_key property ¶
Return the session binding key for use by other subsystems.
core.session¶
crp.core.session ¶
Session data types - SessionHandle, SessionStatus, CostEstimate.
These dataclasses implement the JSON schemas used by the CRP session API: session-handle.json, session-status.json, cost-estimate.json, quality-report.json, envelope-preview.json, stream-event.json, and persisted-state-header.json.
SessionHandle dataclass ¶
Session identity and lifetime metadata.
Implements the session-handle.json schema. The session_key is managed separately by SessionBindingManager and is never serialised to JSON or cold storage.
Attributes:
| Name | Type | Description |
|---|---|---|
session_id | str | Unique UUID for this session. |
protocol_version | str | CRP protocol version running. |
capabilities | list[str] | Operations the session supports. |
created_at | float | Unix timestamp of creation. |
expires_at | float | Unix timestamp after which the session is invalid. |
is_expired property ¶
Return True if the current time is past expires_at.
RemainingBudget dataclass ¶
Remaining quota for a session.
Attributes:
| Name | Type | Description |
|---|---|---|
windows_remaining | int | None | Windows left before |
input_tokens_remaining | int | None | Input tokens left before |
output_tokens_remaining | int | None | Output tokens left before |
SessionStatus dataclass ¶
Live session metrics returned by session_status().
Implements the session-status.json schema.
Attributes:
| Name | Type | Description |
|---|---|---|
session_id | str | Session identifier. |
windows_completed | int | Number of primary + continuation windows completed. |
total_input_tokens | int | Cumulative input tokens across all windows. |
total_output_tokens | int | Cumulative output tokens across all windows. |
facts_in_warm_state | int | Current number of facts in the warm store. |
overhead_ratio | float | Ratio of continuation windows to total windows. |
remaining_budget | RemainingBudget | None | Remaining quota, or None if no caps configured. |
total_cost | float | None | Estimated USD cost, or None if provider pricing unknown. |
CostEstimate dataclass ¶
Pre-flight cost estimate returned by estimate_session().
Implements the cost-estimate.json schema.
Attributes:
| Name | Type | Description |
|---|---|---|
estimated_windows | int | Number of dispatches estimated. |
estimated_input_tokens | int | Total input tokens estimated. |
estimated_output_tokens | int | Total output tokens estimated. |
estimated_cost_usd | float | None | Estimated USD cost, or None if pricing unknown. |
confidence | str | "high", "medium", or "low" depending on input completeness. |
SecurityFlags dataclass ¶
Security validation results - always present in QualityReport (§7.4).
Attributes:
| Name | Type | Description |
|---|---|---|
injection_markers_detected | int | Count of injection patterns found. |
injection_marker_details | list[dict[str, Any]] | Details for each detected marker. |
unicode_normalized | bool | Whether unicode input was normalised. |
control_chars_stripped | int | Number of control characters removed. |
input_truncated | bool | Whether input was truncated to fit budget. |
integrity_violations | int | Count of provenance-chain violations. |
output_injection_facts_penalized | int | Output-side injection detections (§7.5.2). |
QualityReport dataclass ¶
Governance summary returned by every dispatch() call.
Implements the quality-report.json schema. output is the COMPLETE, UNMODIFIED LLM output (Axiom 9).
Attributes:
| Name | Type | Description |
|---|---|---|
session_id | str | Session identifier. |
window_id | str | Window identifier for this dispatch. |
output | str | Raw LLM output. |
facts_extracted | int | Number of facts extracted from the output. |
security_flags | SecurityFlags | Security validation results. |
continuation_windows | int | Number of continuation windows triggered. |
envelope_saturation | float | Envelope token utilisation ratio. |
quality_tier | str | None | "S", "A", "B", "C", or "D". |
telemetry | dict[str, Any] | None | Optional per-window telemetry dict. |
EnvelopePreview dataclass ¶
Envelope inspection result returned by preview_envelope().
Implements the envelope-preview.json schema.
Attributes:
| Name | Type | Description |
|---|---|---|
total_tokens | int | System + task + envelope tokens. |
envelope_tokens | int | Tokens consumed by the constructed envelope. |
generation_reserve | int | Tokens reserved for LLM generation. |
facts_included | int | Facts packed into the envelope. |
facts_available | int | Total facts available in the warm store. |
saturation | float | Envelope token utilisation ratio. |
StreamEvent dataclass ¶
Single event emitted by dispatch_stream() / async_dispatch_stream().
Implements the stream-event.json schema. Concatenating all token events' data values MUST produce the same string as non-streaming output.
Attributes:
| Name | Type | Description |
|---|---|---|
event_type | str | One of "token", "extraction", "continuation", "window_complete", "done", or "error". |
data | Any | Event payload (string for tokens, dict for other events). |
PersistedStateHeader dataclass ¶
Header for all persisted state data.
Implements the persisted-state-header.json schema.
Attributes:
| Name | Type | Description |
|---|---|---|
schema_version | str | Data schema version. |
protocol_version | str | CRP protocol version. |
created_at | float | Unix timestamp of persistence. |
checksum | str | BLAKE3 hash of the payload. |
core.task_intent¶
crp.core.task_intent ¶
TaskIntent - structured task description for CRP dispatch (§2.3).
This module defines the TaskIntent dataclass and OutputType enum, which together describe what the user wants from a single dispatch. All fields are optional; CRP infers sensible defaults from raw task_input.
OutputType ¶
Bases: str, Enum
Expected output formats per the task-intent.json schema.
TEXT = 'text' class-attribute instance-attribute ¶
Plain text response.
JSON = 'json' class-attribute instance-attribute ¶
Structured JSON response; may be validated against output_schema.
MARKDOWN = 'markdown' class-attribute instance-attribute ¶
Markdown-formatted response.
CODE = 'code' class-attribute instance-attribute ¶
Source code response.
TaskIntent dataclass ¶
Structured task description per §2.3.
All fields are optional - CRP infers missing values from the raw task input. Matches the task-intent.json schema exactly.
Attributes:
| Name | Type | Description |
|---|---|---|
description | str | None | Optional human-readable summary of the task. |
system_prompt | str | None | Optional system prompt override for this dispatch. |
task_input | str | None | The user's actual request / question. |
expected_output_type | OutputType | str | None | Desired response format. |
max_windows | int | None | Maximum continuation windows allowed. |
max_output_tokens | int | None | Maximum tokens to generate in this dispatch. |
output_schema | dict[str, Any] | None | JSON Schema for structured output validation. |
metadata | dict[str, Any] | Arbitrary key-value context for routing or observability. |
core.window¶
crp.core.window ¶
TaskWindow, WindowDAG, and window lifecycle types (§2.4, §5, SPEC-004).
Defines the provenance DAG, window state machine, context-transfer types, and budget helpers used by the orchestrator and continuation manager.
Window lifecycle
CREATED → ASSEMBLED → DISPATCHED → GENERATING → COMPLETED → EXTRACTED
WindowState ¶
Bases: Enum
Lifecycle states - transitions are strictly forward-only.
WindowPattern ¶
Bases: str, Enum
How a window relates to its parent(s) in the DAG (§3.1).
LINEAR = 'LINEAR' class-attribute instance-attribute ¶
Single parent, single child chain.
FAN_OUT = 'FAN_OUT' class-attribute instance-attribute ¶
One parent spawns multiple parallel children.
FAN_IN = 'FAN_IN' class-attribute instance-attribute ¶
Multiple parents merge into one child.
BRANCH = 'BRANCH' class-attribute instance-attribute ¶
Exploratory branch that may later merge or terminate.
TransferType ¶
Bases: str, Enum
What information flows from a parent window to a child (§8.1).
FULL_CONTEXT = 'FULL_CONTEXT' class-attribute instance-attribute ¶
Complete parent response plus envelope.
SUMMARY = 'SUMMARY' class-attribute instance-attribute ¶
Compressed summary (default).
FACTS_ONLY = 'FACTS_ONLY' class-attribute instance-attribute ¶
Deferred facts only, no response content.
RESULT_ONLY = 'RESULT_ONLY' class-attribute instance-attribute ¶
Final answer only (fan-in synthesis).
WindowEdge dataclass ¶
A directed context-flow edge from a parent to a child window (§3.2).
Attributes:
| Name | Type | Description |
|---|---|---|
source_id | str | Parent window ID. |
target_id | str | Child window ID. |
transfer_type | TransferType | Mode of information transfer. |
transferred_tokens | int | Tokens carried across this edge. |
summary_hash | str | Hash of the transferred summary for integrity checks. |
to_dict() ¶
Serialise the edge to a JSON-compatible dict.
WindowNode dataclass ¶
Represents a single task window in the DAG (§2.4).
advance(to_state) ¶
Transition to a new state, enforcing the forward-only invariant.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
to_state | WindowState | Target state. | required |
Raises:
| Type | Description |
|---|---|
ValueError | If the transition is not allowed. |
WindowMetrics dataclass ¶
Recorded per window for telemetry.jsonl output.
to_dict() ¶
Serialise to a flat dict for JSONL telemetry output.
WindowDAG ¶
Tracks the provenance graph of all windows in a session (§2.4, Axiom 7).
nodes property ¶
Mapping from window ID to WindowNode.
edges property ¶
List of all context-flow edges.
add_node(node) ¶
Register a created window.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
node | WindowNode | Window node to add. | required |
Raises:
| Type | Description |
|---|---|
ValueError | If the window ID already exists. |
set_parent(child_id, parent_id) ¶
Declare a provenance edge: parent contributed facts to child.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
child_id | str | Child window ID. | required |
parent_id | str | Parent window ID. | required |
add_edge(edge) ¶
Record a context-flow edge with transfer metadata (§3.2).
Establishes the parent/child relationship and rejects edges that would introduce a cycle, preserving the acyclic invariant (§3.3).
descendants(window_id) ¶
Return all transitive descendant window IDs.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
window_id | str | Window to descend from. | required |
Returns:
| Type | Description |
|---|---|
set[str] | Set of all descendant window IDs (transitive children). |
get(window_id) ¶
Return the WindowNode for window_id.
roots() ¶
Return windows with no parents (initial windows).
leaves() ¶
Return windows with no children (terminal windows).
ancestors(window_id) ¶
Return all transitive ancestor window IDs.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
window_id | str | Window to ascend from. | required |
Returns:
| Type | Description |
|---|---|
set[str] | Set of all ancestor window IDs (transitive parents). |
window_count() ¶
Return the number of windows in the DAG.
lineage(window_id) ¶
Return the DAG path from a root to window_id (CRP-SPEC-004 §14.2).
Follows the first parent at each step; suitable for emitting CRP-Provenance-Window-Lineage (root -> ... -> current).
detect_cycle() ¶
Return True if the graph contains a cycle (violates §3.3).
Uses a three-colour DFS over all nodes.
clear() ¶
Remove all nodes and edges (session reset).
select_transfer_type(*, available_budget, parent_response_tokens, summary_tokens, deferred_facts_tokens, reserve_fraction=0.6) ¶
Select a transfer type from token-budget pressure (§8.2).
The reserve_fraction (default 0.60) leaves 40% of the budget for new facts specific to the child's query.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
available_budget | int | Tokens available in the child window. | required |
parent_response_tokens | int | Tokens of the full parent response. | required |
summary_tokens | int | Tokens of a compressed parent summary. | required |
deferred_facts_tokens | int | Tokens of deferred facts. | required |
reserve_fraction | float | Fraction of budget that may be spent on context relay. | 0.6 |
Returns:
| Type | Description |
|---|---|
TransferType | The most complete transfer type that fits within the reserved budget. |
partition_fan_in_budget(parent_budgets) ¶
Fan-in safety budget is the MINIMUM of all parents (§7.3.4, §6.4).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
parent_budgets | list[float] | Envelope budgets of each parent window. | required |
Returns:
| Type | Description |
|---|---|
float | The conservative minimum budget, or 0.0 if no parents exist. |
resolve_generation_reserve(max_output_tokens, provider_max_output, context_window, is_thinking_model=False) ¶
Determine generation reserve G by 3-layer precedence (§2.1).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
max_output_tokens | int | None | User-specified output limit (highest precedence). | required |
provider_max_output | int | None | Provider-reported output limit. | required |
context_window | int | Model context window size. | required |
is_thinking_model | bool | Whether the model uses internal reasoning tokens. | False |
Returns:
| Type | Description |
|---|---|
int | The resolved generation reserve in tokens. |
Layer 1: User explicit (TaskIntent.max_output_tokens) Layer 2: Provider reported (LLMProvider.max_output_tokens) Layer 3: Conservative default scaled to the context window.
Small-context models (4K / 8K) cannot afford a full C//4 generation reserve because it leaves almost no room for the envelope. The continuation manager stitches multi-window answers together, so each individual window only needs a modest generation budget:
- C <= 4096 → 384 tokens
- C <= 8192 → 768 tokens
- otherwise → min(C // 4, 16384)
For thinking models (qwen3, deepseek-r1, o1, etc.) the reserve is doubled because the model spends a significant portion of tokens on internal reasoning_content before producing final output.
compute_envelope_budget(context_window, system_tokens, task_tokens, generation_reserve) ¶
Compute E = C - S - T - G (§2.1, Axiom 2).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
context_window | int | Model context window size | required |
system_tokens | int | Tokens consumed by the system prompt | required |
task_tokens | int | Tokens consumed by the task input | required |
generation_reserve | int | Tokens reserved for generation | required |
Returns:
| Type | Description |
|---|---|
int | Non-negative envelope budget |