from pathlib import Path class ReferenceDocsService: """Service to load and provide reference documents for agents.""" def __init__(self, base_path: str | None = None): """ Initialize the reference docs service. Args: base_path: Path to the reference_docs directory. Defaults to ../reference_docs relative to backend/ """ if base_path is None: # Default to reference_docs at project root (sibling to backend/) base_path = Path(__file__).parent.parent.parent.parent / "reference_docs" self.base_path = Path(base_path) # Path to prompts directory at project root (sibling to backend/) self.prompts_path = Path(__file__).parent.parent.parent.parent / "prompts" # Cache loaded documents self._brand_context: str | None = None self._channel_context: str | None = None self._barclaycard_brand_spec: str | None = None self._barclays_brand_spec: str | None = None self._channel_best_practices_spec: str | None = None self._channel_tech_specs_spec: str | None = None self._legal_spec: str | None = None def get_brand_context(self) -> str: """Load and return all brand guideline documents as a single context string.""" if self._brand_context is None: brand_path = self.base_path / "brand" self._brand_context = self._load_all_markdown_files(brand_path) return self._brand_context def get_barclaycard_brand_spec(self) -> str: """Load and return the Barclaycard brand specification from prompts directory.""" if self._barclaycard_brand_spec is None: spec_path = self.prompts_path / "brand_barclaycard.md" try: if spec_path.exists(): self._barclaycard_brand_spec = spec_path.read_text(encoding="utf-8") else: print(f"Warning: Barclaycard brand spec not found at {spec_path}") # Fall back to raw brand context self._barclaycard_brand_spec = self.get_brand_context() except Exception as e: print(f"Warning: Could not read Barclaycard brand spec: {e}") self._barclaycard_brand_spec = self.get_brand_context() return self._barclaycard_brand_spec def get_barclays_brand_spec(self) -> str: """Load and return the Barclays brand specification from prompts directory.""" # Check cache first if not hasattr(self, '_barclays_brand_spec'): self._barclays_brand_spec = None if self._barclays_brand_spec is None: spec_path = self.prompts_path / "brand_barclays.md" try: if spec_path.exists(): self._barclays_brand_spec = spec_path.read_text(encoding="utf-8") else: print(f"Warning: Barclays brand spec not found at {spec_path}, using raw brand context") # Fall back to raw brand context from reference_docs/brand/ self._barclays_brand_spec = self.get_brand_context() except Exception as e: print(f"Warning: Could not read Barclays brand spec: {e}") self._barclays_brand_spec = self.get_brand_context() return self._barclays_brand_spec def get_channel_context(self) -> str: """Load and return all channel guideline documents as a single context string.""" if self._channel_context is None: channel_path = self.base_path / "channel" self._channel_context = self._load_all_markdown_files(channel_path) return self._channel_context def get_channel_best_practices_spec(self) -> str: """Load and return the Channel Best Practices specification from prompts directory.""" if self._channel_best_practices_spec is None: spec_path = self.prompts_path / "channel_best_practices.md" try: if spec_path.exists(): self._channel_best_practices_spec = spec_path.read_text(encoding="utf-8") else: print(f"Warning: Channel Best Practices spec not found at {spec_path}") self._channel_best_practices_spec = self.get_channel_context() except Exception as e: print(f"Warning: Could not read Channel Best Practices spec: {e}") self._channel_best_practices_spec = self.get_channel_context() return self._channel_best_practices_spec def get_channel_tech_specs_spec(self) -> str: """Load and return the Channel Tech Specs specification from prompts directory.""" if self._channel_tech_specs_spec is None: spec_path = self.prompts_path / "channel_tech_specs.md" try: if spec_path.exists(): self._channel_tech_specs_spec = spec_path.read_text(encoding="utf-8") else: print(f"Warning: Channel Tech Specs spec not found at {spec_path}") self._channel_tech_specs_spec = self.get_channel_context() except Exception as e: print(f"Warning: Could not read Channel Tech Specs spec: {e}") self._channel_tech_specs_spec = self.get_channel_context() return self._channel_tech_specs_spec def get_legal_spec(self) -> str: """Load and return the Legal specification from prompts directory.""" if self._legal_spec is None: spec_path = self.prompts_path / "legal.md" try: if spec_path.exists(): self._legal_spec = spec_path.read_text(encoding="utf-8") else: print(f"Warning: Legal spec not found at {spec_path}") self._legal_spec = "No legal specification found. Apply general legal compliance checks." except Exception as e: print(f"Warning: Could not read Legal spec: {e}") self._legal_spec = "No legal specification found. Apply general legal compliance checks." return self._legal_spec def _load_all_markdown_files(self, directory: Path) -> str: """ Load all .md files from a directory and concatenate them. Args: directory: Path to the directory containing markdown files Returns: Concatenated content of all markdown files with section headers """ contents = [] if directory.exists(): # Sort files for consistent ordering for md_file in sorted(directory.glob("*.md")): try: content = md_file.read_text(encoding="utf-8") # Add file name as section header contents.append(f"## {md_file.stem}\n\n{content}") except Exception as e: print(f"Warning: Could not read {md_file}: {e}") if not contents: return "No reference documents found." return "\n\n---\n\n".join(contents) def get_context_summary(self) -> dict: """Return summary info about loaded documents.""" brand_path = self.base_path / "brand" channel_path = self.base_path / "channel" brand_files = list(brand_path.glob("*.md")) if brand_path.exists() else [] channel_files = list(channel_path.glob("*.md")) if channel_path.exists() else [] return { "brand_files": [f.name for f in brand_files], "channel_files": [f.name for f in channel_files], "brand_context_length": len(self.get_brand_context()), "channel_context_length": len(self.get_channel_context()), }