diagram-to-iac 1.0.1__py3-none-any.whl → 1.0.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- diagram_to_iac/__init__.py +34 -8
- diagram_to_iac/agents/demonstrator_langgraph/agent.py +12 -8
- diagram_to_iac/agents/git_langgraph/agent.py +74 -24
- diagram_to_iac/agents/hello_langgraph/agent.py +69 -13
- diagram_to_iac/agents/policy_agent/agent.py +41 -18
- diagram_to_iac/agents/supervisor_langgraph/agent.py +70 -25
- diagram_to_iac/agents/terraform_langgraph/agent.py +75 -27
- diagram_to_iac/core/config_loader.py +281 -0
- diagram_to_iac/core/memory.py +7 -2
- diagram_to_iac/tools/api_utils.py +61 -20
- diagram_to_iac/tools/git/git.py +69 -18
- diagram_to_iac/tools/sec_utils.py +248 -35
- diagram_to_iac/tools/shell/shell.py +89 -32
- diagram_to_iac/tools/tf/terraform.py +43 -32
- {diagram_to_iac-1.0.1.dist-info → diagram_to_iac-1.0.2.dist-info}/METADATA +2 -2
- {diagram_to_iac-1.0.1.dist-info → diagram_to_iac-1.0.2.dist-info}/RECORD +19 -18
- {diagram_to_iac-1.0.1.dist-info → diagram_to_iac-1.0.2.dist-info}/WHEEL +0 -0
- {diagram_to_iac-1.0.1.dist-info → diagram_to_iac-1.0.2.dist-info}/entry_points.txt +0 -0
- {diagram_to_iac-1.0.1.dist-info → diagram_to_iac-1.0.2.dist-info}/top_level.txt +0 -0
diagram_to_iac/__init__.py
CHANGED
@@ -1,10 +1,36 @@
|
|
1
|
+
# Initialize diagram-to-iac package with centralized configuration and secret management
|
2
|
+
|
3
|
+
# Initialize configuration system first
|
4
|
+
try:
|
5
|
+
from diagram_to_iac.core.config_loader import get_config_loader
|
6
|
+
# Initialize the global configuration loader
|
7
|
+
config_loader = get_config_loader()
|
8
|
+
# Pre-load configuration to catch any issues early
|
9
|
+
config = config_loader.get_config()
|
10
|
+
except ImportError as e:
|
11
|
+
# If config_loader can't be imported, log but continue
|
12
|
+
print(f"⚠️ Warning: Could not import configuration system: {e}")
|
13
|
+
except Exception as e:
|
14
|
+
# If configuration loading fails, log but continue
|
15
|
+
print(f"⚠️ Warning: Configuration system initialization failed: {e}")
|
16
|
+
|
17
|
+
# Initialize secret management system
|
1
18
|
try:
|
2
|
-
from diagram_to_iac.tools.sec_utils import
|
3
|
-
|
4
|
-
|
19
|
+
from diagram_to_iac.tools.sec_utils import load_secrets
|
20
|
+
load_secrets()
|
21
|
+
except ImportError:
|
22
|
+
# If sec_utils can't be imported, that's fine during development
|
23
|
+
pass
|
24
|
+
except SystemExit:
|
25
|
+
# load_secrets() calls sys.exit() on critical errors
|
26
|
+
# In development, we don't want to crash the entire package import
|
27
|
+
pass
|
28
|
+
except Exception as e:
|
29
|
+
print(f"⚠️ Warning: Secret management initialization failed: {e}")
|
5
30
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
31
|
+
# Make configuration easily accessible
|
32
|
+
try:
|
33
|
+
from diagram_to_iac.core.config_loader import get_config, get_config_section, get_config_value
|
34
|
+
__all__ = ["get_config", "get_config_section", "get_config_value"]
|
35
|
+
except ImportError:
|
36
|
+
__all__ = []
|
@@ -22,6 +22,7 @@ from langgraph.checkpoint.memory import MemorySaver
|
|
22
22
|
|
23
23
|
from diagram_to_iac.core.agent_base import AgentBase
|
24
24
|
from diagram_to_iac.core.memory import create_memory
|
25
|
+
from diagram_to_iac.core.config_loader import get_config, get_config_value
|
25
26
|
from diagram_to_iac.tools.llm_utils.router import LLMRouter, get_llm
|
26
27
|
from diagram_to_iac.tools.text_utils import (
|
27
28
|
generate_organic_issue_title,
|
@@ -151,16 +152,19 @@ class DemonstratorAgent(AgentBase):
|
|
151
152
|
self.logger.info("DemonstratorAgent initialized successfully with organic LangGraph architecture")
|
152
153
|
|
153
154
|
def _set_default_config(self):
|
154
|
-
"""Set default configuration values."""
|
155
|
+
"""Set default configuration values using centralized system."""
|
155
156
|
self.config = {
|
156
|
-
"llm": {
|
157
|
+
"llm": {
|
158
|
+
"model_name": get_config_value("ai.default_model", "gpt-4o-mini"),
|
159
|
+
"temperature": get_config_value("ai.default_temperature", 0.1)
|
160
|
+
},
|
157
161
|
"routing_keys": {
|
158
|
-
"analyze": "ROUTE_TO_ANALYZE",
|
159
|
-
"demonstrate": "ROUTE_TO_DEMONSTRATE",
|
160
|
-
"collect_inputs": "ROUTE_TO_COLLECT_INPUTS",
|
161
|
-
"retry": "ROUTE_TO_RETRY",
|
162
|
-
"create_issue": "ROUTE_TO_CREATE_ISSUE",
|
163
|
-
"end": "ROUTE_TO_END",
|
162
|
+
"analyze": get_config_value("routing.tokens.analyze", "ROUTE_TO_ANALYZE"),
|
163
|
+
"demonstrate": get_config_value("routing.tokens.demonstrate", "ROUTE_TO_DEMONSTRATE"),
|
164
|
+
"collect_inputs": get_config_value("routing.tokens.collect_inputs", "ROUTE_TO_COLLECT_INPUTS"),
|
165
|
+
"retry": get_config_value("routing.tokens.retry", "ROUTE_TO_RETRY"),
|
166
|
+
"create_issue": get_config_value("routing.tokens.create_issue", "ROUTE_TO_CREATE_ISSUE"),
|
167
|
+
"end": get_config_value("routing.tokens.end", "ROUTE_TO_END"),
|
164
168
|
},
|
165
169
|
"prompts": {
|
166
170
|
"planner_prompt": """User request: "{user_input}"
|
@@ -46,6 +46,7 @@ from diagram_to_iac.tools.llm_utils.router import get_llm, LLMRouter
|
|
46
46
|
from diagram_to_iac.core.agent_base import AgentBase
|
47
47
|
from diagram_to_iac.core.memory import create_memory, LangGraphMemoryAdapter
|
48
48
|
from diagram_to_iac.services.observability import log_event
|
49
|
+
from diagram_to_iac.core.config_loader import get_config, get_config_value
|
49
50
|
|
50
51
|
from diagram_to_iac.services.observability import log_event
|
51
52
|
|
@@ -131,26 +132,42 @@ class GitAgent(AgentBase):
|
|
131
132
|
# Store memory type for tool initialization
|
132
133
|
self.memory_type = memory_type
|
133
134
|
|
134
|
-
# Load configuration
|
135
|
+
# Load configuration using centralized system
|
135
136
|
if config_path is None:
|
136
137
|
base_dir = os.path.dirname(os.path.abspath(__file__))
|
137
138
|
config_path = os.path.join(base_dir, 'config.yaml')
|
138
139
|
self.logger.debug(f"Default config path set to: {config_path}")
|
139
140
|
|
140
141
|
try:
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
142
|
+
# Use centralized configuration loading with hierarchical merging
|
143
|
+
base_config = get_config()
|
144
|
+
|
145
|
+
# Load agent-specific config if provided
|
146
|
+
agent_config = {}
|
147
|
+
if config_path and os.path.exists(config_path):
|
148
|
+
with open(config_path, 'r') as f:
|
149
|
+
agent_config = yaml.safe_load(f) or {}
|
150
|
+
|
151
|
+
# Deep merge base config with agent-specific overrides
|
152
|
+
self.config = self._deep_merge(base_config, agent_config)
|
153
|
+
self.logger.info(f"Configuration loaded successfully from centralized system")
|
154
|
+
except Exception as e:
|
155
|
+
self.logger.warning(f"Failed to load configuration via centralized system: {e}. Using fallback.")
|
156
|
+
# Fallback to direct YAML loading for backward compatibility
|
157
|
+
try:
|
158
|
+
with open(config_path, 'r') as f:
|
159
|
+
self.config = yaml.safe_load(f)
|
160
|
+
if self.config is None:
|
161
|
+
self.logger.warning(f"Configuration file at {config_path} is empty. Using defaults.")
|
162
|
+
self._set_default_config()
|
163
|
+
else:
|
164
|
+
self.logger.info(f"Configuration loaded successfully from {config_path}")
|
165
|
+
except FileNotFoundError:
|
166
|
+
self.logger.warning(f"Configuration file not found at {config_path}. Using defaults.")
|
167
|
+
self._set_default_config()
|
168
|
+
except yaml.YAMLError as e:
|
169
|
+
self.logger.error(f"Error parsing YAML configuration: {e}. Using defaults.", exc_info=True)
|
145
170
|
self._set_default_config()
|
146
|
-
else:
|
147
|
-
self.logger.info(f"Configuration loaded successfully from {config_path}")
|
148
|
-
except FileNotFoundError:
|
149
|
-
self.logger.warning(f"Configuration file not found at {config_path}. Using defaults.")
|
150
|
-
self._set_default_config()
|
151
|
-
except yaml.YAMLError as e:
|
152
|
-
self.logger.error(f"Error parsing YAML configuration: {e}. Using defaults.", exc_info=True)
|
153
|
-
self._set_default_config()
|
154
171
|
|
155
172
|
# Initialize enhanced LLM router
|
156
173
|
self.llm_router = LLMRouter()
|
@@ -172,27 +189,60 @@ class GitAgent(AgentBase):
|
|
172
189
|
self.logger.info("GitAgent initialized successfully")
|
173
190
|
|
174
191
|
def _set_default_config(self):
|
175
|
-
"""Set default configuration values."""
|
192
|
+
"""Set default configuration values using centralized system."""
|
176
193
|
self.logger.info("Setting default configuration for GitAgent")
|
177
194
|
self.config = {
|
178
195
|
'llm': {
|
179
|
-
'model_name':
|
180
|
-
'temperature': 0.1
|
196
|
+
'model_name': get_config_value("ai.default_model", "gpt-4o-mini"),
|
197
|
+
'temperature': get_config_value("ai.git_agent_temperature", 0.1)
|
181
198
|
},
|
182
199
|
'routing_keys': {
|
183
|
-
'git_clone':
|
184
|
-
'github_cli':
|
185
|
-
'shell_exec':
|
186
|
-
'create_pr':
|
187
|
-
'end':
|
200
|
+
'git_clone': get_config_value("routing.tokens.git_clone", "ROUTE_TO_GIT_CLONE"),
|
201
|
+
'github_cli': get_config_value("routing.tokens.github_cli", "ROUTE_TO_GITHUB_CLI"),
|
202
|
+
'shell_exec': get_config_value("routing.tokens.shell_exec", "ROUTE_TO_SHELL_EXEC"),
|
203
|
+
'create_pr': get_config_value("routing.tokens.create_pr", "ROUTE_TO_CREATE_PR"),
|
204
|
+
'end': get_config_value("routing.tokens.end", "ROUTE_TO_END")
|
188
205
|
},
|
189
206
|
'git_pr_creator': {
|
190
|
-
'copilot_assignee': "CopilotUser",
|
191
|
-
'default_assignees': ["team-infra"],
|
192
|
-
'remote_name': "origin"
|
207
|
+
'copilot_assignee': get_config_value("github.copilot_assignee", "CopilotUser"),
|
208
|
+
'default_assignees': get_config_value("github.default_assignees", ["team-infra"]),
|
209
|
+
'remote_name': get_config_value("tools.git.remote_name", "origin")
|
210
|
+
},
|
211
|
+
'tools': {
|
212
|
+
'git_clone': {
|
213
|
+
'enabled': True,
|
214
|
+
'timeout': get_config_value("network.github_timeout", 300)
|
215
|
+
},
|
216
|
+
'gh_open_issue': {
|
217
|
+
'enabled': True,
|
218
|
+
'timeout': get_config_value("network.github_timeout", 15)
|
219
|
+
},
|
220
|
+
'shell_exec': {
|
221
|
+
'enabled': True,
|
222
|
+
'timeout': get_config_value("network.shell_timeout", 30)
|
223
|
+
}
|
193
224
|
}
|
194
225
|
}
|
195
226
|
|
227
|
+
def _deep_merge(self, base: dict, overlay: dict) -> dict:
|
228
|
+
"""
|
229
|
+
Deep merge two dictionaries, with overlay taking precedence.
|
230
|
+
|
231
|
+
Args:
|
232
|
+
base: Base dictionary
|
233
|
+
overlay: Dictionary to overlay on base
|
234
|
+
|
235
|
+
Returns:
|
236
|
+
Merged dictionary
|
237
|
+
"""
|
238
|
+
result = base.copy()
|
239
|
+
for key, value in overlay.items():
|
240
|
+
if key in result and isinstance(result[key], dict) and isinstance(value, dict):
|
241
|
+
result[key] = self._deep_merge(result[key], value)
|
242
|
+
else:
|
243
|
+
result[key] = value
|
244
|
+
return result
|
245
|
+
|
196
246
|
def _initialize_tools(self):
|
197
247
|
"""Initialize the DevOps tools."""
|
198
248
|
try:
|
@@ -24,6 +24,7 @@ from diagram_to_iac.core.memory import (
|
|
24
24
|
save_agent_state,
|
25
25
|
current_git_sha,
|
26
26
|
)
|
27
|
+
from diagram_to_iac.core.config_loader import get_config, get_config_value
|
27
28
|
|
28
29
|
|
29
30
|
# --- Pydantic Schemas for Agent I/O ---
|
@@ -81,25 +82,55 @@ class HelloAgent(AgentBase):
|
|
81
82
|
datefmt='%Y-%m-%d %H:%M:%S'
|
82
83
|
)
|
83
84
|
|
85
|
+
# Load configuration using centralized system
|
84
86
|
if config_path is None:
|
85
87
|
base_dir = os.path.dirname(os.path.abspath(__file__))
|
86
88
|
config_path = os.path.join(base_dir, 'config.yaml')
|
87
89
|
self.logger.debug(f"Default config path set to: {config_path}")
|
88
90
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
if
|
93
|
-
self.logger.warning(f"Configuration file at {config_path}
|
91
|
+
# Handle configuration loading with proper fallback and logging for tests
|
92
|
+
if config_path:
|
93
|
+
# First try to load agent-specific config directly for test compatibility
|
94
|
+
if not os.path.exists(config_path):
|
95
|
+
self.logger.warning(f"Configuration file not found at {config_path}. Using default values.")
|
94
96
|
self._set_default_config()
|
95
97
|
else:
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
98
|
+
try:
|
99
|
+
with open(config_path, 'r') as f:
|
100
|
+
content = f.read().strip()
|
101
|
+
if not content:
|
102
|
+
self.logger.warning(f"Configuration file at {config_path} is empty. Using default values.")
|
103
|
+
self._set_default_config()
|
104
|
+
else:
|
105
|
+
f.seek(0) # Reset file pointer
|
106
|
+
agent_config = yaml.safe_load(f)
|
107
|
+
if agent_config is None:
|
108
|
+
self.logger.warning(f"Configuration file at {config_path} is empty. Using default values.")
|
109
|
+
self._set_default_config()
|
110
|
+
else:
|
111
|
+
# Use centralized config as base and merge with agent-specific overrides
|
112
|
+
try:
|
113
|
+
base_config = get_config()
|
114
|
+
self.config = self._deep_merge(base_config, agent_config)
|
115
|
+
self.logger.info(f"Configuration loaded successfully from {config_path}")
|
116
|
+
except Exception as e:
|
117
|
+
# Fallback to agent-specific config only
|
118
|
+
self.config = agent_config
|
119
|
+
self.logger.info(f"Configuration loaded successfully from {config_path}")
|
120
|
+
except yaml.YAMLError as e:
|
121
|
+
self.logger.error(f"Error parsing YAML configuration from {config_path}: {e}. Using default values.", exc_info=True)
|
122
|
+
self._set_default_config()
|
123
|
+
except Exception as e:
|
124
|
+
self.logger.error(f"Unexpected error loading configuration from {config_path}: {e}. Using default values.", exc_info=True)
|
125
|
+
self._set_default_config()
|
126
|
+
else:
|
127
|
+
# No config path provided, use centralized config only
|
128
|
+
try:
|
129
|
+
self.config = get_config()
|
130
|
+
self.logger.info(f"Configuration loaded successfully from centralized system")
|
131
|
+
except Exception as e:
|
132
|
+
self.logger.warning(f"Failed to load configuration via centralized system: {e}. Using default values.")
|
133
|
+
self._set_default_config()
|
103
134
|
|
104
135
|
# Ensure a dummy API key is set so tests can initialize the router without real credentials
|
105
136
|
if not os.getenv("OPENAI_API_KEY"):
|
@@ -137,13 +168,38 @@ class HelloAgent(AgentBase):
|
|
137
168
|
|
138
169
|
def _set_default_config(self):
|
139
170
|
self.logger.info("Setting default configuration for HelloAgent.")
|
171
|
+
# Use hardcoded defaults when agent-specific config fails
|
172
|
+
# This ensures test compatibility with expected DEFAULT_TEMP and DEFAULT_MODEL
|
140
173
|
self.config = {
|
141
174
|
'llm': {
|
142
|
-
'model_name':
|
175
|
+
'model_name': "gpt-4o-mini",
|
143
176
|
'temperature': 0.0
|
177
|
+
},
|
178
|
+
'routing_keys': {
|
179
|
+
'addition': "ROUTE_TO_ADDITION",
|
180
|
+
'multiplication': "ROUTE_TO_MULTIPLICATION"
|
144
181
|
}
|
145
182
|
}
|
146
183
|
|
184
|
+
def _deep_merge(self, base: dict, overlay: dict) -> dict:
|
185
|
+
"""
|
186
|
+
Deep merge two dictionaries, with overlay taking precedence.
|
187
|
+
|
188
|
+
Args:
|
189
|
+
base: Base dictionary
|
190
|
+
overlay: Dictionary to overlay on base
|
191
|
+
|
192
|
+
Returns:
|
193
|
+
Merged dictionary
|
194
|
+
"""
|
195
|
+
result = base.copy()
|
196
|
+
for key, value in overlay.items():
|
197
|
+
if key in result and isinstance(result[key], dict) and isinstance(value, dict):
|
198
|
+
result[key] = self._deep_merge(result[key], value)
|
199
|
+
else:
|
200
|
+
result[key] = value
|
201
|
+
return result
|
202
|
+
|
147
203
|
def _planner_llm_node(self, state: HelloAgentState):
|
148
204
|
"""LLM decides if tool is needed, and which specific tool to route to."""
|
149
205
|
# Config values for the LLM
|
@@ -37,6 +37,7 @@ from pydantic import BaseModel, Field
|
|
37
37
|
# Import core infrastructure
|
38
38
|
from diagram_to_iac.core.agent_base import AgentBase
|
39
39
|
from diagram_to_iac.core.memory import create_memory, LangGraphMemoryAdapter
|
40
|
+
from diagram_to_iac.core.config_loader import get_config, get_config_value
|
40
41
|
from .tools.tfsec_tool import TfSecTool, TfSecScanInput, TfSecScanOutput
|
41
42
|
|
42
43
|
|
@@ -89,12 +90,12 @@ class PolicyAgent(AgentBase):
|
|
89
90
|
"""Initialize PolicyAgent with configuration and tools."""
|
90
91
|
self.config_path = config_path
|
91
92
|
|
93
|
+
# Initialize logging first
|
94
|
+
self.logger = logging.getLogger(__name__)
|
95
|
+
|
92
96
|
# Load configuration
|
93
97
|
self.config = self._load_config()
|
94
98
|
|
95
|
-
# Initialize logging
|
96
|
-
self.logger = logging.getLogger(__name__)
|
97
|
-
|
98
99
|
# Initialize memory
|
99
100
|
self.memory = create_memory(memory_type=memory_type)
|
100
101
|
self.memory_adapter = LangGraphMemoryAdapter(self.memory)
|
@@ -106,24 +107,46 @@ class PolicyAgent(AgentBase):
|
|
106
107
|
self.workflow = self._build_workflow()
|
107
108
|
|
108
109
|
def _load_config(self) -> Dict[str, Any]:
|
109
|
-
"""Load agent configuration
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
110
|
+
"""Load agent configuration using centralized system with fallback to direct file loading."""
|
111
|
+
try:
|
112
|
+
config = get_config()
|
113
|
+
policy_config = config.get('agents', {}).get('policy_agent', {})
|
114
|
+
|
115
|
+
if policy_config:
|
116
|
+
self.logger.info("Configuration loaded from centralized system")
|
117
|
+
return policy_config
|
118
|
+
else:
|
119
|
+
self.logger.warning("No policy agent configuration found in centralized system. Using defaults.")
|
120
|
+
return self._get_fallback_config()
|
121
|
+
except Exception as e:
|
122
|
+
self.logger.warning(f"Failed to load from centralized config: {e}. Falling back to direct file loading.")
|
123
|
+
# Fallback to direct file loading for backward compatibility
|
124
|
+
if self.config_path and os.path.exists(self.config_path):
|
125
|
+
with open(self.config_path, 'r') as f:
|
126
|
+
return yaml.safe_load(f)
|
127
|
+
|
128
|
+
# Default configuration
|
129
|
+
config_file = os.path.join(os.path.dirname(__file__), "config.yaml")
|
130
|
+
if os.path.exists(config_file):
|
131
|
+
with open(config_file, 'r') as f:
|
132
|
+
return yaml.safe_load(f)
|
133
|
+
|
134
|
+
# Fallback minimal configuration
|
135
|
+
return self._get_fallback_config()
|
136
|
+
|
137
|
+
def _get_fallback_config(self) -> Dict[str, Any]:
|
138
|
+
"""Get fallback configuration using centralized values."""
|
121
139
|
return {
|
140
|
+
"llm": {
|
141
|
+
"model_name": get_config_value("ai.default_model", "gpt-4o-mini"),
|
142
|
+
"temperature": get_config_value("ai.default_temperature", 0.1)
|
143
|
+
},
|
122
144
|
"policy": {
|
123
145
|
"tfsec": {
|
124
|
-
"enabled": True,
|
125
|
-
"
|
126
|
-
"
|
146
|
+
"enabled": get_config_value("tools.policy.tfsec_enabled", True),
|
147
|
+
"timeout_seconds": get_config_value("network.shell_timeout", 120),
|
148
|
+
"block_on_severity": get_config_value("tools.policy.block_on_severity", ["CRITICAL", "HIGH"]),
|
149
|
+
"artifact_on_severity": get_config_value("tools.policy.artifact_on_severity", ["CRITICAL", "HIGH", "MEDIUM"])
|
127
150
|
}
|
128
151
|
}
|
129
152
|
}
|
@@ -40,6 +40,7 @@ from diagram_to_iac.core.agent_base import AgentBase
|
|
40
40
|
from diagram_to_iac.core.memory import create_memory, LangGraphMemoryAdapter
|
41
41
|
from diagram_to_iac.core import IssueTracker, MissingSecretError
|
42
42
|
from diagram_to_iac.services.observability import log_event
|
43
|
+
from diagram_to_iac.core.config_loader import get_config, get_config_value
|
43
44
|
from .guards import check_required_secrets
|
44
45
|
from diagram_to_iac.tools.llm_utils.router import get_llm, LLMRouter
|
45
46
|
from diagram_to_iac.agents.git_langgraph import GitAgent, GitAgentInput, GitAgentOutput
|
@@ -147,34 +148,50 @@ class SupervisorAgent(AgentBase):
|
|
147
148
|
datefmt="%Y-%m-%d %H:%M:%S",
|
148
149
|
)
|
149
150
|
|
150
|
-
# Load configuration
|
151
|
+
# Load configuration using centralized system
|
151
152
|
if config_path is None:
|
152
153
|
base_dir = os.path.dirname(os.path.abspath(__file__))
|
153
154
|
config_path = os.path.join(base_dir, "config.yaml")
|
154
155
|
self.logger.debug(f"Default config path set to: {config_path}")
|
155
156
|
|
156
157
|
try:
|
157
|
-
|
158
|
-
|
159
|
-
|
158
|
+
# Use centralized configuration loading with hierarchical merging
|
159
|
+
base_config = get_config()
|
160
|
+
|
161
|
+
# Load agent-specific config if provided
|
162
|
+
agent_config = {}
|
163
|
+
if config_path and os.path.exists(config_path):
|
164
|
+
with open(config_path, 'r') as f:
|
165
|
+
agent_config = yaml.safe_load(f) or {}
|
166
|
+
|
167
|
+
# Deep merge base config with agent-specific overrides
|
168
|
+
self.config = self._deep_merge(base_config, agent_config)
|
169
|
+
self.logger.info(f"Configuration loaded successfully from centralized system")
|
170
|
+
except Exception as e:
|
171
|
+
self.logger.warning(f"Failed to load configuration via centralized system: {e}. Using fallback.")
|
172
|
+
# Fallback to direct YAML loading for backward compatibility
|
173
|
+
try:
|
174
|
+
with open(config_path, "r") as f:
|
175
|
+
self.config = yaml.safe_load(f)
|
176
|
+
if self.config is None:
|
177
|
+
self.logger.warning(
|
178
|
+
f"Configuration file at {config_path} is empty. Using defaults."
|
179
|
+
)
|
180
|
+
self._set_default_config()
|
181
|
+
else:
|
182
|
+
self.logger.info(
|
183
|
+
f"Configuration loaded successfully from {config_path}"
|
184
|
+
)
|
185
|
+
except FileNotFoundError:
|
160
186
|
self.logger.warning(
|
161
|
-
f"Configuration file at {config_path}
|
187
|
+
f"Configuration file not found at {config_path}. Using defaults."
|
162
188
|
)
|
163
189
|
self._set_default_config()
|
164
|
-
|
165
|
-
self.logger.
|
166
|
-
f"
|
190
|
+
except yaml.YAMLError as e:
|
191
|
+
self.logger.error(
|
192
|
+
f"Error parsing YAML configuration: {e}. Using defaults.", exc_info=True
|
167
193
|
)
|
168
|
-
|
169
|
-
self.logger.warning(
|
170
|
-
f"Configuration file not found at {config_path}. Using defaults."
|
171
|
-
)
|
172
|
-
self._set_default_config()
|
173
|
-
except yaml.YAMLError as e:
|
174
|
-
self.logger.error(
|
175
|
-
f"Error parsing YAML configuration: {e}. Using defaults.", exc_info=True
|
176
|
-
)
|
177
|
-
self._set_default_config()
|
194
|
+
self._set_default_config()
|
178
195
|
|
179
196
|
# Initialize enhanced LLM router
|
180
197
|
self.llm_router = LLMRouter()
|
@@ -236,15 +253,18 @@ class SupervisorAgent(AgentBase):
|
|
236
253
|
|
237
254
|
|
238
255
|
def _set_default_config(self):
|
239
|
-
"""Set default configuration values."""
|
256
|
+
"""Set default configuration values using centralized system."""
|
240
257
|
self.config = {
|
241
|
-
"llm": {
|
258
|
+
"llm": {
|
259
|
+
"model_name": get_config_value("ai.default_model", "gpt-4o-mini"),
|
260
|
+
"temperature": get_config_value("ai.default_temperature", 0.1)
|
261
|
+
},
|
242
262
|
"routing_keys": {
|
243
|
-
"clone": "ROUTE_TO_CLONE",
|
244
|
-
"stack_detect": "ROUTE_TO_STACK_DETECT",
|
245
|
-
"terraform": "ROUTE_TO_TERRAFORM",
|
246
|
-
"issue": "ROUTE_TO_ISSUE",
|
247
|
-
"end": "ROUTE_TO_END",
|
263
|
+
"clone": get_config_value("routing.tokens.git_clone", "ROUTE_TO_CLONE"),
|
264
|
+
"stack_detect": get_config_value("routing.tokens.analyze", "ROUTE_TO_STACK_DETECT"),
|
265
|
+
"terraform": get_config_value("routing.tokens.terraform_init", "ROUTE_TO_TERRAFORM"),
|
266
|
+
"issue": get_config_value("routing.tokens.open_issue", "ROUTE_TO_ISSUE"),
|
267
|
+
"end": get_config_value("routing.tokens.end", "ROUTE_TO_END"),
|
248
268
|
},
|
249
269
|
"prompts": {
|
250
270
|
"planner_prompt": """User input: "{user_input}"
|
@@ -258,9 +278,34 @@ Analyze this R2D (Repo-to-Deployment) request and determine the appropriate acti
|
|
258
278
|
|
259
279
|
Important: Only use routing tokens if the input contains actionable R2D workflow requests."""
|
260
280
|
},
|
281
|
+
"workflow": {
|
282
|
+
"timeout_seconds": get_config_value("network.terraform_timeout", 600),
|
283
|
+
"working_directory": get_config_value("system.workspace_base", "/workspace"),
|
284
|
+
"auto_branch_naming": True,
|
285
|
+
"enhanced_terraform": True
|
286
|
+
}
|
261
287
|
}
|
262
288
|
self.logger.info("Default configuration set")
|
263
289
|
|
290
|
+
def _deep_merge(self, base: dict, overlay: dict) -> dict:
|
291
|
+
"""
|
292
|
+
Deep merge two dictionaries, with overlay taking precedence.
|
293
|
+
|
294
|
+
Args:
|
295
|
+
base: Base dictionary
|
296
|
+
overlay: Dictionary to overlay on base
|
297
|
+
|
298
|
+
Returns:
|
299
|
+
Merged dictionary
|
300
|
+
"""
|
301
|
+
result = base.copy()
|
302
|
+
for key, value in overlay.items():
|
303
|
+
if key in result and isinstance(result[key], dict) and isinstance(value, dict):
|
304
|
+
result[key] = self._deep_merge(result[key], value)
|
305
|
+
else:
|
306
|
+
result[key] = value
|
307
|
+
return result
|
308
|
+
|
264
309
|
# --- AgentBase interface -------------------------------------------------
|
265
310
|
def plan(self, query: str, **kwargs):
|
266
311
|
"""Generate a plan for the R2D workflow (required by AgentBase)."""
|