"""FunctionPlugin ABC per GCAP-SPEC-FUNCTION-DESCRIPTOR-0001 section 4.""" from __future__ import annotations import hashlib import json from abc import ABC, abstractmethod from dataclasses import dataclass, field from typing import Any @dataclass class FunctionContext: trigger_event_kind: str = "" trigger_event_cid: str = "" chronicle_session_id: str = "" gsap_context_id: str = "" invocation_id: str = "" pipeline_run_id: str = "" @dataclass class FunctionResult: success: bool = False data: Any = None error: str | None = None lineage_cid: str = "" duration_ms: float = 0.0 metadata: dict[str, Any] = field(default_factory=dict) def output_cid(self) -> str: """Compute content-addressable hash of the output data.""" canonical = json.dumps(self.data, sort_keys=True, separators=(",", ":"), default=str) return "sha256:" + hashlib.sha256(canonical.encode()).hexdigest() class FunctionPlugin(ABC): """Abstract base for governed serverless functions.""" function_id: str = "" corpus_entry_cid: str = "" capability_mask: int = 0 trigger_events: list[str] = [] accord_template: str = "" gsap_required: bool = True chronicle_enabled: bool = True max_duration_seconds: int = 30 display_name: str = "" description: str = "" version: str = "0.1.0" @abstractmethod async def handle(self, event: dict[str, Any], context: FunctionContext) -> FunctionResult: ... @abstractmethod def health_check(self) -> bool: ... def descriptor(self) -> dict[str, Any]: """JSON-LD descriptor per GCAP-SPEC-FUNCTION-DESCRIPTOR-0001 ยง2.""" return { "@context": "https://schema.gsap.dev/function/v1", "@type": "FunctionDescriptor", "function_id": self.function_id, "corpus_entry_cid": self.corpus_entry_cid, "capability_mask": self.capability_mask, "trigger_events": self.trigger_events, "accord_template": self.accord_template, "gsap_required": self.gsap_required, "chronicle_enabled": self.chronicle_enabled, "max_duration_seconds": self.max_duration_seconds, "display_name": self.display_name, "description": self.description, "version": self.version, } def knative_manifest(self, image: str, namespace: str = "default") -> dict[str, Any]: """Generate Knative Service manifest.""" return { "apiVersion": "serving.knative.dev/v1", "kind": "Service", "metadata": { "name": self.function_id, "namespace": namespace, "labels": { "gcap.dev/function-id": self.function_id, "gcap.dev/capability-mask": str(self.capability_mask), }, "annotations": { "gcap.dev/corpus-entry-cid": self.corpus_entry_cid, "gcap.dev/gsap-required": str(self.gsap_required).lower(), }, }, "spec": { "template": { "metadata": { "annotations": { "autoscaling.knative.dev/minScale": "0", "autoscaling.knative.dev/maxScale": "10", }, }, "spec": { "timeoutSeconds": self.max_duration_seconds, "containers": [ { "image": image, "env": [ {"name": "GCAP_FUNCTION_ID", "value": self.function_id}, {"name": "GCAP_CAPABILITY_MASK", "value": str(self.capability_mask)}, ], } ], }, }, }, }