from typing import Dict, Any, Optional
from sqlalchemy.orm import Session
from app.db import models
import logging
logger = logging.getLogger(__name__)
class PromptService:
"""
Manages centralized system prompts, including fetching, rendering,
and permission validation.
"""
def get_prompt_by_slug(self, db: Session, slug: str, user_id: str) -> Optional[models.PromptTemplate]:
"""
Fetches a prompt template by its unique slug, verifying if the user has access.
"""
prompt = db.query(models.PromptTemplate).filter(models.PromptTemplate.slug == slug).first()
if not prompt:
logger.warning(f"Prompt with slug '{slug}' not found.")
return None
if self._can_access_prompt(db, prompt, user_id):
return prompt
logger.warning(f"User '{user_id}' denied access to prompt '{slug}'.")
return None
def _can_access_prompt(self, db: Session, prompt: models.PromptTemplate, user_id: str) -> bool:
"""
Internal logic to check if a user can access a specific prompt template.
"""
# 1. Is it public?
if prompt.is_public:
return True
# 2. Is the user the owner?
if prompt.owner_id == user_id:
return True
# 3. Does the user belong to the allowed group?
user = db.query(models.User).filter(models.User.id == user_id).first()
if user and prompt.group_id and user.group_id == prompt.group_id:
return True
# 4. Check explicit granular permissions
permission = db.query(models.AssetPermission).filter(
models.AssetPermission.resource_type == 'prompt',
models.AssetPermission.resource_id == prompt.id,
(models.AssetPermission.user_id == user_id) |
((models.AssetPermission.group_id == user_id) if user and user.group_id else False)
).first()
if permission:
return True
return False
def render_prompt(self, prompt: models.PromptTemplate, variables: Dict[str, Any]) -> str:
"""
Renders a prompt template string using the provided variables.
"""
try:
return prompt.content.format(**variables)
except KeyError as e:
logger.error(f"Missing variable for prompt rendering: {e}")
return prompt.content # Fallback to raw if rendering fails partially
except Exception as e:
logger.error(f"Error rendering prompt: {e}")
return prompt.content