idun-agent-engine 0.3.4__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.
Files changed (60) hide show
  1. idun_agent_engine/__init__.py +24 -0
  2. idun_agent_engine/_version.py +3 -0
  3. idun_agent_engine/agent/__init__.py +10 -0
  4. idun_agent_engine/agent/adk/__init__.py +5 -0
  5. idun_agent_engine/agent/adk/adk.py +296 -0
  6. idun_agent_engine/agent/base.py +112 -0
  7. idun_agent_engine/agent/haystack/__init__.py +9 -0
  8. idun_agent_engine/agent/haystack/haystack.py +274 -0
  9. idun_agent_engine/agent/haystack/haystack_model.py +13 -0
  10. idun_agent_engine/agent/haystack/utils.py +13 -0
  11. idun_agent_engine/agent/langgraph/__init__.py +7 -0
  12. idun_agent_engine/agent/langgraph/langgraph.py +553 -0
  13. idun_agent_engine/core/__init__.py +11 -0
  14. idun_agent_engine/core/app_factory.py +73 -0
  15. idun_agent_engine/core/config_builder.py +657 -0
  16. idun_agent_engine/core/engine_config.py +21 -0
  17. idun_agent_engine/core/server_runner.py +145 -0
  18. idun_agent_engine/guardrails/__init__.py +0 -0
  19. idun_agent_engine/guardrails/base.py +24 -0
  20. idun_agent_engine/guardrails/guardrails_hub/guardrails_hub.py +101 -0
  21. idun_agent_engine/guardrails/guardrails_hub/utils.py +1 -0
  22. idun_agent_engine/mcp/__init__.py +5 -0
  23. idun_agent_engine/mcp/helpers.py +97 -0
  24. idun_agent_engine/mcp/registry.py +109 -0
  25. idun_agent_engine/observability/__init__.py +17 -0
  26. idun_agent_engine/observability/base.py +172 -0
  27. idun_agent_engine/observability/gcp_logging/__init__.py +0 -0
  28. idun_agent_engine/observability/gcp_logging/gcp_logging_handler.py +52 -0
  29. idun_agent_engine/observability/gcp_trace/__init__.py +0 -0
  30. idun_agent_engine/observability/gcp_trace/gcp_trace_handler.py +116 -0
  31. idun_agent_engine/observability/langfuse/__init__.py +5 -0
  32. idun_agent_engine/observability/langfuse/langfuse_handler.py +79 -0
  33. idun_agent_engine/observability/phoenix/__init__.py +5 -0
  34. idun_agent_engine/observability/phoenix/phoenix_handler.py +65 -0
  35. idun_agent_engine/observability/phoenix_local/__init__.py +5 -0
  36. idun_agent_engine/observability/phoenix_local/phoenix_local_handler.py +123 -0
  37. idun_agent_engine/py.typed +0 -0
  38. idun_agent_engine/server/__init__.py +5 -0
  39. idun_agent_engine/server/dependencies.py +52 -0
  40. idun_agent_engine/server/lifespan.py +106 -0
  41. idun_agent_engine/server/routers/__init__.py +5 -0
  42. idun_agent_engine/server/routers/agent.py +204 -0
  43. idun_agent_engine/server/routers/agui.py +47 -0
  44. idun_agent_engine/server/routers/base.py +114 -0
  45. idun_agent_engine/server/server_config.py +8 -0
  46. idun_agent_engine/templates/__init__.py +1 -0
  47. idun_agent_engine/templates/correction.py +65 -0
  48. idun_agent_engine/templates/deep_research.py +40 -0
  49. idun_agent_engine/templates/translation.py +70 -0
  50. idun_agent_engine-0.3.4.dist-info/METADATA +335 -0
  51. idun_agent_engine-0.3.4.dist-info/RECORD +60 -0
  52. idun_agent_engine-0.3.4.dist-info/WHEEL +4 -0
  53. idun_agent_engine-0.3.4.dist-info/entry_points.txt +2 -0
  54. idun_platform_cli/__init__.py +0 -0
  55. idun_platform_cli/groups/__init__.py +0 -0
  56. idun_platform_cli/groups/agent/__init__.py +0 -0
  57. idun_platform_cli/groups/agent/main.py +16 -0
  58. idun_platform_cli/groups/agent/package.py +70 -0
  59. idun_platform_cli/groups/agent/serve.py +107 -0
  60. idun_platform_cli/main.py +14 -0
@@ -0,0 +1,657 @@
1
+ """Configuration Builder for Idun Agent Engine.
2
+
3
+ This module provides a fluent API for building configuration objects using Pydantic models.
4
+ This approach ensures type safety, validation, and consistency with the rest of the codebase.
5
+ """
6
+
7
+ from pathlib import Path
8
+ from typing import Any
9
+
10
+ from idun_agent_schema.engine.guardrails import Guardrails as GuardrailsV1
11
+ import yaml
12
+ from idun_agent_schema.engine.agent_framework import AgentFramework
13
+ from idun_agent_schema.engine.haystack import HaystackAgentConfig
14
+ from idun_agent_schema.engine.langgraph import (
15
+ LangGraphAgentConfig,
16
+ SqliteCheckpointConfig,
17
+ )
18
+ from idun_agent_schema.engine.adk import AdkAgentConfig
19
+ from idun_agent_schema.engine.mcp_server import MCPServer
20
+ from idun_agent_schema.engine.observability_v2 import ObservabilityConfig
21
+ from idun_agent_schema.engine.guardrails_v2 import GuardrailsV2 as Guardrails
22
+ from idun_agent_engine.server.server_config import ServerAPIConfig
23
+ from yaml import YAMLError
24
+
25
+ from ..agent.base import BaseAgent
26
+ from .engine_config import AgentConfig, EngineConfig, ServerConfig
27
+
28
+
29
+ class ConfigBuilder:
30
+ """A fluent builder for creating Idun Agent Engine configurations using Pydantic models.
31
+
32
+ This class provides a convenient way to build strongly-typed configuration objects
33
+ that are validated at creation time, ensuring consistency and catching errors early.
34
+ It also handles agent initialization and management.
35
+
36
+ Example:
37
+ config = (ConfigBuilder()
38
+ .with_api_port(8080)
39
+ .with_langgraph_agent(
40
+ name="My Agent",
41
+ graph_definition="my_agent.py:graph",
42
+ sqlite_checkpointer="agent.db")
43
+ .build())
44
+
45
+ app = create_app(config_dict=config.model_dump())
46
+ """
47
+
48
+ def __init__(self):
49
+ """Initialize a new configuration builder with default values."""
50
+ self._server_config = ServerConfig()
51
+ self._agent_config: AgentConfig | None = None
52
+ # TODO: add mcp_servers config
53
+ self._mcp_servers: list[MCPServer] | None = None
54
+ self._observability: list[ObservabilityConfig] | None = None
55
+ self._guardrails: Guardrails | None = None
56
+
57
+ def with_api_port(self, port: int) -> "ConfigBuilder":
58
+ """Set the API port for the server.
59
+
60
+ Args:
61
+ port: The port number to bind the server to
62
+
63
+ Returns:
64
+ ConfigBuilder: This builder instance for method chaining
65
+ """
66
+ # Create new API config with updated port
67
+ api_config = ServerAPIConfig(port=port)
68
+ self._server_config = ServerConfig(
69
+ api=api_config,
70
+ )
71
+ return self
72
+
73
+ def with_server_config(
74
+ self, api_port: int | None = None, telemetry_provider: str | None = None
75
+ ) -> "ConfigBuilder":
76
+ """Set server configuration options directly.
77
+
78
+ Args:
79
+ api_port: Optional API port
80
+ telemetry_provider: Optional telemetry provider
81
+
82
+ Returns:
83
+ ConfigBuilder: This builder instance for method chaining
84
+ """
85
+ api_config = (
86
+ ServerAPIConfig(port=api_port) if api_port else self._server_config.api
87
+ )
88
+
89
+ self._server_config = ServerConfig(api=api_config)
90
+ return self
91
+
92
+ def with_config_from_api(self, agent_api_key: str, url: str) -> "ConfigBuilder":
93
+ """Fetches the yaml config file, from idun agent manager api.
94
+
95
+ Requires the agent api key to pass in the headers.
96
+ """
97
+ import requests
98
+ import yaml
99
+
100
+ headers = {"auth": f"Bearer {agent_api_key}"}
101
+ try:
102
+ print(f"Fetching config from {url + '/api/v1/agents/config'}")
103
+ response = requests.get(url=url + "/api/v1/agents/config", headers=headers)
104
+ if response.status_code != 200:
105
+ raise ValueError(
106
+ f"Error sending retrieving config from url. response : {response.json()}"
107
+ )
108
+ yaml_config = yaml.safe_load(response.text)
109
+ try:
110
+ self._server_config = yaml_config.get("engine_config", {}).get("server")
111
+ except Exception as e:
112
+ raise YAMLError(
113
+ f"Failed to parse yaml file for ServerConfig: {e}"
114
+ ) from e
115
+ try:
116
+ self._agent_config = yaml_config.get("engine_config", {}).get("agent")
117
+ except Exception as e:
118
+ raise YAMLError(
119
+ f"Failed to parse yaml file for Engine config: {e}"
120
+ ) from e
121
+ try:
122
+ guardrails = yaml_config.get("engine_config", {}).get("guardrails", "")
123
+ if not guardrails:
124
+ # self._guardrails = Guardrails(enabled=False)
125
+ self._guardrails = None
126
+ except Exception as e:
127
+ raise YAMLError(f"Failed to parse yaml file for Guardrails: {e}") from e
128
+
129
+ try:
130
+ observability_list = yaml_config.get("engine_config", {}).get(
131
+ "observability"
132
+ )
133
+ if observability_list:
134
+ self._observability = [
135
+ ObservabilityConfig.model_validate(obs)
136
+ for obs in observability_list
137
+ ]
138
+ else:
139
+ self._observability = None
140
+ except Exception as e:
141
+ raise YAMLError(
142
+ f"Failed to parse yaml file for Observability: {e}"
143
+ ) from e
144
+ # try:
145
+ # mcp_servers_list = yaml_config.get("engine_config", {}).get("mcp_servers") or yaml_config.get("engine_config", {}).get("mcpServers") # TODO to fix camelcase issues
146
+ # if mcp_servers_list:
147
+ # self._mcp_servers = [
148
+ # MCPServer.model_validate(server) for server in mcp_servers_list
149
+ # ]
150
+ # else:
151
+ # self._mcp_servers = None
152
+ # except Exception as e:
153
+ # raise YAMLError(f"Failed to parse yaml file for MCP Servers: {e}") from e
154
+
155
+ return self
156
+
157
+ except Exception as e:
158
+ raise ValueError(f"Error occured while getting config from api: {e}") from e
159
+
160
+ def with_langgraph_agent(
161
+ self,
162
+ name: str,
163
+ graph_definition: str,
164
+ sqlite_checkpointer: str | None = None,
165
+ **additional_config,
166
+ ) -> "ConfigBuilder":
167
+ """Configure a LangGraph agent using the LangGraphAgentConfig model.
168
+
169
+ Args:
170
+ name: Human-readable name for the agent
171
+ graph_definition: Path to the graph in format "module.py:variable_name"
172
+ sqlite_checkpointer: Optional path to SQLite database for checkpointing
173
+ **additional_config: Additional configuration parameters
174
+
175
+ Returns:
176
+ ConfigBuilder: This builder instance for method chaining
177
+ """
178
+ # Build the agent config dictionary
179
+ agent_config_dict = {
180
+ "name": name,
181
+ "graph_definition": graph_definition,
182
+ **additional_config,
183
+ }
184
+
185
+ # Add checkpointer if specified
186
+ if sqlite_checkpointer:
187
+ checkpointer = SqliteCheckpointConfig(
188
+ type="sqlite", db_url=f"sqlite:///{sqlite_checkpointer}"
189
+ )
190
+ agent_config_dict["checkpointer"] = checkpointer
191
+
192
+ # Create and validate the LangGraph config
193
+ langgraph_config = LangGraphAgentConfig.model_validate(agent_config_dict)
194
+
195
+ # Create the agent config (store as strongly-typed model, not dict)
196
+ self._agent_config = AgentConfig(
197
+ type=AgentFramework.LANGGRAPH, config=langgraph_config
198
+ )
199
+ return self
200
+
201
+ # TODO: remove unused fns
202
+
203
+ def with_custom_agent(
204
+ self, agent_type: str, config: dict[str, Any]
205
+ ) -> "ConfigBuilder":
206
+ """Configure a custom agent type.
207
+
208
+ This method allows for configuring agent types that don't have
209
+ dedicated builder methods yet. The config will be validated
210
+ when the AgentConfig is created.
211
+
212
+ Args:
213
+ agent_type: The type of agent (e.g., "crewai", "autogen")
214
+ config: Configuration dictionary specific to the agent type
215
+
216
+ Returns:
217
+ ConfigBuilder: This builder instance for method chaining
218
+ """
219
+ if agent_type == AgentFramework.LANGGRAPH:
220
+ self._agent_config = AgentConfig(
221
+ type=AgentFramework.LANGGRAPH,
222
+ config=LangGraphAgentConfig.model_validate(config),
223
+ )
224
+
225
+ elif agent_type == AgentFramework.HAYSTACK:
226
+ self._agent_config = AgentConfig(
227
+ type=AgentFramework.HAYSTACK,
228
+ config=HaystackAgentConfig.model_validate(config),
229
+ )
230
+ else:
231
+ raise ValueError(f"Unsupported agent type: {agent_type}")
232
+ return self
233
+
234
+ def build(self) -> EngineConfig:
235
+ """Build and return the complete configuration as a validated Pydantic model.
236
+
237
+ Returns:
238
+ EngineConfig: The complete, validated configuration object
239
+
240
+ Raises:
241
+ ValueError: If the configuration is incomplete or invalid
242
+ """
243
+ if not self._agent_config:
244
+ raise ValueError(
245
+ "Agent configuration is required. Use with_langgraph_agent() or with_custom_agent()"
246
+ )
247
+
248
+ # Create and validate the complete configuration
249
+ return EngineConfig(
250
+ server=self._server_config,
251
+ agent=self._agent_config,
252
+ guardrails=self._guardrails,
253
+ observability=self._observability,
254
+ mcp_servers=self._mcp_servers,
255
+ )
256
+
257
+ def build_dict(self) -> dict[str, Any]:
258
+ """Build and return the configuration as a dictionary.
259
+
260
+ This is a convenience method for backward compatibility.
261
+
262
+ Returns:
263
+ Dict[str, Any]: The complete configuration dictionary
264
+ """
265
+ engine_config = self.build()
266
+ return engine_config.model_dump()
267
+
268
+ def save_to_file(self, file_path: str) -> None:
269
+ """Save the configuration to a YAML file.
270
+
271
+ Args:
272
+ file_path: Path where to save the configuration file
273
+ """
274
+ config = self.build_dict()
275
+ with open(file_path, "w") as f:
276
+ yaml.dump(config, f, default_flow_style=False, indent=2)
277
+
278
+ async def build_and_initialize_agent(
279
+ self, mcp_registry: Any | None = None
280
+ ) -> BaseAgent:
281
+ """Build configuration and initialize the agent in one step.
282
+
283
+ Returns:
284
+ BaseAgent: Initialized agent instance
285
+
286
+ Raises:
287
+ ValueError: If agent type is unsupported or configuration is invalid
288
+ """
289
+ engine_config = self.build()
290
+ return await self.initialize_agent_from_config(
291
+ engine_config, mcp_registry=mcp_registry
292
+ )
293
+
294
+ @staticmethod
295
+ async def initialize_agent_from_config(
296
+ engine_config: EngineConfig, mcp_registry: Any | None = None
297
+ ) -> BaseAgent:
298
+ """Initialize an agent instance from a validated EngineConfig.
299
+
300
+ Args:
301
+ engine_config: Validated configuration object
302
+ mcp_registry: Optional MCP registry client.
303
+
304
+ Returns:
305
+ BaseAgent: Initialized agent instance
306
+
307
+ Raises:
308
+ ValueError: If agent type is unsupported
309
+ """
310
+ agent_config_obj = engine_config.agent.config
311
+ agent_type = engine_config.agent.type
312
+ observability_config = engine_config.observability
313
+ # mcp_servers = engine_config.mcp_servers
314
+ # Initialize the appropriate agent
315
+ agent_instance = None
316
+ if agent_type == AgentFramework.LANGGRAPH:
317
+ from idun_agent_engine.agent.langgraph.langgraph import LanggraphAgent
318
+ import os
319
+
320
+ print("Current directory: ", os.getcwd()) # TODO remove
321
+ try:
322
+ validated_config = LangGraphAgentConfig.model_validate(agent_config_obj)
323
+
324
+ except Exception as e:
325
+ raise ValueError(
326
+ f"Cannot validate into a LangGraphAgentConfig model. Got {agent_config_obj}"
327
+ ) from e
328
+
329
+ agent_instance = LanggraphAgent()
330
+
331
+ elif agent_type == AgentFramework.TRANSLATION_AGENT:
332
+ from idun_agent_engine.agent.langgraph.langgraph import LanggraphAgent
333
+ from idun_agent_schema.engine.templates import TranslationAgentConfig
334
+ import os
335
+
336
+ try:
337
+ translation_config = TranslationAgentConfig.model_validate(
338
+ agent_config_obj
339
+ )
340
+ except Exception as e:
341
+ raise ValueError(
342
+ f"Cannot validate into a TranslationAgentConfig model. Got {agent_config_obj}"
343
+ ) from e
344
+
345
+ # Configure environment for the template
346
+ os.environ["TRANSLATION_MODEL"] = translation_config.model_name
347
+ os.environ["TRANSLATION_SOURCE_LANG"] = translation_config.source_lang
348
+ os.environ["TRANSLATION_TARGET_LANG"] = translation_config.target_lang
349
+
350
+ # Create LangGraph config for the template
351
+ validated_config = LangGraphAgentConfig(
352
+ name=translation_config.name,
353
+ graph_definition="idun_agent_engine.templates.translation:graph",
354
+ input_schema_definition=translation_config.input_schema_definition,
355
+ output_schema_definition=translation_config.output_schema_definition,
356
+ observability=translation_config.observability,
357
+ checkpointer=translation_config.checkpointer,
358
+ )
359
+ agent_instance = LanggraphAgent()
360
+
361
+ elif agent_type == AgentFramework.CORRECTION_AGENT:
362
+ from idun_agent_engine.agent.langgraph.langgraph import LanggraphAgent
363
+ from idun_agent_schema.engine.templates import CorrectionAgentConfig
364
+ import os
365
+
366
+ try:
367
+ correction_config = CorrectionAgentConfig.model_validate(
368
+ agent_config_obj
369
+ )
370
+ except Exception as e:
371
+ raise ValueError(
372
+ f"Cannot validate into a CorrectionAgentConfig model. Got {agent_config_obj}"
373
+ ) from e
374
+
375
+ os.environ["CORRECTION_MODEL"] = correction_config.model_name
376
+ os.environ["CORRECTION_LANGUAGE"] = correction_config.language
377
+
378
+ validated_config = LangGraphAgentConfig(
379
+ name=correction_config.name,
380
+ graph_definition="idun_agent_engine.templates.correction:graph",
381
+ input_schema_definition=correction_config.input_schema_definition,
382
+ output_schema_definition=correction_config.output_schema_definition,
383
+ observability=correction_config.observability,
384
+ checkpointer=correction_config.checkpointer,
385
+ )
386
+ agent_instance = LanggraphAgent()
387
+
388
+ elif agent_type == AgentFramework.DEEP_RESEARCH_AGENT:
389
+ from idun_agent_engine.agent.langgraph.langgraph import LanggraphAgent
390
+ from idun_agent_schema.engine.templates import DeepResearchAgentConfig
391
+ import os
392
+
393
+ try:
394
+ deep_research_config = DeepResearchAgentConfig.model_validate(
395
+ agent_config_obj
396
+ )
397
+ except Exception as e:
398
+ raise ValueError(
399
+ f"Cannot validate into a DeepResearchAgentConfig model. Got {agent_config_obj}"
400
+ ) from e
401
+
402
+ os.environ["DEEP_RESEARCH_MODEL"] = deep_research_config.model_name
403
+ os.environ["DEEP_RESEARCH_PROMPT"] = deep_research_config.system_prompt
404
+ os.environ["TAVILY_API_KEY"] = deep_research_config.tavily_api_key
405
+
406
+ validated_config = LangGraphAgentConfig(
407
+ name=deep_research_config.name,
408
+ graph_definition="idun_agent_engine.templates.deep_research:graph",
409
+ input_schema_definition=deep_research_config.input_schema_definition,
410
+ output_schema_definition=deep_research_config.output_schema_definition,
411
+ observability=deep_research_config.observability,
412
+ checkpointer=deep_research_config.checkpointer,
413
+ )
414
+ agent_instance = LanggraphAgent()
415
+
416
+ elif agent_type == AgentFramework.HAYSTACK:
417
+ from idun_agent_engine.agent.haystack.haystack import HaystackAgent
418
+
419
+ try:
420
+ validated_config = HaystackAgentConfig.model_validate(agent_config_obj)
421
+
422
+ except Exception as e:
423
+ raise ValueError(
424
+ f"Cannot validate into a HaystackAgentConfig model. Got {agent_config_obj}"
425
+ ) from e
426
+ agent_instance = HaystackAgent()
427
+ elif agent_type == AgentFramework.ADK:
428
+ from idun_agent_engine.agent.adk.adk import AdkAgent
429
+
430
+ try:
431
+ validated_config = AdkAgentConfig.model_validate(agent_config_obj)
432
+ except Exception as e:
433
+ raise ValueError(
434
+ f"Cannot validate into a AdkAgentConfig model. Got {agent_config_obj}"
435
+ ) from e
436
+ agent_instance = AdkAgent()
437
+ else:
438
+ raise ValueError(f"Unsupported agent type: {agent_type}")
439
+
440
+ # Initialize the agent with its configuration
441
+ await agent_instance.initialize(
442
+ validated_config,
443
+ observability_config, # , mcp_registry=mcp_registry
444
+ ) # type: ignore[arg-type]
445
+ return agent_instance
446
+
447
+ @staticmethod
448
+ def get_agent_class(agent_type: str) -> type[BaseAgent]:
449
+ """Get the agent class for a given agent type without initializing it.
450
+
451
+ Args:
452
+ agent_type: The type of agent
453
+
454
+ Returns:
455
+ Type[BaseAgent]: The agent class
456
+
457
+ Raises:
458
+ ValueError: If agent type is unsupported
459
+ """
460
+ if (
461
+ agent_type == "langgraph"
462
+ or agent_type == AgentFramework.LANGGRAPH
463
+ or agent_type == AgentFramework.TRANSLATION_AGENT
464
+ or agent_type == AgentFramework.CORRECTION_AGENT
465
+ or agent_type == AgentFramework.DEEP_RESEARCH_AGENT
466
+ ):
467
+ from ..agent.langgraph.langgraph import LanggraphAgent
468
+
469
+ return LanggraphAgent
470
+
471
+ else:
472
+ raise ValueError(f"Unsupported agent type: {agent_type}")
473
+
474
+ @staticmethod
475
+ def validate_agent_config(
476
+ agent_type: str, config: dict[str, Any]
477
+ ) -> dict[str, Any]:
478
+ """Validate agent configuration against the appropriate Pydantic model.
479
+
480
+ Args:
481
+ agent_type: The type of agent
482
+ config: Configuration dictionary to validate
483
+
484
+ Returns:
485
+ Dict[str, Any]: Validated configuration dictionary
486
+
487
+ Raises:
488
+ ValueError: If agent type is unsupported or config is invalid
489
+ """
490
+ if agent_type == "langgraph":
491
+ validated_config = LangGraphAgentConfig.model_validate(config)
492
+ return validated_config.model_dump()
493
+ elif agent_type == AgentFramework.TRANSLATION_AGENT:
494
+ from idun_agent_schema.engine.templates import TranslationAgentConfig
495
+
496
+ validated_config = TranslationAgentConfig.model_validate(config)
497
+ return validated_config.model_dump()
498
+ elif agent_type == AgentFramework.CORRECTION_AGENT:
499
+ from idun_agent_schema.engine.templates import CorrectionAgentConfig
500
+
501
+ validated_config = CorrectionAgentConfig.model_validate(config)
502
+ return validated_config.model_dump()
503
+ elif agent_type == AgentFramework.DEEP_RESEARCH_AGENT:
504
+ from idun_agent_schema.engine.templates import DeepResearchAgentConfig
505
+
506
+ validated_config = DeepResearchAgentConfig.model_validate(config)
507
+ return validated_config.model_dump()
508
+ else:
509
+ raise ValueError(f"Unsupported agent type: {agent_type}")
510
+
511
+ @staticmethod
512
+ def load_from_file(config_path: str = "config.yaml") -> EngineConfig:
513
+ """Load configuration from a YAML file and return a validated EngineConfig.
514
+
515
+ Args:
516
+ config_path: Path to the configuration YAML file
517
+
518
+ Returns:
519
+ EngineConfig: Validated configuration object
520
+
521
+ Raises:
522
+ FileNotFoundError: If the configuration file doesn't exist
523
+ ValidationError: If the configuration is invalid
524
+ """
525
+ path = Path(config_path)
526
+ if not path.is_absolute():
527
+ # Resolve relative to the current working directory
528
+ path = Path.cwd() / path
529
+
530
+ with open(path) as f:
531
+ config_data = yaml.safe_load(f)
532
+
533
+ return EngineConfig.model_validate(config_data)
534
+
535
+ @staticmethod
536
+ async def load_and_initialize_agent(
537
+ config_path: str = "config.yaml",
538
+ mcp_registry: Any | None = None,
539
+ ) -> tuple[EngineConfig, BaseAgent]:
540
+ """Load configuration and initialize agent in one step.
541
+
542
+ Args:
543
+ config_path: Path to the configuration YAML file
544
+ mcp_registry: Optional MCP registry client.
545
+
546
+ Returns:
547
+ tuple[EngineConfig, BaseAgent]: Configuration and initialized agent
548
+ """
549
+ engine_config = ConfigBuilder.load_from_file(config_path)
550
+ agent = await ConfigBuilder.initialize_agent_from_config(
551
+ engine_config, mcp_registry=mcp_registry
552
+ )
553
+ return engine_config, agent
554
+
555
+ @staticmethod
556
+ def resolve_config(
557
+ config_path: str | None = None,
558
+ config_dict: dict[str, Any] | None = None,
559
+ engine_config: EngineConfig | None = None,
560
+ ) -> EngineConfig:
561
+ print(config_dict)
562
+ """Umbrella function to resolve configuration from various sources.
563
+
564
+ This function handles all the different ways configuration can be provided
565
+ and returns a validated EngineConfig. It follows a priority order:
566
+ 1. engine_config (pre-validated EngineConfig from ConfigBuilder)
567
+ 2. config_dict (dictionary to be validated)
568
+ 3. config_path (file path to load and validate)
569
+ 4. default "config.yaml" file
570
+
571
+ Args:
572
+ config_path: Path to a YAML configuration file
573
+ config_dict: Dictionary containing configuration
574
+ engine_config: Pre-validated EngineConfig instance
575
+
576
+ Returns:
577
+ EngineConfig: Validated configuration object
578
+
579
+ Raises:
580
+ FileNotFoundError: If config file doesn't exist
581
+ ValidationError: If configuration is invalid
582
+ """
583
+ if engine_config:
584
+ # Use pre-validated EngineConfig (from ConfigBuilder)
585
+ print("✅ Using pre-validated EngineConfig")
586
+ return engine_config
587
+ elif config_dict:
588
+ # Validate dictionary config
589
+ print("✅ Validated dictionary configuration")
590
+ return EngineConfig.model_validate(config_dict)
591
+ elif config_path:
592
+ # Load from file using ConfigB/uilder
593
+ print(f"✅ Loaded configuration from {config_path}")
594
+ return ConfigBuilder.load_from_file(config_path)
595
+ else:
596
+ # Default to loading config.yaml
597
+ print("✅ Loaded default configuration from config.yaml")
598
+ return ConfigBuilder.load_from_file("config.yaml")
599
+
600
+ @classmethod
601
+ def from_dict(cls, config_dict: dict[str, Any]) -> "ConfigBuilder":
602
+ """Create a ConfigBuilder from an existing configuration dictionary.
603
+
604
+ This method validates the input dictionary against the Pydantic models.
605
+
606
+ Args:
607
+ config_dict: Existing configuration dictionary
608
+
609
+ Returns:
610
+ ConfigBuilder: A new builder instance with the provided configuration
611
+
612
+ Raises:
613
+ ValidationError: If the configuration dictionary is invalid
614
+ """
615
+ # Validate the entire config first
616
+ engine_config = EngineConfig.model_validate(config_dict)
617
+
618
+ # Create a new builder
619
+ builder = cls()
620
+ builder._server_config = engine_config.server
621
+ builder._agent_config = engine_config.agent
622
+ builder._guardrails = engine_config.guardrails
623
+ builder._observability = engine_config.observability
624
+ builder._mcp_servers = engine_config.mcp_servers
625
+ return builder
626
+
627
+ @classmethod
628
+ def from_file(cls, config_path: str = "config.yaml") -> "ConfigBuilder":
629
+ """Create a ConfigBuilder from a YAML configuration file.
630
+
631
+ Args:
632
+ config_path: Path to the configuration YAML file
633
+
634
+ Returns:
635
+ ConfigBuilder: A new builder instance with the loaded configuration
636
+ """
637
+ engine_config = cls.load_from_file(config_path)
638
+ return cls.from_engine_config(engine_config)
639
+
640
+ @classmethod
641
+ def from_engine_config(cls, engine_config: EngineConfig) -> "ConfigBuilder":
642
+ """Create a ConfigBuilder from an existing EngineConfig instance.
643
+
644
+ Args:
645
+ engine_config: Existing EngineConfig instance
646
+
647
+ Returns:
648
+ ConfigBuilder: A new builder instance with the provided configuration
649
+ """
650
+ builder = cls()
651
+ builder._server_config = engine_config.server
652
+ builder._agent_config = engine_config.agent
653
+ builder._guardrails = engine_config.guardrails
654
+ builder._observability = engine_config.observability
655
+ builder._mcp_servers = engine_config.mcp_servers
656
+
657
+ return builder
@@ -0,0 +1,21 @@
1
+ """Compatibility re-exports for Engine configuration models."""
2
+
3
+ from idun_agent_schema.engine.agent import ( # noqa: F401
4
+ AgentConfig,
5
+ BaseAgentConfig, # noqa: F401
6
+ )
7
+ from idun_agent_schema.engine.engine import ( # noqa: F401
8
+ EngineConfig,
9
+ )
10
+ from idun_agent_schema.engine.langgraph import ( # noqa: F401
11
+ LangGraphAgentConfig,
12
+ )
13
+ from idun_agent_schema.engine.server import ServerConfig # noqa: F401
14
+
15
+ __all__ = [
16
+ "AgentConfig",
17
+ "EngineConfig",
18
+ "LangGraphAgentConfig",
19
+ "BaseAgentConfig",
20
+ "ServerConfig",
21
+ ]