idun-agent-engine 0.4.1__py3-none-any.whl → 0.4.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.
Files changed (43) hide show
  1. idun_agent_engine/_version.py +1 -1
  2. idun_agent_engine/agent/adk/adk.py +2 -2
  3. idun_agent_engine/agent/haystack/__init__.py +0 -2
  4. idun_agent_engine/agent/haystack/haystack.py +9 -5
  5. idun_agent_engine/agent/langgraph/langgraph.py +10 -13
  6. idun_agent_engine/core/config_builder.py +26 -13
  7. idun_agent_engine/guardrails/guardrails_hub/guardrails_hub.py +52 -9
  8. idun_agent_engine/mcp/__init__.py +2 -2
  9. idun_agent_engine/mcp/helpers.py +11 -15
  10. idun_agent_engine/mcp/registry.py +5 -5
  11. idun_agent_engine/observability/base.py +11 -2
  12. idun_agent_engine/observability/gcp_trace/gcp_trace_handler.py +3 -1
  13. idun_agent_engine/observability/langfuse/langfuse_handler.py +1 -3
  14. idun_agent_engine/server/dependencies.py +7 -2
  15. idun_agent_engine/server/lifespan.py +2 -7
  16. idun_agent_engine/server/routers/agent.py +2 -1
  17. idun_agent_engine/server/routers/base.py +7 -5
  18. idun_agent_engine/telemetry/__init__.py +0 -1
  19. idun_agent_engine/telemetry/config.py +0 -1
  20. idun_agent_engine/telemetry/telemetry.py +3 -4
  21. idun_agent_engine/templates/correction.py +4 -7
  22. idun_agent_engine/templates/deep_research.py +1 -0
  23. idun_agent_engine/templates/translation.py +4 -4
  24. {idun_agent_engine-0.4.1.dist-info → idun_agent_engine-0.4.2.dist-info}/METADATA +1 -1
  25. {idun_agent_engine-0.4.1.dist-info → idun_agent_engine-0.4.2.dist-info}/RECORD +40 -43
  26. idun_platform_cli/groups/agent/package.py +1 -1
  27. idun_platform_cli/telemetry.py +2 -1
  28. idun_platform_cli/tui/schemas/create_agent.py +8 -4
  29. idun_platform_cli/tui/screens/create_agent.py +38 -12
  30. idun_platform_cli/tui/utils/config.py +1 -1
  31. idun_platform_cli/tui/validators/guardrails.py +8 -6
  32. idun_platform_cli/tui/validators/mcps.py +9 -6
  33. idun_platform_cli/tui/widgets/chat_widget.py +3 -1
  34. idun_platform_cli/tui/widgets/guardrails_widget.py +4 -4
  35. idun_platform_cli/tui/widgets/mcps_widget.py +112 -24
  36. idun_platform_cli/tui/widgets/memory_widget.py +0 -1
  37. idun_platform_cli/tui/widgets/observability_widget.py +2 -2
  38. idun_platform_cli/tui/widgets/serve_widget.py +2 -3
  39. idun_agent_engine/agent/haystack/haystack_model.py +0 -13
  40. idun_agent_engine/guardrails/guardrails_hub/utils.py +0 -1
  41. idun_agent_engine/server/routers/agui.py +0 -47
  42. {idun_agent_engine-0.4.1.dist-info → idun_agent_engine-0.4.2.dist-info}/WHEEL +0 -0
  43. {idun_agent_engine-0.4.1.dist-info → idun_agent_engine-0.4.2.dist-info}/entry_points.txt +0 -0
@@ -1,3 +1,3 @@
1
1
  """Version information for Idun Agent Engine."""
2
2
 
3
- __version__ = "0.4.1"
3
+ __version__ = "0.4.2"
@@ -5,6 +5,7 @@ import uuid
5
5
  from collections.abc import AsyncGenerator
6
6
  from typing import Any
7
7
 
8
+ from ag_ui_adk import ADKAgent as ADKAGUIAgent
8
9
  from google.adk.apps.app import App
9
10
  from google.adk.memory import (
10
11
  InMemoryMemoryService,
@@ -25,9 +26,8 @@ from idun_agent_schema.engine.adk import (
25
26
  )
26
27
  from idun_agent_schema.engine.observability_v2 import ObservabilityConfig
27
28
 
28
- from ag_ui_adk import ADKAgent as ADKAGUIAgent
29
- from idun_agent_engine.agent import base as agent_base
30
29
  from idun_agent_engine import observability
30
+ from idun_agent_engine.agent import base as agent_base
31
31
 
32
32
 
33
33
  class AdkAgent(agent_base.BaseAgent):
@@ -1,9 +1,7 @@
1
1
  """LangGraph agent package."""
2
2
 
3
3
  from .haystack import HaystackAgent
4
- from .haystack_model import HaystackAgentConfig
5
4
 
6
5
  __all__ = [
7
6
  "HaystackAgent",
8
- "HaystackAgentConfig",
9
7
  ]
@@ -10,9 +10,10 @@ from haystack import Pipeline
10
10
  from haystack.components.agents import Agent
11
11
  from haystack.dataclasses import ChatMessage
12
12
  from haystack_integrations.components.connectors.langfuse import LangfuseConnector
13
+ from idun_agent_schema.engine.haystack import HaystackAgentConfig
14
+ from idun_agent_schema.engine.observability_v2 import ObservabilityConfig
13
15
 
14
16
  from idun_agent_engine.agent.base import BaseAgent
15
- from idun_agent_schema.engine.haystack import HaystackAgentConfig
16
17
  from idun_agent_engine.agent.haystack.utils import _parse_component_definition
17
18
 
18
19
  logging.basicConfig(
@@ -77,7 +78,9 @@ class HaystackAgent(BaseAgent):
77
78
  Raises:
78
79
  RuntimeError: If the CopilotKit agent is not yet initialized.
79
80
  """
80
- raise NotImplementedError("CopilotKit agent instance not supported yet for Haystack agent.")
81
+ raise NotImplementedError(
82
+ "CopilotKit agent instance not supported yet for Haystack agent."
83
+ )
81
84
 
82
85
  @property
83
86
  def configuration(self) -> HaystackAgentConfig:
@@ -146,9 +149,10 @@ class HaystackAgent(BaseAgent):
146
149
  # TODO: await persistence haystack
147
150
  # TODO OBS block
148
151
 
149
- # check if config has observability `enabled` or `disabled`, so that we adjust our component to
150
- # either add a tracer or not
151
- if self._configuration.observability.enabled:
152
+ if (
153
+ self._configuration.observability
154
+ and self._configuration.observability.enabled
155
+ ): # FIXED: should also check for enabled
152
156
  self._enable_tracing = True
153
157
  logger.info("Enabling tracing...")
154
158
  component: Agent | Pipeline = self._load_component(
@@ -1,7 +1,7 @@
1
1
  """LangGraph agent adapter implementing the BaseAgent protocol."""
2
2
 
3
- import importlib.util
4
3
  import importlib
4
+ import importlib.util
5
5
  import uuid
6
6
  from collections.abc import AsyncGenerator
7
7
  from typing import Any
@@ -9,6 +9,7 @@ from typing import Any
9
9
  import aiosqlite
10
10
  from ag_ui.core import events as ag_events
11
11
  from ag_ui.core import types as ag_types
12
+ from copilotkit import LangGraphAGUIAgent
12
13
  from idun_agent_schema.engine.langgraph import (
13
14
  InMemoryCheckpointConfig,
14
15
  LangGraphAgentConfig,
@@ -24,7 +25,6 @@ from langgraph.graph.state import CompiledStateGraph
24
25
 
25
26
  from idun_agent_engine import observability
26
27
  from idun_agent_engine.agent import base as agent_base
27
- from copilotkit import LangGraphAGUIAgent
28
28
 
29
29
 
30
30
  class LanggraphAgent(agent_base.BaseAgent):
@@ -202,8 +202,11 @@ class LanggraphAgent(agent_base.BaseAgent):
202
202
  self._agent_instance = graph_builder.compile(
203
203
  checkpointer=self._checkpointer, store=self._store
204
204
  )
205
- elif isinstance(graph_builder, CompiledStateGraph): # TODO: to remove, dirty fix for template deepagent langgraph
206
- self._agent_instance = graph_builder
205
+ elif isinstance(graph_builder, CompiledStateGraph):
206
+ # TODO: this was made for supporting langgraph's DeepAgent, modernize.
207
+ raise TypeError(
208
+ "Expected StateGraph, Got CompiledStateGraph. Make sure not to run `compile` on your agent's graph."
209
+ )
207
210
 
208
211
  self._copilotkit_agent_instance = LangGraphAGUIAgent(
209
212
  name=self._name,
@@ -212,12 +215,6 @@ class LanggraphAgent(agent_base.BaseAgent):
212
215
  config={"callbacks": self._obs_callbacks} if self._obs_callbacks else None,
213
216
  )
214
217
 
215
- self._copilotkit_agent_instance = LangGraphAGUIAgent(
216
- name=self._name,
217
- description="Agent description", # TODO: add agent description
218
- graph=self._agent_instance,
219
- )
220
-
221
218
  if self._agent_instance:
222
219
  try:
223
220
  self._input_schema = self._agent_instance.input_schema
@@ -252,9 +249,10 @@ class LanggraphAgent(agent_base.BaseAgent):
252
249
 
253
250
  if self._configuration.checkpointer:
254
251
  if isinstance(self._configuration.checkpointer, SqliteCheckpointConfig):
255
- self._connection = await aiosqlite.connect(
256
- self._configuration.checkpointer.db_path
252
+ db_path = self._configuration.checkpointer.db_url.replace(
253
+ "sqlite:///", ""
257
254
  )
255
+ self._connection = await aiosqlite.connect(db_path)
258
256
  self._checkpointer = AsyncSqliteSaver(conn=self._connection)
259
257
  self._infos["checkpointer"] = (
260
258
  self._configuration.checkpointer.model_dump()
@@ -344,7 +342,6 @@ class LanggraphAgent(agent_base.BaseAgent):
344
342
  def _validate_graph_builder(
345
343
  self, graph_builder: Any, module_path: str, graph_variable_name: str
346
344
  ) -> StateGraph:
347
- # TODO to remove, dirty fix for template deepagent langgraph
348
345
  if not isinstance(graph_builder, StateGraph) and not isinstance(
349
346
  graph_builder, CompiledStateGraph
350
347
  ):
@@ -51,7 +51,6 @@ class ConfigBuilder:
51
51
  """Initialize a new configuration builder with default values."""
52
52
  self._server_config = ServerConfig()
53
53
  self._agent_config: AgentConfig | None = None
54
- # TODO: add mcp_servers config
55
54
  self._mcp_servers: list[MCPServer] | None = None
56
55
  self._observability: list[ObservabilityConfig] | None = None
57
56
  self._guardrails: Guardrails | None = None
@@ -260,7 +259,7 @@ class ConfigBuilder:
260
259
  mcp_servers=self._mcp_servers,
261
260
  )
262
261
 
263
- def build_dict(self) -> dict[str, Any]:
262
+ def build_dict(self) -> dict[str, Any]: # NOT USED
264
263
  """Build and return the configuration as a dictionary.
265
264
 
266
265
  This is a convenience method for backward compatibility.
@@ -269,17 +268,24 @@ class ConfigBuilder:
269
268
  Dict[str, Any]: The complete configuration dictionary
270
269
  """
271
270
  engine_config = self.build()
272
- return engine_config.model_dump()
271
+ return engine_config.model_dump(mode="python")
273
272
 
274
- def save_to_file(self, file_path: str) -> None:
273
+ def save_to_file(
274
+ self, file_path: str
275
+ ) -> None: # FIXED: old method doesn't serialize enums correctly
275
276
  """Save the configuration to a YAML file.
276
277
 
277
278
  Args:
278
279
  file_path: Path where to save the configuration file
279
280
  """
280
- config = self.build_dict()
281
+ engine_model = self.build()
281
282
  with open(file_path, "w") as f:
282
- yaml.dump(config, f, default_flow_style=False, indent=2)
283
+ yaml.dump(
284
+ engine_model.model_dump(mode="json"),
285
+ f,
286
+ default_flow_style=False,
287
+ indent=2,
288
+ )
283
289
 
284
290
  async def build_and_initialize_agent(
285
291
  self, mcp_registry: Any | None = None
@@ -320,9 +326,10 @@ class ConfigBuilder:
320
326
  # Initialize the appropriate agent
321
327
  agent_instance = None
322
328
  if agent_type == AgentFramework.LANGGRAPH:
323
- from idun_agent_engine.agent.langgraph.langgraph import LanggraphAgent
324
329
  import os
325
330
 
331
+ from idun_agent_engine.agent.langgraph.langgraph import LanggraphAgent
332
+
326
333
  print("Current directory: ", os.getcwd()) # TODO remove
327
334
  try:
328
335
  validated_config = LangGraphAgentConfig.model_validate(agent_config_obj)
@@ -335,10 +342,12 @@ class ConfigBuilder:
335
342
  agent_instance = LanggraphAgent()
336
343
 
337
344
  elif agent_type == AgentFramework.TRANSLATION_AGENT:
338
- from idun_agent_engine.agent.langgraph.langgraph import LanggraphAgent
339
- from idun_agent_schema.engine.templates import TranslationAgentConfig
340
345
  import os
341
346
 
347
+ from idun_agent_schema.engine.templates import TranslationAgentConfig
348
+
349
+ from idun_agent_engine.agent.langgraph.langgraph import LanggraphAgent
350
+
342
351
  try:
343
352
  translation_config = TranslationAgentConfig.model_validate(
344
353
  agent_config_obj
@@ -365,10 +374,12 @@ class ConfigBuilder:
365
374
  agent_instance = LanggraphAgent()
366
375
 
367
376
  elif agent_type == AgentFramework.CORRECTION_AGENT:
368
- from idun_agent_engine.agent.langgraph.langgraph import LanggraphAgent
369
- from idun_agent_schema.engine.templates import CorrectionAgentConfig
370
377
  import os
371
378
 
379
+ from idun_agent_schema.engine.templates import CorrectionAgentConfig
380
+
381
+ from idun_agent_engine.agent.langgraph.langgraph import LanggraphAgent
382
+
372
383
  try:
373
384
  correction_config = CorrectionAgentConfig.model_validate(
374
385
  agent_config_obj
@@ -392,10 +403,12 @@ class ConfigBuilder:
392
403
  agent_instance = LanggraphAgent()
393
404
 
394
405
  elif agent_type == AgentFramework.DEEP_RESEARCH_AGENT:
395
- from idun_agent_engine.agent.langgraph.langgraph import LanggraphAgent
396
- from idun_agent_schema.engine.templates import DeepResearchAgentConfig
397
406
  import os
398
407
 
408
+ from idun_agent_schema.engine.templates import DeepResearchAgentConfig
409
+
410
+ from idun_agent_engine.agent.langgraph.langgraph import LanggraphAgent
411
+
399
412
  try:
400
413
  deep_research_config = DeepResearchAgentConfig.model_validate(
401
414
  agent_config_obj
@@ -9,26 +9,51 @@ from ..base import BaseGuardrail
9
9
 
10
10
  def get_guard_instance(name: GuardrailConfigId) -> Guard:
11
11
  """Returns a map of guard type -> guard instance."""
12
- if name.value == "ban_list":
12
+ if name == GuardrailConfigId.BAN_LIST:
13
13
  from guardrails.hub import BanList
14
14
 
15
15
  return BanList
16
16
 
17
- elif name.value == "detect_pii":
17
+ elif name == GuardrailConfigId.DETECT_PII:
18
18
  from guardrails.hub import DetectPII
19
19
 
20
20
  return DetectPII
21
21
 
22
- elif name.value == "nsfw":
22
+ elif name == GuardrailConfigId.NSFW_TEXT:
23
23
  from guardrails.hub import NSFWText
24
24
 
25
25
  return NSFWText
26
26
 
27
- elif name.value == "competitor_check":
27
+ elif name == GuardrailConfigId.COMPETITION_CHECK:
28
28
  from guardrails.hub import CompetitorCheck
29
29
 
30
30
  return CompetitorCheck
31
31
 
32
+ elif name == GuardrailConfigId.BIAS_CHECK:
33
+ from guardrails.hub import BiasCheck
34
+
35
+ return BiasCheck
36
+
37
+ elif name == GuardrailConfigId.CORRECT_LANGUAGE:
38
+ from guardrails.hub import ValidLanguage
39
+
40
+ return ValidLanguage
41
+
42
+ elif name == GuardrailConfigId.GIBBERISH_TEXT:
43
+ from guardrails.hub import GibberishText
44
+
45
+ return GibberishText
46
+
47
+ elif name == GuardrailConfigId.TOXIC_LANGUAGE:
48
+ from guardrails.hub import ToxicLanguage
49
+
50
+ return ToxicLanguage
51
+
52
+ elif name == GuardrailConfigId.RESTRICT_TO_TOPIC:
53
+ from guardrails.hub import RestrictToTopic
54
+
55
+ return RestrictToTopic
56
+
32
57
  else:
33
58
  raise ValueError(f"Guard {name} not found.")
34
59
 
@@ -90,11 +115,29 @@ class GuardrailsHubGuard(BaseGuardrail):
90
115
  f"Guard: {self.guard_id} is not yet supported, or does not exist."
91
116
  )
92
117
 
93
- guard_instance_params = self._guardrail_config.guard_params.model_dump()
94
- guard_instance = guard(**guard_instance_params)
95
- for param, value in guard_instance_params.items():
96
- setattr(guard_instance, param, value)
97
- return guard_instance
118
+ if hasattr(self._guardrail_config, "guard_params"):
119
+ guard_instance_params = self._guardrail_config.guard_params.model_dump()
120
+ else:
121
+ config_dict = self._guardrail_config.model_dump()
122
+ exclude_fields = {"config_id", "api_key", "reject_message", "guard_url"}
123
+ guard_instance_params = {
124
+ k: v for k, v in config_dict.items() if k not in exclude_fields
125
+ }
126
+
127
+ try:
128
+ guard_instance = guard(**guard_instance_params)
129
+ for param, value in guard_instance_params.items():
130
+ setattr(guard_instance, param, value)
131
+ return guard_instance
132
+ except SystemError:
133
+ # sentencepiece mutex lock error when loading models in quick succession
134
+ import time
135
+
136
+ time.sleep(0.5)
137
+ guard_instance = guard(**guard_instance_params)
138
+ for param, value in guard_instance_params.items():
139
+ setattr(guard_instance, param, value)
140
+ return guard_instance
98
141
 
99
142
  def validate(self, input: str) -> bool:
100
143
  """TODO."""
@@ -1,14 +1,14 @@
1
1
  """MCP utilities for Idun Agent Engine."""
2
2
 
3
- from .registry import MCPClientRegistry
4
3
  from .helpers import (
4
+ get_adk_tools,
5
5
  get_adk_tools_from_api,
6
6
  get_adk_tools_from_file,
7
- get_adk_tools,
8
7
  get_langchain_tools,
9
8
  get_langchain_tools_from_api,
10
9
  get_langchain_tools_from_file,
11
10
  )
11
+ from .registry import MCPClientRegistry
12
12
 
13
13
  __all__ = [
14
14
  "MCPClientRegistry",
@@ -1,11 +1,13 @@
1
+ import os
1
2
  from pathlib import Path
2
3
  from typing import Any
3
- import yaml
4
+
4
5
  import requests
5
- import os
6
- from idun_agent_engine.mcp.registry import MCPClientRegistry
6
+ import yaml
7
7
  from idun_agent_schema.engine.mcp_server import MCPServer
8
8
 
9
+ from idun_agent_engine.mcp.registry import MCPClientRegistry
10
+
9
11
 
10
12
  def _extract_mcp_configs(config_data: dict[str, Any]) -> list[MCPServer]:
11
13
  """Parse MCP server configs from a config dictionary."""
@@ -89,8 +91,7 @@ def _fetch_config_from_api() -> dict[str, Any]:
89
91
 
90
92
 
91
93
  def get_adk_tools_from_file(config_path: str | Path) -> list[Any]:
92
- """
93
- Loads MCP configurations from a YAML file and returns a list of ADK toolsets.
94
+ """Loads MCP configurations from a YAML file and returns a list of ADK toolsets.
94
95
 
95
96
  Args:
96
97
  config_path: Path to the configuration YAML file.
@@ -103,8 +104,7 @@ def get_adk_tools_from_file(config_path: str | Path) -> list[Any]:
103
104
 
104
105
 
105
106
  def get_adk_tools_from_api() -> list[Any]:
106
- """
107
- Fetches configuration from the Idun Manager API and returns a list of ADK toolsets.
107
+ """Fetches configuration from the Idun Manager API and returns a list of ADK toolsets.
108
108
 
109
109
  Returns:
110
110
  List of initialized ADK McpToolset instances.
@@ -114,8 +114,7 @@ def get_adk_tools_from_api() -> list[Any]:
114
114
 
115
115
 
116
116
  def get_adk_tools(config_path: str | Path | None = None) -> list[Any]:
117
- """
118
- Returns ADK toolsets using config from file when provided, from IDUN_CONFIG_PATH env var, or from API.
117
+ """Returns ADK toolsets using config from file when provided, from IDUN_CONFIG_PATH env var, or from API.
119
118
 
120
119
  The function resolves configuration in the following order:
121
120
  1. Uses the provided config_path if specified
@@ -144,24 +143,21 @@ def get_adk_tools(config_path: str | Path | None = None) -> list[Any]:
144
143
 
145
144
 
146
145
  async def get_langchain_tools_from_file(config_path: str | Path) -> list[Any]:
147
- """
148
- Loads MCP configurations from a YAML file and returns LangChain tool instances.
146
+ """Loads MCP configurations from a YAML file and returns LangChain tool instances.
149
147
  """
150
148
  config_data = _load_config_from_file(config_path)
151
149
  return await _get_langchain_tools_from_data(config_data)
152
150
 
153
151
 
154
152
  async def get_langchain_tools_from_api() -> list[Any]:
155
- """
156
- Fetches configuration from the Idun Manager API and returns LangChain tool instances.
153
+ """Fetches configuration from the Idun Manager API and returns LangChain tool instances.
157
154
  """
158
155
  config_data = _fetch_config_from_api()
159
156
  return await _get_langchain_tools_from_data(config_data)
160
157
 
161
158
 
162
159
  async def get_langchain_tools(config_path: str | Path | None = None) -> list[Any]:
163
- """
164
- Returns LangChain tool instances using config from file when provided, from IDUN_CONFIG_PATH env var, or from API.
160
+ """Returns LangChain tool instances using config from file when provided, from IDUN_CONFIG_PATH env var, or from API.
165
161
 
166
162
  The function resolves configuration in the following order:
167
163
  1. Uses the provided config_path if specified
@@ -2,7 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from typing import Any, cast, TYPE_CHECKING
5
+ from typing import TYPE_CHECKING, Any, cast
6
6
 
7
7
  from langchain_mcp_adapters.client import MultiServerMCPClient
8
8
  from langchain_mcp_adapters.sessions import Connection
@@ -80,15 +80,15 @@ class MCPClientRegistry:
80
80
  return await self._client.get_tools(server_name=name)
81
81
 
82
82
  async def get_langchain_tools(self, name: str | None = None) -> list[Any]:
83
- """
84
- Alias for get_tools to make intent explicit when using LangChain/LangGraph agents.
85
- """
83
+ """Alias for get_tools to make intent explicit when using LangChain/LangGraph agents."""
86
84
  return await self.get_tools(name=name)
87
85
 
88
86
  def get_adk_toolsets(self) -> list[Any]:
89
87
  """Return a list of Google ADK McpToolset instances for configured servers."""
90
88
  if McpToolset is None or StdioServerParameters is None:
91
- raise ImportError("google-adk and mcp packages are required for ADK toolsets.")
89
+ raise ImportError(
90
+ "google-adk and mcp packages are required for ADK toolsets."
91
+ )
92
92
 
93
93
  toolsets = []
94
94
  for config in self._configs:
@@ -9,9 +9,13 @@ import os
9
9
  from abc import ABC, abstractmethod
10
10
  from typing import Any
11
11
 
12
- from idun_agent_schema.engine.observability import ObservabilityConfig as ObservabilityConfigV1
12
+ from idun_agent_schema.engine.observability import (
13
+ ObservabilityConfig as ObservabilityConfigV1,
14
+ )
13
15
  from idun_agent_schema.engine.observability_v2 import (
14
16
  ObservabilityConfig as ObservabilityConfigV2,
17
+ )
18
+ from idun_agent_schema.engine.observability_v2 import (
15
19
  ObservabilityProvider,
16
20
  )
17
21
 
@@ -49,7 +53,11 @@ def _normalize_config(
49
53
  if not config.enabled:
50
54
  return {"enabled": False}
51
55
 
52
- provider = config.provider.value if hasattr(config.provider, "value") else config.provider
56
+ provider = (
57
+ config.provider.value
58
+ if hasattr(config.provider, "value")
59
+ else config.provider
60
+ )
53
61
  options = config.config.model_dump()
54
62
  return {
55
63
  "provider": provider,
@@ -152,6 +160,7 @@ def create_observability_handler(
152
160
  "error": "Unsupported provider",
153
161
  }
154
162
 
163
+
155
164
  def create_observability_handlers(
156
165
  configs: list[ObservabilityConfigV2 | ObservabilityConfigV1] | None,
157
166
  ) -> tuple[list[ObservabilityHandlerBase], list[dict[str, Any]]]:
@@ -108,7 +108,9 @@ class GCPTraceHandler(ObservabilityHandlerBase):
108
108
  except ImportError:
109
109
  pass
110
110
 
111
- logger.info("GCP Trace initialized for project: %s", project_id or "auto-detected")
111
+ logger.info(
112
+ "GCP Trace initialized for project: %s", project_id or "auto-detected"
113
+ )
112
114
 
113
115
  def get_callbacks(self) -> list[Any]:
114
116
  """Return callbacks."""
@@ -59,9 +59,7 @@ class LangfuseHandler(ObservabilityHandlerBase):
59
59
  # Initialize callback handler
60
60
  # We pass the resolved credentials explicitly to ensure they are used
61
61
  # even if env vars were not successfully set or read.
62
- self._callbacks = [
63
- CallbackHandler()
64
- ]
62
+ self._callbacks = [CallbackHandler()]
65
63
  except Exception as e:
66
64
  print(f"Failed to initialize Langfuse callback/client: {e}")
67
65
  self._callbacks = []
@@ -23,6 +23,7 @@ async def get_agent(request: Request):
23
23
  agent = await ConfigBuilder.initialize_agent_from_config(app_config)
24
24
  return agent
25
25
 
26
+
26
27
  async def get_copilotkit_agent(request: Request):
27
28
  """Return the pre-initialized agent instance from the app state.
28
29
 
@@ -34,7 +35,9 @@ async def get_copilotkit_agent(request: Request):
34
35
  # This is a fallback for cases where the lifespan event did not run,
35
36
  # like in some testing scenarios.
36
37
  # Consider logging a warning here.
37
- print("⚠️ CopilotKit agent not found in app state, initializing fallback agent...")
38
+ print(
39
+ "⚠️ CopilotKit agent not found in app state, initializing fallback agent..."
40
+ )
38
41
 
39
42
  app_config = ConfigBuilder.load_from_file()
40
43
  copilotkit_agent = await ConfigBuilder.initialize_agent_from_config(app_config)
@@ -43,7 +46,9 @@ async def get_copilotkit_agent(request: Request):
43
46
 
44
47
  def get_mcp_registry(request: Request) -> MCPClientRegistry:
45
48
  """Return the configured MCP registry if available."""
46
- registry: MCPClientRegistry | None = getattr(request.app.state, "mcp_registry", None)
49
+ registry: MCPClientRegistry | None = getattr(
50
+ request.app.state, "mcp_registry", None
51
+ )
47
52
  if registry is None or not registry.enabled:
48
53
  raise HTTPException(
49
54
  status_code=status.HTTP_404_NOT_FOUND,
@@ -8,19 +8,15 @@ from collections.abc import Sequence
8
8
  from contextlib import asynccontextmanager
9
9
 
10
10
  from fastapi import FastAPI
11
+ from idun_agent_schema.engine.guardrails import Guardrails
11
12
 
12
13
  from ..core.config_builder import ConfigBuilder
13
- from ..mcp import MCPClientRegistry
14
-
15
- from idun_agent_schema.engine.guardrails import Guardrails, Guardrail
16
-
17
14
  from ..guardrails.base import BaseGuardrail
18
15
  from ..telemetry import get_telemetry, sanitize_telemetry_config
19
16
 
20
17
 
21
18
  def _parse_guardrails(guardrails_obj: Guardrails) -> Sequence[BaseGuardrail]:
22
19
  """Adds the position of the guardrails (input/output) and returns the lift of updated guardrails."""
23
-
24
20
  from ..guardrails.guardrails_hub.guardrails_hub import GuardrailsHubGuard as GHGuard
25
21
 
26
22
  if not guardrails_obj:
@@ -70,8 +66,8 @@ async def configure_app(app: FastAPI, engine_config):
70
66
  print(f"✅ Agent '{agent_name}' initialized and ready to serve!")
71
67
 
72
68
  # Setup AGUI routes if the agent is a LangGraph agent
73
- from ..agent.langgraph.langgraph import LanggraphAgent
74
69
  from ..agent.adk.adk import AdkAgent
70
+ from ..agent.langgraph.langgraph import LanggraphAgent
75
71
  # from ..server.routers.agui import setup_agui_router
76
72
 
77
73
  if isinstance(agent_instance, (LanggraphAgent, AdkAgent)):
@@ -91,7 +87,6 @@ async def configure_app(app: FastAPI, engine_config):
91
87
  @asynccontextmanager
92
88
  async def lifespan(app: FastAPI):
93
89
  """FastAPI lifespan context to initialize and teardown the agent."""
94
-
95
90
  # Load config and initialize agent on startup
96
91
  print("Server starting up...")
97
92
  if not app.state.engine_config:
@@ -84,6 +84,8 @@ async def invoke(
84
84
  )
85
85
  return ChatResponse(session_id=message["session_id"], response=response_content)
86
86
 
87
+ except HTTPException:
88
+ raise
87
89
  except Exception as e: # noqa: BLE001
88
90
  raise HTTPException(status_code=500, detail=str(e)) from e
89
91
 
@@ -142,7 +144,6 @@ async def copilotkit_stream(
142
144
  try:
143
145
  # Get the accept header from the request
144
146
  accept_header = request.headers.get("accept")
145
- agent_id = request.url.path.lstrip("/")
146
147
 
147
148
  # Create an event encoder to properly format SSE events
148
149
  encoder = EventEncoder(accept=accept_header or "")
@@ -1,8 +1,8 @@
1
1
  """Base routes for service health and landing info."""
2
2
 
3
3
  import os
4
- from typing import Optional
5
- from fastapi import APIRouter, Request, HTTPException
4
+
5
+ from fastapi import APIRouter, HTTPException, Request
6
6
  from pydantic import BaseModel
7
7
 
8
8
  from ..._version import __version__
@@ -15,7 +15,7 @@ base_router = APIRouter()
15
15
  class ReloadRequest(BaseModel):
16
16
  """Request body for reload endpoint."""
17
17
 
18
- path: Optional[str] = None
18
+ path: str | None = None
19
19
 
20
20
 
21
21
  @base_router.get("/health")
@@ -25,9 +25,8 @@ def health_check():
25
25
 
26
26
 
27
27
  @base_router.post("/reload")
28
- async def reload_config(request: Request, body: Optional[ReloadRequest] = None):
28
+ async def reload_config(request: Request, body: ReloadRequest | None = None):
29
29
  """Reload the agent configuration from the manager or a file."""
30
-
31
30
  try:
32
31
  if body and body.path:
33
32
  print(f"🔄 Reloading configuration from file: {body.path}...")
@@ -60,6 +59,9 @@ async def reload_config(request: Request, body: Optional[ReloadRequest] = None):
60
59
  "message": "Agent configuration reloaded successfully",
61
60
  }
62
61
 
62
+ except HTTPException:
63
+ raise
64
+
63
65
  except Exception as e:
64
66
  print(f"❌ Error reloading configuration: {e}")
65
67
  raise HTTPException(
@@ -9,7 +9,6 @@ _telemetry_singleton: IdunTelemetry | None = None
9
9
 
10
10
  def get_telemetry() -> IdunTelemetry:
11
11
  """Return the process-wide telemetry singleton."""
12
-
13
12
  global _telemetry_singleton
14
13
  if _telemetry_singleton is None:
15
14
  _telemetry_singleton = IdunTelemetry()
@@ -13,7 +13,6 @@ def telemetry_enabled(environ: dict[str, str] | None = None) -> bool:
13
13
  Telemetry is ON by default. Users can disable it by setting the environment
14
14
  variable `IDUN_TELEMETRY_ENABLED` to a falsy value (e.g. "false", "0", "no").
15
15
  """
16
-
17
16
  env = os.environ if environ is None else environ
18
17
  raw = env.get(IDUN_TELEMETRY_ENABLED_ENV)
19
18
  if raw is None: