| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240 |
- """
- Unified config loader for LambdaRAG scripts.
- All scripts import paths from here instead of hardcoding.
- Usage:
- from config import cfg
- print(cfg.raw_dir) # Path to raw files
- print(cfg.processed_dir) # Path to processed text
- print(cfg.wiki_dir) # Path to wiki
- print(cfg.base_dir) # Knowledge base directory
- """
- import os
- import sys
- from pathlib import Path
- import yaml
- class Config:
- """Loads paths from agent-config.yml + environment variables."""
- def __init__(self):
- self._cfg = {}
- self._load()
- def _find_agent_config(self):
- """Search for agent-config.yml in standard locations"""
- candidates = [
- os.environ.get('AGENT_CONFIG', ''),
- # Relative to this script: scripts/ -> parent = agent dir
- str(Path(__file__).parent.parent / 'agent-config.yml'),
- # Docker paths
- '/app/agentexample/qaagent67lambda/agent-config.yml',
- '/app/agentexample/qaagent67/agent-config.yml',
- ]
- for p in candidates:
- if p and Path(p).is_file():
- return Path(p)
- return None
- def _find_instance_config(self):
- """Search for instance.yml.
- Resolution order:
- 1. INSTANCE_CONFIG env var
- 2. {baseDir}/instance.yml (after agent-config.yml is loaded)
- 3. instance.yml in the directory containing this config.py file
- (lets build scripts auto-detect domain when run from a domain dir)
- """
- here = Path(__file__).parent
- base_dir = (self._cfg.get("knowledge", {}) or {}).get("baseDir", "")
- candidates = [
- os.environ.get("INSTANCE_CONFIG", ""),
- str(Path(base_dir) / "instance.yml") if base_dir else "",
- str(here / "instance.yml"),
- ]
- for p in candidates:
- if p and Path(p).is_file():
- return Path(p)
- return None
- def _load(self):
- config_path = self._find_agent_config()
- if config_path:
- with open(config_path) as f:
- self._cfg = yaml.safe_load(f) or {}
- # Load instance.yml (overrides agent-config.yml)
- instance_path = self._find_instance_config()
- if instance_path and Path(instance_path).is_file():
- with open(instance_path) as f:
- instance = yaml.safe_load(f) or {}
- # Deep merge: instance overrides agent config
- self._cfg = self._deep_merge(self._cfg, instance)
- def _deep_merge(self, base, override):
- import copy
- result = copy.deepcopy(base)
- for k, v in override.items():
- if k in result and isinstance(result[k], dict) and isinstance(v, dict):
- result[k] = self._deep_merge(result[k], v)
- else:
- result[k] = copy.deepcopy(v)
- return result
- # ── Knowledge paths ──
- @property
- def base_dir(self):
- return Path(
- self._cfg.get('knowledge', {}).get('baseDir', '')
- or os.environ.get('KNOWLEDGE_DIR', '')
- or str(Path(__file__).parent.parent / 'knowledge')
- )
- @property
- def raw_dir(self):
- return Path(
- self._cfg.get('knowledge', {}).get('rawDir', '')
- or str(self.base_dir / 'raw')
- )
- @property
- def processed_dir(self):
- return Path(
- self._cfg.get('knowledge', {}).get('processedDir', '')
- or str(self.base_dir / 'processed')
- )
- @property
- def index_file(self):
- return Path(
- self._cfg.get('knowledge', {}).get('indexFile', '')
- or str(self.base_dir / 'rag_index.json')
- )
- @property
- def keyword_file(self):
- return Path(
- self._cfg.get('knowledge', {}).get('keywordFile', '')
- or str(self.base_dir / 'rag_index.keywords.json')
- )
- @property
- def vector_index(self):
- return Path(
- self._cfg.get('knowledge', {}).get('vectorIndex', '')
- or str(self.base_dir / 'rag_vectors_v2.npy')
- )
- @property
- def vector_meta(self):
- return Path(
- self._cfg.get('knowledge', {}).get('vectorMeta', '')
- or str(self.base_dir / 'rag_vectors_meta_v2.pkl')
- )
- # ── Wiki paths ──
- @property
- def wiki_dir(self):
- return Path(
- self._cfg.get('wiki', {}).get('dir', '')
- or os.environ.get('WIKI_DIR', '')
- or str(Path(__file__).parent.parent / 'wiki')
- )
- @property
- def relations_file(self):
- return Path(
- self._cfg.get('wiki', {}).get('relationsFile', '')
- or str(self.wiki_dir / 'relations.json')
- )
- # ── Embedding config ──
- @property
- def embed_url(self):
- return (
- self._cfg.get('retrieval', {}).get('skills', {}).get('embedding', {}).get('baseUrl', '')
- or os.environ.get('OLLAMA_EMBED_URL', 'http://127.0.0.1:11435/api/embed')
- )
- @property
- def embed_model(self):
- return (
- self._cfg.get('retrieval', {}).get('skills', {}).get('embedding', {}).get('model', '')
- or os.environ.get('EMBED_MODEL', 'bge-m3')
- )
- # ── LLM config ──
- @property
- def vllm_url(self):
- return os.environ.get('VLLM_URL', 'http://127.0.0.1:8000/v1/chat/completions')
- @property
- def llm_model(self):
- return self._cfg.get('model', {}).get('name', 'qwen2.5-32b')
- # ── Retrieval weights ──
- @property
- def weight_profiles(self):
- return self._cfg.get('retrieval', {}).get('weights', {
- 'fact': {'bm25': 1.0, 'vector': 0.5, 'graph': 0.1, 'wiki': 0.1},
- 'compare': {'bm25': 0.5, 'vector': 0.8, 'graph': 0.4, 'wiki': 0.7},
- 'relation': {'bm25': 0.5, 'vector': 0.3, 'graph': 0.7, 'wiki': 0.8},
- 'synthesis': {'bm25': 0.6, 'vector': 0.5, 'graph': 0.5, 'wiki': 0.6},
- 'temporal': {'bm25': 1.0, 'vector': 0.2, 'graph': 0.05, 'wiki': 0.2},
- })
- @property
- def default_weights(self):
- return self._cfg.get('retrieval', {}).get('weights', {}).get('default',
- {'bm25': 0.7, 'vector': 0.5, 'graph': 0.3, 'wiki': 0.3})
- @property
- def rrf_k(self):
- return self._cfg.get('retrieval', {}).get('orchestration', {}).get('rrfK', 60)
- # ── RAG params ──
- @property
- def chunk_size(self):
- return self._cfg.get('rag', {}).get('chunkSize', 512)
- @property
- def chunk_overlap(self):
- return self._cfg.get('rag', {}).get('chunkOverlap', 64)
- @property
- def top_k(self):
- return self._cfg.get('rag', {}).get('topK', 5)
- # ── Workspace ──
- @property
- def workspace_dir(self):
- return Path(__file__).parent.parent / 'workspace'
- # ── Full config dict ──
- @property
- def raw(self):
- return self._cfg
- def __repr__(self):
- return (f"Config(base_dir={self.base_dir}, raw_dir={self.raw_dir}, "
- f"wiki_dir={self.wiki_dir}, embed_url={self.embed_url})")
- # Singleton
- cfg = Config()
- if __name__ == '__main__':
- print(cfg)
- print(f"\n base_dir: {cfg.base_dir}")
- print(f" raw_dir: {cfg.raw_dir}")
- print(f" processed_dir: {cfg.processed_dir}")
- print(f" index_file: {cfg.index_file}")
- print(f" wiki_dir: {cfg.wiki_dir}")
- print(f" relations: {cfg.relations_file}")
- print(f" embed_url: {cfg.embed_url}")
- print(f" vllm_url: {cfg.vllm_url}")
- print(f" weights: {cfg.weight_profiles}")
- print(f" chunk_size: {cfg.chunk_size}")
- print(f" workspace: {cfg.workspace_dir}")
|