diff --git a/hindsight-api/hindsight_api/api/http.py b/hindsight-api/hindsight_api/api/http.py index e128de00..69bd685e 100644 --- a/hindsight-api/hindsight_api/api/http.py +++ b/hindsight-api/hindsight_api/api/http.py @@ -2363,6 +2363,23 @@ def _register_routes(app: FastAPI): ): """Create a mental model (async - returns operation_id).""" try: + # Pre-operation validation hook + validator = app.state.memory._operation_validator + if validator: + from hindsight_api.extensions.operation_validator import MentalModelRefreshContext + + ctx = MentalModelRefreshContext( + bank_id=bank_id, + mental_model_id=None, # Not yet created + request_context=request_context, + ) + validation = await validator.validate_mental_model_refresh(ctx) + if not validation.allowed: + raise OperationValidationError( + validation.reason or "Operation not allowed", + status_code=validation.status_code, + ) + # 1. Create the mental model with placeholder content mental_model = await app.state.memory.create_mental_model( bank_id=bank_id, @@ -2385,6 +2402,8 @@ def _register_routes(app: FastAPI): raise HTTPException(status_code=400, detail=str(e)) except (AuthenticationError, HTTPException): raise + except OperationValidationError as e: + raise HTTPException(status_code=e.status_code, detail=e.reason) except Exception as e: import traceback @@ -2407,6 +2426,23 @@ def _register_routes(app: FastAPI): ): """Refresh a mental model by re-running its source query (async).""" try: + # Pre-operation validation hook + validator = app.state.memory._operation_validator + if validator: + from hindsight_api.extensions.operation_validator import MentalModelRefreshContext + + ctx = MentalModelRefreshContext( + bank_id=bank_id, + mental_model_id=mental_model_id, + request_context=request_context, + ) + validation = await validator.validate_mental_model_refresh(ctx) + if not validation.allowed: + raise OperationValidationError( + validation.reason or "Operation not allowed", + status_code=validation.status_code, + ) + result = await app.state.memory.submit_async_refresh_mental_model( bank_id=bank_id, mental_model_id=mental_model_id, @@ -2417,6 +2453,8 @@ def _register_routes(app: FastAPI): raise HTTPException(status_code=404, detail=str(e)) except (AuthenticationError, HTTPException): raise + except OperationValidationError as e: + raise HTTPException(status_code=e.status_code, detail=e.reason) except Exception as e: import traceback diff --git a/hindsight-api/hindsight_api/extensions/__init__.py b/hindsight-api/hindsight_api/extensions/__init__.py index 5a7dabeb..c5752831 100644 --- a/hindsight-api/hindsight_api/extensions/__init__.py +++ b/hindsight-api/hindsight_api/extensions/__init__.py @@ -27,6 +27,7 @@ from hindsight_api.extensions.operation_validator import ( # Mental Model operations MentalModelGetContext, MentalModelGetResult, + MentalModelRefreshContext, MentalModelRefreshResult, # Core operations OperationValidationError, @@ -72,6 +73,7 @@ __all__ = [ # Operation Validator - Mental Model "MentalModelGetContext", "MentalModelGetResult", + "MentalModelRefreshContext", "MentalModelRefreshResult", # Tenant/Auth "ApiKeyTenantExtension", diff --git a/hindsight-api/hindsight_api/extensions/operation_validator.py b/hindsight-api/hindsight_api/extensions/operation_validator.py index 0b778ca7..e5b34557 100644 --- a/hindsight-api/hindsight_api/extensions/operation_validator.py +++ b/hindsight-api/hindsight_api/extensions/operation_validator.py @@ -210,6 +210,15 @@ class MentalModelGetContext: request_context: "RequestContext" +@dataclass +class MentalModelRefreshContext: + """Context for a mental model refresh/create operation validation (pre-operation).""" + + bank_id: str + mental_model_id: str | None # None for create (not yet assigned) + request_context: "RequestContext" + + @dataclass class MentalModelGetResult: """Result context for post-mental-model-GET hook.""" @@ -466,6 +475,23 @@ class OperationValidatorExtension(Extension, ABC): """ return ValidationResult.accept() + async def validate_mental_model_refresh(self, ctx: MentalModelRefreshContext) -> ValidationResult: + """ + Validate a mental model refresh/create operation before execution. + + Override to implement custom validation logic for mental model refresh. + + Args: + ctx: Context containing: + - bank_id: Bank identifier + - mental_model_id: Mental model identifier (None for create) + - request_context: Request context with auth info + + Returns: + ValidationResult indicating whether the operation is allowed. + """ + return ValidationResult.accept() + # ========================================================================= # Mental Model - Post-operation hooks (optional - override to implement) # =========================================================================