AffixIO's picture
Publish affix-huggingface 0.3.0
551b309 verified
Raw
History Blame Contribute Delete
5.39 kB
"""Public value objects for AffixIO-controlled Hugging Face inference."""
# Credit: @paparichens
from __future__ import annotations
import hashlib
import json
from collections.abc import Mapping
from dataclasses import dataclass, field
from typing import Any, Literal
HostingClass = Literal["hf_inference", "inference_endpoint", "self_hosted_tgi"]
GateMode = Literal["check", "consume"]
EvidenceMode = Literal["required", "best_effort", "off"]
@dataclass(frozen=True)
class ModelRef:
"""
A Hugging Face model or remote Inference Endpoint.
``revision`` is declared audit metadata. Hugging Face's public inference
call does not accept a revision pin, so use a pinned dedicated endpoint
where exact deployed weights are required.
"""
model_id: str
revision: str | None = None
endpoint_url: str | None = None
provider: str | None = None
hosting: HostingClass = "hf_inference"
def __post_init__(self) -> None:
if not self.model_id.strip():
raise ValueError("model_id must not be empty")
if self.endpoint_url and not self.endpoint_url.startswith("https://"):
raise ValueError("endpoint_url must use HTTPS")
@property
def inference_target(self) -> str:
return self.endpoint_url or self.model_id
def binding(self) -> dict[str, str]:
values = {
"model_id": self.model_id,
"revision": self.revision,
"endpoint_url": self.endpoint_url,
"provider": self.provider,
"hosting": self.hosting,
}
return {key: value for key, value in values.items() if value is not None}
@property
def binding_digest(self) -> str:
canonical = json.dumps(
self.binding(),
sort_keys=True,
separators=(",", ":"),
ensure_ascii=True,
).encode("utf-8")
return hashlib.sha256(canonical).hexdigest()
@dataclass(frozen=True)
class AdmissionChecks:
"""
Customer-evaluated predicates submitted for remote AffixIO proving.
These values contain no prompt, output, or subject identifier. Applications
remain responsible for evaluating their own entitlement and policy records.
"""
entitled: bool
data_route_allowed: bool
controls_satisfied: bool
@property
def allow_hint(self) -> bool:
return self.entitled and self.data_route_allowed and self.controls_satisfied
def prove_fields(self) -> dict[str, bool]:
return {
"condition_greater1": self.entitled,
"condition_greater2": self.data_route_allowed,
"condition_greater3": self.controls_satisfied,
}
@dataclass(frozen=True)
class AdmissionProof:
"""Proof material accepted by the AffixIO gate."""
proof: str
circuit_id: str = "yesno"
policy_id: str = "gate.agent"
def __post_init__(self) -> None:
if not self.proof.strip():
raise ValueError("proof must not be empty")
@dataclass(frozen=True)
class GateReceipt:
"""AffixIO admission response retained alongside an HF result."""
allow: bool
reason_code: str
receipt_id: str | None
request_id: str | None
proof_digest: str | None
proof_ref: str | None
policy_id: str | None
policy_version: str | None
mode: str | None
spent: bool
merkle_root: str | None
merkle_leaf_hash: str | None
attestation: Mapping[str, Any] | None
raw: Mapping[str, Any] = field(repr=False)
@classmethod
def from_response(
cls,
data: Mapping[str, Any],
*,
request_id: str | None,
) -> GateReceipt:
return cls(
allow=bool(data.get("allow", False)),
reason_code=str(data.get("reason_code") or data.get("error") or "UNKNOWN"),
receipt_id=_optional_str(data.get("receipt_id")),
request_id=request_id,
proof_digest=_optional_str(data.get("proof_digest")),
proof_ref=_optional_str(data.get("proof_ref")),
policy_id=_optional_str(data.get("policy_id")),
policy_version=_optional_str(data.get("policy_version")),
mode=_optional_str(data.get("mode")),
spent=bool(data.get("spent", False)),
merkle_root=_optional_str(data.get("merkle_root")),
merkle_leaf_hash=_optional_str(data.get("merkle_leaf_hash")),
attestation=_mapping_or_none(data.get("attestation")),
raw=dict(data),
)
@dataclass(frozen=True)
class CompletionEvidence:
"""Post-inference AffixIO attestation over metadata only."""
payload: Mapping[str, Any]
attestation: Mapping[str, Any]
request_id: str | None
@dataclass(frozen=True)
class AffixReceipt:
"""Admission and optional post-inference evidence."""
model: ModelRef
operation: str
gate: GateReceipt
completion: CompletionEvidence | None = None
evidence_error: str | None = None
@dataclass(frozen=True)
class AuthorisedResult:
"""Hugging Face output paired with its AffixIO receipt."""
output: Any
receipt: AffixReceipt
def _optional_str(value: Any) -> str | None:
if value is None:
return None
return str(value)
def _mapping_or_none(value: Any) -> Mapping[str, Any] | None:
if isinstance(value, Mapping):
return dict(value)
return None