idun-agent-engine 0.3.9__py3-none-any.whl → 0.4.1__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.
- idun_agent_engine/_version.py +1 -1
- idun_agent_engine/agent/adk/adk.py +5 -2
- idun_agent_engine/agent/langgraph/langgraph.py +1 -1
- idun_agent_engine/core/app_factory.py +1 -1
- idun_agent_engine/core/config_builder.py +11 -5
- idun_agent_engine/guardrails/guardrails_hub/__init__.py +2 -2
- idun_agent_engine/mcp/__init__.py +18 -2
- idun_agent_engine/mcp/helpers.py +135 -43
- idun_agent_engine/mcp/registry.py +7 -1
- idun_agent_engine/server/lifespan.py +22 -0
- idun_agent_engine/telemetry/__init__.py +19 -0
- idun_agent_engine/telemetry/config.py +29 -0
- idun_agent_engine/telemetry/telemetry.py +248 -0
- {idun_agent_engine-0.3.9.dist-info → idun_agent_engine-0.4.1.dist-info}/METADATA +12 -8
- {idun_agent_engine-0.3.9.dist-info → idun_agent_engine-0.4.1.dist-info}/RECORD +45 -17
- idun_platform_cli/groups/agent/package.py +3 -0
- idun_platform_cli/groups/agent/serve.py +2 -0
- idun_platform_cli/groups/init.py +25 -0
- idun_platform_cli/main.py +3 -0
- idun_platform_cli/telemetry.py +54 -0
- idun_platform_cli/tui/__init__.py +0 -0
- idun_platform_cli/tui/css/__init__.py +0 -0
- idun_platform_cli/tui/css/create_agent.py +912 -0
- idun_platform_cli/tui/css/main.py +89 -0
- idun_platform_cli/tui/main.py +87 -0
- idun_platform_cli/tui/schemas/__init__.py +0 -0
- idun_platform_cli/tui/schemas/create_agent.py +60 -0
- idun_platform_cli/tui/screens/__init__.py +0 -0
- idun_platform_cli/tui/screens/create_agent.py +622 -0
- idun_platform_cli/tui/utils/__init__.py +0 -0
- idun_platform_cli/tui/utils/config.py +182 -0
- idun_platform_cli/tui/validators/__init__.py +0 -0
- idun_platform_cli/tui/validators/guardrails.py +88 -0
- idun_platform_cli/tui/validators/mcps.py +84 -0
- idun_platform_cli/tui/validators/observability.py +65 -0
- idun_platform_cli/tui/widgets/__init__.py +19 -0
- idun_platform_cli/tui/widgets/chat_widget.py +153 -0
- idun_platform_cli/tui/widgets/guardrails_widget.py +356 -0
- idun_platform_cli/tui/widgets/identity_widget.py +252 -0
- idun_platform_cli/tui/widgets/mcps_widget.py +230 -0
- idun_platform_cli/tui/widgets/memory_widget.py +195 -0
- idun_platform_cli/tui/widgets/observability_widget.py +382 -0
- idun_platform_cli/tui/widgets/serve_widget.py +82 -0
- {idun_agent_engine-0.3.9.dist-info → idun_agent_engine-0.4.1.dist-info}/WHEEL +0 -0
- {idun_agent_engine-0.3.9.dist-info → idun_agent_engine-0.4.1.dist-info}/entry_points.txt +0 -0
idun_agent_engine/_version.py
CHANGED
|
@@ -123,7 +123,7 @@ class AdkAgent(agent_base.BaseAgent):
|
|
|
123
123
|
# Observability (provider-agnostic)
|
|
124
124
|
if observability_config:
|
|
125
125
|
handlers, infos = observability.create_observability_handlers(
|
|
126
|
-
observability_config
|
|
126
|
+
observability_config # type: ignore[arg-type]
|
|
127
127
|
)
|
|
128
128
|
self._obs_callbacks = []
|
|
129
129
|
for handler in handlers:
|
|
@@ -153,6 +153,7 @@ class AdkAgent(agent_base.BaseAgent):
|
|
|
153
153
|
|
|
154
154
|
if is_langfuse_enabled:
|
|
155
155
|
import os
|
|
156
|
+
|
|
156
157
|
langfuse_pk = os.environ.get("LANGFUSE_PUBLIC_KEY")
|
|
157
158
|
langfuse_host = os.environ.get("LANGFUSE_BASE_URL")
|
|
158
159
|
print(f"LANGFUSE_PUBLIC_KEY: {langfuse_pk}")
|
|
@@ -171,7 +172,9 @@ class AdkAgent(agent_base.BaseAgent):
|
|
|
171
172
|
except Exception as e:
|
|
172
173
|
print(f"Failed to instrument Google ADK: {e}")
|
|
173
174
|
except Exception as e:
|
|
174
|
-
print(
|
|
175
|
+
print(
|
|
176
|
+
f"Error checking observability config for ADK instrumentation: {e}"
|
|
177
|
+
)
|
|
175
178
|
|
|
176
179
|
# Initialize Session Service
|
|
177
180
|
await self._initialize_session_service()
|
|
@@ -202,7 +202,7 @@ 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):
|
|
205
|
+
elif isinstance(graph_builder, CompiledStateGraph): # TODO: to remove, dirty fix for template deepagent langgraph
|
|
206
206
|
self._agent_instance = graph_builder
|
|
207
207
|
|
|
208
208
|
self._copilotkit_agent_instance = LangGraphAGUIAgent(
|
|
@@ -10,12 +10,12 @@ from typing import Any
|
|
|
10
10
|
from fastapi import FastAPI
|
|
11
11
|
from fastapi.middleware.cors import CORSMiddleware
|
|
12
12
|
|
|
13
|
+
from .._version import __version__
|
|
13
14
|
from ..server.lifespan import lifespan
|
|
14
15
|
from ..server.routers.agent import agent_router
|
|
15
16
|
from ..server.routers.base import base_router
|
|
16
17
|
from .config_builder import ConfigBuilder
|
|
17
18
|
from .engine_config import EngineConfig
|
|
18
|
-
from .._version import __version__
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
def create_app(
|
|
@@ -8,24 +8,24 @@ import os
|
|
|
8
8
|
from pathlib import Path
|
|
9
9
|
from typing import Any
|
|
10
10
|
|
|
11
|
-
from idun_agent_schema.engine.guardrails import Guardrails as GuardrailsV1
|
|
12
11
|
import yaml
|
|
12
|
+
from idun_agent_schema.engine.adk import AdkAgentConfig
|
|
13
13
|
from idun_agent_schema.engine.agent_framework import AgentFramework
|
|
14
|
+
from idun_agent_schema.engine.guardrails_v2 import GuardrailsV2 as Guardrails
|
|
14
15
|
from idun_agent_schema.engine.haystack import HaystackAgentConfig
|
|
15
16
|
from idun_agent_schema.engine.langgraph import (
|
|
16
17
|
LangGraphAgentConfig,
|
|
17
18
|
SqliteCheckpointConfig,
|
|
18
19
|
)
|
|
19
|
-
from idun_agent_schema.engine.adk import AdkAgentConfig
|
|
20
20
|
from idun_agent_schema.engine.mcp_server import MCPServer
|
|
21
21
|
from idun_agent_schema.engine.observability_v2 import ObservabilityConfig
|
|
22
|
-
from idun_agent_schema.
|
|
23
|
-
from idun_agent_engine.server.server_config import ServerAPIConfig
|
|
22
|
+
from idun_agent_schema.manager.guardrail_configs import convert_guardrail
|
|
24
23
|
from yaml import YAMLError
|
|
25
24
|
|
|
25
|
+
from idun_agent_engine.server.server_config import ServerAPIConfig
|
|
26
|
+
|
|
26
27
|
from ..agent.base import BaseAgent
|
|
27
28
|
from .engine_config import AgentConfig, EngineConfig, ServerConfig
|
|
28
|
-
from idun_agent_schema.manager.guardrail_configs import convert_guardrail
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
class ConfigBuilder:
|
|
@@ -518,6 +518,9 @@ class ConfigBuilder:
|
|
|
518
518
|
def load_from_file(config_path: str = "config.yaml") -> EngineConfig:
|
|
519
519
|
"""Load configuration from a YAML file and return a validated EngineConfig.
|
|
520
520
|
|
|
521
|
+
Sets IDUN_CONFIG_PATH environment variable to enable MCP helper functions
|
|
522
|
+
(get_adk_tools, get_langchain_tools) to automatically discover the config file.
|
|
523
|
+
|
|
521
524
|
Args:
|
|
522
525
|
config_path: Path to the configuration YAML file
|
|
523
526
|
|
|
@@ -533,6 +536,9 @@ class ConfigBuilder:
|
|
|
533
536
|
# Resolve relative to the current working directory
|
|
534
537
|
path = Path.cwd() / path
|
|
535
538
|
|
|
539
|
+
# Set IDUN_CONFIG_PATH for MCP helpers to discover
|
|
540
|
+
os.environ["IDUN_CONFIG_PATH"] = str(path)
|
|
541
|
+
|
|
536
542
|
with open(path) as f:
|
|
537
543
|
config_data = yaml.safe_load(f)
|
|
538
544
|
|
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
"""MCP utilities for Idun Agent Engine."""
|
|
2
2
|
|
|
3
3
|
from .registry import MCPClientRegistry
|
|
4
|
-
from .helpers import
|
|
5
|
-
|
|
4
|
+
from .helpers import (
|
|
5
|
+
get_adk_tools_from_api,
|
|
6
|
+
get_adk_tools_from_file,
|
|
7
|
+
get_adk_tools,
|
|
8
|
+
get_langchain_tools,
|
|
9
|
+
get_langchain_tools_from_api,
|
|
10
|
+
get_langchain_tools_from_file,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"MCPClientRegistry",
|
|
15
|
+
"get_adk_tools_from_api",
|
|
16
|
+
"get_adk_tools_from_file",
|
|
17
|
+
"get_adk_tools",
|
|
18
|
+
"get_langchain_tools",
|
|
19
|
+
"get_langchain_tools_from_api",
|
|
20
|
+
"get_langchain_tools_from_file",
|
|
21
|
+
]
|
idun_agent_engine/mcp/helpers.py
CHANGED
|
@@ -6,36 +6,53 @@ import os
|
|
|
6
6
|
from idun_agent_engine.mcp.registry import MCPClientRegistry
|
|
7
7
|
from idun_agent_schema.engine.mcp_server import MCPServer
|
|
8
8
|
|
|
9
|
-
def _get_toolsets_from_data(config_data: dict[str, Any]) -> list[Any]:
|
|
10
|
-
"""Internal helper to extract toolsets from config dictionary."""
|
|
11
|
-
# Handle both snake_case and camelCase for mcp_servers
|
|
12
|
-
# Note: logic in ConfigBuilder suggests looking inside 'engine_config' if present,
|
|
13
|
-
# but this helper expects the dictionary containing 'mcp_servers' directly
|
|
14
|
-
# or performs the search itself.
|
|
15
9
|
|
|
10
|
+
def _extract_mcp_configs(config_data: dict[str, Any]) -> list[MCPServer]:
|
|
11
|
+
"""Parse MCP server configs from a config dictionary."""
|
|
16
12
|
mcp_configs_data = config_data.get("mcp_servers") or config_data.get("mcpServers")
|
|
17
|
-
|
|
18
13
|
if not mcp_configs_data:
|
|
19
14
|
return []
|
|
15
|
+
return [MCPServer.model_validate(c) for c in mcp_configs_data]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _unwrap_engine_config(config_data: dict[str, Any]) -> dict[str, Any]:
|
|
19
|
+
"""Return engine-level config if wrapped under engine_config."""
|
|
20
|
+
if not isinstance(config_data, dict):
|
|
21
|
+
raise ValueError("Configuration payload is empty or invalid")
|
|
22
|
+
if "engine_config" in config_data:
|
|
23
|
+
return config_data["engine_config"]
|
|
24
|
+
return config_data
|
|
25
|
+
|
|
20
26
|
|
|
21
|
-
|
|
22
|
-
registry
|
|
27
|
+
def _build_registry(config_data: dict[str, Any]) -> MCPClientRegistry | None:
|
|
28
|
+
"""Instantiate an MCP client registry from config data."""
|
|
29
|
+
mcp_configs = _extract_mcp_configs(config_data)
|
|
30
|
+
if not mcp_configs:
|
|
31
|
+
return None
|
|
32
|
+
return MCPClientRegistry(mcp_configs)
|
|
23
33
|
|
|
34
|
+
|
|
35
|
+
def _get_toolsets_from_data(config_data: dict[str, Any]) -> list[Any]:
|
|
36
|
+
"""Internal helper to extract ADK toolsets from config dictionary."""
|
|
37
|
+
registry = _build_registry(config_data)
|
|
38
|
+
if not registry:
|
|
39
|
+
return []
|
|
24
40
|
try:
|
|
25
41
|
return registry.get_adk_toolsets()
|
|
26
42
|
except ImportError:
|
|
27
43
|
raise
|
|
28
44
|
|
|
29
|
-
def get_adk_tools_from_file(config_path: str | Path) -> list[Any]:
|
|
30
|
-
"""
|
|
31
|
-
Loads MCP configurations from a YAML file and returns a list of ADK toolsets.
|
|
32
45
|
|
|
33
|
-
|
|
34
|
-
|
|
46
|
+
async def _get_langchain_tools_from_data(config_data: dict[str, Any]) -> list[Any]:
|
|
47
|
+
"""Internal helper to extract LangChain tools from config dictionary."""
|
|
48
|
+
registry = _build_registry(config_data)
|
|
49
|
+
if not registry:
|
|
50
|
+
return []
|
|
51
|
+
return await registry.get_tools()
|
|
35
52
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
"""
|
|
53
|
+
|
|
54
|
+
def _load_config_from_file(config_path: str | Path) -> dict[str, Any]:
|
|
55
|
+
"""Load YAML config (optionally wrapped in engine_config) from disk."""
|
|
39
56
|
path = Path(config_path)
|
|
40
57
|
if not path.exists():
|
|
41
58
|
raise FileNotFoundError(f"Configuration file not found at {path}")
|
|
@@ -43,55 +60,130 @@ def get_adk_tools_from_file(config_path: str | Path) -> list[Any]:
|
|
|
43
60
|
with open(path) as f:
|
|
44
61
|
config_data = yaml.safe_load(f)
|
|
45
62
|
|
|
46
|
-
|
|
47
|
-
if "engine_config" in config_data:
|
|
48
|
-
config_data = config_data["engine_config"]
|
|
49
|
-
|
|
50
|
-
return _get_toolsets_from_data(config_data)
|
|
51
|
-
|
|
52
|
-
def get_adk_tools_from_api() -> list[Any]:
|
|
53
|
-
"""
|
|
54
|
-
Fetches configuration from the Idun Manager API and returns a list of ADK toolsets.
|
|
63
|
+
return _unwrap_engine_config(config_data)
|
|
55
64
|
|
|
56
|
-
Args:
|
|
57
|
-
agent_api_key: The API key for authentication.
|
|
58
|
-
manager_url: The base URL of the Idun Manager (e.g. http://localhost:8000).
|
|
59
65
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
"""
|
|
66
|
+
def _fetch_config_from_api() -> dict[str, Any]:
|
|
67
|
+
"""Fetch configuration from the Idun Manager API."""
|
|
63
68
|
api_key = os.environ.get("IDUN_AGENT_API_KEY")
|
|
64
69
|
manager_host = os.environ.get("IDUN_MANAGER_HOST")
|
|
70
|
+
|
|
71
|
+
if not api_key:
|
|
72
|
+
raise ValueError("Environment variable 'IDUN_AGENT_API_KEY' is not set")
|
|
73
|
+
|
|
74
|
+
if not manager_host:
|
|
75
|
+
raise ValueError("Environment variable 'IDUN_MANAGER_HOST' is not set")
|
|
76
|
+
|
|
65
77
|
headers = {"auth": f"Bearer {api_key}"}
|
|
66
78
|
url = f"{manager_host.rstrip('/')}/api/v1/agents/config"
|
|
67
79
|
|
|
68
80
|
try:
|
|
69
81
|
response = requests.get(url=url, headers=headers)
|
|
70
82
|
response.raise_for_status()
|
|
71
|
-
|
|
72
83
|
config_data = yaml.safe_load(response.text)
|
|
73
|
-
|
|
74
|
-
# Config from API is typically wrapped in engine_config
|
|
75
|
-
if "engine_config" in config_data:
|
|
76
|
-
config_data = config_data["engine_config"]
|
|
77
|
-
|
|
78
|
-
return _get_toolsets_from_data(config_data)
|
|
79
|
-
|
|
84
|
+
return _unwrap_engine_config(config_data)
|
|
80
85
|
except requests.RequestException as e:
|
|
81
86
|
raise ValueError(f"Failed to fetch config from API: {e}") from e
|
|
82
87
|
except yaml.YAMLError as e:
|
|
83
88
|
raise ValueError(f"Failed to parse config YAML: {e}") from e
|
|
84
89
|
|
|
85
90
|
|
|
86
|
-
def
|
|
91
|
+
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
|
+
|
|
95
|
+
Args:
|
|
96
|
+
config_path: Path to the configuration YAML file.
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
List of initialized ADK McpToolset instances.
|
|
100
|
+
"""
|
|
101
|
+
config_data = _load_config_from_file(config_path)
|
|
102
|
+
return _get_toolsets_from_data(config_data)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def get_adk_tools_from_api() -> list[Any]:
|
|
87
106
|
"""
|
|
88
107
|
Fetches configuration from the Idun Manager API and returns a list of ADK toolsets.
|
|
89
108
|
|
|
109
|
+
Returns:
|
|
110
|
+
List of initialized ADK McpToolset instances.
|
|
111
|
+
"""
|
|
112
|
+
config_data = _fetch_config_from_api()
|
|
113
|
+
return _get_toolsets_from_data(config_data)
|
|
114
|
+
|
|
115
|
+
|
|
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.
|
|
119
|
+
|
|
120
|
+
The function resolves configuration in the following order:
|
|
121
|
+
1. Uses the provided config_path if specified
|
|
122
|
+
2. Uses IDUN_CONFIG_PATH environment variable if set
|
|
123
|
+
3. Falls back to fetching from Idun Manager API
|
|
124
|
+
|
|
90
125
|
Args:
|
|
91
|
-
|
|
92
|
-
manager_url: The base URL of the Idun Manager (e.g. http://localhost:8000).
|
|
126
|
+
config_path: Optional path to configuration YAML file. If provided, takes precedence.
|
|
93
127
|
|
|
94
128
|
Returns:
|
|
95
129
|
List of initialized ADK McpToolset instances.
|
|
130
|
+
|
|
131
|
+
Raises:
|
|
132
|
+
ValueError: If no config source is available or API credentials are missing.
|
|
133
|
+
FileNotFoundError: If specified config file doesn't exist.
|
|
96
134
|
"""
|
|
135
|
+
if config_path:
|
|
136
|
+
return get_adk_tools_from_file(config_path)
|
|
137
|
+
|
|
138
|
+
# Check for IDUN_CONFIG_PATH environment variable
|
|
139
|
+
env_config_path = os.environ.get("IDUN_CONFIG_PATH")
|
|
140
|
+
if env_config_path:
|
|
141
|
+
return get_adk_tools_from_file(env_config_path)
|
|
142
|
+
|
|
97
143
|
return get_adk_tools_from_api()
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
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.
|
|
149
|
+
"""
|
|
150
|
+
config_data = _load_config_from_file(config_path)
|
|
151
|
+
return await _get_langchain_tools_from_data(config_data)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
async def get_langchain_tools_from_api() -> list[Any]:
|
|
155
|
+
"""
|
|
156
|
+
Fetches configuration from the Idun Manager API and returns LangChain tool instances.
|
|
157
|
+
"""
|
|
158
|
+
config_data = _fetch_config_from_api()
|
|
159
|
+
return await _get_langchain_tools_from_data(config_data)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
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.
|
|
165
|
+
|
|
166
|
+
The function resolves configuration in the following order:
|
|
167
|
+
1. Uses the provided config_path if specified
|
|
168
|
+
2. Uses IDUN_CONFIG_PATH environment variable if set
|
|
169
|
+
3. Falls back to fetching from Idun Manager API
|
|
170
|
+
|
|
171
|
+
Args:
|
|
172
|
+
config_path: Optional path to configuration YAML file. If provided, takes precedence.
|
|
173
|
+
|
|
174
|
+
Returns:
|
|
175
|
+
List of initialized LangChain tool instances.
|
|
176
|
+
|
|
177
|
+
Raises:
|
|
178
|
+
ValueError: If no config source is available or API credentials are missing.
|
|
179
|
+
FileNotFoundError: If specified config file doesn't exist.
|
|
180
|
+
"""
|
|
181
|
+
if config_path:
|
|
182
|
+
return await get_langchain_tools_from_file(config_path)
|
|
183
|
+
|
|
184
|
+
# Check for IDUN_CONFIG_PATH environment variable
|
|
185
|
+
env_config_path = os.environ.get("IDUN_CONFIG_PATH")
|
|
186
|
+
if env_config_path:
|
|
187
|
+
return await get_langchain_tools_from_file(env_config_path)
|
|
188
|
+
|
|
189
|
+
return await get_langchain_tools_from_api()
|
|
@@ -79,7 +79,13 @@ class MCPClientRegistry:
|
|
|
79
79
|
raise RuntimeError("MCP client registry is not enabled.")
|
|
80
80
|
return await self._client.get_tools(server_name=name)
|
|
81
81
|
|
|
82
|
-
def
|
|
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
|
+
"""
|
|
86
|
+
return await self.get_tools(name=name)
|
|
87
|
+
|
|
88
|
+
def get_adk_toolsets(self) -> list[Any]:
|
|
83
89
|
"""Return a list of Google ADK McpToolset instances for configured servers."""
|
|
84
90
|
if McpToolset is None or StdioServerParameters is None:
|
|
85
91
|
raise ImportError("google-adk and mcp packages are required for ADK toolsets.")
|
|
@@ -15,6 +15,7 @@ from ..mcp import MCPClientRegistry
|
|
|
15
15
|
from idun_agent_schema.engine.guardrails import Guardrails, Guardrail
|
|
16
16
|
|
|
17
17
|
from ..guardrails.base import BaseGuardrail
|
|
18
|
+
from ..telemetry import get_telemetry, sanitize_telemetry_config
|
|
18
19
|
|
|
19
20
|
|
|
20
21
|
def _parse_guardrails(guardrails_obj: Guardrails) -> Sequence[BaseGuardrail]:
|
|
@@ -98,9 +99,30 @@ async def lifespan(app: FastAPI):
|
|
|
98
99
|
|
|
99
100
|
await configure_app(app, app.state.engine_config)
|
|
100
101
|
|
|
102
|
+
try:
|
|
103
|
+
telemetry = get_telemetry()
|
|
104
|
+
app.state.telemetry = telemetry
|
|
105
|
+
agent = getattr(app.state, "agent", None)
|
|
106
|
+
telemetry.capture(
|
|
107
|
+
"engine started",
|
|
108
|
+
properties={
|
|
109
|
+
"agent_type": type(agent).__name__ if agent is not None else None,
|
|
110
|
+
"has_agent": agent is not None,
|
|
111
|
+
"engine_config": sanitize_telemetry_config(app.state.engine_config),
|
|
112
|
+
},
|
|
113
|
+
)
|
|
114
|
+
except Exception as e:
|
|
115
|
+
print(f"⚠️ Warning: Failed to start telemetry: {e}")
|
|
116
|
+
app.state.telemetry = None
|
|
117
|
+
|
|
101
118
|
yield
|
|
102
119
|
|
|
103
120
|
# Clean up on shutdown
|
|
104
121
|
print("🔄 Idun Agent Engine shutting down...")
|
|
122
|
+
telemetry = getattr(app.state, "telemetry", None)
|
|
123
|
+
if telemetry is not None:
|
|
124
|
+
telemetry.capture("engine stopped")
|
|
105
125
|
await cleanup_agent(app)
|
|
126
|
+
if telemetry is not None:
|
|
127
|
+
telemetry.shutdown()
|
|
106
128
|
print("✅ Agent resources cleaned up successfully.")
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Telemetry package for Idun Agent Engine."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from .telemetry import IdunTelemetry, sanitize_telemetry_config
|
|
6
|
+
|
|
7
|
+
_telemetry_singleton: IdunTelemetry | None = None
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def get_telemetry() -> IdunTelemetry:
|
|
11
|
+
"""Return the process-wide telemetry singleton."""
|
|
12
|
+
|
|
13
|
+
global _telemetry_singleton
|
|
14
|
+
if _telemetry_singleton is None:
|
|
15
|
+
_telemetry_singleton = IdunTelemetry()
|
|
16
|
+
return _telemetry_singleton
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
__all__ = ["IdunTelemetry", "get_telemetry", "sanitize_telemetry_config"]
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""Telemetry configuration utilities."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
|
|
7
|
+
IDUN_TELEMETRY_ENABLED_ENV = "IDUN_TELEMETRY_ENABLED"
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def telemetry_enabled(environ: dict[str, str] | None = None) -> bool:
|
|
11
|
+
"""Return whether telemetry is enabled.
|
|
12
|
+
|
|
13
|
+
Telemetry is ON by default. Users can disable it by setting the environment
|
|
14
|
+
variable `IDUN_TELEMETRY_ENABLED` to a falsy value (e.g. "false", "0", "no").
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
env = os.environ if environ is None else environ
|
|
18
|
+
raw = env.get(IDUN_TELEMETRY_ENABLED_ENV)
|
|
19
|
+
if raw is None:
|
|
20
|
+
return True
|
|
21
|
+
|
|
22
|
+
value = raw.strip().lower()
|
|
23
|
+
if value in {"0", "false", "no", "off", "disable", "disabled"}:
|
|
24
|
+
return False
|
|
25
|
+
if value in {"1", "true", "yes", "on", "enable", "enabled"}:
|
|
26
|
+
return True
|
|
27
|
+
|
|
28
|
+
# Unknown values default to enabled (opt-out).
|
|
29
|
+
return True
|