"""Versioned FastAPI service skeleton. The service layer is intentionally thin: route handlers translate HTTP requests into service/runtime contracts and must not own domain behavior. """ from __future__ import annotations import json from dataclasses import dataclass, field from datetime import datetime from importlib import metadata from typing import Any from kontextual_engine.adapters.memory import InMemoryAssetRegistryRepository from kontextual_engine.core import ( Actor, ActorType, AuditEvent, AuditOutcome, Classification, ContextEntity, ContextEntityType, IngestionIdentityPolicy, IngestionJobStatus, LifecycleState, MetadataRecord, OperationContext, PolicyDecision, PolicyEffect, RelationshipTargetKind, RetrievalFeedbackLabel, SourceReference, TransformationRunStatus, WorkflowExceptionKind, WorkflowExceptionStatus, WorkflowInputDefinition, WorkflowReviewDecisionType, WorkflowReviewStatus, WorkflowRunStatus, WorkflowStepDefinition, WorkflowTemplate, content_digest, new_id, stable_json_dumps, utc_now, ) from kontextual_engine.errors import AuthorizationError, KontextualError, NotFoundError, ValidationError from kontextual_engine.ports import AllowAllPolicyGateway, AssetRegistryRepository, PolicyGateway from kontextual_engine.services import ( AssetIngestionService, AssetQueryRequest, AssetRegistryService, AssetRetrievalService, ContextEntityQueryRequest, RelationshipQueryRequest, RetrievalFeedbackRequest, TransformationRequest, TransformationService, WorkflowInvocation, WorkflowService, ) API_VERSION = "v1" OPENAPI_VERSION = "1.0.0" AGENT_OPERATION_CATALOG: tuple[dict[str, Any], ...] = ( { "operation_id": "inspect_asset", "description": "Read one asset envelope by ID.", "input_schema": {"required": ["asset_id"]}, "output_schema": {"type": "asset"}, "required_permissions": ["agent.operation.inspect_asset", "asset.retrieve"], "audit_operation": "agent.operation.inspect_asset", "failure_modes": ["not_found", "permission_denied"], "dry_run_supported": True, }, { "operation_id": "retrieve_asset", "description": "Read one source-grounded asset bundle with metadata, representations, and relationships.", "input_schema": {"required": ["asset_id"]}, "output_schema": {"type": "asset_bundle"}, "required_permissions": ["agent.operation.retrieve_asset", "asset.retrieve"], "audit_operation": "agent.operation.retrieve_asset", "failure_modes": ["not_found", "permission_denied"], "dry_run_supported": True, }, { "operation_id": "search_assets", "description": "Run a governed retrieval query over assets.", "input_schema": {"required": ["query"]}, "output_schema": {"type": "asset_query_result"}, "required_permissions": ["agent.operation.search_assets", "retrieval.assets.query"], "audit_operation": "agent.operation.search_assets", "failure_modes": ["permission_denied", "validation_error", "zero_results"], "dry_run_supported": True, }, { "operation_id": "assemble_context", "description": "Assemble a non-durable source-grounded context preview from a bounded asset query.", "input_schema": {"required": ["query"], "optional": ["intent", "instructions", "constraints"]}, "output_schema": {"type": "context_preview"}, "required_permissions": ["agent.operation.assemble_context", "retrieval.assets.query"], "audit_operation": "agent.operation.assemble_context", "failure_modes": ["permission_denied", "validation_error", "zero_results"], "dry_run_supported": True, }, { "operation_id": "enrich_metadata", "description": "Add one metadata record to an asset.", "input_schema": {"required": ["asset_id", "metadata"]}, "output_schema": {"type": "asset_change"}, "required_permissions": ["agent.operation.enrich_metadata", "asset.metadata.add"], "audit_operation": "agent.operation.enrich_metadata", "failure_modes": ["not_found", "permission_denied", "validation_error", "version_conflict"], "dry_run_supported": True, }, { "operation_id": "classify_asset", "description": "Request the classify transformation operation for an asset.", "input_schema": {"required": ["asset_id"]}, "output_schema": {"type": "transformation_run_result"}, "required_permissions": ["agent.operation.classify_asset", "transformation.run.execute"], "audit_operation": "agent.operation.classify_asset", "failure_modes": ["permission_denied", "adapter_unavailable", "operation_failed"], "dry_run_supported": True, }, { "operation_id": "transform_asset", "description": "Execute a registered transformation operation.", "input_schema": {"required": ["transformation"]}, "output_schema": {"type": "transformation_run_result"}, "required_permissions": ["agent.operation.transform_asset", "transformation.run.execute"], "audit_operation": "agent.operation.transform_asset", "failure_modes": ["permission_denied", "adapter_unavailable", "operation_failed"], "dry_run_supported": True, }, { "operation_id": "invoke_workflow", "description": "Invoke a registered workflow template.", "input_schema": {"required": ["workflow"]}, "output_schema": {"type": "workflow_run_result"}, "required_permissions": ["agent.operation.invoke_workflow", "workflow.run.execute"], "audit_operation": "agent.operation.invoke_workflow", "failure_modes": ["not_found", "permission_denied", "operation_failed", "review_required"], "dry_run_supported": True, }, { "operation_id": "submit_review", "description": "Submit a decision for one open workflow review task.", "input_schema": {"required": ["run_id", "review_id", "decision"]}, "output_schema": {"type": "workflow_run_result"}, "required_permissions": ["agent.operation.submit_review", "workflow.review.decide"], "audit_operation": "agent.operation.submit_review", "failure_modes": ["not_found", "permission_denied", "review_not_open", "operation_failed"], "dry_run_supported": True, }, { "operation_id": "report_result", "description": "Record an agent result report without mutating domain assets.", "input_schema": {"required": ["summary"], "optional": ["result_ref", "metadata"]}, "output_schema": {"type": "agent_report"}, "required_permissions": ["agent.operation.report_result"], "audit_operation": "agent.operation.report_result", "failure_modes": ["permission_denied", "validation_error"], "dry_run_supported": True, }, ) @dataclass class ServiceRuntime: repository: AssetRegistryRepository = field(default_factory=InMemoryAssetRegistryRepository) policy_gateway: PolicyGateway = field(default_factory=AllowAllPolicyGateway) api_version: str = API_VERSION service_name: str = "kontextual-engine" started_at: str = field(default_factory=lambda: utc_now().isoformat()) def asset_service(self) -> AssetRegistryService: return AssetRegistryService(self.repository, policy_gateway=self.policy_gateway) def ingestion_service(self) -> AssetIngestionService: return AssetIngestionService(self.repository, asset_service=self.asset_service()) def retrieval_service(self) -> AssetRetrievalService: return AssetRetrievalService(self.repository, policy_gateway=self.policy_gateway) def transformation_service(self) -> TransformationService: return TransformationService( self.repository, policy_gateway=self.policy_gateway, asset_service=self.asset_service(), ) def workflow_service(self) -> WorkflowService: return WorkflowService( self.repository, transformation_service=self.transformation_service(), policy_gateway=self.policy_gateway, ) def operation_context( self, *, actor_id: str = "api-user", actor_type: str = "human", display_name: str | None = None, external_ref: str | None = None, correlation_id: str | None = None, groups: list[str] | None = None, delegated_actor_id: str | None = None, delegated_actor_type: str = "human", delegated_actor_display_name: str | None = None, delegated_actor_external_ref: str | None = None, delegated_actor_groups: list[str] | None = None, request_scope: dict[str, Any] | None = None, policy_scope: dict[str, Any] | None = None, agent_id: str | None = None, agent_name: str | None = None, agent_run_id: str | None = None, agent_tool: str | None = None, metadata: dict[str, Any] | None = None, ) -> OperationContext: actor_metadata = dict(metadata or {}) agent_metadata = _agent_metadata( agent_id=agent_id, agent_name=agent_name, agent_run_id=agent_run_id, agent_tool=agent_tool, ) if agent_metadata: actor_metadata["agent"] = agent_metadata actor = Actor.create( ActorType(actor_type), actor_id=actor_id, display_name=display_name, external_ref=external_ref, groups=groups, metadata=actor_metadata, ) delegated_actor = None if delegated_actor_id: delegated_actor = Actor.create( ActorType(delegated_actor_type), actor_id=delegated_actor_id, display_name=delegated_actor_display_name, external_ref=delegated_actor_external_ref, groups=delegated_actor_groups, ) context_metadata: dict[str, Any] = {} if agent_metadata: context_metadata["agent"] = agent_metadata if delegated_actor is not None: context_metadata["delegation"] = { "mode": "on_behalf_of", "actor_id": actor.id, "delegated_actor_id": delegated_actor.id, } return OperationContext.create( actor, correlation_id=correlation_id, delegated_actor=delegated_actor, request_scope=request_scope, policy_scope=policy_scope, metadata=context_metadata, ) @property def package_version(self) -> str: try: return metadata.version("kontextual-engine") except metadata.PackageNotFoundError: return "0.1.0" def health(self) -> dict[str, Any]: return { "status": "ok", "service": self.service_name, "api_version": self.api_version, "package_version": self.package_version, "started_at": self.started_at, } def readiness(self) -> dict[str, Any]: checks: dict[str, dict[str, Any]] = {} try: asset_count = len(self.repository.list_assets()) checks["asset_registry"] = { "status": "ok", "repository": type(self.repository).__name__, "asset_count": asset_count, } except Exception as exc: checks["asset_registry"] = { "status": "error", "repository": type(self.repository).__name__, "error_type": type(exc).__name__, "message": str(exc), } ready = all(item["status"] == "ok" for item in checks.values()) return { "status": "ready" if ready else "not_ready", "ready": ready, "service": self.service_name, "api_version": self.api_version, "checks": checks, } def version(self) -> dict[str, Any]: return { "service": self.service_name, "api_version": self.api_version, "package_version": self.package_version, "openapi_version": OPENAPI_VERSION, } def create_asset(self, payload: dict[str, Any], context: OperationContext) -> dict[str, Any]: classification = Classification.from_dict(payload["classification"]) result = self.asset_service().create_asset( payload["title"], classification, context, asset_id=payload.get("asset_id"), source_refs=[_source_reference(item) for item in payload.get("source_refs", ())], metadata_records=[_metadata_record(item) for item in payload.get("metadata_records", ())], idempotency_key=payload.get("idempotency_key"), ) return _asset_change_result(result) def get_asset(self, asset_id: str) -> dict[str, Any]: return self.asset_service().get_asset(asset_id).to_dict() def list_assets( self, *, lifecycle: str | None = None, asset_type: str | None = None, sensitivity: str | None = None, owner: str | None = None, topic: str | None = None, review_state: str | None = None, ) -> dict[str, Any]: assets = self.asset_service().list_assets( lifecycle=LifecycleState(lifecycle) if lifecycle else None, asset_type=asset_type, sensitivity=sensitivity, owner=owner, topic=topic, review_state=review_state, ) return {"items": [asset.to_dict() for asset in assets], "count": len(assets)} def add_metadata_record( self, asset_id: str, payload: dict[str, Any], context: OperationContext, ) -> dict[str, Any]: result = self.asset_service().add_metadata_record( asset_id, _metadata_record(payload), context, expected_current_version_id=payload.get("expected_current_version_id"), ) return _asset_change_result(result) def list_metadata_records(self, asset_id: str) -> dict[str, Any]: records = self.repository.list_metadata_records(asset_id) return {"items": [record.to_dict() for record in records], "count": len(records)} def transition_lifecycle( self, asset_id: str, payload: dict[str, Any], context: OperationContext, ) -> dict[str, Any]: result = self.asset_service().transition_lifecycle( asset_id, LifecycleState(payload["lifecycle"]), context, expected_current_version_id=payload.get("expected_current_version_id"), ) return _asset_change_result(result) def create_relationship(self, payload: dict[str, Any], context: OperationContext) -> dict[str, Any]: target_kind = RelationshipTargetKind(payload.get("target_kind", RelationshipTargetKind.ASSET.value)) service = self.asset_service() if target_kind == RelationshipTargetKind.CONTEXT_ENTITY: entity_payload = payload.get("context_entity") or {} entity = ContextEntity( entity_id=payload["target_id"], entity_type=ContextEntityType(entity_payload.get("entity_type", ContextEntityType.TOPIC.value)), name=entity_payload.get("name", payload["target_id"]), external_ref=entity_payload.get("external_ref"), metadata=dict(entity_payload.get("metadata", {})), ) result = service.link_asset_to_context_entity( payload["source_asset_id"], entity, payload["predicate"], context, confidence=payload.get("confidence"), provenance=dict(payload.get("provenance", {})), expected_current_version_id=payload.get("expected_current_version_id"), ) else: result = service.link_asset_to_asset( payload["source_asset_id"], payload["target_id"], payload["predicate"], context, confidence=payload.get("confidence"), provenance=dict(payload.get("provenance", {})), expected_current_version_id=payload.get("expected_current_version_id"), ) return { "relationship": result.relationship.to_dict(), "version": result.version.to_dict(), "audit_event": result.audit_event.to_dict(), "policy_decision": result.policy_decision.to_dict(), } def list_relationships( self, *, source_id: str | None = None, target_id: str | None = None, ) -> dict[str, Any]: relationships = self.repository.list_relationships(source_id=source_id, target_id=target_id) return { "items": [relationship.to_dict() for relationship in relationships], "count": len(relationships), } def list_audit_events( self, *, target: str | None = None, correlation_id: str | None = None, ) -> dict[str, Any]: events = self.repository.list_audit_events(target=target, correlation_id=correlation_id) return {"items": [event.to_dict() for event in events], "count": len(events)} def evaluate_policy(self, payload: dict[str, Any], context: OperationContext) -> dict[str, Any]: try: decision = self.policy_gateway.authorize( context, payload["action"], payload["resource"], resource_metadata=dict(payload.get("resource_metadata", {})), ) except Exception as exc: decision = PolicyDecision.fail_closed( context.actor.id, payload.get("action", "unknown"), payload.get("resource", "unknown"), reason=str(exc), context={"gateway_error": type(exc).__name__}, ) return decision.to_dict() def ingestion_capabilities(self) -> dict[str, Any]: service = self.ingestion_service() return { "connectors": service.connector_capabilities(), "extractors": service.extractor_capabilities(), } def start_ingestion_job(self, payload: dict[str, Any], context: OperationContext) -> dict[str, Any]: service = self.ingestion_service() mode = payload.get("mode", "file") classification = _optional_classification(payload.get("classification")) identity_policy = payload.get("identity_policy", IngestionIdentityPolicy.SOURCE_LOCATION.value) if mode == "directory": job = service.ingest_directory( payload["path"], context, recursive=bool(payload.get("recursive", True)), classification=classification, identity_policy=identity_policy, skip_unchanged=bool(payload.get("skip_unchanged", True)), ) return _ingestion_job_envelope(job) if mode != "file": raise ValidationError( "Unsupported ingestion mode", details={"mode": mode, "supported": ["file", "directory"]}, ) result = service.ingest_file( payload["path"], context, asset_id=payload.get("asset_id"), title=payload.get("title"), classification=classification, idempotency_key=payload.get("idempotency_key"), identity_policy=identity_policy, skip_unchanged=bool(payload.get("skip_unchanged", True)), ) return _ingestion_result_envelope(result) def get_ingestion_job(self, job_id: str) -> dict[str, Any]: return _ingestion_job_envelope(self.ingestion_service().get_job(job_id)) def list_ingestion_jobs(self, *, status: str | None = None) -> dict[str, Any]: parsed_status = _enum_filter(IngestionJobStatus, status, "ingestion job status") jobs = self.ingestion_service().list_jobs(status=parsed_status) return {"items": [_ingestion_job_envelope(job) for job in jobs], "count": len(jobs)} def refresh_retrieval_index(self) -> dict[str, Any]: return self.retrieval_service().refresh_index().to_dict() def query_assets(self, payload: dict[str, Any], context: OperationContext) -> dict[str, Any]: service = self.retrieval_service() service.refresh_index() return service.query_assets(_asset_query_request(payload), context).to_dict() def query_context_entities(self, payload: dict[str, Any], context: OperationContext) -> dict[str, Any]: return self.retrieval_service().query_context_entities( _context_entity_query_request(payload), context, ).to_dict() def query_relationships(self, payload: dict[str, Any], context: OperationContext) -> dict[str, Any]: return self.retrieval_service().query_relationships( _relationship_query_request(payload), context, ).to_dict() def record_retrieval_feedback(self, payload: dict[str, Any], context: OperationContext) -> dict[str, Any]: return self.retrieval_service().record_feedback( RetrievalFeedbackRequest( label=payload["label"], query=dict(payload.get("query", {})), result_ref=dict(payload.get("result_ref", {})), notes=payload.get("notes"), metadata=dict(payload.get("metadata", {})), ), context, ).to_dict() def list_retrieval_feedback( self, *, correlation_id: str | None = None, label: str | None = None, ) -> dict[str, Any]: parsed_label = _enum_filter(RetrievalFeedbackLabel, label, "retrieval feedback label") records = self.retrieval_service().list_feedback(correlation_id=correlation_id, label=parsed_label) return {"items": [record.to_dict() for record in records], "count": len(records)} def retrieval_quality_metrics(self) -> dict[str, Any]: return self.retrieval_service().quality_metrics().to_dict() def list_transformation_operations(self) -> dict[str, Any]: operations = self.transformation_service().list_operations() return {"items": [operation.to_dict() for operation in operations], "count": len(operations)} def execute_transformation(self, payload: dict[str, Any], context: OperationContext) -> dict[str, Any]: result = self.transformation_service().execute_transformation( _transformation_request(payload), context, ) return _transformation_result_envelope(result) def get_transformation_run(self, run_id: str) -> dict[str, Any]: return _transformation_run_envelope(self.transformation_service().get_run(run_id)) def list_transformation_runs( self, *, status: str | None = None, operation_id: str | None = None, ) -> dict[str, Any]: parsed_status = _enum_filter(TransformationRunStatus, status, "transformation run status") runs = self.transformation_service().list_runs(status=parsed_status, operation_id=operation_id) return {"items": [_transformation_run_envelope(run) for run in runs], "count": len(runs)} def retry_transformation_run(self, run_id: str, context: OperationContext) -> dict[str, Any]: return _transformation_result_envelope(self.transformation_service().retry_run(run_id, context)) def cancel_transformation_run( self, run_id: str, payload: dict[str, Any], context: OperationContext, ) -> dict[str, Any]: run = self.transformation_service().cancel_run(run_id, context, reason=payload.get("reason")) return _transformation_run_envelope(run) def register_workflow_template(self, payload: dict[str, Any], context: OperationContext) -> dict[str, Any]: return self.workflow_service().register_template(_workflow_template(payload), context).to_dict() def get_workflow_template(self, template_id: str, *, version: str | None = None) -> dict[str, Any]: return self.workflow_service().get_template(template_id, version=version).to_dict() def list_workflow_templates(self, *, template_id: str | None = None) -> dict[str, Any]: templates = self.workflow_service().list_templates(template_id=template_id) return {"items": [template.to_dict() for template in templates], "count": len(templates)} def queue_workflow_run(self, payload: dict[str, Any], context: OperationContext) -> dict[str, Any]: return _workflow_result_envelope( self.workflow_service().queue_template(_workflow_invocation(payload), context) ) def invoke_workflow_run(self, payload: dict[str, Any], context: OperationContext) -> dict[str, Any]: return _workflow_result_envelope( self.workflow_service().invoke_template(_workflow_invocation(payload), context) ) def get_workflow_run(self, run_id: str) -> dict[str, Any]: return _workflow_run_envelope(self.repository.get_workflow_run(run_id)) def list_workflow_runs( self, *, status: str | None = None, template_id: str | None = None, ) -> dict[str, Any]: parsed_status = _enum_filter(WorkflowRunStatus, status, "workflow run status") runs = self.repository.list_workflow_runs(status=parsed_status, template_id=template_id) return {"items": [_workflow_run_envelope(run) for run in runs], "count": len(runs)} def resume_workflow_run(self, run_id: str, context: OperationContext) -> dict[str, Any]: return _workflow_result_envelope(self.workflow_service().resume_run(run_id, context)) def retry_workflow_run(self, run_id: str, context: OperationContext) -> dict[str, Any]: return _workflow_result_envelope(self.workflow_service().retry_run(run_id, context)) def cancel_workflow_run( self, run_id: str, payload: dict[str, Any], context: OperationContext, ) -> dict[str, Any]: run = self.workflow_service().cancel_run(run_id, context, reason=payload.get("reason")) return _workflow_run_envelope(run) def reconstruct_workflow_run(self, run_id: str) -> dict[str, Any]: return self.workflow_service().reconstruct_run(run_id).to_dict() def list_workflow_review_tasks( self, *, status: str | None = WorkflowReviewStatus.OPEN.value, workflow_run_id: str | None = None, ) -> dict[str, Any]: parsed_status = _enum_filter(WorkflowReviewStatus, status, "workflow review status") reviews = self.workflow_service().list_review_tasks( status=parsed_status, workflow_run_id=workflow_run_id, ) return {"items": [review.to_dict() for review in reviews], "count": len(reviews)} def list_workflow_exceptions( self, *, status: str | None = WorkflowExceptionStatus.OPEN.value, kind: str | None = None, workflow_run_id: str | None = None, ) -> dict[str, Any]: parsed_status = _enum_filter(WorkflowExceptionStatus, status, "workflow exception status") parsed_kind = _enum_filter(WorkflowExceptionKind, kind, "workflow exception kind") exceptions = self.workflow_service().list_exception_queue( status=parsed_status, kind=parsed_kind, workflow_run_id=workflow_run_id, ) return {"items": [exception.to_dict() for exception in exceptions], "count": len(exceptions)} def record_workflow_review_decision( self, run_id: str, review_id: str, payload: dict[str, Any], context: OperationContext, ) -> dict[str, Any]: decision = payload.get("decision", WorkflowReviewDecisionType.CONTINUE.value) return _workflow_result_envelope( self.workflow_service().record_review_decision( run_id, review_id, decision, context, note=payload.get("note", ""), correction=dict(payload.get("correction", {})), ) ) def list_agent_operations(self) -> dict[str, Any]: return {"items": [dict(item) for item in AGENT_OPERATION_CATALOG], "count": len(AGENT_OPERATION_CATALOG)} def get_agent_operation(self, operation_id: str) -> dict[str, Any]: return dict(_agent_operation(operation_id)) def execute_agent_operation( self, operation_id: str, payload: dict[str, Any], context: OperationContext, ) -> dict[str, Any]: operation = _agent_operation(operation_id) dry_run = bool(payload.get("dry_run", False)) operation_payload = dict(payload.get("payload", payload)) operation_payload.pop("dry_run", None) decision = self._authorize_agent_operation(operation, operation_payload, context) effect = decision.effect if effect == PolicyEffect.REQUIRE_REVIEW: event = self._audit_agent_operation( operation, AuditOutcome.REVIEW_REQUIRED, context, decision, details={"phase": "review_required", "payload_keys": sorted(operation_payload)}, ) return _agent_review_required_envelope(operation, decision, event, context) if effect == PolicyEffect.DRY_RUN_ONLY and not dry_run: event = self._audit_agent_operation( operation, AuditOutcome.DRY_RUN, context, decision, details={"phase": "dry_run_required", "payload_keys": sorted(operation_payload)}, ) return _agent_dry_run_required_envelope(operation, decision, event, context) queued_event = self._audit_agent_operation( operation, AuditOutcome.DRY_RUN if dry_run else AuditOutcome.SUCCESS, context, decision, details={ "phase": "accepted", "dry_run": dry_run, "payload_keys": sorted(operation_payload), }, ) if dry_run: return { "operation_id": operation_id, "dry_run": True, "success": True, "would_execute": operation, "correlation_id": context.correlation_id, "policy_decision": decision.to_dict(), "audit_event": queued_event.to_dict(), } try: result = self._dispatch_agent_operation(operation_id, operation_payload, context) except Exception as exc: failed_event = self._audit_agent_operation( operation, AuditOutcome.FAILED, context, decision, details={ "phase": "failed", "error_type": type(exc).__name__, "payload_keys": sorted(operation_payload), }, ) if isinstance(exc, KontextualError): exc.details.setdefault("agent_audit_event_id", failed_event.event_id) raise completed_event = self._audit_agent_operation( operation, AuditOutcome.SUCCESS, context, decision, details={ "phase": "completed", "result_keys": sorted(result) if isinstance(result, dict) else [], }, ) return { "operation_id": operation_id, "dry_run": False, "success": True, "correlation_id": context.correlation_id, "result": result, "policy_decision": decision.to_dict(), "audit_event": completed_event.to_dict(), } def _dispatch_agent_operation( self, operation_id: str, payload: dict[str, Any], context: OperationContext, ) -> dict[str, Any]: if operation_id == "inspect_asset": return self.get_asset(payload["asset_id"]) if operation_id == "retrieve_asset": return self._asset_bundle(payload["asset_id"]) if operation_id == "search_assets": return self.query_assets(dict(payload.get("query", {})), context) if operation_id == "assemble_context": query_result = self.query_assets(dict(payload.get("query", {})), context) return { "context_preview": { "intent": payload.get("intent", "Support a bounded agent task with source-grounded context."), "instructions": payload.get("instructions", ""), "constraints": dict(payload.get("constraints", {})), "correlation_id": query_result["correlation_id"], "source_grounded": True, "items": query_result["results"], "result_count": query_result["result_count"], "total": query_result["total"], } } if operation_id == "enrich_metadata": return self.add_metadata_record(payload["asset_id"], dict(payload["metadata"]), context) if operation_id == "classify_asset": request = { "operation_id": "classify", "source_asset_ids": [payload["asset_id"]], "parameters": dict(payload.get("parameters", {})), "metadata": dict(payload.get("metadata", {})), } return self.execute_transformation(request, context) if operation_id == "transform_asset": return self.execute_transformation(dict(payload["transformation"]), context) if operation_id == "invoke_workflow": return self.invoke_workflow_run(dict(payload["workflow"]), context) if operation_id == "submit_review": return self.record_workflow_review_decision( payload["run_id"], payload["review_id"], payload, context, ) if operation_id == "report_result": return self._agent_report(payload, context) raise ValidationError("Unsupported agent operation", details={"operation_id": operation_id}) def _authorize_agent_operation( self, operation: dict[str, Any], payload: dict[str, Any], context: OperationContext, ) -> PolicyDecision: action = f"agent.operation.{operation['operation_id']}" resource = f"agent_operation:{operation['operation_id']}" try: decision = self.policy_gateway.authorize( context, action, resource, resource_metadata={ "operation": operation, "payload_keys": sorted(payload), "dry_run_supported": operation["dry_run_supported"], }, ) except Exception as exc: decision = PolicyDecision.fail_closed( context.actor.id, action, resource, reason=str(exc) or "Agent operation policy gateway failed", context={"gateway_error": type(exc).__name__}, ) if not decision.allowed and decision.effect not in ( PolicyEffect.REQUIRE_REVIEW, PolicyEffect.DRY_RUN_ONLY, ): event = self._audit_agent_operation(operation, AuditOutcome.DENIED, context, decision) raise AuthorizationError( "Operation denied by policy", details={ "action": action, "resource": resource, "correlation_id": context.correlation_id, "agent_audit_event_id": event.event_id, "policy_decision": decision.to_dict(), }, ) return decision def _audit_agent_operation( self, operation: dict[str, Any], outcome: AuditOutcome, context: OperationContext, policy_decision: PolicyDecision, *, details: dict[str, Any] | None = None, ) -> AuditEvent: event = AuditEvent.from_context( operation["audit_operation"], f"agent_operation:{operation['operation_id']}", outcome, context, policy_decision=policy_decision, details=details, ) return self.repository.save_audit_event(event) def operational_metrics(self) -> dict[str, Any]: assets = self.repository.list_assets() ingestion_jobs = self.repository.list_ingestion_jobs() transformation_runs = self.repository.list_transformation_runs() workflow_runs = self.repository.list_workflow_runs() audit_events = self.repository.list_audit_events() retrieval_events = [event for event in audit_events if event.operation.startswith("retrieval.")] query_latencies = [ float(event.details["permission_filter_duration_ms"]) for event in retrieval_events if "permission_filter_duration_ms" in event.details ] failed_jobs = [job for job in ingestion_jobs if job.status == IngestionJobStatus.FAILED] failed_transformations = [run for run in transformation_runs if run.status == TransformationRunStatus.FAILED] failed_workflows = [run for run in workflow_runs if run.status == WorkflowRunStatus.FAILED] queue_ages = [ _age_seconds(job.created_at, job.completed_at or utc_now().isoformat()) for job in ingestion_jobs if job.status in (IngestionJobStatus.QUEUED, IngestionJobStatus.RUNNING) ] return { "generated_at": utc_now().isoformat(), "repository": type(self.repository).__name__, "assets": { "count": len(assets), "representations": len(self.repository.list_representations()), "relationships": len(self.repository.list_relationships()), "context_entities": len(self.repository.list_context_entities()), }, "ingestion": { "job_count": len(ingestion_jobs), "completed": _count_by_value(job.status.value for job in ingestion_jobs).get("completed", 0), "failed": len(failed_jobs), "partial": _count_by_value(job.status.value for job in ingestion_jobs).get("partially_completed", 0), "throughput_assets": sum(len(job.output_asset_ids) for job in ingestion_jobs), "failure_rate": _ratio(len(failed_jobs), len(ingestion_jobs)), }, "retrieval": { "query_events": len(retrieval_events), "average_permission_filter_duration_ms": _average(query_latencies), "quality": self.retrieval_quality_metrics(), }, "transformations": { "run_count": len(transformation_runs), "completed": _count_by_value(run.status.value for run in transformation_runs).get("completed", 0), "failed": len(failed_transformations), "failure_rate": _ratio(len(failed_transformations), len(transformation_runs)), }, "workflows": { "run_count": len(workflow_runs), "completed": _count_by_value(run.status.value for run in workflow_runs).get("completed", 0), "failed": len(failed_workflows), "waiting": _count_by_value(run.status.value for run in workflow_runs).get("waiting", 0), "failure_rate": _ratio(len(failed_workflows), len(workflow_runs)), }, "permissions": { "policy_events": len([event for event in audit_events if event.policy_decision is not None]), "denied_events": len([event for event in audit_events if event.outcome == AuditOutcome.DENIED]), "review_required_events": len( [event for event in audit_events if event.outcome == AuditOutcome.REVIEW_REQUIRED] ), }, "service": { "started_at": self.started_at, "uptime_seconds": _age_seconds(self.started_at, utc_now().isoformat()), "api_latency_observation_count": 0, }, "storage_index_health": self.readiness()["checks"], "queue_age_seconds": { "max": max(queue_ages) if queue_ages else 0.0, "average": _average(queue_ages), }, } def inspect_jobs( self, *, kind: str | None = None, status: str | None = None, correlation_id: str | None = None, ) -> dict[str, Any]: items: list[dict[str, Any]] = [] if kind in (None, "ingestion"): parsed = _enum_filter(IngestionJobStatus, status, "ingestion job status") if status else None for job in self.repository.list_ingestion_jobs(status=parsed): if correlation_id is None or job.correlation_id == correlation_id: items.append({"kind": "ingestion", **_ingestion_job_envelope(job)}) if kind in (None, "transformation"): parsed = _enum_filter(TransformationRunStatus, status, "transformation run status") if status else None for run in self.repository.list_transformation_runs(status=parsed): if correlation_id is None or run.correlation_id == correlation_id: items.append({"kind": "transformation", "run": _transformation_run_envelope(run)}) if kind in (None, "workflow"): parsed = _enum_filter(WorkflowRunStatus, status, "workflow run status") if status else None for run in self.repository.list_workflow_runs(status=parsed): if correlation_id is None or run.correlation_id == correlation_id: items.append({"kind": "workflow", "run": _workflow_run_envelope(run)}) return {"items": items, "count": len(items)} def operational_events( self, *, correlation_id: str | None = None, operation_prefix: str | None = None, ) -> dict[str, Any]: events = self.repository.list_audit_events(correlation_id=correlation_id) if operation_prefix: events = [event for event in events if event.operation.startswith(operation_prefix)] return { "items": [ { "event_id": event.event_id, "operation": event.operation, "target": event.target, "outcome": event.outcome.value, "actor_id": event.actor_id, "correlation_id": event.correlation_id, "occurred_at": event.occurred_at, "details": dict(event.details), } for event in events ], "count": len(events), } def recovery_actions(self) -> dict[str, Any]: actions = [ { "action": "retry_ingestion_job", "target": "ingestion_job", "required": ["job_id"], "permission": "operations.recovery.retry_ingestion_job", }, { "action": "retry_transformation_run", "target": "transformation_run", "required": ["run_id"], "permission": "operations.recovery.retry_transformation_run", }, { "action": "cancel_transformation_run", "target": "transformation_run", "required": ["run_id"], "permission": "operations.recovery.cancel_transformation_run", }, { "action": "retry_workflow_run", "target": "workflow_run", "required": ["run_id"], "permission": "operations.recovery.retry_workflow_run", }, { "action": "cancel_workflow_run", "target": "workflow_run", "required": ["run_id"], "permission": "operations.recovery.cancel_workflow_run", }, { "action": "refresh_retrieval_index", "target": "retrieval_index", "required": [], "permission": "operations.recovery.refresh_retrieval_index", }, { "action": "inspect_failure", "target": "job_or_run", "required": ["kind", "id"], "permission": "operations.recovery.inspect_failure", }, ] return {"items": actions, "count": len(actions)} def execute_recovery_action( self, action: str, payload: dict[str, Any], context: OperationContext, ) -> dict[str, Any]: decision = self._authorize_operator_action(f"operations.recovery.{action}", f"recovery:{action}", payload, context) if action == "retry_ingestion_job": job = self.repository.get_ingestion_job(payload["job_id"]) result = self.start_ingestion_job(_ingestion_retry_payload(job), context) elif action == "retry_transformation_run": result = self.retry_transformation_run(payload["run_id"], context) elif action == "cancel_transformation_run": result = self.cancel_transformation_run(payload["run_id"], payload, context) elif action == "retry_workflow_run": result = self.retry_workflow_run(payload["run_id"], context) elif action == "cancel_workflow_run": result = self.cancel_workflow_run(payload["run_id"], payload, context) elif action == "refresh_retrieval_index": result = self.refresh_retrieval_index() elif action == "inspect_failure": result = self._inspect_failure(payload) else: raise ValidationError( "Unsupported recovery action", details={"action": action, "supported": [item["action"] for item in self.recovery_actions()["items"]]}, ) event = self._audit_operator_action(f"operations.recovery.{action}", f"recovery:{action}", context, decision) return { "action": action, "success": True, "correlation_id": context.correlation_id, "result": result, "policy_decision": decision.to_dict(), "audit_event": event.to_dict(), } def create_export_package(self, payload: dict[str, Any], context: OperationContext) -> dict[str, Any]: asset_ids = self._export_asset_ids(payload, context) decision = self._authorize_operator_action( "export.package.create", "export_package:new", {"asset_ids": asset_ids, "scope": dict(payload.get("scope", {}))}, context, ) records = [_export_asset_bundle(self.repository, asset_id) for asset_id in asset_ids] audit_events = [ event.to_dict() for asset_id in asset_ids for event in self.repository.list_audit_events(target=f"asset:{asset_id}") ] package = { "kind": "kontextual.export_package", "schema_version": "1", "package_id": payload.get("package_id") or new_id("export"), "created_at": utc_now().isoformat(), "actor": context.actor.to_dict(), "correlation_id": context.correlation_id, "scope": dict(payload.get("scope", {})), "policy_context": decision.to_dict(), "records": records, "audit_refs": audit_events, "adapter_sections": _export_adapter_sections(records), } package["manifest"] = _export_manifest(package) event = self._audit_operator_action( "export.package.create", f"export_package:{package['package_id']}", context, decision, details={"asset_count": len(asset_ids), "export_hash": package["manifest"]["export_hash"]}, ) package["audit_event"] = event.to_dict() return package def validate_export_package(self, payload: dict[str, Any], context: OperationContext) -> dict[str, Any]: package = dict(payload.get("package", payload)) decision = self._authorize_operator_action( "export.package.validate", f"export_package:{package.get('package_id', 'unknown')}", {"package_id": package.get("package_id")}, context, ) expected = package.get("manifest", {}) actual = _export_manifest({key: value for key, value in package.items() if key != "manifest"}) issues = [] for key in ("asset_count", "metadata_count", "representation_count", "relationship_count", "version_count"): if expected.get(key) != actual.get(key): issues.append({"code": "export.count_mismatch", "field": key, "expected": expected.get(key), "actual": actual.get(key)}) if expected.get("export_hash") != actual.get("export_hash"): issues.append( { "code": "export.integrity_mismatch", "field": "export_hash", "expected": expected.get("export_hash"), "actual": actual.get("export_hash"), } ) event = self._audit_operator_action( "export.package.validate", f"export_package:{package.get('package_id', 'unknown')}", context, decision, details={"valid": not issues, "issue_count": len(issues)}, ) return { "valid": not issues, "issues": issues, "expected_manifest": expected, "actual_manifest": actual, "policy_decision": decision.to_dict(), "audit_event": event.to_dict(), } def governance_report(self, payload: dict[str, Any], context: OperationContext) -> dict[str, Any]: asset_ids = self._export_asset_ids(payload, context) if payload else [asset.id for asset in self.repository.list_assets()] decision = self._authorize_operator_action("governance.report.generate", "governance:report", payload, context) findings: list[dict[str, Any]] = [] for asset_id in asset_ids: asset = self.repository.get_asset(asset_id) metadata = self.repository.list_metadata_records(asset_id) if not asset.classification.owner: findings.append({"asset_id": asset_id, "code": "governance.owner_missing", "severity": "warning"}) if not metadata: findings.append({"asset_id": asset_id, "code": "governance.metadata_missing", "severity": "warning"}) if not asset.source_refs: findings.append({"asset_id": asset_id, "code": "governance.source_ref_missing", "severity": "error"}) if asset.classification.sensitivity.value in {"confidential", "restricted"}: has_review = any(record.key in {"review_state", "legal_hold", "retention"} for record in metadata) if not has_review: findings.append({"asset_id": asset_id, "code": "governance.sensitive_without_review_metadata", "severity": "warning"}) if not self.repository.list_audit_events(target=f"asset:{asset_id}"): findings.append({"asset_id": asset_id, "code": "governance.audit_missing", "severity": "error"}) event = self._audit_operator_action( "governance.report.generate", "governance:report", context, decision, details={"asset_count": len(asset_ids), "finding_count": len(findings)}, ) return { "generated_at": utc_now().isoformat(), "scope": {"asset_ids": asset_ids}, "summary": _count_by_value(finding["code"] for finding in findings), "findings": findings, "redaction": {"policy_enforced": True, "content_included": False}, "policy_decision": decision.to_dict(), "audit_event": event.to_dict(), } def extension_catalog(self) -> dict[str, Any]: ingestion = self.ingestion_capabilities() return { "source_connectors": ingestion["connectors"], "extractors": ingestion["extractors"], "transformations": self.list_transformation_operations()["items"], "event_types": _extension_event_types(), "backend_abstractions": [ "asset_registry_repository", "policy_gateway", "source_connector", "format_extractor", "transformation_operation_registry", "event_publisher", "search_index", "ai_model_adapter", ], "markitect_boundary": "Markdown parsing, selectors, contracts, snapshots, and markdown context-package rendering stay delegated to markitect-tool adapters.", } def emit_extension_event(self, payload: dict[str, Any], context: OperationContext) -> dict[str, Any]: event_type = payload["event_type"] if event_type not in _extension_event_types(): raise ValidationError( "Unsupported extension event type", details={"event_type": event_type, "supported": _extension_event_types()}, ) decision = self._authorize_operator_action( "extension.event.emit", f"extension_event:{event_type}", payload, context, ) event = self._audit_operator_action( f"extension.{event_type}", payload.get("target", f"extension_event:{event_type}"), context, decision, details={"payload": dict(payload.get("payload", {})), "metadata": dict(payload.get("metadata", {}))}, ) return {"event": event.to_dict(), "policy_decision": decision.to_dict()} def record_quality_signal(self, payload: dict[str, Any], context: OperationContext) -> dict[str, Any]: signal_type = payload["signal_type"] target = payload.get("target", f"quality_signal:{signal_type}") decision = self._authorize_operator_action("quality.signal.record", target, payload, context) event = self._audit_operator_action( "quality.signal.recorded", target, context, decision, details={ "signal_type": signal_type, "asset_id": payload.get("asset_id"), "workflow_run_id": payload.get("workflow_run_id"), "agent_id": payload.get("agent_id"), "application_id": payload.get("application_id"), "metrics": dict(payload.get("metrics", {})), "ai_usage": dict(payload.get("ai_usage", {})), "cost": dict(payload.get("cost", {})), }, ) return {"event": event.to_dict(), "policy_decision": decision.to_dict()} def quality_cost_signals(self) -> dict[str, Any]: events = [ event for event in self.repository.list_audit_events() if event.operation in {"quality.signal.recorded", "agent.report.recorded"} ] ai_usage = [event.details.get("ai_usage", {}) for event in events if event.details.get("ai_usage")] costs = [event.details.get("cost", {}) for event in events if event.details.get("cost")] return { "retrieval": self.retrieval_quality_metrics(), "signal_count": len(events), "ai_usage": { "observation_count": len(ai_usage), "tokens": sum(int(item.get("tokens", 0)) for item in ai_usage), "provider_errors": sum(1 for item in ai_usage if item.get("error")), }, "cost": { "observation_count": len(costs), "estimated_total": sum(float(item.get("estimated", 0.0)) for item in costs), "currency": costs[0].get("currency") if costs else None, }, "attribution_dimensions": ["asset_id", "workflow_run_id", "agent_id", "application_id", "actor_id"], } def performance_smoke_report(self) -> dict[str, Any]: metrics = self.operational_metrics() return { "generated_at": utc_now().isoformat(), "smoke_targets": ["ingestion", "retrieval", "workflow", "export"], "measurements": { "ingestion_jobs": metrics["ingestion"]["job_count"], "retrieval_query_events": metrics["retrieval"]["query_events"], "workflow_runs": metrics["workflows"]["run_count"], "export_events": len( [event for event in self.repository.list_audit_events() if event.operation.startswith("export.")] ), }, "history_note": "Longitudinal pytest performance history is captured by tests/conftest.py.", } def mvp_compliance_report(self) -> dict[str, Any]: implemented = { "asset_registry": self.repository.list_assets is not None, "ingestion_jobs": hasattr(self.repository, "list_ingestion_jobs"), "governed_retrieval": True, "transformations": True, "workflow_jobs": True, "service_api": True, "agent_operations": True, "context_packages": True, "observability": True, "exports": True, "governance_reporting": True, } return { "generated_at": utc_now().isoformat(), "perspective": "V0.2 MVP acceptance", "requirements": [ {"requirement": "FR-200..FR-207 observability and recovery", "status": "implemented"}, {"requirement": "FR-220..FR-225 export and portability", "status": "implemented"}, {"requirement": "FR-120..FR-132 governance and audit", "status": "implemented"}, {"requirement": "FR-160..FR-188 agent-safe service operation", "status": "implemented"}, {"requirement": "P1/P2 enterprise adapters", "status": "explicitly_deferred"}, ], "implemented_capabilities": implemented, "remaining_gaps": [ "External webhook delivery adapters are represented as event contracts, not network emitters.", "Provider-backed AI cost depends on adapters supplying usage metadata.", "API request latency needs middleware instrumentation when FastAPI service runtime is deployed.", ], } def _export_asset_ids(self, payload: dict[str, Any], context: OperationContext) -> list[str]: scope = dict(payload.get("scope", payload)) if scope.get("asset_ids"): return list(dict.fromkeys(str(item) for item in scope["asset_ids"])) if scope.get("asset_id"): return [str(scope["asset_id"])] if scope.get("query"): result = self.query_assets(dict(scope["query"]), context) return [item["asset_id"] for item in result.get("results", ())] assets = self.repository.list_assets( lifecycle=LifecycleState(scope["lifecycle"]) if scope.get("lifecycle") else None, asset_type=scope.get("asset_type"), sensitivity=scope.get("sensitivity"), owner=scope.get("owner"), topic=scope.get("topic"), ) return [asset.id for asset in assets] def _inspect_failure(self, payload: dict[str, Any]) -> dict[str, Any]: kind = payload["kind"] identifier = payload["id"] if kind == "ingestion": return _ingestion_job_envelope(self.repository.get_ingestion_job(identifier)) if kind == "transformation": return _transformation_run_envelope(self.repository.get_transformation_run(identifier)) if kind == "workflow": return _workflow_run_envelope(self.repository.get_workflow_run(identifier)) raise ValidationError("Unsupported failure inspection kind", details={"kind": kind}) def _authorize_operator_action( self, action: str, resource: str, payload: dict[str, Any], context: OperationContext, ) -> PolicyDecision: try: decision = self.policy_gateway.authorize( context, action, resource, resource_metadata={"payload_keys": sorted(payload)}, ) except Exception as exc: decision = PolicyDecision.fail_closed( context.actor.id, action, resource, reason=str(exc) or "Operator policy gateway failed", context={"gateway_error": type(exc).__name__}, ) if not decision.allowed: event = self._audit_operator_action(action, resource, context, decision, outcome=AuditOutcome.DENIED) raise AuthorizationError( "Operation denied by policy", details={ "action": action, "resource": resource, "correlation_id": context.correlation_id, "audit_event_id": event.event_id, "policy_decision": decision.to_dict(), }, ) return decision def _audit_operator_action( self, operation: str, target: str, context: OperationContext, decision: PolicyDecision, *, outcome: AuditOutcome = AuditOutcome.SUCCESS, details: dict[str, Any] | None = None, ) -> AuditEvent: event = AuditEvent.from_context( operation, target, outcome, context, policy_decision=decision, details=details, ) return self.repository.save_audit_event(event) def _asset_bundle(self, asset_id: str) -> dict[str, Any]: asset = self.repository.get_asset(asset_id) metadata_records = self.repository.list_metadata_records(asset_id) representations = self.repository.list_representations(asset_id=asset_id) relationships = self.repository.list_relationships(source_id=asset_id) return { "asset": asset.to_dict(), "metadata_records": [record.to_dict() for record in metadata_records], "representations": [representation.to_dict() for representation in representations], "relationships": [relationship.to_dict() for relationship in relationships], "source_grounded": bool(asset.source_refs or representations), } def _agent_report(self, payload: dict[str, Any], context: OperationContext) -> dict[str, Any]: if not payload.get("summary"): raise ValidationError("Agent result report requires a summary", details={"required": ["summary"]}) decision = PolicyDecision.allow( context.actor.id, "agent.operation.report_result.record", f"agent:{context.actor.id}", context={"correlation_id": context.correlation_id}, ) event = AuditEvent.from_context( "agent.report.recorded", f"agent:{context.actor.id}", AuditOutcome.SUCCESS, context, policy_decision=decision, details={ "summary": payload["summary"], "result_ref": dict(payload.get("result_ref", {})), "metadata": dict(payload.get("metadata", {})), }, ) saved = self.repository.save_audit_event(event) return { "summary": payload["summary"], "result_ref": dict(payload.get("result_ref", {})), "metadata": dict(payload.get("metadata", {})), "audit_event": saved.to_dict(), } def context_package_schema(self) -> dict[str, Any]: return { "kind": "kontextual.context_package", "version": "1", "required": ["query"], "optional": [ "package_id", "title", "intent", "instructions", "constraints", "external_memory_refs", "metadata", "format", ], "formats": ["kontextual", "markitect"], "source_grounding": ["source_refs", "representations", "snippets", "relationships", "metadata_records"], "memory_boundary": "external_memory_refs are opaque pointers; memory graph contents are not embedded", } def assemble_context_package(self, payload: dict[str, Any], context: OperationContext) -> dict[str, Any]: query = dict(payload.get("query", {})) query.setdefault("include_snippets", True) query.setdefault("include_relationships", True) query.setdefault("max_snippets", 5) constraints = dict(payload.get("constraints", {})) decision = self._authorize_context_package(query, constraints, context) query_result = self.query_assets(query, context) package = _context_package_payload( payload, query_result, context, constraints=constraints, ) event = self._audit_context_package(package, context, decision) package["audit_event"] = event.to_dict() package["policy_decision"] = decision.to_dict() return package def _authorize_context_package( self, query: dict[str, Any], constraints: dict[str, Any], context: OperationContext, ) -> PolicyDecision: try: decision = self.policy_gateway.authorize( context, "context_package.assemble", "context_package:new", resource_metadata={ "query": query, "constraints": constraints, "external_memory_refs": "opaque", }, ) except Exception as exc: decision = PolicyDecision.fail_closed( context.actor.id, "context_package.assemble", "context_package:new", reason=str(exc) or "Context package policy gateway failed", context={"gateway_error": type(exc).__name__}, ) if not decision.allowed: event = AuditEvent.from_context( "context_package.assemble", "context_package:new", AuditOutcome.DENIED, context, policy_decision=decision, details={"query": query, "constraints": constraints}, ) saved = self.repository.save_audit_event(event) raise AuthorizationError( "Operation denied by policy", details={ "action": "context_package.assemble", "resource": "context_package:new", "correlation_id": context.correlation_id, "audit_event_id": saved.event_id, "policy_decision": decision.to_dict(), }, ) return decision def _audit_context_package( self, package: dict[str, Any], context: OperationContext, decision: PolicyDecision, ) -> AuditEvent: event = AuditEvent.from_context( "context_package.assemble", f"context_package:{package['package_id']}", AuditOutcome.SUCCESS, context, policy_decision=decision, details={ "result_count": package["result_count"], "source_grounded": package["source_grounded"], "format": package["format"], "external_memory_ref_count": len(package["external_memory_refs"]), }, ) return self.repository.save_audit_event(event) def create_app(runtime: ServiceRuntime | None = None): try: from fastapi import Depends, FastAPI, Header, HTTPException, Query from fastapi.responses import JSONResponse except ImportError as exc: # pragma: no cover - exercised when optional extra is absent raise RuntimeError( "FastAPI service dependencies are not installed. Install kontextual-engine[service]." ) from exc runtime = runtime or ServiceRuntime() app = FastAPI( title="Kontextual Engine Service API", version=OPENAPI_VERSION, openapi_url="/openapi.json", docs_url="/docs", redoc_url="/redoc", ) app.state.kontextual_runtime = runtime @app.exception_handler(NotFoundError) async def not_found_error_handler(_request, exc: NotFoundError) -> JSONResponse: return JSONResponse(status_code=404, content=_error_payload(exc)) @app.exception_handler(AuthorizationError) async def authorization_error_handler(_request, exc: AuthorizationError) -> JSONResponse: return JSONResponse(status_code=403, content=_authorization_error_payload(exc)) @app.exception_handler(ValidationError) async def validation_error_handler(_request, exc: ValidationError) -> JSONResponse: return JSONResponse(status_code=422, content=_error_payload(exc)) @app.exception_handler(KontextualError) async def kontextual_error_handler(_request, exc: KontextualError) -> JSONResponse: return JSONResponse(status_code=400, content=_error_payload(exc)) @app.get("/health", tags=["system"]) def health() -> dict[str, Any]: return runtime.health() @app.get("/ready", tags=["system"]) def ready() -> dict[str, Any]: return runtime.readiness() @app.get("/version", tags=["system"]) def version() -> dict[str, Any]: return runtime.version() prefix = f"/api/{runtime.api_version}" @app.get(f"{prefix}/health", tags=["system"]) def versioned_health() -> dict[str, Any]: return runtime.health() @app.get(f"{prefix}/ready", tags=["system"]) def versioned_ready() -> dict[str, Any]: return runtime.readiness() @app.get(f"{prefix}/version", tags=["system"]) def versioned_version() -> dict[str, Any]: return runtime.version() def context_from_headers( x_actor_id: str | None = Header(None), x_actor_type: str | None = Header(None), x_actor_display_name: str | None = Header(None), x_actor_external_ref: str | None = Header(None), x_actor_groups: str | None = Header(None), x_correlation_id: str | None = Header(None), x_delegated_actor_id: str | None = Header(None), x_delegated_actor_type: str | None = Header(None), x_delegated_actor_display_name: str | None = Header(None), x_delegated_actor_external_ref: str | None = Header(None), x_delegated_actor_groups: str | None = Header(None), x_agent_id: str | None = Header(None), x_agent_name: str | None = Header(None), x_agent_run_id: str | None = Header(None), x_agent_tool: str | None = Header(None), x_request_scope: str | None = Header(None), x_policy_scope: str | None = Header(None), ) -> OperationContext: actor_id = x_actor_id or x_agent_id or "api-user" actor_type = x_actor_type or ("ai_agent" if x_agent_id else "human") return runtime.operation_context( actor_id=actor_id, actor_type=actor_type, display_name=x_actor_display_name, external_ref=x_actor_external_ref, correlation_id=x_correlation_id, groups=_split_header_list(x_actor_groups), delegated_actor_id=x_delegated_actor_id, delegated_actor_type=x_delegated_actor_type or "human", delegated_actor_display_name=x_delegated_actor_display_name, delegated_actor_external_ref=x_delegated_actor_external_ref, delegated_actor_groups=_split_header_list(x_delegated_actor_groups), request_scope=_json_header(x_request_scope, "X-Request-Scope"), policy_scope=_json_header(x_policy_scope, "X-Policy-Scope"), agent_id=x_agent_id, agent_name=x_agent_name, agent_run_id=x_agent_run_id, agent_tool=x_agent_tool, ) def response(callable_obj, *args: Any, **kwargs: Any) -> Any: try: return callable_obj(*args, **kwargs) except NotFoundError as exc: raise HTTPException(status_code=404, detail=_error_payload(exc)) from exc except AuthorizationError as exc: raise HTTPException(status_code=403, detail=_authorization_error_payload(exc)) from exc except ValidationError as exc: raise HTTPException(status_code=422, detail=_error_payload(exc)) from exc except KontextualError as exc: raise HTTPException(status_code=400, detail=_error_payload(exc)) from exc except (KeyError, TypeError, ValueError) as exc: raise HTTPException( status_code=422, detail={ "code": "kontextual.validation", "message": "Invalid request payload", "details": {"error_type": type(exc).__name__, "message": str(exc)}, }, ) from exc @app.get(f"{prefix}/context", tags=["context"]) def current_context( context: OperationContext = Depends(context_from_headers), ) -> dict[str, Any]: return context.to_dict() @app.post(f"{prefix}/assets", tags=["assets"]) def create_asset( payload: dict[str, Any], context: OperationContext = Depends(context_from_headers), ) -> dict[str, Any]: return response(runtime.create_asset, payload, context) @app.get(f"{prefix}/assets", tags=["assets"]) def list_assets( lifecycle: str | None = Query(None), asset_type: str | None = Query(None), sensitivity: str | None = Query(None), owner: str | None = Query(None), topic: str | None = Query(None), review_state: str | None = Query(None), ) -> dict[str, Any]: return response( runtime.list_assets, lifecycle=lifecycle, asset_type=asset_type, sensitivity=sensitivity, owner=owner, topic=topic, review_state=review_state, ) @app.get(f"{prefix}/assets/{{asset_id}}", tags=["assets"]) def get_asset(asset_id: str) -> dict[str, Any]: return response(runtime.get_asset, asset_id) @app.post(f"{prefix}/assets/{{asset_id}}/metadata", tags=["metadata"]) def add_metadata( asset_id: str, payload: dict[str, Any], context: OperationContext = Depends(context_from_headers), ) -> dict[str, Any]: return response(runtime.add_metadata_record, asset_id, payload, context) @app.get(f"{prefix}/assets/{{asset_id}}/metadata", tags=["metadata"]) def list_metadata(asset_id: str) -> dict[str, Any]: return response(runtime.list_metadata_records, asset_id) @app.post(f"{prefix}/assets/{{asset_id}}/lifecycle", tags=["assets"]) def transition_lifecycle( asset_id: str, payload: dict[str, Any], context: OperationContext = Depends(context_from_headers), ) -> dict[str, Any]: return response(runtime.transition_lifecycle, asset_id, payload, context) @app.post(f"{prefix}/relationships", tags=["relationships"]) def create_relationship( payload: dict[str, Any], context: OperationContext = Depends(context_from_headers), ) -> dict[str, Any]: return response(runtime.create_relationship, payload, context) @app.get(f"{prefix}/relationships", tags=["relationships"]) def list_relationships( source_id: str | None = Query(None), target_id: str | None = Query(None), ) -> dict[str, Any]: return response(runtime.list_relationships, source_id=source_id, target_id=target_id) @app.get(f"{prefix}/audit/events", tags=["audit"]) def list_audit_events( target: str | None = Query(None), correlation_id: str | None = Query(None), ) -> dict[str, Any]: return response(runtime.list_audit_events, target=target, correlation_id=correlation_id) @app.post(f"{prefix}/policy/evaluate", tags=["policy"]) def evaluate_policy( payload: dict[str, Any], context: OperationContext = Depends(context_from_headers), ) -> dict[str, Any]: return response(runtime.evaluate_policy, payload, context) @app.get(f"{prefix}/ingestion/capabilities", tags=["ingestion"]) def ingestion_capabilities() -> dict[str, Any]: return response(runtime.ingestion_capabilities) @app.post(f"{prefix}/ingestion/jobs", tags=["ingestion"]) def start_ingestion_job( payload: dict[str, Any], context: OperationContext = Depends(context_from_headers), ) -> dict[str, Any]: return response(runtime.start_ingestion_job, payload, context) @app.get(f"{prefix}/ingestion/jobs", tags=["ingestion"]) def list_ingestion_jobs(status: str | None = Query(None)) -> dict[str, Any]: return response(runtime.list_ingestion_jobs, status=status) @app.get(f"{prefix}/ingestion/jobs/{{job_id}}", tags=["ingestion"]) def get_ingestion_job(job_id: str) -> dict[str, Any]: return response(runtime.get_ingestion_job, job_id) @app.post(f"{prefix}/retrieval/index/refresh", tags=["retrieval"]) def refresh_retrieval_index() -> dict[str, Any]: return response(runtime.refresh_retrieval_index) @app.post(f"{prefix}/retrieval/assets", tags=["retrieval"]) def query_assets( payload: dict[str, Any], context: OperationContext = Depends(context_from_headers), ) -> dict[str, Any]: return response(runtime.query_assets, payload, context) @app.post(f"{prefix}/retrieval/context-entities", tags=["retrieval"]) def query_context_entities( payload: dict[str, Any], context: OperationContext = Depends(context_from_headers), ) -> dict[str, Any]: return response(runtime.query_context_entities, payload, context) @app.post(f"{prefix}/retrieval/relationships", tags=["retrieval"]) def query_retrieval_relationships( payload: dict[str, Any], context: OperationContext = Depends(context_from_headers), ) -> dict[str, Any]: return response(runtime.query_relationships, payload, context) @app.post(f"{prefix}/retrieval/feedback", tags=["retrieval"]) def record_retrieval_feedback( payload: dict[str, Any], context: OperationContext = Depends(context_from_headers), ) -> dict[str, Any]: return response(runtime.record_retrieval_feedback, payload, context) @app.get(f"{prefix}/retrieval/feedback", tags=["retrieval"]) def list_retrieval_feedback( correlation_id: str | None = Query(None), label: str | None = Query(None), ) -> dict[str, Any]: return response(runtime.list_retrieval_feedback, correlation_id=correlation_id, label=label) @app.get(f"{prefix}/retrieval/quality", tags=["retrieval"]) def retrieval_quality_metrics() -> dict[str, Any]: return response(runtime.retrieval_quality_metrics) @app.get(f"{prefix}/transformations/operations", tags=["transformations"]) def list_transformation_operations() -> dict[str, Any]: return response(runtime.list_transformation_operations) @app.post(f"{prefix}/transformations/runs", tags=["transformations"]) def execute_transformation( payload: dict[str, Any], context: OperationContext = Depends(context_from_headers), ) -> dict[str, Any]: return response(runtime.execute_transformation, payload, context) @app.get(f"{prefix}/transformations/runs", tags=["transformations"]) def list_transformation_runs( status: str | None = Query(None), operation_id: str | None = Query(None), ) -> dict[str, Any]: return response(runtime.list_transformation_runs, status=status, operation_id=operation_id) @app.get(f"{prefix}/transformations/runs/{{run_id}}", tags=["transformations"]) def get_transformation_run(run_id: str) -> dict[str, Any]: return response(runtime.get_transformation_run, run_id) @app.post(f"{prefix}/transformations/runs/{{run_id}}/retry", tags=["transformations"]) def retry_transformation_run( run_id: str, context: OperationContext = Depends(context_from_headers), ) -> dict[str, Any]: return response(runtime.retry_transformation_run, run_id, context) @app.post(f"{prefix}/transformations/runs/{{run_id}}/cancel", tags=["transformations"]) def cancel_transformation_run( run_id: str, payload: dict[str, Any], context: OperationContext = Depends(context_from_headers), ) -> dict[str, Any]: return response(runtime.cancel_transformation_run, run_id, payload, context) @app.post(f"{prefix}/workflows/templates", tags=["workflows"]) def register_workflow_template( payload: dict[str, Any], context: OperationContext = Depends(context_from_headers), ) -> dict[str, Any]: return response(runtime.register_workflow_template, payload, context) @app.get(f"{prefix}/workflows/templates", tags=["workflows"]) def list_workflow_templates(template_id: str | None = Query(None)) -> dict[str, Any]: return response(runtime.list_workflow_templates, template_id=template_id) @app.get(f"{prefix}/workflows/templates/{{template_id}}", tags=["workflows"]) def get_workflow_template( template_id: str, version: str | None = Query(None), ) -> dict[str, Any]: return response(runtime.get_workflow_template, template_id, version=version) @app.post(f"{prefix}/workflows/runs", tags=["workflows"]) def invoke_workflow_run( payload: dict[str, Any], context: OperationContext = Depends(context_from_headers), ) -> dict[str, Any]: return response(runtime.invoke_workflow_run, payload, context) @app.post(f"{prefix}/workflows/runs/queue", tags=["workflows"]) def queue_workflow_run( payload: dict[str, Any], context: OperationContext = Depends(context_from_headers), ) -> dict[str, Any]: return response(runtime.queue_workflow_run, payload, context) @app.get(f"{prefix}/workflows/runs", tags=["workflows"]) def list_workflow_runs( status: str | None = Query(None), template_id: str | None = Query(None), ) -> dict[str, Any]: return response(runtime.list_workflow_runs, status=status, template_id=template_id) @app.get(f"{prefix}/workflows/runs/{{run_id}}", tags=["workflows"]) def get_workflow_run(run_id: str) -> dict[str, Any]: return response(runtime.get_workflow_run, run_id) @app.post(f"{prefix}/workflows/runs/{{run_id}}/resume", tags=["workflows"]) def resume_workflow_run( run_id: str, context: OperationContext = Depends(context_from_headers), ) -> dict[str, Any]: return response(runtime.resume_workflow_run, run_id, context) @app.post(f"{prefix}/workflows/runs/{{run_id}}/retry", tags=["workflows"]) def retry_workflow_run( run_id: str, context: OperationContext = Depends(context_from_headers), ) -> dict[str, Any]: return response(runtime.retry_workflow_run, run_id, context) @app.post(f"{prefix}/workflows/runs/{{run_id}}/cancel", tags=["workflows"]) def cancel_workflow_run( run_id: str, payload: dict[str, Any], context: OperationContext = Depends(context_from_headers), ) -> dict[str, Any]: return response(runtime.cancel_workflow_run, run_id, payload, context) @app.get(f"{prefix}/workflows/runs/{{run_id}}/reconstruction", tags=["workflows"]) def reconstruct_workflow_run(run_id: str) -> dict[str, Any]: return response(runtime.reconstruct_workflow_run, run_id) @app.get(f"{prefix}/workflows/reviews", tags=["workflows"]) def list_workflow_review_tasks( status: str | None = Query(WorkflowReviewStatus.OPEN.value), workflow_run_id: str | None = Query(None), ) -> dict[str, Any]: return response(runtime.list_workflow_review_tasks, status=status, workflow_run_id=workflow_run_id) @app.get(f"{prefix}/workflows/exceptions", tags=["workflows"]) def list_workflow_exceptions( status: str | None = Query(WorkflowExceptionStatus.OPEN.value), kind: str | None = Query(None), workflow_run_id: str | None = Query(None), ) -> dict[str, Any]: return response( runtime.list_workflow_exceptions, status=status, kind=kind, workflow_run_id=workflow_run_id, ) @app.post(f"{prefix}/workflows/runs/{{run_id}}/reviews/{{review_id}}/decision", tags=["workflows"]) def record_workflow_review_decision( run_id: str, review_id: str, payload: dict[str, Any], context: OperationContext = Depends(context_from_headers), ) -> dict[str, Any]: return response(runtime.record_workflow_review_decision, run_id, review_id, payload, context) @app.get(f"{prefix}/agents/operations", tags=["agents"]) def list_agent_operations() -> dict[str, Any]: return response(runtime.list_agent_operations) @app.get(f"{prefix}/agents/operations/{{operation_id}}", tags=["agents"]) def get_agent_operation(operation_id: str) -> dict[str, Any]: return response(runtime.get_agent_operation, operation_id) @app.post(f"{prefix}/agents/operations/{{operation_id}}", tags=["agents"]) def execute_agent_operation( operation_id: str, payload: dict[str, Any], context: OperationContext = Depends(context_from_headers), ) -> dict[str, Any]: return response(runtime.execute_agent_operation, operation_id, payload, context) @app.get(f"{prefix}/context-packages/schema", tags=["context-packages"]) def context_package_schema() -> dict[str, Any]: return response(runtime.context_package_schema) @app.post(f"{prefix}/context-packages", tags=["context-packages"]) def assemble_context_package( payload: dict[str, Any], context: OperationContext = Depends(context_from_headers), ) -> dict[str, Any]: return response(runtime.assemble_context_package, payload, context) @app.get(f"{prefix}/operations/metrics", tags=["operations"]) def operational_metrics() -> dict[str, Any]: return response(runtime.operational_metrics) @app.get(f"{prefix}/operations/jobs", tags=["operations"]) def inspect_jobs( kind: str | None = Query(None), status: str | None = Query(None), correlation_id: str | None = Query(None), ) -> dict[str, Any]: return response(runtime.inspect_jobs, kind=kind, status=status, correlation_id=correlation_id) @app.get(f"{prefix}/operations/events", tags=["operations"]) def operational_events( correlation_id: str | None = Query(None), operation_prefix: str | None = Query(None), ) -> dict[str, Any]: return response(runtime.operational_events, correlation_id=correlation_id, operation_prefix=operation_prefix) @app.get(f"{prefix}/operations/recovery/actions", tags=["operations"]) def recovery_actions() -> dict[str, Any]: return response(runtime.recovery_actions) @app.post(f"{prefix}/operations/recovery/{{action}}", tags=["operations"]) def execute_recovery_action( action: str, payload: dict[str, Any], context: OperationContext = Depends(context_from_headers), ) -> dict[str, Any]: return response(runtime.execute_recovery_action, action, payload, context) @app.post(f"{prefix}/exports", tags=["exports"]) def create_export_package( payload: dict[str, Any], context: OperationContext = Depends(context_from_headers), ) -> dict[str, Any]: return response(runtime.create_export_package, payload, context) @app.post(f"{prefix}/exports/validate", tags=["exports"]) def validate_export_package( payload: dict[str, Any], context: OperationContext = Depends(context_from_headers), ) -> dict[str, Any]: return response(runtime.validate_export_package, payload, context) @app.post(f"{prefix}/governance/report", tags=["governance"]) def governance_report( payload: dict[str, Any], context: OperationContext = Depends(context_from_headers), ) -> dict[str, Any]: return response(runtime.governance_report, payload, context) @app.get(f"{prefix}/extensions/catalog", tags=["extensions"]) def extension_catalog() -> dict[str, Any]: return response(runtime.extension_catalog) @app.post(f"{prefix}/extensions/events", tags=["extensions"]) def emit_extension_event( payload: dict[str, Any], context: OperationContext = Depends(context_from_headers), ) -> dict[str, Any]: return response(runtime.emit_extension_event, payload, context) @app.post(f"{prefix}/quality/signals", tags=["quality"]) def record_quality_signal( payload: dict[str, Any], context: OperationContext = Depends(context_from_headers), ) -> dict[str, Any]: return response(runtime.record_quality_signal, payload, context) @app.get(f"{prefix}/quality/cost", tags=["quality"]) def quality_cost_signals() -> dict[str, Any]: return response(runtime.quality_cost_signals) @app.get(f"{prefix}/performance/smoke", tags=["compliance"]) def performance_smoke_report() -> dict[str, Any]: return response(runtime.performance_smoke_report) @app.get(f"{prefix}/compliance/mvp", tags=["compliance"]) def mvp_compliance_report() -> dict[str, Any]: return response(runtime.mvp_compliance_report) return app def _age_seconds(start: str, end: str) -> float: try: start_dt = datetime.fromisoformat(start.replace("Z", "+00:00")) end_dt = datetime.fromisoformat(end.replace("Z", "+00:00")) return max(0.0, round((end_dt - start_dt).total_seconds(), 3)) except ValueError: return 0.0 def _average(values: list[float]) -> float | None: return round(sum(values) / len(values), 3) if values else None def _ratio(numerator: int, denominator: int) -> float: return round(numerator / denominator, 4) if denominator else 0.0 def _count_by_value(values: Any) -> dict[str, int]: counts: dict[str, int] = {} for value in values: counts[str(value)] = counts.get(str(value), 0) + 1 return counts def _ingestion_retry_payload(job: Any) -> dict[str, Any]: source_uri = job.input.get("source_uri") payload = { "mode": job.input.get("mode", "file"), "path": source_uri, "identity_policy": job.input.get("identity_policy", IngestionIdentityPolicy.SOURCE_LOCATION.value), "skip_unchanged": bool(job.input.get("skip_unchanged", True)), } if job.input.get("mode") == "directory": payload["recursive"] = bool(job.input.get("recursive", True)) return payload def _export_asset_bundle(repository: AssetRegistryRepository, asset_id: str) -> dict[str, Any]: return { "asset": repository.get_asset(asset_id).to_dict(), "metadata_records": [record.to_dict() for record in repository.list_metadata_records(asset_id)], "representations": [representation.to_dict() for representation in repository.list_representations(asset_id=asset_id)], "relationships": [ relationship.to_dict() for relationship in repository.list_relationships(source_id=asset_id) + repository.list_relationships(target_id=asset_id) ], "versions": [version.to_dict() for version in repository.list_versions(asset_id)], "derived_lineage": [ lineage.to_dict() for lineage in repository.list_derived_lineage(output_asset_id=asset_id) + repository.list_derived_lineage(source_asset_id=asset_id) ], } def _export_manifest(package: dict[str, Any]) -> dict[str, Any]: records = list(package.get("records", ())) payload = { "schema_version": package.get("schema_version", "1"), "records": records, "audit_refs": list(package.get("audit_refs", ())), "adapter_sections": dict(package.get("adapter_sections", {})), } metadata_count = sum(len(record.get("metadata_records", ())) for record in records) representation_count = sum(len(record.get("representations", ())) for record in records) relationship_count = sum(len(record.get("relationships", ())) for record in records) version_count = sum(len(record.get("versions", ())) for record in records) lineage_count = sum(len(record.get("derived_lineage", ())) for record in records) serialized = stable_json_dumps(payload) return { "schema_version": package.get("schema_version", "1"), "asset_count": len(records), "metadata_count": metadata_count, "representation_count": representation_count, "relationship_count": relationship_count, "version_count": version_count, "lineage_count": lineage_count, "audit_ref_count": len(package.get("audit_refs", ())), "export_hash": content_digest(serialized.encode("utf-8")), "hash_algorithm": "sha256", } def _export_adapter_sections(records: list[dict[str, Any]]) -> dict[str, Any]: markitect_representations = [ representation for record in records for representation in record.get("representations", ()) if representation.get("producer") == "markitect-tool" or representation.get("metadata", {}).get("extractor") == "markitect-tool" ] return { "markitect_tool": { "included": bool(markitect_representations), "representation_ids": [item.get("representation_id") for item in markitect_representations], "boundary": "Adapter provenance is exported; markdown semantics remain owned by markitect-tool.", } } def _extension_event_types() -> list[str]: return [ "asset.changed", "ingestion.completed", "workflow.status_changed", "policy.exception", "derived_artifact.created", "review.decided", ] def _agent_metadata( *, agent_id: str | None, agent_name: str | None, agent_run_id: str | None, agent_tool: str | None, ) -> dict[str, Any]: return { key: value for key, value in { "agent_id": agent_id, "agent_name": agent_name, "agent_run_id": agent_run_id, "agent_tool": agent_tool, }.items() if value } def _agent_operation(operation_id: str) -> dict[str, Any]: for operation in AGENT_OPERATION_CATALOG: if operation["operation_id"] == operation_id: return operation raise ValidationError( "Unsupported agent operation", details={ "operation_id": operation_id, "supported": [operation["operation_id"] for operation in AGENT_OPERATION_CATALOG], }, ) def _agent_review_required_envelope( operation: dict[str, Any], decision: PolicyDecision, event: AuditEvent, context: OperationContext, ) -> dict[str, Any]: return { "operation_id": operation["operation_id"], "dry_run": False, "success": False, "review_required": True, "correlation_id": context.correlation_id, "review": { "reason": decision.reason, "obligations": dict(decision.obligations), "required_permissions": list(operation["required_permissions"]), }, "policy_decision": decision.to_dict(), "audit_event": event.to_dict(), } def _agent_dry_run_required_envelope( operation: dict[str, Any], decision: PolicyDecision, event: AuditEvent, context: OperationContext, ) -> dict[str, Any]: return { "operation_id": operation["operation_id"], "dry_run": False, "success": False, "dry_run_required": True, "correlation_id": context.correlation_id, "reason": decision.reason, "policy_decision": decision.to_dict(), "audit_event": event.to_dict(), } def _context_package_payload( request_payload: dict[str, Any], query_result: dict[str, Any], context: OperationContext, *, constraints: dict[str, Any], ) -> dict[str, Any]: package_format = request_payload.get("format", "kontextual") if package_format not in {"kontextual", "markitect"}: raise ValidationError( "Unsupported context package format", details={"format": package_format, "supported": ["kontextual", "markitect"]}, ) items = [_context_package_item(item) for item in query_result.get("results", ())] package = { "kind": "kontextual.context_package", "version": "1", "package_id": request_payload.get("package_id") or new_id("ctxpkg"), "title": request_payload.get("title", "Kontextual Context Package"), "intent": request_payload.get("intent", "Provide bounded, source-grounded context."), "instructions": request_payload.get("instructions", ""), "format": package_format, "correlation_id": context.correlation_id, "query": query_result.get("query", {}), "result_count": query_result.get("result_count", len(items)), "total": query_result.get("total", len(items)), "source_grounded": bool(items) and all(item["source_refs"] or item["snippets"] for item in items), "policy_constraints": constraints, "external_memory_refs": [ _opaque_memory_ref(item) for item in request_payload.get("external_memory_refs", ()) ], "items": items, "metadata": dict(request_payload.get("metadata", {})), } if package_format == "markitect": package["markitect_payload"] = _markitect_context_payload(package) return package def _context_package_item(result: dict[str, Any]) -> dict[str, Any]: return { "asset_id": result["asset_id"], "title": result.get("title"), "classification": dict(result.get("classification", {})), "lifecycle": result.get("lifecycle"), "source_refs": list(result.get("source_refs", ())), "snippets": list(result.get("snippets", ())), "metadata_records": list(result.get("metadata_records", ())), "relationships": list(result.get("relationships", ())), "representations": [ { "representation_id": item.get("representation_id"), "kind": item.get("kind"), "media_type": item.get("media_type"), "source_ref_id": item.get("source_ref_id"), "storage_ref": item.get("storage_ref"), "producer": item.get("producer"), "metadata": { key: value for key, value in dict(item.get("metadata", {})).items() if key in {"extractor", "producer", "normalized_hash", "search_text_length"} }, } for item in result.get("representations", ()) ], "relevance": dict(result.get("relevance", {})), } def _opaque_memory_ref(data: dict[str, Any]) -> dict[str, Any]: ref_id = data.get("ref_id") or data.get("id") or data.get("uri") if not ref_id: raise ValidationError( "External memory reference requires ref_id, id, or uri", details={"required": ["ref_id"]}, ) return { "ref_id": str(ref_id), "system": data.get("system", "phase-memory"), "kind": data.get("kind", "memory_ref"), "opaque": True, "metadata": dict(data.get("metadata", {})), } def _markitect_context_payload(package: dict[str, Any]) -> dict[str, Any]: return { "kind": "markitect.context_package", "version": "1", "id": package["package_id"], "title": package["title"], "intent": package["intent"], "instructions": package["instructions"], "policy_constraints": dict(package["policy_constraints"]), "external_memory_refs": list(package["external_memory_refs"]), "items": [ { "asset_id": item["asset_id"], "title": item["title"], "source_refs": list(item["source_refs"]), "snippets": list(item["snippets"]), "metadata_records": list(item["metadata_records"]), "relationships": list(item["relationships"]), } for item in package["items"] ], "adapter_boundary": "markdown rendering and selector semantics are delegated to markitect-tool", } def _split_header_list(value: str | None) -> list[str] | None: if value is None: return None return [item.strip() for item in value.split(",") if item.strip()] def _json_header(value: str | None, header_name: str) -> dict[str, Any] | None: if value is None or not value.strip(): return None try: parsed = json.loads(value) except json.JSONDecodeError as exc: raise ValidationError( "Header must contain a JSON object", details={"header": header_name, "message": str(exc)}, ) from exc if not isinstance(parsed, dict): raise ValidationError( "Header must contain a JSON object", details={"header": header_name, "actual_type": type(parsed).__name__}, ) return parsed def _optional_classification(data: dict[str, Any] | None) -> Classification | None: return Classification.from_dict(data) if data else None def _enum_filter(enum_type: Any, value: Any, label: str) -> Any: if value is None or isinstance(value, enum_type): return value try: return enum_type(value) except ValueError as exc: raise ValidationError( f"Unsupported {label}", details={"value": value, "supported": [item.value for item in enum_type]}, ) from exc def _dataclass_request(request_type: Any, data: dict[str, Any]) -> Any: try: return request_type(**data) except TypeError as exc: raise ValidationError( "Invalid request payload", details={"request_type": request_type.__name__, "message": str(exc)}, ) from exc def _asset_query_request(data: dict[str, Any]) -> AssetQueryRequest: payload = dict(data) if "tags" in payload: payload["tags"] = tuple(payload["tags"]) return _dataclass_request(AssetQueryRequest, payload) def _context_entity_query_request(data: dict[str, Any]) -> ContextEntityQueryRequest: return _dataclass_request(ContextEntityQueryRequest, dict(data)) def _relationship_query_request(data: dict[str, Any]) -> RelationshipQueryRequest: return _dataclass_request(RelationshipQueryRequest, dict(data)) def _transformation_request(data: dict[str, Any]) -> TransformationRequest: payload = dict(data) if "source_asset_ids" in payload: payload["source_asset_ids"] = tuple(payload["source_asset_ids"]) if "parameters" in payload: payload["parameters"] = dict(payload["parameters"]) if "metadata" in payload: payload["metadata"] = dict(payload["metadata"]) return _dataclass_request(TransformationRequest, payload) def _workflow_template(data: dict[str, Any]) -> WorkflowTemplate: if "created_at" in data and "updated_at" in data and "template_id" in data: return WorkflowTemplate.from_dict(data) kwargs: dict[str, Any] = { "name": data["name"], "version": data.get("version", "1"), "description": data.get("description", ""), "inputs": tuple(WorkflowInputDefinition.from_dict(item) for item in data.get("inputs", ())), "steps": tuple(WorkflowStepDefinition.from_dict(item) for item in data.get("steps", ())), "policy_checks": tuple(dict(item) for item in data.get("policy_checks", ())), "failure_behavior": data.get("failure_behavior", "fail_workflow"), "metadata": dict(data.get("metadata", {})), } for key in ("template_id", "created_by"): if data.get(key) is not None: kwargs[key] = data[key] return WorkflowTemplate(**kwargs) def _workflow_invocation(data: dict[str, Any]) -> WorkflowInvocation: return WorkflowInvocation( template_id=data["template_id"], template_version=data.get("template_version"), inputs=dict(data.get("inputs", {})), metadata=dict(data.get("metadata", {})), ) def _ingestion_result_envelope(result: Any) -> dict[str, Any]: payload = _ingestion_job_envelope(result.job) payload["action"] = result.action payload["asset"] = result.asset.to_dict() if result.asset else None payload["asset_change"] = _asset_change_result(result.asset_change) if result.asset_change else None return payload def _ingestion_job_envelope(job: Any) -> dict[str, Any]: data = job.to_dict() retry_options = _ingestion_retry_options(job) data["retry_options"] = retry_options return { "job_id": job.job_id, "status": job.status.value, "correlation_id": job.correlation_id, "output_asset_ids": list(job.output_asset_ids), "failures": [failure.to_dict() for failure in job.failures], "retry_options": retry_options, "job": data, } def _ingestion_retry_options(job: Any) -> dict[str, Any]: if job.retry_options: return dict(job.retry_options) return { "retryable": any(failure.retriable for failure in job.failures), "retryable_failure_codes": [failure.code for failure in job.failures if failure.retriable], } def _transformation_result_envelope(result: Any) -> dict[str, Any]: payload = result.to_dict() if result.run is not None: payload["run"] = _transformation_run_envelope(result.run) payload["correlation_id"] = result.run.correlation_id payload["retry_options"] = payload["run"]["retry_options"] return payload def _transformation_run_envelope(run: Any) -> dict[str, Any]: data = run.to_dict() retryable = run.status in (TransformationRunStatus.FAILED, TransformationRunStatus.CANCELED) cancelable = run.status in (TransformationRunStatus.QUEUED, TransformationRunStatus.RUNNING) data["retry_options"] = { "retryable": retryable, "retry_endpoint": f"/api/v1/transformations/runs/{run.run_id}/retry" if retryable else None, "cancelable": cancelable, "cancel_endpoint": f"/api/v1/transformations/runs/{run.run_id}/cancel" if cancelable else None, } return data def _workflow_result_envelope(result: Any) -> dict[str, Any]: payload = result.to_dict() payload["run"] = _workflow_run_envelope(result.run) payload["correlation_id"] = result.run.correlation_id payload["retry_options"] = payload["run"]["retry_options"] return payload def _workflow_run_envelope(run: Any) -> dict[str, Any]: data = run.to_dict() retryable = run.status in ( WorkflowRunStatus.FAILED, WorkflowRunStatus.CANCELED, WorkflowRunStatus.PARTIALLY_COMPLETED, ) cancelable = run.status in (WorkflowRunStatus.QUEUED, WorkflowRunStatus.RUNNING, WorkflowRunStatus.WAITING) data["retry_options"] = { "retryable": retryable, "retry_endpoint": f"/api/v1/workflows/runs/{run.run_id}/retry" if retryable else None, "cancelable": cancelable, "cancel_endpoint": f"/api/v1/workflows/runs/{run.run_id}/cancel" if cancelable else None, } return data def _metadata_record(data: dict[str, Any]) -> MetadataRecord: if "record_id" in data and "created_at" in data: return MetadataRecord.from_dict(data) return MetadataRecord( key=data["key"], value=data.get("value"), provenance=dict(data.get("provenance", {})), confidence=data.get("confidence"), confirmed=bool(data.get("confirmed", False)), record_id=data.get("record_id") or MetadataRecord(data["key"], data.get("value")).record_id, ) def _source_reference(data: dict[str, Any]) -> SourceReference: if "id" in data: return SourceReference.from_dict(data) return SourceReference( source_system=data["source_system"], path=data.get("path"), uri=data.get("uri"), external_id=data.get("external_id"), checksum=data.get("checksum"), connector_ref=data.get("connector_ref"), metadata=dict(data.get("metadata", {})), ) def _asset_change_result(result: Any) -> dict[str, Any]: return { "asset": result.asset.to_dict(), "version": result.version.to_dict(), "audit_event": result.audit_event.to_dict(), "policy_decision": result.policy_decision.to_dict(), } def _error_payload(error: KontextualError) -> dict[str, Any]: return { "code": error.code, "message": str(error), "details": dict(error.details), } def _authorization_error_payload(error: AuthorizationError) -> dict[str, Any]: payload = _error_payload(error) details = dict(payload.get("details", {})) decision = details.get("policy_decision") if isinstance(decision, dict): details["policy_decision"] = _public_policy_decision(decision) payload["details"] = details return payload def _public_policy_decision(decision: dict[str, Any]) -> dict[str, Any]: allowed_fields = { "decision_id", "effect", "subject_id", "action", "resource", "reason", "obligations", "decided_at", } public = {key: value for key, value in decision.items() if key in allowed_fields} context = decision.get("context") if isinstance(context, dict): public_context = { key: value for key, value in context.items() if key not in {"resource_metadata", "protected_metadata", "source_payload"} } if public_context: public["context"] = public_context return public