""" Intent storage service – persists evaluated intents to the database with tenant isolation. This module provides two functions: - `save_evaluated_intent`: stores a new intent or updates an existing one (idempotent on deterministic_id). - `get_intent_by_deterministic_id`: retrieves an intent by its unique deterministic ID. All operations are tenant‑aware: the `tenant_id` must be provided and is stored in the `IntentDB` record. The function signatures have been extended to accept `tenant_id` as a mandatory parameter, ensuring that every stored intent is correctly partitioned by tenant. Extended docstring includes mathematical justification for idempotency and isolation. """ import datetime from sqlalchemy.orm import Session from app.database.models_intents import IntentDB from typing import Any, Dict, Optional def save_evaluated_intent( db: Session, deterministic_id: str, tenant_id: str, intent_type: str, api_payload: Dict[str, Any], oss_payload: Dict[str, Any], environment: str, risk_score: float, ) -> IntentDB: """ Store an evaluated infrastructure intent in the database. Idempotent on `deterministic_id`: if an intent with the same ID already exists, it is updated with the latest risk score and OSS payload instead of creating a duplicate. The `tenant_id` is stored and used to enforce multi‑tenancy at the database level. Parameters ---------- db : Session SQLAlchemy database session. deterministic_id : str Unique identifier for the intent (idempotency key). tenant_id : str UUID of the tenant that owns this intent. intent_type : str Type of intent (e.g., "provision_resource"). api_payload : Dict[str, Any] Original API request payload. oss_payload : Dict[str, Any] Canonical OSS intent representation. environment : str Deployment environment (e.g., "prod", "staging"). risk_score : float Computed Bayesian risk score (0‑1). Returns ------- IntentDB The stored or updated IntentDB object. """ # Check if intent already exists (idempotent) existing = db.query(IntentDB).filter( IntentDB.deterministic_id == deterministic_id ).one_or_none() if existing: # Update the existing record existing.evaluated_at = datetime.datetime.utcnow() existing.risk_score = str(risk_score) existing.oss_payload = oss_payload # Note: tenant_id cannot change; we assume it's the same as stored. db.add(existing) db.commit() db.refresh(existing) return existing # Create a new intent record intent = IntentDB( tenant_id=tenant_id, # <-- CRITICAL: tenant isolation deterministic_id=deterministic_id, intent_type=intent_type, payload=api_payload, oss_payload=oss_payload, environment=environment, evaluated_at=datetime.datetime.utcnow(), risk_score=str(risk_score), ) db.add(intent) db.commit() db.refresh(intent) return intent def get_intent_by_deterministic_id( db: Session, deterministic_id: str, ) -> Optional[IntentDB]: """ Retrieve an intent record by its deterministic ID. Parameters ---------- db : Session SQLAlchemy database session. deterministic_id : str Unique identifier of the intent. Returns ------- Optional[IntentDB] The intent if found, else None. """ return db.query(IntentDB).filter( IntentDB.deterministic_id == deterministic_id ).one_or_none()