Coverage for src / agent_contracts / config / loader.py: 58%
45 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-09 00:42 +0900
« prev ^ index » next coverage.py v7.13.1, created at 2026-01-09 00:42 +0900
1"""Configuration loader.
3YAML-based configuration loading with caching.
4"""
5from __future__ import annotations
7from pathlib import Path
9import yaml
11from agent_contracts.config.schema import FrameworkConfig, SupervisorConfig, InterviewConfig
12from agent_contracts.config.questions import QuestionDefinition, QuestionsConfig
15# =============================================================================
16# Framework Config
17# =============================================================================
19_config: FrameworkConfig | None = None
22def load_config(path: Path | str) -> FrameworkConfig:
23 """Load framework configuration from YAML.
25 Args:
26 path: Path to YAML config file
28 Returns:
29 FrameworkConfig instance
30 """
31 with open(path, encoding="utf-8") as f:
32 data = yaml.safe_load(f) or {}
34 # Parse supervisor config
35 supervisor_data = data.get("supervisor", {}) if isinstance(data, dict) else {}
36 response_types = data.get("response_types", {}) if isinstance(data, dict) else {}
37 supervisor = SupervisorConfig(
38 max_iterations=supervisor_data.get("max_iterations", 10),
39 terminal_response_types=response_types.get("terminal_states", []),
40 )
42 # Parse interview configs
43 interview_data = data.get("interview", {}) if isinstance(data, dict) else {}
44 interview: dict[str, InterviewConfig] = {}
45 for name, config in interview_data.items():
46 interview[name] = InterviewConfig(
47 max_turns=config.get("max_turns", 10),
48 max_questions=config.get("max_questions", 5),
49 )
51 return FrameworkConfig(
52 supervisor=supervisor,
53 interview=interview,
54 )
57def set_config(config: FrameworkConfig | None) -> None:
58 """Set global framework configuration.
60 Args:
61 config: FrameworkConfig instance (or None to reset)
62 """
63 global _config
64 _config = config
67def get_config() -> FrameworkConfig:
68 """Get current framework configuration.
70 Returns:
71 Current FrameworkConfig (or default if not set)
72 """
73 return _config or FrameworkConfig()
76# =============================================================================
77# Questions Config
78# =============================================================================
80_questions: QuestionsConfig | None = None
83def load_questions(path: Path | str) -> QuestionsConfig:
84 """Load questions configuration from YAML.
86 Args:
87 path: Path to questions YAML file
89 Returns:
90 QuestionsConfig (dict of question groups)
91 """
92 with open(path, encoding="utf-8") as f:
93 data = yaml.safe_load(f) or {}
95 # Validate with Pydantic
96 result: QuestionsConfig = {}
97 if not isinstance(data, dict):
98 return {}
100 for group_name, questions in data.items():
101 if not isinstance(questions, dict):
102 continue
103 result[group_name] = {}
104 for qid, qdef in questions.items():
105 if isinstance(qdef, dict):
106 result[group_name][qid] = QuestionDefinition(**qdef)
108 return result
111def set_questions(config: QuestionsConfig) -> None:
112 """Set global questions configuration.
114 Args:
115 config: QuestionsConfig instance
116 """
117 global _questions
118 _questions = config
121def get_question_group(group_name: str) -> dict[str, QuestionDefinition]:
122 """Get a question group by name.
124 Args:
125 group_name: Name of the question group
127 Returns:
128 Dict of question ID -> QuestionDefinition
129 """
130 if _questions is None:
131 return {}
132 return _questions.get(group_name, {})
135def get_question(group_name: str, question_id: str) -> QuestionDefinition | None:
136 """Get a specific question.
138 Args:
139 group_name: Name of the question group
140 question_id: Question ID
142 Returns:
143 QuestionDefinition or None if not found
144 """
145 group = get_question_group(group_name)
146 return group.get(question_id)