| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556 |
- """Registry for converting LLM responses via pluggable parsers."""
- from __future__ import annotations
- from pathlib import Path
- from threading import Lock
- from typing import Dict, Optional, Type
- import yaml
- from app.const import CONFIG_DIR
- from .parsers import BaseParser, PARSER_REGISTRY
- class ParserManager:
- """Lazy singleton keeping parser instances referenced by name."""
- _instance: Optional["ParserManager"] = None
- _lock = Lock()
- def __new__(cls, config_path: Optional[Path] = None) -> "ParserManager":
- with cls._lock:
- if cls._instance is None:
- cls._instance = super().__new__(cls)
- cls._instance._build(config_path)
- return cls._instance
- def _build(self, config_path: Optional[Path]) -> None:
- path = Path(config_path or (CONFIG_DIR / "parser_settings.yaml"))
- self._parsers: Dict[str, BaseParser] = {}
- self._register_defaults()
- if path.exists():
- self._load_custom_parsers(path)
- def _register_defaults(self) -> None:
- for name, parser_cls in PARSER_REGISTRY.items():
- self._parsers[name] = parser_cls()
- def _load_custom_parsers(self, path: Path) -> None:
- with path.open("r", encoding="utf-8") as fh:
- parser_settings = yaml.safe_load(fh) or {}
- for name, config in parser_settings.items():
- parser_type = config.get("type")
- if not parser_type or parser_type not in PARSER_REGISTRY:
- continue
- parser_cls: Type[BaseParser] = PARSER_REGISTRY[parser_type]
- self._parsers[name] = parser_cls(**config.get("options", {}))
- def get_parser(self, name: str) -> BaseParser:
- if name not in self._parsers:
- raise KeyError(f"Parser {name} is not registered")
- return self._parsers[name]
- def register_parser(self, name: str, parser: BaseParser) -> None:
- self._parsers[name] = parser
|