idun-agent-engine 0.2.7__py3-none-any.whl → 0.3.0__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 (41) hide show
  1. idun_agent_engine/_version.py +1 -1
  2. idun_agent_engine/agent/adk/__init__.py +5 -0
  3. idun_agent_engine/agent/adk/adk.py +296 -0
  4. idun_agent_engine/agent/base.py +7 -1
  5. idun_agent_engine/agent/haystack/haystack.py +5 -1
  6. idun_agent_engine/agent/langgraph/langgraph.py +146 -55
  7. idun_agent_engine/core/app_factory.py +9 -0
  8. idun_agent_engine/core/config_builder.py +214 -23
  9. idun_agent_engine/core/engine_config.py +1 -2
  10. idun_agent_engine/core/server_runner.py +2 -3
  11. idun_agent_engine/guardrails/__init__.py +0 -0
  12. idun_agent_engine/guardrails/base.py +24 -0
  13. idun_agent_engine/guardrails/guardrails_hub/guardrails_hub.py +101 -0
  14. idun_agent_engine/guardrails/guardrails_hub/utils.py +1 -0
  15. idun_agent_engine/mcp/__init__.py +5 -0
  16. idun_agent_engine/mcp/helpers.py +97 -0
  17. idun_agent_engine/mcp/registry.py +109 -0
  18. idun_agent_engine/observability/__init__.py +6 -2
  19. idun_agent_engine/observability/base.py +73 -12
  20. idun_agent_engine/observability/gcp_logging/__init__.py +0 -0
  21. idun_agent_engine/observability/gcp_logging/gcp_logging_handler.py +52 -0
  22. idun_agent_engine/observability/gcp_trace/__init__.py +0 -0
  23. idun_agent_engine/observability/gcp_trace/gcp_trace_handler.py +116 -0
  24. idun_agent_engine/observability/langfuse/langfuse_handler.py +17 -10
  25. idun_agent_engine/server/dependencies.py +13 -1
  26. idun_agent_engine/server/lifespan.py +83 -16
  27. idun_agent_engine/server/routers/agent.py +116 -24
  28. idun_agent_engine/server/routers/agui.py +47 -0
  29. idun_agent_engine/server/routers/base.py +55 -1
  30. idun_agent_engine/templates/__init__.py +1 -0
  31. idun_agent_engine/templates/correction.py +65 -0
  32. idun_agent_engine/templates/deep_research.py +40 -0
  33. idun_agent_engine/templates/translation.py +70 -0
  34. {idun_agent_engine-0.2.7.dist-info → idun_agent_engine-0.3.0.dist-info}/METADATA +62 -10
  35. idun_agent_engine-0.3.0.dist-info/RECORD +60 -0
  36. {idun_agent_engine-0.2.7.dist-info → idun_agent_engine-0.3.0.dist-info}/WHEEL +1 -1
  37. idun_platform_cli/groups/agent/package.py +3 -3
  38. idun_platform_cli/groups/agent/serve.py +8 -5
  39. idun_agent_engine/cli/__init__.py +0 -16
  40. idun_agent_engine-0.2.7.dist-info/RECORD +0 -43
  41. {idun_agent_engine-0.2.7.dist-info → idun_agent_engine-0.3.0.dist-info}/entry_points.txt +0 -0
@@ -6,6 +6,7 @@ from typing import Annotated
6
6
  from fastapi import APIRouter, Depends, HTTPException, Request, status
7
7
  from fastapi.responses import StreamingResponse
8
8
  from idun_agent_schema.engine.api import ChatRequest, ChatResponse
9
+ from idun_agent_schema.engine.guardrails import Guardrail
9
10
 
10
11
  from idun_agent_engine.agent.base import BaseAgent
11
12
  from idun_agent_engine.server.dependencies import get_agent, get_copilotkit_agent
@@ -13,6 +14,7 @@ from idun_agent_engine.server.dependencies import get_agent, get_copilotkit_agen
13
14
  from ag_ui.core.types import RunAgentInput
14
15
  from ag_ui.encoder import EventEncoder
15
16
  from copilotkit import LangGraphAGUIAgent
17
+ from ag_ui_adk import ADKAgent as ADKAGUIAgent
16
18
 
17
19
  logging.basicConfig(
18
20
  format="%(asctime)s %(levelname)-8s %(message)s",
@@ -24,6 +26,16 @@ logger = logging.getLogger(__name__)
24
26
  agent_router = APIRouter()
25
27
 
26
28
 
29
+ def _run_guardrails(
30
+ guardrails: list[Guardrail], message: dict[str, str] | str, position: str
31
+ ) -> None:
32
+ """Validates the request's message, by running it on given guardrails. If input is a dict -> input, else its an output guardrails."""
33
+ text = message["query"] if isinstance(message, dict) else message
34
+ for guard in guardrails:
35
+ if guard.position == position and not guard.validate(text): # type: ignore[attr-defined]
36
+ raise HTTPException(status_code=429, detail=guard.reject_message) # type: ignore[attr-defined]
37
+
38
+
27
39
  @agent_router.get("/config")
28
40
  async def get_config(request: Request):
29
41
  """Get the current agent configuration."""
@@ -35,21 +47,28 @@ async def get_config(request: Request):
35
47
  )
36
48
 
37
49
  config = request.app.state.engine_config.agent
38
- logger.info(f"Fetched config for agent: {config}")
50
+ logger.info(f"Fetched config for agent: {request.app.state.engine_config}")
39
51
  return {"config": config}
40
52
 
41
53
 
42
54
  @agent_router.post("/invoke", response_model=ChatResponse)
43
55
  async def invoke(
44
- request: ChatRequest,
56
+ chat_request: ChatRequest,
57
+ request: Request,
45
58
  agent: Annotated[BaseAgent, Depends(get_agent)],
46
59
  ):
47
60
  """Process a chat message with the agent without streaming."""
48
61
  try:
49
- message = {"query": request.query, "session_id": request.session_id}
50
- response_content = await agent.invoke(message)
51
-
52
- return ChatResponse(session_id=request.session_id, response=response_content)
62
+ message = {"query": chat_request.query, "session_id": chat_request.session_id}
63
+ guardrails = getattr(request.app.state, 'guardrails', [])
64
+ if guardrails:
65
+ _run_guardrails(guardrails, message, position="input")
66
+ response_content = await agent.invoke(
67
+ {"query": message["query"], "session_id": message["session_id"]}
68
+ )
69
+ if guardrails:
70
+ _run_guardrails(guardrails, response_content, position="output")
71
+ return ChatResponse(session_id=message["session_id"], response=response_content)
53
72
  except Exception as e: # noqa: BLE001
54
73
  raise HTTPException(status_code=500, detail=str(e)) from e
55
74
 
@@ -74,23 +93,96 @@ async def stream(
74
93
  async def copilotkit_stream(
75
94
  input_data: RunAgentInput,
76
95
  request: Request,
77
- copilotkit_agent: Annotated[LangGraphAGUIAgent, Depends(get_copilotkit_agent)],
96
+ copilotkit_agent: Annotated[
97
+ LangGraphAGUIAgent | ADKAGUIAgent, Depends(get_copilotkit_agent)
98
+ ],
78
99
  ):
79
100
  """Process a message with the agent, streaming ag-ui events."""
80
- try:
81
- # Get the accept header from the request
82
- accept_header = request.headers.get("accept")
83
-
84
- # Create an event encoder to properly format SSE events
85
- encoder = EventEncoder(accept=accept_header or "") # type: ignore[arg-type]
86
-
87
- async def event_generator():
88
- async for event in copilotkit_agent.run(input_data):
89
- yield encoder.encode(event)
90
-
91
- return StreamingResponse(
92
- event_generator(), # type: ignore[arg-type]
93
- media_type=encoder.get_content_type()
94
- )
95
- except Exception as e: # noqa: BLE001
96
- raise HTTPException(status_code=500, detail=str(e)) from e
101
+ if isinstance(copilotkit_agent, LangGraphAGUIAgent):
102
+ try:
103
+ # Get the accept header from the request
104
+ accept_header = request.headers.get("accept")
105
+
106
+ # Create an event encoder to properly format SSE events
107
+ encoder = EventEncoder(accept=accept_header or "") # type: ignore[arg-type]
108
+
109
+ async def event_generator():
110
+ async for event in copilotkit_agent.run(input_data):
111
+ yield encoder.encode(event) # type: ignore[arg-type]
112
+
113
+ return StreamingResponse(
114
+ event_generator(), # type: ignore[arg-type]
115
+ media_type=encoder.get_content_type(),
116
+ )
117
+ except Exception as e: # noqa: BLE001
118
+ raise HTTPException(status_code=500, detail=str(e)) from e
119
+ elif isinstance(copilotkit_agent, ADKAGUIAgent):
120
+ try:
121
+ # Get the accept header from the request
122
+ accept_header = request.headers.get("accept")
123
+ agent_id = request.url.path.lstrip("/")
124
+
125
+ # Create an event encoder to properly format SSE events
126
+ encoder = EventEncoder(accept=accept_header or "")
127
+
128
+ async def event_generator():
129
+ """Generate events from ADK agent."""
130
+ try:
131
+ async for event in copilotkit_agent.run(input_data):
132
+ try:
133
+ encoded = encoder.encode(event)
134
+ logger.debug(f"HTTP Response: {encoded}")
135
+ yield encoded
136
+ except Exception as encoding_error:
137
+ # Handle encoding-specific errors
138
+ logger.error(
139
+ f"❌ Event encoding error: {encoding_error}",
140
+ exc_info=True,
141
+ )
142
+ # Create a RunErrorEvent for encoding failures
143
+ from ag_ui.core import RunErrorEvent, EventType
144
+
145
+ error_event = RunErrorEvent(
146
+ type=EventType.RUN_ERROR,
147
+ message=f"Event encoding failed: {str(encoding_error)}",
148
+ code="ENCODING_ERROR",
149
+ )
150
+ try:
151
+ error_encoded = encoder.encode(error_event)
152
+ yield error_encoded
153
+ except Exception:
154
+ # If we can't even encode the error event, yield a basic SSE error
155
+ logger.error(
156
+ "Failed to encode error event, yielding basic SSE error"
157
+ )
158
+ yield 'event: error\ndata: {"error": "Event encoding failed"}\n\n'
159
+ break # Stop the stream after an encoding error
160
+ except Exception as agent_error:
161
+ # Handle errors from ADKAgent.run() itself
162
+ logger.error(f"❌ ADKAgent error: {agent_error}", exc_info=True)
163
+ # ADKAgent should have yielded a RunErrorEvent, but if something went wrong
164
+ # in the async generator itself, we need to handle it
165
+ try:
166
+ from ag_ui.core import RunErrorEvent, EventType
167
+
168
+ error_event = RunErrorEvent(
169
+ type=EventType.RUN_ERROR,
170
+ message=f"Agent execution failed: {str(agent_error)}",
171
+ code="AGENT_ERROR",
172
+ )
173
+ error_encoded = encoder.encode(error_event)
174
+ yield error_encoded
175
+ except Exception:
176
+ # If we can't encode the error event, yield a basic SSE error
177
+ logger.error(
178
+ "Failed to encode agent error event, yielding basic SSE error"
179
+ )
180
+ yield 'event: error\ndata: {"error": "Agent execution failed"}\n\n'
181
+
182
+ return StreamingResponse(
183
+ event_generator(), media_type=encoder.get_content_type()
184
+ )
185
+ except Exception as e: # noqa: BLE001
186
+ raise HTTPException(status_code=500, detail=str(e)) from e
187
+ else:
188
+ raise HTTPException(status_code=400, detail="Invalid agent type")
@@ -0,0 +1,47 @@
1
+ # """AGUI routes for CopilotKit integration with LangGraph agents."""
2
+
3
+ # import logging
4
+ # from typing import Annotated
5
+
6
+ # from ag_ui_langgraph import add_langgraph_fastapi_endpoint
7
+ # from copilotkit import LangGraphAGUIAgent
8
+ # from ag_ui_adk import ADKAgent as ADKAGUIAgent
9
+ # from ag_ui_adk import add_adk_fastapi_endpoint
10
+ # from fastapi import APIRouter, Depends, HTTPException, Request
11
+
12
+ # from idun_agent_engine.agent.langgraph.langgraph import LanggraphAgent
13
+ # from idun_agent_engine.agent.adk.adk import AdkAgent
14
+ # from idun_agent_engine.server.dependencies import get_agent
15
+
16
+ # logging.basicConfig(
17
+ # format="%(asctime)s %(levelname)-8s %(message)s",
18
+ # level=logging.INFO,
19
+ # datefmt="%Y-%m-%d %H:%M:%S",
20
+ # )
21
+
22
+ # logger = logging.getLogger(__name__)
23
+
24
+
25
+ # def setup_agui_router(app, agent: LanggraphAgent | AdkAgent) -> LangGraphAGUIAgent | ADKAGUIAgent:
26
+ # """Set up AGUI routes for CopilotKit integration.
27
+
28
+ # This function adds the LangGraph agent as a CopilotKit-compatible endpoint.
29
+
30
+ # Args:
31
+ # app: The FastAPI application instance
32
+ # agent: The initialized LangGraph agent instance
33
+ # """
34
+ # try:
35
+ # if isinstance(agent, LanggraphAgent):
36
+ # # Create the AGUI agent wrapper
37
+ # agui_agent = agent.copilotkit_agent_instance
38
+ # elif isinstance(agent, AdkAgent):
39
+ # # Create the AGUI agent wrapper
40
+ # agui_agent = agent.copilotkit_agent_instance # TODO: duplicate in agent.adk.adk.py init
41
+ # else:
42
+ # raise ValueError(f"Unsupported agent type: {type(agent)}")
43
+ # return agui_agent
44
+ # logger.info(f"✅ AGUI endpoint configured at /agui for agent: {agent.name}")
45
+ # except Exception as e:
46
+ # logger.error(f"❌ Failed to setup AGUI router: {e}")
47
+ # raise HTTPException(status_code=500, detail=f"Failed to setup AGUI router: {e}") from e
@@ -1,18 +1,72 @@
1
1
  """Base routes for service health and landing info."""
2
2
 
3
- from fastapi import APIRouter
3
+ import os
4
+ from typing import Optional
5
+ from fastapi import APIRouter, Request, HTTPException
6
+ from pydantic import BaseModel
4
7
 
5
8
  from ..._version import __version__
9
+ from ...core.config_builder import ConfigBuilder
10
+ from ..lifespan import cleanup_agent, configure_app
6
11
 
7
12
  base_router = APIRouter()
8
13
 
9
14
 
15
+ class ReloadRequest(BaseModel):
16
+ """Request body for reload endpoint."""
17
+
18
+ path: Optional[str] = None
19
+
20
+
10
21
  @base_router.get("/health")
11
22
  def health_check():
12
23
  """Health check endpoint for monitoring and load balancers."""
13
24
  return {"status": "healthy", "engine_version": __version__}
14
25
 
15
26
 
27
+ @base_router.post("/reload")
28
+ async def reload_config(request: Request, body: Optional[ReloadRequest] = None):
29
+ """Reload the agent configuration from the manager or a file."""
30
+
31
+ try:
32
+ if body and body.path:
33
+ print(f"🔄 Reloading configuration from file: {body.path}...")
34
+ new_config = ConfigBuilder.load_from_file(body.path)
35
+ else:
36
+ print("🔄 Reloading configuration from manager...")
37
+ agent_api_key = os.getenv("IDUN_AGENT_API_KEY")
38
+ manager_host = os.getenv("IDUN_MANAGER_HOST")
39
+
40
+ if not agent_api_key or not manager_host:
41
+ raise HTTPException(
42
+ status_code=400,
43
+ detail="Cannot reload from manager: IDUN_AGENT_API_KEY or IDUN_MANAGER_HOST environment variables are missing.",
44
+ )
45
+
46
+ # Fetch new config
47
+ config_builder = ConfigBuilder().with_config_from_api(
48
+ agent_api_key=agent_api_key, url=manager_host
49
+ )
50
+ new_config = config_builder.build()
51
+
52
+ # Cleanup old agent
53
+ await cleanup_agent(request.app)
54
+
55
+ # Initialize new agent
56
+ await configure_app(request.app, new_config)
57
+
58
+ return {
59
+ "status": "success",
60
+ "message": "Agent configuration reloaded successfully",
61
+ }
62
+
63
+ except Exception as e:
64
+ print(f"❌ Error reloading configuration: {e}")
65
+ raise HTTPException(
66
+ status_code=500, detail=f"Failed to reload configuration: {str(e)}"
67
+ )
68
+
69
+
16
70
  # Add a root endpoint with helpful information
17
71
  @base_router.get("/")
18
72
  def read_root():
@@ -0,0 +1 @@
1
+ """Agent templates package."""
@@ -0,0 +1,65 @@
1
+ """Correction Agent Template."""
2
+
3
+ import os
4
+ from typing import TypedDict, Annotated, List, Any
5
+
6
+ try:
7
+ from langchain.chat_models import init_chat_model
8
+ except ImportError:
9
+ try:
10
+ from langchain_core.language_models import init_chat_model
11
+ except ImportError:
12
+ init_chat_model = None
13
+
14
+ from langchain_core.messages import SystemMessage, BaseMessage
15
+ from langgraph.graph import StateGraph, START, END
16
+ from langgraph.graph.message import add_messages
17
+
18
+
19
+ class State(TypedDict):
20
+ messages: Annotated[List[BaseMessage], add_messages]
21
+
22
+
23
+ MODEL_NAME = os.getenv("CORRECTION_MODEL", "gemini-2.5-flash")
24
+ LANGUAGE = os.getenv("CORRECTION_LANGUAGE", "French")
25
+
26
+ llm: Any = None
27
+ if init_chat_model and callable(init_chat_model):
28
+ try:
29
+ llm = init_chat_model(MODEL_NAME)
30
+ except Exception as e:
31
+ print(f"Warning: Failed to init model {MODEL_NAME}: {e}")
32
+ else:
33
+ print("Warning: init_chat_model not found in langchain.")
34
+
35
+
36
+
37
+ async def correct_text(state: State):
38
+ """Correct the spelling, syntax, and grammar of the text."""
39
+ if not llm:
40
+
41
+ return {
42
+ "messages": [
43
+ SystemMessage(content="Error: Model not initialized. Check logs.")
44
+ ]
45
+ }
46
+
47
+
48
+ prompt = (
49
+ f"You are a professional text corrector for {LANGUAGE}. "
50
+ f"Correct the spelling, syntax, grammar, and conjugation of the following text. "
51
+ f"Return ONLY the corrected text without any explanations or modifications to the meaning."
52
+ )
53
+
54
+ messages = [SystemMessage(content=prompt)] + state["messages"]
55
+
56
+ response = await llm.ainvoke(messages)
57
+ return {"messages": [response]}
58
+
59
+
60
+ workflow = StateGraph(State)
61
+ workflow.add_node("correct", correct_text)
62
+ workflow.add_edge(START, "correct")
63
+ workflow.add_edge("correct", END)
64
+
65
+ graph = workflow.compile()
@@ -0,0 +1,40 @@
1
+ """Deep Research Agent Template."""
2
+
3
+ import os
4
+ from deepagents import create_deep_agent
5
+ from tavily import TavilyClient
6
+
7
+ try:
8
+ from langchain.chat_models import init_chat_model
9
+ except ImportError:
10
+ try:
11
+ from langchain_core.language_models import init_chat_model
12
+ except ImportError:
13
+ init_chat_model = None
14
+
15
+ MODEL_NAME = os.getenv("DEEP_RESEARCH_MODEL", "gemini-2.5-flash")
16
+
17
+ SYSTEM_PROMPT = os.getenv(
18
+ "DEEP_RESEARCH_PROMPT", "Conduct research and write a polished report."
19
+ )
20
+
21
+ TAVILY_API_KEY = os.getenv("TAVILY_API_KEY")
22
+
23
+ tavily_client = TavilyClient(api_key=TAVILY_API_KEY)
24
+
25
+
26
+ def internet_search(query: str, max_results: int = 5):
27
+ """Run a web search"""
28
+ return tavily_client.search(query, max_results=max_results)
29
+
30
+
31
+ llm = None
32
+ if init_chat_model:
33
+ try:
34
+ llm = init_chat_model(MODEL_NAME)
35
+ except Exception as e:
36
+ print(f"Warning: Failed to init model {MODEL_NAME}: {e}")
37
+ else:
38
+ print("Warning: init_chat_model not found in langchain.")
39
+
40
+ graph = create_deep_agent(llm, [internet_search], system_prompt=SYSTEM_PROMPT)
@@ -0,0 +1,70 @@
1
+ """Translation Agent Template."""
2
+
3
+ import os
4
+ from typing import TypedDict, Annotated, List
5
+
6
+ # Try importing init_chat_model, fallback if necessary
7
+ try:
8
+ from langchain.chat_models import init_chat_model
9
+ except ImportError:
10
+ try:
11
+ from langchain_core.language_models import init_chat_model
12
+ except ImportError:
13
+ init_chat_model = None
14
+
15
+ from langchain_core.messages import SystemMessage, BaseMessage
16
+ from langgraph.graph import StateGraph, START, END
17
+ from langgraph.graph.message import add_messages
18
+
19
+
20
+ # Define the state
21
+ class State(TypedDict):
22
+ messages: Annotated[List[BaseMessage], add_messages]
23
+
24
+
25
+ # Read configuration from environment variables
26
+ # These are set by ConfigBuilder when initializing the agent
27
+ MODEL_NAME = os.getenv("TRANSLATION_MODEL", "gemini-2.5-flash")
28
+ SOURCE_LANG = os.getenv("TRANSLATION_SOURCE_LANG", "English")
29
+ TARGET_LANG = os.getenv("TRANSLATION_TARGET_LANG", "French")
30
+
31
+ # Initialize the model
32
+ llm = None
33
+ if init_chat_model:
34
+ try:
35
+ # init_chat_model requires langchain>=0.2.x or similar.
36
+ # It auto-detects provider from model name (e.g. "gpt-4" -> openai, "claude" -> anthropic)
37
+ # provided the integration packages are installed.
38
+ llm = init_chat_model(MODEL_NAME)
39
+ except Exception as e:
40
+ print(f"Warning: Failed to init model {MODEL_NAME}: {e}")
41
+ else:
42
+ print("Warning: init_chat_model not found in langchain.")
43
+
44
+
45
+ async def translate(state: State):
46
+ """Translate the last message."""
47
+ if not llm:
48
+ return {
49
+ "messages": [
50
+ SystemMessage(content="Error: Model not initialized. Check logs.")
51
+ ]
52
+ }
53
+
54
+ prompt = (
55
+ f"You are a professional translator. Translate the following text "
56
+ f"from {SOURCE_LANG} to {TARGET_LANG}. Output ONLY the translation."
57
+ )
58
+
59
+ messages = [SystemMessage(content=prompt)] + state["messages"]
60
+
61
+ response = await llm.ainvoke(messages)
62
+ return {"messages": [response]}
63
+
64
+
65
+ workflow = StateGraph(State)
66
+ workflow.add_node("translate", translate)
67
+ workflow.add_edge(START, "translate")
68
+ workflow.add_edge("translate", END)
69
+
70
+ graph = workflow.compile()
@@ -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:
@@ -0,0 +1,60 @@
1
+ idun_agent_engine/__init__.py,sha256=PhOL6foq5V0eXaoXw7xKUeCWXIWrOHrAFB8OuJnBqyM,550
2
+ idun_agent_engine/_version.py,sha256=0EcilWMIT4cjN03dXuEtQDhswH9tkXZPcYqYHIbsc-o,72
3
+ idun_agent_engine/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ idun_agent_engine/agent/__init__.py,sha256=foyOoRdI_04q1b6f2A5EXEpWSCKjZxpgWMWrKcsHNl8,220
5
+ idun_agent_engine/agent/base.py,sha256=c-3gljSHQpm6aY0JNDmXkaPtcW55rXdsI8Cgv9l4bCs,3294
6
+ idun_agent_engine/agent/adk/__init__.py,sha256=jLV2Z9qQGZtBpF_0pQI1FRCPJ_J1G3Z6cEAzHnQyuu4,83
7
+ idun_agent_engine/agent/adk/adk.py,sha256=29YC5rgLjdod-CU3a2JqgAvzYcD5u2EY7pylozGNi9c,11375
8
+ idun_agent_engine/agent/haystack/__init__.py,sha256=y5ADrD8gWBeYIvV7tmu6OpPdJ8POHt-tyraIL7RkkWI,179
9
+ idun_agent_engine/agent/haystack/haystack.py,sha256=k4NAx_zTBO9uiExM9NtuDAN94H1lrCWtHf1GEWEN16g,10966
10
+ idun_agent_engine/agent/haystack/haystack_model.py,sha256=EtOYnsWRufcrQufTRMeB3V-rZVQqfnmwKwPsYGfZdCs,362
11
+ idun_agent_engine/agent/haystack/utils.py,sha256=sKRoPhzZWFw1NPsYwCockafzMBCCq3lGOrndbNE_C3M,609
12
+ idun_agent_engine/agent/langgraph/__init__.py,sha256=CoBdkp9P4livdy5B0bvj9o7ftoqKmXEr9cZv4TZLncs,107
13
+ idun_agent_engine/agent/langgraph/langgraph.py,sha256=ozc0ClaAl4B-_dSxiRt8KO2oxGIwNI_m-Hyk2qyJV_c,22641
14
+ idun_agent_engine/core/__init__.py,sha256=F0DMDlWcSWS_1dvh3xMbrdcVvZRHVnoAFFgREuSJfBI,408
15
+ idun_agent_engine/core/app_factory.py,sha256=XiLrctFT_n8LP3flKFwJoJDbiWPiw98N9lbkpR8P1O0,2597
16
+ idun_agent_engine/core/config_builder.py,sha256=j_gia6z66Xun2ZOtaO4TWwRMfAIA6lnk8IGaRMJKEZM,25484
17
+ idun_agent_engine/core/engine_config.py,sha256=IR8WhbenDstNSL7ORrUW8AnzgS3exFQxtwip66pFhcM,545
18
+ idun_agent_engine/core/server_runner.py,sha256=vLlgLQM-xyvFgJMgFW2eWZoN1oc0x9FGL6bH8WsF6O8,4897
19
+ idun_agent_engine/guardrails/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
+ idun_agent_engine/guardrails/base.py,sha256=vC554AbqOup9qjuOaTfG-w3fnd73J_7goMwab63jcIg,792
21
+ idun_agent_engine/guardrails/guardrails_hub/guardrails_hub.py,sha256=7fCFoL8soXVkFW6p-TUD6XyR5x3j3I2ekofUG__sXu8,3396
22
+ idun_agent_engine/guardrails/guardrails_hub/utils.py,sha256=bC6-MsCVF7xKTr48z7OzJJUeWvqAB7BiHeNTiKsme70,20
23
+ idun_agent_engine/mcp/__init__.py,sha256=tsX4sJa7UZD-lr8O1acMwDrdDWJc_hMlB9IrX1NH8Wk,255
24
+ idun_agent_engine/mcp/helpers.py,sha256=aFiLlk63pifBjGgctREFWxuSbb-um6QDOVpyikQ5NJ0,3224
25
+ idun_agent_engine/mcp/registry.py,sha256=K9-OLF_7FoKmyrwi65i6E3goZROHLRgISlF1yPwkEm8,3996
26
+ idun_agent_engine/observability/__init__.py,sha256=DCtK6b3xiX4dh0_8GBDOcSXQdcIJz2wTqqPa_ZFAES4,430
27
+ idun_agent_engine/observability/base.py,sha256=C6mY_ZafDEHkMz8_ZeaEh--pyYuzJYEUqhoJ0sVP2wY,5570
28
+ idun_agent_engine/observability/gcp_logging/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
29
+ idun_agent_engine/observability/gcp_logging/gcp_logging_handler.py,sha256=cMUKfukmSZhIdO2v5oWkqI2_c6umtYqlhzflnOeoq6A,1685
30
+ idun_agent_engine/observability/gcp_trace/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
31
+ idun_agent_engine/observability/gcp_trace/gcp_trace_handler.py,sha256=YBlUqUwPuOPi0yDNEy9x_g52nJBlSe9a0EEHDIHIr68,3898
32
+ idun_agent_engine/observability/langfuse/__init__.py,sha256=J8XcHV4aT1pF97k5EZiqrnYYPs9VjwfV5rUMihc5Pgk,128
33
+ idun_agent_engine/observability/langfuse/langfuse_handler.py,sha256=RP9MXiYDRe7zWTmZmzQBn6AjRn0y8jrBnWOaUUaMxNA,2795
34
+ idun_agent_engine/observability/phoenix/__init__.py,sha256=tEwJYijcvSGNhFW4QJmvBcTu1D0YVJkZRTmkNCGTteM,130
35
+ idun_agent_engine/observability/phoenix/phoenix_handler.py,sha256=lGqSq-L1vmoEhAr9rbWO3KlNX5HSgBhCKESHMdZ-AfY,2539
36
+ idun_agent_engine/observability/phoenix_local/__init__.py,sha256=m9dIw1GWGKAW4wP08jxA7j4yrOg0Nxq_08bwVh8YogE,146
37
+ idun_agent_engine/observability/phoenix_local/phoenix_local_handler.py,sha256=wjOZuMpAxdD5D33rzxycNEzFMunETpPnYjiHjbjz5GA,4252
38
+ idun_agent_engine/server/__init__.py,sha256=WaFektUsy37bNg2niAUy_TykzStukgWPnxC-t49CEwo,177
39
+ idun_agent_engine/server/dependencies.py,sha256=MVH8STOQ8wu-iYE_28y5dvw5FGT5PX0uQl0ldkpxw6Y,2080
40
+ idun_agent_engine/server/lifespan.py,sha256=REB93NTAivgYtZ9-7xFKb5RdDZSy3IrMAF5LM39-SAo,3886
41
+ idun_agent_engine/server/server_config.py,sha256=RYA7Y0c5aRw_WXaX8svFUIEtTPqzn3o-WQRm2p52C6g,213
42
+ idun_agent_engine/server/routers/__init__.py,sha256=BgNzSVvHtGPGn5zhXhomwpKlDYBkeFi7xCbdcWVOgc8,102
43
+ idun_agent_engine/server/routers/agent.py,sha256=OtviTU3nClyEHHUR5mJOMJvaNgcGR5CAkD5dGMA6Clk,8392
44
+ idun_agent_engine/server/routers/agui.py,sha256=Z1G4fuo57MazQWfp7ao8QZ1or2H9BXLS_JB1nFPXAkE,1891
45
+ idun_agent_engine/server/routers/base.py,sha256=wICOXCokCIRjmHuDPDTWqeXqOVPes1CtDtiR_HvtsF0,3756
46
+ idun_agent_engine/templates/__init__.py,sha256=xxbJZXaX6VEm_UrqzAOQcuujpEji5yqYzwQfwiqig8o,31
47
+ idun_agent_engine/templates/correction.py,sha256=qApH4W99K31maxNrGf0hPVe_Wd4xjYjJrEKlu7PbRpw,1823
48
+ idun_agent_engine/templates/deep_research.py,sha256=j4WypBkBz7V3EG-U8vawfNXDNhcCf2QiEK0veI4M68s,1077
49
+ idun_agent_engine/templates/translation.py,sha256=vjp1yEyhDMFO4nEbq5WG7LAA7nLrhVAjznOkV7ob_Ss,2149
50
+ idun_platform_cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
51
+ idun_platform_cli/main.py,sha256=jWL7Ob0p4KdRUqgPTP_EB68n7z2LyMKC2DeUsfWlBO4,200
52
+ idun_platform_cli/groups/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
53
+ idun_platform_cli/groups/agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
54
+ idun_platform_cli/groups/agent/main.py,sha256=QMGQi3JZ76SeFI3miIjVWpMt0L-hGz5FwxtTPQX4-Uw,301
55
+ idun_platform_cli/groups/agent/package.py,sha256=CdZCbqmDG_PFoI1EFPn6Ibl9Bexel_8eO7XgmzYVCRg,2525
56
+ idun_platform_cli/groups/agent/serve.py,sha256=2AbL0G1WqR33jlyiGaNvAoPZ3G1o52KYUptz_HaAjIg,3863
57
+ idun_agent_engine-0.3.0.dist-info/METADATA,sha256=prVc5VpdJJCfRNPVUTZyUxZmF4DCCC9wxpTC9TA7X-c,10648
58
+ idun_agent_engine-0.3.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
59
+ idun_agent_engine-0.3.0.dist-info/entry_points.txt,sha256=XG3oxlSOaCrYKT1oyhKa0Ag1iJPMZ-WF6gaV_mzIJW4,52
60
+ idun_agent_engine-0.3.0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.27.0
2
+ Generator: hatchling 1.28.0
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any