Skip to content

RFC-010: Autonomous Intelligence Governance Protocol (AIGP) — 8. Python Client Implementation

AIGP SpecificationRFC-010: Autonomous Intelligence Governance Protocol (AIGP) › 8. Python Client Implementation

← 7. Registration Schema · Section index · 9. Governance Authority (governance-server) Schema →

8. Python Client Implementation

class GovernanceClient:
"""AIGP client consent-based autonomous intelligence governance protocol."""
def __init__(self, gov_url: str, app_id: str, hmac_secret: str,
enabled: bool = True, mode: str = "REPORT"):
self._url = gov_url.rstrip("/")
self._app_id = app_id
self._secret = hmac_secret
self._enabled = enabled
self._mode = mode # REPORT | ENFORCE
self._decision_cache: dict = {}
self._http = httpx.AsyncClient(timeout=5.0)
async def start_heartbeat(self, interval_seconds: int = 3600):
"""Send heartbeat every interval. Run as background task."""
while True:
await self.heartbeat()
await asyncio.sleep(interval_seconds)
async def heartbeat(self):
if not self._enabled:
return
path = f"/api/v1/register/{self._app_id}"
await self._http.get(f"{self._url}{path}", headers=self._sign("GET", path, b""))
def _sign(self, method: str, path: str, body: bytes) -> dict:
ts = datetime.now(timezone.utc).isoformat()
body_hash = hashlib.sha256(body).hexdigest()
message = f"{ts}\n{method}\n{path}\n{body_hash}"
sig = hmac.new(self._secret.encode(), message.encode(), hashlib.sha256).hexdigest()
return {
"X-AIGP-Signature": f"hmac-sha256={sig}",
"X-AIGP-Timestamp": ts,
"X-AIGP-App-Id": self._app_id,
}
async def check(self, use_case: str, model_id: str, **kwargs) -> GovernanceDecision:
"""Pre-invocation policy check. Cached by use_case+model."""
if not self._enabled:
return GovernanceDecision({"decision": "ALLOW"})
cache_key = f"{use_case}:{model_id}"
if cached := self._decision_cache.get(cache_key):
if time.time() - cached["ts"] < cached["ttl"]:
return cached["decision"]
path = "/api/v1/request"
body = json.dumps({
"protocol_version": "2.0", "message_type": "REQUEST",
"app_id": self._app_id, "use_case": use_case,
"model_id": model_id, **kwargs,
}).encode()
try:
resp = await self._http.post(
f"{self._url}{path}", content=body,
headers={**self._sign("POST", path, body), "Content-Type": "application/json"},
)
decision = GovernanceDecision(resp.json())
self._decision_cache[cache_key] = {"decision": decision, "ts": time.time(), "ttl": decision.ttl}
return decision
except Exception:
# ENFORCE = fail-closed; REPORT = fail-open
if self._mode == "ENFORCE":
return GovernanceDecision({"decision": "DENY", "reason": "Governance authority unreachable (ENFORCE mode)"})
return GovernanceDecision({"decision": "ALLOW", "reason": "Governance authority unreachable (REPORT mode)"})
async def record(self, use_case: str, model_id: str,
input_tokens: int, output_tokens: int,
duration_ms: float, status: str = "SUCCESS", **kwargs) -> None:
"""Post-invocation activity record. Fire-and-forget."""
if not self._enabled:
return
path = "/api/v1/record"
body = json.dumps({
"protocol_version": "2.0", "message_type": "RECORD",
"app_id": self._app_id, "use_case": use_case,
"model_id": model_id, "timestamp": datetime.now(timezone.utc).isoformat(),
"duration_ms": int(duration_ms), "status": status,
"tokens": {"input_tokens": input_tokens, "output_tokens": output_tokens,
"total_tokens": input_tokens + output_tokens},
}).encode()
try:
await self._http.post(
f"{self._url}{path}", content=body,
headers={**self._sign("POST", path, body), "Content-Type": "application/json"},
)
except Exception:
pass # fire-and-forget

← 7. Registration Schema · Section index · 9. Governance Authority (governance-server) Schema →