idun-agent-engine 0.2.7__tar.gz → 0.3.0__tar.gz

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 (64) hide show
  1. {idun_agent_engine-0.2.7 → idun_agent_engine-0.3.0}/.gitignore +1 -0
  2. {idun_agent_engine-0.2.7 → idun_agent_engine-0.3.0}/PKG-INFO +62 -10
  3. {idun_agent_engine-0.2.7 → idun_agent_engine-0.3.0}/README.md +38 -0
  4. {idun_agent_engine-0.2.7 → idun_agent_engine-0.3.0}/pyproject.toml +28 -10
  5. {idun_agent_engine-0.2.7 → idun_agent_engine-0.3.0}/src/idun_agent_engine/_version.py +1 -1
  6. idun_agent_engine-0.3.0/src/idun_agent_engine/agent/adk/__init__.py +5 -0
  7. idun_agent_engine-0.3.0/src/idun_agent_engine/agent/adk/adk.py +296 -0
  8. {idun_agent_engine-0.2.7 → idun_agent_engine-0.3.0}/src/idun_agent_engine/agent/base.py +7 -1
  9. {idun_agent_engine-0.2.7 → idun_agent_engine-0.3.0}/src/idun_agent_engine/agent/haystack/haystack.py +5 -1
  10. {idun_agent_engine-0.2.7 → idun_agent_engine-0.3.0}/src/idun_agent_engine/agent/langgraph/langgraph.py +146 -55
  11. {idun_agent_engine-0.2.7 → idun_agent_engine-0.3.0}/src/idun_agent_engine/core/app_factory.py +9 -0
  12. {idun_agent_engine-0.2.7 → idun_agent_engine-0.3.0}/src/idun_agent_engine/core/config_builder.py +214 -23
  13. {idun_agent_engine-0.2.7 → idun_agent_engine-0.3.0}/src/idun_agent_engine/core/engine_config.py +1 -2
  14. {idun_agent_engine-0.2.7 → idun_agent_engine-0.3.0}/src/idun_agent_engine/core/server_runner.py +2 -3
  15. idun_agent_engine-0.3.0/src/idun_agent_engine/guardrails/base.py +24 -0
  16. idun_agent_engine-0.3.0/src/idun_agent_engine/guardrails/guardrails_hub/guardrails_hub.py +101 -0
  17. idun_agent_engine-0.3.0/src/idun_agent_engine/guardrails/guardrails_hub/utils.py +1 -0
  18. idun_agent_engine-0.3.0/src/idun_agent_engine/mcp/__init__.py +5 -0
  19. idun_agent_engine-0.3.0/src/idun_agent_engine/mcp/helpers.py +97 -0
  20. idun_agent_engine-0.3.0/src/idun_agent_engine/mcp/registry.py +109 -0
  21. {idun_agent_engine-0.2.7 → idun_agent_engine-0.3.0}/src/idun_agent_engine/observability/__init__.py +6 -2
  22. {idun_agent_engine-0.2.7 → idun_agent_engine-0.3.0}/src/idun_agent_engine/observability/base.py +73 -12
  23. idun_agent_engine-0.3.0/src/idun_agent_engine/observability/gcp_logging/gcp_logging_handler.py +52 -0
  24. idun_agent_engine-0.3.0/src/idun_agent_engine/observability/gcp_trace/gcp_trace_handler.py +116 -0
  25. {idun_agent_engine-0.2.7 → idun_agent_engine-0.3.0}/src/idun_agent_engine/observability/langfuse/langfuse_handler.py +17 -10
  26. {idun_agent_engine-0.2.7 → idun_agent_engine-0.3.0}/src/idun_agent_engine/server/dependencies.py +13 -1
  27. idun_agent_engine-0.3.0/src/idun_agent_engine/server/lifespan.py +109 -0
  28. idun_agent_engine-0.3.0/src/idun_agent_engine/server/routers/agent.py +188 -0
  29. idun_agent_engine-0.3.0/src/idun_agent_engine/server/routers/agui.py +47 -0
  30. idun_agent_engine-0.3.0/src/idun_agent_engine/server/routers/base.py +114 -0
  31. idun_agent_engine-0.3.0/src/idun_agent_engine/templates/__init__.py +1 -0
  32. idun_agent_engine-0.3.0/src/idun_agent_engine/templates/correction.py +65 -0
  33. idun_agent_engine-0.3.0/src/idun_agent_engine/templates/deep_research.py +40 -0
  34. idun_agent_engine-0.3.0/src/idun_agent_engine/templates/translation.py +70 -0
  35. idun_agent_engine-0.3.0/src/idun_platform_cli/__init__.py +0 -0
  36. idun_agent_engine-0.3.0/src/idun_platform_cli/groups/__init__.py +0 -0
  37. idun_agent_engine-0.3.0/src/idun_platform_cli/groups/agent/__init__.py +0 -0
  38. {idun_agent_engine-0.2.7 → idun_agent_engine-0.3.0}/src/idun_platform_cli/groups/agent/package.py +3 -3
  39. {idun_agent_engine-0.2.7 → idun_agent_engine-0.3.0}/src/idun_platform_cli/groups/agent/serve.py +8 -5
  40. idun_agent_engine-0.2.7/src/idun_agent_engine/cli/__init__.py +0 -16
  41. idun_agent_engine-0.2.7/src/idun_agent_engine/server/lifespan.py +0 -42
  42. idun_agent_engine-0.2.7/src/idun_agent_engine/server/routers/agent.py +0 -96
  43. idun_agent_engine-0.2.7/src/idun_agent_engine/server/routers/base.py +0 -60
  44. {idun_agent_engine-0.2.7 → idun_agent_engine-0.3.0}/src/idun_agent_engine/__init__.py +0 -0
  45. {idun_agent_engine-0.2.7 → idun_agent_engine-0.3.0}/src/idun_agent_engine/agent/__init__.py +0 -0
  46. {idun_agent_engine-0.2.7 → idun_agent_engine-0.3.0}/src/idun_agent_engine/agent/haystack/__init__.py +0 -0
  47. {idun_agent_engine-0.2.7 → idun_agent_engine-0.3.0}/src/idun_agent_engine/agent/haystack/haystack_model.py +0 -0
  48. {idun_agent_engine-0.2.7 → idun_agent_engine-0.3.0}/src/idun_agent_engine/agent/haystack/utils.py +0 -0
  49. {idun_agent_engine-0.2.7 → idun_agent_engine-0.3.0}/src/idun_agent_engine/agent/langgraph/__init__.py +0 -0
  50. {idun_agent_engine-0.2.7 → idun_agent_engine-0.3.0}/src/idun_agent_engine/core/__init__.py +0 -0
  51. {idun_agent_engine-0.2.7/src/idun_platform_cli → idun_agent_engine-0.3.0/src/idun_agent_engine/guardrails}/__init__.py +0 -0
  52. {idun_agent_engine-0.2.7/src/idun_platform_cli/groups → idun_agent_engine-0.3.0/src/idun_agent_engine/observability/gcp_logging}/__init__.py +0 -0
  53. {idun_agent_engine-0.2.7/src/idun_platform_cli/groups/agent → idun_agent_engine-0.3.0/src/idun_agent_engine/observability/gcp_trace}/__init__.py +0 -0
  54. {idun_agent_engine-0.2.7 → idun_agent_engine-0.3.0}/src/idun_agent_engine/observability/langfuse/__init__.py +0 -0
  55. {idun_agent_engine-0.2.7 → idun_agent_engine-0.3.0}/src/idun_agent_engine/observability/phoenix/__init__.py +0 -0
  56. {idun_agent_engine-0.2.7 → idun_agent_engine-0.3.0}/src/idun_agent_engine/observability/phoenix/phoenix_handler.py +0 -0
  57. {idun_agent_engine-0.2.7 → idun_agent_engine-0.3.0}/src/idun_agent_engine/observability/phoenix_local/__init__.py +0 -0
  58. {idun_agent_engine-0.2.7 → idun_agent_engine-0.3.0}/src/idun_agent_engine/observability/phoenix_local/phoenix_local_handler.py +0 -0
  59. {idun_agent_engine-0.2.7 → idun_agent_engine-0.3.0}/src/idun_agent_engine/py.typed +0 -0
  60. {idun_agent_engine-0.2.7 → idun_agent_engine-0.3.0}/src/idun_agent_engine/server/__init__.py +0 -0
  61. {idun_agent_engine-0.2.7 → idun_agent_engine-0.3.0}/src/idun_agent_engine/server/routers/__init__.py +0 -0
  62. {idun_agent_engine-0.2.7 → idun_agent_engine-0.3.0}/src/idun_agent_engine/server/server_config.py +0 -0
  63. {idun_agent_engine-0.2.7 → idun_agent_engine-0.3.0}/src/idun_platform_cli/groups/agent/main.py +0 -0
  64. {idun_agent_engine-0.2.7 → idun_agent_engine-0.3.0}/src/idun_platform_cli/main.py +0 -0
@@ -3,6 +3,7 @@ __pycache__/
3
3
  *.py[cod]
4
4
  *$py.class
5
5
 
6
+ .python-version
6
7
  # C extensions
7
8
  *.so
8
9
  .DS_Store
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: idun-agent-engine
3
- Version: 0.2.7
3
+ Version: 0.3.0
4
4
  Summary: Python SDK and runtime to serve AI agents with FastAPI, LangGraph, and observability.
5
5
  Project-URL: Homepage, https://github.com/geoffreyharrazi/idun-agent-platform
6
6
  Project-URL: Repository, https://github.com/geoffreyharrazi/idun-agent-platform
@@ -19,29 +19,43 @@ Classifier: Programming Language :: Python :: 3.13
19
19
  Classifier: Topic :: Software Development :: Libraries
20
20
  Classifier: Typing :: Typed
21
21
  Requires-Python: <3.14,>=3.12
22
+ Requires-Dist: ag-ui-adk<0.4.0,>=0.3.4
22
23
  Requires-Dist: ag-ui-langgraph<0.1.0,>=0.0.20
23
24
  Requires-Dist: ag-ui-protocol<0.2.0,>=0.1.8
24
25
  Requires-Dist: aiosqlite<0.22.0,>=0.21.0
25
26
  Requires-Dist: arize-phoenix-otel<1.0.0,>=0.2.0
26
27
  Requires-Dist: arize-phoenix<12.0.0,>=11.22.0
27
- Requires-Dist: click>=8.2.1
28
+ Requires-Dist: click>=8.2.0
28
29
  Requires-Dist: copilotkit<0.2.0,>=0.1.72
30
+ Requires-Dist: deepagents<1.0.0,>=0.2.8
29
31
  Requires-Dist: fastapi<0.116.0,>=0.115.0
30
- Requires-Dist: google-adk<2.0.0,>=1.9.0
32
+ Requires-Dist: google-adk<2.0.0,>=1.19.0
33
+ Requires-Dist: google-cloud-logging<4.0.0,>=3.10.0
34
+ Requires-Dist: guardrails-ai<0.8.0,>=0.7.0
31
35
  Requires-Dist: httpx<0.29.0,>=0.28.1
32
- Requires-Dist: idun-agent-schema<0.3.0,>=0.2.7
33
- Requires-Dist: langchain-core<0.4.0,>=0.3.72
34
- Requires-Dist: langchain-google-vertexai<3.0.0,>=2.0.27
35
- Requires-Dist: langchain<0.4,>=0.3.27
36
+ Requires-Dist: idun-agent-schema<1.0.0,>=0.2.7
37
+ Requires-Dist: langchain-core<2.0.0,>=1.0.0
38
+ Requires-Dist: langchain-google-vertexai<4.0.0,>=2.0.27
39
+ Requires-Dist: langchain-mcp-adapters<0.2.0,>=0.1.0
40
+ Requires-Dist: langchain<2.0.0,>=1.0.0
36
41
  Requires-Dist: langfuse-haystack>=2.3.0
37
- Requires-Dist: langfuse==2.60.8
38
- Requires-Dist: langgraph-checkpoint-sqlite<3.0.0,>=2.0.11
39
- Requires-Dist: langgraph<0.7.0,>=0.6.3
42
+ Requires-Dist: langfuse<4.0.0,>=2.60.8
43
+ Requires-Dist: langgraph-checkpoint-postgres<4.0.0,>=3.0.0
44
+ Requires-Dist: langgraph-checkpoint-sqlite<4.0.0,>=3.0.0
45
+ Requires-Dist: langgraph<2.0.0,>=1.0.0
46
+ Requires-Dist: mcp<2.0.0,>=1.0.0
47
+ Requires-Dist: openinference-instrumentation-google-adk<1.0.0,>=0.1.0
48
+ Requires-Dist: openinference-instrumentation-guardrails<1.0.0,>=0.1.0
40
49
  Requires-Dist: openinference-instrumentation-langchain<1.0.0,>=0.1.13
50
+ Requires-Dist: openinference-instrumentation-mcp<2.0.0,>=1.0.0
51
+ Requires-Dist: openinference-instrumentation-vertexai<1.0.0,>=0.1.0
52
+ Requires-Dist: opentelemetry-exporter-gcp-trace<2.0.0,>=1.6.0
53
+ Requires-Dist: opentelemetry-exporter-otlp-proto-http<2.0.0,>=1.22.0
41
54
  Requires-Dist: pydantic<3.0.0,>=2.11.7
42
55
  Requires-Dist: python-dotenv>=1.1.1
43
56
  Requires-Dist: sqlalchemy<3.0.0,>=2.0.36
44
57
  Requires-Dist: streamlit<2.0.0,>=1.47.1
58
+ Requires-Dist: tavily-python<0.8.0,>=0.7.9
45
59
  Requires-Dist: uvicorn<0.36.0,>=0.35.0
46
60
  Description-Content-Type: text/markdown
47
61
 
@@ -214,6 +228,7 @@ agent:
214
228
  - `agent.config.graph_definition` (str): absolute or relative `path/to/file.py:variable`
215
229
  - `agent.config.checkpointer` (sqlite): `{ type: "sqlite", db_url: "sqlite:///file.db" }`
216
230
  - `agent.config.observability` (optional): provider options as shown above
231
+ - `mcp_servers` (list, optional): collection of MCP servers that should be available to your agent runtime. Each entry matches the fields supported by `langchain-mcp-adapters` (name, transport, url/command, headers, etc.).
217
232
 
218
233
  Config can be sourced by:
219
234
 
@@ -221,6 +236,43 @@ Config can be sourced by:
221
236
  - `config_dict`: dict validated at runtime
222
237
  - `config_path`: path to YAML; defaults to `config.yaml`
223
238
 
239
+ ### MCP Servers
240
+
241
+ You can mount MCP servers directly in your engine config. The engine will automatically
242
+ create a `MultiServerMCPClient` and expose it on `app.state.mcp_registry`.
243
+
244
+ ```yaml
245
+ mcp_servers:
246
+ - name: "math"
247
+ transport: "stdio"
248
+ command: "python"
249
+ args:
250
+ - "/path/to/math_server.py"
251
+ - name: "weather"
252
+ transport: "streamable_http"
253
+ url: "http://localhost:8000/mcp"
254
+ ```
255
+
256
+ Inside your FastAPI dependencies or handlers:
257
+
258
+ ```python
259
+ from idun_agent_engine.server.dependencies import get_mcp_registry
260
+
261
+ @router.get("/mcp/{server}/tools")
262
+ async def list_tools(server: str, registry = Depends(get_mcp_registry)):
263
+ return await registry.get_tools(server)
264
+ ```
265
+
266
+ Or outside of FastAPI:
267
+
268
+ ```python
269
+ from langchain_mcp_adapters.tools import load_mcp_tools
270
+
271
+ registry = app.state.mcp_registry
272
+ async with registry.get_session("math") as session:
273
+ tools = await load_mcp_tools(session)
274
+ ```
275
+
224
276
  ## Examples
225
277
 
226
278
  The `examples/` folder contains complete projects:
@@ -167,6 +167,7 @@ agent:
167
167
  - `agent.config.graph_definition` (str): absolute or relative `path/to/file.py:variable`
168
168
  - `agent.config.checkpointer` (sqlite): `{ type: "sqlite", db_url: "sqlite:///file.db" }`
169
169
  - `agent.config.observability` (optional): provider options as shown above
170
+ - `mcp_servers` (list, optional): collection of MCP servers that should be available to your agent runtime. Each entry matches the fields supported by `langchain-mcp-adapters` (name, transport, url/command, headers, etc.).
170
171
 
171
172
  Config can be sourced by:
172
173
 
@@ -174,6 +175,43 @@ Config can be sourced by:
174
175
  - `config_dict`: dict validated at runtime
175
176
  - `config_path`: path to YAML; defaults to `config.yaml`
176
177
 
178
+ ### MCP Servers
179
+
180
+ You can mount MCP servers directly in your engine config. The engine will automatically
181
+ create a `MultiServerMCPClient` and expose it on `app.state.mcp_registry`.
182
+
183
+ ```yaml
184
+ mcp_servers:
185
+ - name: "math"
186
+ transport: "stdio"
187
+ command: "python"
188
+ args:
189
+ - "/path/to/math_server.py"
190
+ - name: "weather"
191
+ transport: "streamable_http"
192
+ url: "http://localhost:8000/mcp"
193
+ ```
194
+
195
+ Inside your FastAPI dependencies or handlers:
196
+
197
+ ```python
198
+ from idun_agent_engine.server.dependencies import get_mcp_registry
199
+
200
+ @router.get("/mcp/{server}/tools")
201
+ async def list_tools(server: str, registry = Depends(get_mcp_registry)):
202
+ return await registry.get_tools(server)
203
+ ```
204
+
205
+ Or outside of FastAPI:
206
+
207
+ ```python
208
+ from langchain_mcp_adapters.tools import load_mcp_tools
209
+
210
+ registry = app.state.mcp_registry
211
+ async with registry.get_session("math") as session:
212
+ tools = await load_mcp_tools(session)
213
+ ```
214
+
177
215
  ## Examples
178
216
 
179
217
  The `examples/` folder contains complete projects:
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "idun-agent-engine"
3
- version = "0.2.7"
3
+ version = "0.3.0"
4
4
  description = "Python SDK and runtime to serve AI agents with FastAPI, LangGraph, and observability."
5
5
  authors = [{ name = "Geoffrey HARRAZI", email = "geoffreyharrazi@gmail.com" }]
6
6
  requires-python = ">=3.12,<3.14"
@@ -24,34 +24,52 @@ classifiers = [
24
24
  "Intended Audience :: Developers",
25
25
  "Topic :: Software Development :: Libraries",
26
26
  "Typing :: Typed",
27
+
28
+
29
+
27
30
  ]
31
+
28
32
  dependencies = [
33
+ "tavily-python>=0.7.9,<0.8.0",
34
+ "deepagents>=0.2.8,<1.0.0",
35
+ "guardrails-ai>=0.7.0, <0.8.0",
29
36
  "fastapi>=0.115.0,<0.116.0",
30
37
  "uvicorn>=0.35.0,<0.36.0",
31
- "langgraph>=0.6.3,<0.7.0",
32
- "langgraph-checkpoint-sqlite>=2.0.11,<3.0.0",
38
+ "langgraph>=1.0.0,<2.0.0",
39
+ "langgraph-checkpoint-sqlite>=3.0.0,<4.0.0",
40
+ "langgraph-checkpoint-postgres>=3.0.0,<4.0.0",
33
41
  "streamlit>=1.47.1,<2.0.0",
34
42
  "httpx>=0.28.1,<0.29.0",
35
43
  "pydantic>=2.11.7,<3.0.0",
36
- "langchain-core>=0.3.72,<0.4.0",
37
- "langchain-google-vertexai>=2.0.27,<3.0.0",
38
- "google-adk>=1.9.0,<2.0.0",
44
+ "langchain-core>=1.0.0,<2.0.0",
45
+ "langchain-google-vertexai>=2.0.27,<4.0.0",
46
+ "google-adk>=1.19.0,<2.0.0",
39
47
  "ag-ui-protocol>=0.1.8,<0.2.0",
40
48
  "aiosqlite>=0.21.0,<0.22.0",
41
- "langfuse==2.60.8",
49
+ "langfuse>=2.60.8,<4.0.0",
42
50
  "arize-phoenix-otel>=0.2.0,<1.0.0",
43
51
  "openinference-instrumentation-langchain>=0.1.13,<1.0.0",
44
- "langchain>=0.3.27,<0.4",
52
+ "langchain>=1.0.0,<2.0.0",
45
53
  # Pin <12: 12.x currently depends on placeholder jmespath==99.99.99 and fails in Docker
46
54
  "arize-phoenix>=11.22.0,<12.0.0",
47
- "idun-agent-schema>=0.2.7,<0.3.0",
55
+ "idun-agent-schema>=0.2.7,<1.0.0",
48
56
  "langfuse-haystack>=2.3.0",
49
57
  "python-dotenv>=1.1.1",
50
- "click>=8.2.1",
58
+ "click>=8.2.0",
51
59
  # Pin SQLAlchemy for Python 3.13 compatibility (required by Phoenix)
52
60
  "sqlalchemy>=2.0.36,<3.0.0",
53
61
  "ag-ui-langgraph>=0.0.20,<0.1.0",
54
62
  "copilotkit>=0.1.72,<0.2.0",
63
+ "langchain-mcp-adapters>=0.1.0,<0.2.0",
64
+ "ag-ui-adk>=0.3.4,<0.4.0",
65
+ "opentelemetry-exporter-gcp-trace>=1.6.0,<2.0.0",
66
+ "openinference-instrumentation-guardrails>=0.1.0,<1.0.0",
67
+ "openinference-instrumentation-vertexai>=0.1.0,<1.0.0",
68
+ "openinference-instrumentation-google-adk>=0.1.0,<1.0.0",
69
+ "openinference-instrumentation-mcp>=1.0.0,<2.0.0",
70
+ "google-cloud-logging>=3.10.0,<4.0.0",
71
+ "opentelemetry-exporter-otlp-proto-http>=1.22.0,<2.0.0",
72
+ "mcp>=1.0.0,<2.0.0",
55
73
  ]
56
74
 
57
75
 
@@ -1,3 +1,3 @@
1
1
  """Version information for Idun Agent Engine."""
2
2
 
3
- __version__ = "0.2.7"
3
+ __version__ = "0.3.0"
@@ -0,0 +1,5 @@
1
+ """ADK Agent implementation."""
2
+
3
+ from .adk import AdkAgent
4
+
5
+ __all__ = ["AdkAgent"]
@@ -0,0 +1,296 @@
1
+ """ADK agent adapter implementing the BaseAgent protocol."""
2
+
3
+ import importlib.util
4
+ import uuid
5
+ from collections.abc import AsyncGenerator
6
+ from typing import Any
7
+
8
+ from google.adk.apps.app import App
9
+ from google.adk.memory import (
10
+ InMemoryMemoryService,
11
+ VertexAiMemoryBankService,
12
+ )
13
+ from google.adk.sessions import (
14
+ DatabaseSessionService,
15
+ InMemorySessionService,
16
+ VertexAiSessionService,
17
+ )
18
+ from idun_agent_schema.engine.adk import (
19
+ AdkAgentConfig,
20
+ AdkDatabaseSessionConfig,
21
+ AdkInMemoryMemoryConfig,
22
+ AdkInMemorySessionConfig,
23
+ AdkVertexAiMemoryConfig,
24
+ AdkVertexAiSessionConfig,
25
+ )
26
+ from idun_agent_schema.engine.observability_v2 import ObservabilityConfig
27
+
28
+ from ag_ui_adk import ADKAgent as ADKAGUIAgent
29
+ from idun_agent_engine.agent import base as agent_base
30
+ from idun_agent_engine import observability
31
+
32
+
33
+ class AdkAgent(agent_base.BaseAgent):
34
+ """ADK agent adapter implementing the BaseAgent protocol."""
35
+
36
+ def __init__(self):
37
+ """Initialize an unconfigured AdkAgent with default state."""
38
+ self._id = str(uuid.uuid4())
39
+ self._agent_type = "ADK"
40
+ self._agent_instance: Any = None
41
+ self._copilotkit_agent_instance: ADKAGUIAgent | None = None
42
+ self._configuration: AdkAgentConfig | None = None
43
+ self._name: str = "Unnamed ADK Agent"
44
+ self._infos: dict[str, Any] = {
45
+ "status": "Uninitialized",
46
+ "name": self._name,
47
+ "id": self._id,
48
+ }
49
+ self._session_service: Any = None
50
+ self._memory_service: Any = None
51
+ # Observability (provider-agnostic)
52
+ self._obs_callbacks: list[Any] | None = None
53
+
54
+ @property
55
+ def id(self) -> str:
56
+ """Return unique identifier for this agent instance."""
57
+ return self._id
58
+
59
+ @property
60
+ def agent_type(self) -> str:
61
+ """Return agent type label."""
62
+ return self._agent_type
63
+
64
+ @property
65
+ def name(self) -> str:
66
+ """Return configured human-readable agent name."""
67
+ return self._name
68
+
69
+ @property
70
+ def agent_instance(self) -> Any:
71
+ """Return the underlying ADK agent instance.
72
+
73
+ Raises:
74
+ RuntimeError: If the agent is not yet initialized.
75
+ """
76
+ if self._agent_instance is None:
77
+ raise RuntimeError("Agent not initialized. Call initialize() first.")
78
+ return self._agent_instance
79
+
80
+ @property
81
+ def copilotkit_agent_instance(self) -> ADKAGUIAgent:
82
+ """Return the CopilotKit agent instance.
83
+
84
+ Raises:
85
+ RuntimeError: If the CopilotKit agent is not yet initialized.
86
+ """
87
+ if self._copilotkit_agent_instance is None:
88
+ raise RuntimeError(
89
+ "CopilotKit agent not initialized. Call initialize() first."
90
+ )
91
+ return self._copilotkit_agent_instance
92
+
93
+ @property
94
+ def configuration(self) -> AdkAgentConfig:
95
+ """Return validated configuration.
96
+
97
+ Raises:
98
+ RuntimeError: If the agent has not been configured yet.
99
+ """
100
+ if not self._configuration:
101
+ raise RuntimeError("Agent not configured. Call initialize() first.")
102
+ return self._configuration
103
+
104
+ @property
105
+ def infos(self) -> dict[str, Any]:
106
+ """Return diagnostic information about the agent instance."""
107
+ self._infos["underlying_agent_type"] = (
108
+ str(type(self._agent_instance)) if self._agent_instance else "N/A"
109
+ )
110
+ return self._infos
111
+
112
+ async def initialize(
113
+ self,
114
+ config: AdkAgentConfig,
115
+ observability_config: list[ObservabilityConfig] | None = None,
116
+ ) -> None:
117
+ """Initialize the ADK agent asynchronously."""
118
+ self._configuration = AdkAgentConfig.model_validate(config)
119
+
120
+ self._name = self._configuration.app_name or "Unnamed ADK Agent"
121
+ self._infos["name"] = self._name
122
+
123
+ # Observability (provider-agnostic)
124
+ if observability_config:
125
+ handlers, infos = observability.create_observability_handlers(
126
+ observability_config # type: ignore[arg-type]
127
+ )
128
+ self._obs_callbacks = []
129
+ for handler in handlers:
130
+ # Even if callbacks aren't used by ADK directly, instantiating the handler
131
+ # might set up global instrumentation (e.g. Phoenix, Langfuse env vars).
132
+ self._obs_callbacks.extend(handler.get_callbacks())
133
+
134
+ if infos:
135
+ self._infos["observability"] = infos
136
+
137
+ if observability_config:
138
+ try:
139
+ # Check if langfuse is enabled in any of the observability configs
140
+ def _is_langfuse_provider(c: Any) -> bool:
141
+ provider = getattr(c, "provider", None)
142
+ if provider is None and isinstance(c, dict):
143
+ provider = c.get("provider")
144
+
145
+ if provider is not None and hasattr(provider, "value"):
146
+ provider = provider.value
147
+
148
+ return str(provider).lower() == "langfuse"
149
+
150
+ is_langfuse_enabled = any(
151
+ _is_langfuse_provider(config) for config in observability_config
152
+ )
153
+
154
+ if is_langfuse_enabled:
155
+ import os
156
+ langfuse_pk = os.environ.get("LANGFUSE_PUBLIC_KEY")
157
+ langfuse_host = os.environ.get("LANGFUSE_BASE_URL")
158
+ print(f"LANGFUSE_PUBLIC_KEY: {langfuse_pk}")
159
+ print(f"LANGFUSE_BASE_URL: {langfuse_host}")
160
+ try:
161
+ from openinference.instrumentation.google_adk import (
162
+ GoogleADKInstrumentor,
163
+ )
164
+
165
+ GoogleADKInstrumentor().instrument()
166
+ print("GoogleADKInstrumentor instrumented successfully.")
167
+ except ImportError:
168
+ print(
169
+ "openinference-instrumentation-google-adk not installed, skipping Google ADK instrumentation."
170
+ )
171
+ except Exception as e:
172
+ print(f"Failed to instrument Google ADK: {e}")
173
+ except Exception as e:
174
+ print(f"Error checking observability config for ADK instrumentation: {e}")
175
+
176
+ # Initialize Session Service
177
+ await self._initialize_session_service()
178
+
179
+ # Initialize Memory Service
180
+ await self._initialize_memory_service()
181
+
182
+ # Load the agent instance
183
+ agent = self._load_agent(self._configuration.agent)
184
+
185
+ self._agent_instance = App(root_agent=agent, name=self._name)
186
+
187
+ # Initialize CopilotKit/AG-UI Agent Wrapper
188
+ # TODO: Pass session and memory services when supported by AG-UI ADK adapter if needed
189
+ self._copilotkit_agent_instance = ADKAGUIAgent(
190
+ adk_agent=agent,
191
+ session_service=self._session_service,
192
+ memory_service=self._memory_service,
193
+ app_name=self._name,
194
+ )
195
+
196
+ self._infos["status"] = "Initialized"
197
+ self._infos["config_used"] = self._configuration.model_dump()
198
+
199
+ async def _initialize_session_service(self) -> None:
200
+ """Initialize the session service based on configuration."""
201
+ if not self._configuration:
202
+ raise RuntimeError("Configuration not initialized")
203
+
204
+ if not self._configuration.session_service:
205
+ # Default to InMemory if not specified
206
+ self._session_service = InMemorySessionService()
207
+ return
208
+
209
+ config = self._configuration.session_service
210
+ if isinstance(config, AdkInMemorySessionConfig):
211
+ self._session_service = InMemorySessionService()
212
+ elif isinstance(config, AdkVertexAiSessionConfig):
213
+ self._session_service = VertexAiSessionService(
214
+ project=config.project_id,
215
+ location=config.location,
216
+ agent_engine_id=config.reasoning_engine_app_name,
217
+ )
218
+ elif isinstance(config, AdkDatabaseSessionConfig):
219
+ self._session_service = DatabaseSessionService(db_url=config.db_url)
220
+ else:
221
+ raise ValueError(f"Unsupported session service type: {config.type}") # type: ignore
222
+
223
+ async def _initialize_memory_service(self) -> None:
224
+ """Initialize the memory service based on configuration."""
225
+ if not self._configuration:
226
+ raise RuntimeError("Configuration not initialized")
227
+
228
+ if not self._configuration.memory_service:
229
+ # Default to InMemory if not specified
230
+ self._memory_service = InMemoryMemoryService()
231
+ return
232
+
233
+ config = self._configuration.memory_service
234
+ if isinstance(config, AdkInMemoryMemoryConfig):
235
+ self._memory_service = InMemoryMemoryService()
236
+ elif isinstance(config, AdkVertexAiMemoryConfig):
237
+ self._memory_service = VertexAiMemoryBankService(
238
+ project=config.project_id,
239
+ location=config.location,
240
+ agent_engine_id=config.memory_bank_id,
241
+ )
242
+ else:
243
+ raise ValueError(f"Unsupported memory service type: {config.type}") # type: ignore
244
+
245
+ def _load_agent(self, agent_definition: str) -> Any:
246
+ """Loads an agent instance from a specified path."""
247
+ try:
248
+ module_path, agent_variable_name = agent_definition.rsplit(":", 1)
249
+ except ValueError:
250
+ raise ValueError(
251
+ "agent_definition must be in the format 'path/to/file.py:variable_name'"
252
+ ) from None
253
+
254
+ try:
255
+ from pathlib import Path
256
+
257
+ resolved_path = Path(module_path).resolve()
258
+ spec = importlib.util.spec_from_file_location(
259
+ agent_variable_name, str(resolved_path)
260
+ )
261
+ if spec is None or spec.loader is None:
262
+ raise ImportError(f"Could not load spec for module at {module_path}")
263
+
264
+ module = importlib.util.module_from_spec(spec)
265
+ spec.loader.exec_module(module)
266
+
267
+ agent_instance = getattr(module, agent_variable_name)
268
+ return agent_instance
269
+ except (FileNotFoundError, ImportError, AttributeError) as e:
270
+ raise ValueError(
271
+ f"Failed to load agent from {agent_definition}: {e}"
272
+ ) from e
273
+
274
+ async def invoke(self, message: Any) -> Any:
275
+ """Process a single input to chat with the agent."""
276
+ if self._agent_instance is None:
277
+ raise RuntimeError(
278
+ "Agent not initialized. Call initialize() before processing messages."
279
+ )
280
+
281
+ # TODO: Implement ADK invoke logic using session and memory services
282
+ raise NotImplementedError("ADK invoke not implemented yet")
283
+
284
+ async def stream(self, message: Any) -> AsyncGenerator[Any]:
285
+ """Process a single input message and return an asynchronous stream."""
286
+ if self._agent_instance is None:
287
+ raise RuntimeError(
288
+ "Agent not initialized. Call initialize() before processing messages."
289
+ )
290
+
291
+ # TODO: Implement ADK stream logic using session and memory services
292
+ raise NotImplementedError("ADK stream not implemented yet")
293
+
294
+ # Required to make this a generator
295
+ if False:
296
+ yield
@@ -8,6 +8,7 @@ from collections.abc import AsyncGenerator
8
8
  from typing import Any
9
9
 
10
10
  from idun_agent_schema.engine.agent import BaseAgentConfig
11
+ from idun_agent_schema.engine.observability_v2 import ObservabilityConfig
11
12
 
12
13
 
13
14
  class BaseAgent[ConfigType: BaseAgentConfig](ABC):
@@ -63,13 +64,18 @@ class BaseAgent[ConfigType: BaseAgentConfig](ABC):
63
64
  pass
64
65
 
65
66
  @abstractmethod
66
- async def initialize(self, config: dict[str, Any]) -> None:
67
+ async def initialize(
68
+ self,
69
+ config: dict[str, Any],
70
+ observability: list[ObservabilityConfig] | None = None,
71
+ ) -> None:
67
72
  """Initialize the agent with a given configuration.
68
73
 
69
74
  This method should set up the underlying agent framework instance.
70
75
 
71
76
  Args:
72
77
  config: A dictionary containing the agent's configuration.
78
+ observability: Optional list of observability configurations.
73
79
  """
74
80
  pass
75
81
 
@@ -126,7 +126,11 @@ class HaystackAgent(BaseAgent):
126
126
  self._langfuse_tracing = True
127
127
  logger.info("Agent tracing not supported yet")
128
128
 
129
- async def initialize(self, config: HaystackAgentConfig | dict[str, Any]) -> None:
129
+ async def initialize(
130
+ self,
131
+ config: HaystackAgentConfig | dict[str, Any],
132
+ observability_config: list[ObservabilityConfig] | None = None,
133
+ ) -> None:
130
134
  try:
131
135
  logger.debug(f"Initializing haystack agent config: {config}...")
132
136