Compare commits

...
Author SHA1 Message Date
Chris Bartholomew c9468e2bf4 Add MentalModelRefreshContext and pre-operation validation for mental model create/refresh
Wire up validate_mental_model_refresh hook in the HTTP routes for both
create and refresh mental model endpoints, allowing extensions to reject
operations (e.g. insufficient credits) before queuing async LLM work.
2026-02-01 11:40:21 -05:00
3 changed files with 66 additions and 0 deletions
+38
View File
@@ -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
@@ -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",
@@ -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)
# =========================================================================