""" Template service for high-level template management operations. This service extends artifact service to handle PromptTemplate-specific operations including macro analysis and dependency extraction. """ from typing import List, Optional from markitect.prompts.models import ArtifactMetadata, ArtifactType from markitect.prompts.templates.models import ( PromptTemplate, TemplateMetadata, ) from markitect.prompts.templates.analyzer import TemplateAnalyzer, TemplateAnalysisResult from markitect.prompts.services.artifact_service import ArtifactService from markitect.prompts.repositories.interfaces import ArtifactNotFoundError class TemplateService: """ Service for template management operations. Provides high-level business logic for creating and analyzing templates, building on top of ArtifactService. """ def __init__(self, artifact_service: ArtifactService): """ Initialize service with artifact service. Args: artifact_service: Artifact service for persistence """ self.artifact_service = artifact_service self.analyzer = TemplateAnalyzer() def create_template( self, space_id: str, name: str, content: str, artifact_metadata: Optional[ArtifactMetadata] = None, template_metadata: Optional[TemplateMetadata] = None, analyze: bool = True, ) -> PromptTemplate: """ Create and optionally analyze a new template. Args: space_id: ID of containing space name: Template name content: Template content with macros artifact_metadata: General artifact metadata template_metadata: Template-specific metadata analyze: Whether to analyze macros immediately Returns: Created template (analyzed if analyze=True) Raises: DuplicateArtifactError: If template already exists MacroParsingError: If macro syntax is invalid (when analyze=True) """ # Create template template = PromptTemplate.create( space_id=space_id, name=name, content=content, artifact_metadata=artifact_metadata, template_metadata=template_metadata, ) # Persist artifact self.artifact_service.create_artifact( space_id=space_id, name=name, content=content, artifact_type=ArtifactType.TEMPLATE, metadata=artifact_metadata, ) # Analyze if requested if analyze: self.analyzer.analyze(template, content) return template def get_template(self, template_id: str, content: str) -> PromptTemplate: """ Retrieve template by ID. Args: template_id: Template identifier content: Template content (needed to avoid storing content in DB twice) Returns: PromptTemplate instance Raises: ArtifactNotFoundError: If template doesn't exist """ artifact = self.artifact_service.get_artifact(template_id) if artifact.artifact_type != ArtifactType.TEMPLATE: raise ValueError( f"Artifact '{template_id}' is not a template " f"(type: {artifact.artifact_type})" ) template = PromptTemplate.from_artifact(artifact) # Analyze to populate macros self.analyzer.analyze(template, content) return template def get_template_by_name( self, space_id: str, name: str, content: str, ) -> PromptTemplate: """ Retrieve template by space and name. Args: space_id: Space identifier name: Template name content: Template content Returns: PromptTemplate instance Raises: ArtifactNotFoundError: If template doesn't exist """ artifact = self.artifact_service.get_artifact_by_name(space_id, name) if artifact.artifact_type != ArtifactType.TEMPLATE: raise ValueError( f"Artifact '{name}' is not a template " f"(type: {artifact.artifact_type})" ) template = PromptTemplate.from_artifact(artifact) self.analyzer.analyze(template, content) return template def analyze_template( self, template: PromptTemplate, content: str, ) -> TemplateAnalysisResult: """ Analyze template to extract macros and dependencies. Args: template: Template to analyze content: Template content Returns: Analysis result with dependency information Raises: MacroParsingError: If macro syntax is invalid """ return self.analyzer.analyze(template, content) def list_templates(self, space_id: str) -> List[PromptTemplate]: """ List all templates in a space. Note: Templates are returned without macro analysis. Call analyze_template() on individual templates as needed. Args: space_id: Space identifier Returns: List of templates (unanalyzed) """ artifacts = self.artifact_service.list_artifacts( space_id=space_id, artifact_type=ArtifactType.TEMPLATE, ) templates = [PromptTemplate.from_artifact(a) for a in artifacts] return templates def quick_check_content(self, content: str) -> dict: """ Quick validation of template content. Useful for checking content before template creation. Args: content: Template content to check Returns: Dictionary with macro counts and validation info """ return self.analyzer.quick_check(content) def update_template_content( self, template_id: str, new_content: str, reanalyze: bool = True, ) -> PromptTemplate: """ Update template content. Args: template_id: Template to update new_content: New content reanalyze: Whether to reanalyze macros Returns: Updated template Raises: ArtifactNotFoundError: If template doesn't exist MacroParsingError: If new content has invalid macros """ # Update artifact content artifact = self.artifact_service.update_artifact_content( template_id, new_content, ) # Create template from updated artifact template = PromptTemplate.from_artifact(artifact) # Reanalyze if requested if reanalyze: self.analyzer.analyze(template, new_content) return template def delete_template(self, template_id: str) -> bool: """ Delete a template. Args: template_id: Template to delete Returns: True if deleted, False if not found """ return self.artifact_service.delete_artifact(template_id) def template_exists(self, space_id: str, name: str) -> bool: """ Check if template exists. Args: space_id: Space identifier name: Template name Returns: True if template exists """ return self.artifact_service.artifact_exists(space_id, name)