idun-agent-engine 0.2.1__tar.gz → 0.2.3__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 (43) hide show
  1. {idun_agent_engine-0.2.1 → idun_agent_engine-0.2.3}/PKG-INFO +7 -4
  2. {idun_agent_engine-0.2.1 → idun_agent_engine-0.2.3}/README.md +236 -236
  3. {idun_agent_engine-0.2.1 → idun_agent_engine-0.2.3}/pyproject.toml +19 -16
  4. {idun_agent_engine-0.2.1 → idun_agent_engine-0.2.3}/src/idun_agent_engine/_version.py +1 -1
  5. {idun_agent_engine-0.2.1 → idun_agent_engine-0.2.3}/src/idun_agent_engine/agent/langgraph/langgraph.py +3 -1
  6. {idun_agent_engine-0.2.1 → idun_agent_engine-0.2.3}/src/idun_agent_engine/observability/phoenix_local/phoenix_local_handler.py +1 -1
  7. idun_agent_engine-0.2.3/src/idun_platform_cli/__init__.py +0 -0
  8. idun_agent_engine-0.2.3/src/idun_platform_cli/groups/__init__.py +0 -0
  9. idun_agent_engine-0.2.3/src/idun_platform_cli/groups/agent/__init__.py +0 -0
  10. idun_agent_engine-0.2.3/src/idun_platform_cli/groups/agent/main.py +16 -0
  11. idun_agent_engine-0.2.3/src/idun_platform_cli/groups/agent/package.py +70 -0
  12. idun_agent_engine-0.2.3/src/idun_platform_cli/groups/agent/serve.py +104 -0
  13. idun_agent_engine-0.2.3/src/idun_platform_cli/main.py +14 -0
  14. {idun_agent_engine-0.2.1 → idun_agent_engine-0.2.3}/.gitignore +0 -0
  15. {idun_agent_engine-0.2.1 → idun_agent_engine-0.2.3}/src/idun_agent_engine/__init__.py +0 -0
  16. {idun_agent_engine-0.2.1 → idun_agent_engine-0.2.3}/src/idun_agent_engine/agent/__init__.py +0 -0
  17. {idun_agent_engine-0.2.1 → idun_agent_engine-0.2.3}/src/idun_agent_engine/agent/base.py +0 -0
  18. {idun_agent_engine-0.2.1 → idun_agent_engine-0.2.3}/src/idun_agent_engine/agent/haystack/__init__.py +0 -0
  19. {idun_agent_engine-0.2.1 → idun_agent_engine-0.2.3}/src/idun_agent_engine/agent/haystack/haystack.py +0 -0
  20. {idun_agent_engine-0.2.1 → idun_agent_engine-0.2.3}/src/idun_agent_engine/agent/haystack/haystack_model.py +0 -0
  21. {idun_agent_engine-0.2.1 → idun_agent_engine-0.2.3}/src/idun_agent_engine/agent/haystack/utils.py +0 -0
  22. {idun_agent_engine-0.2.1 → idun_agent_engine-0.2.3}/src/idun_agent_engine/agent/langgraph/__init__.py +0 -0
  23. {idun_agent_engine-0.2.1 → idun_agent_engine-0.2.3}/src/idun_agent_engine/cli/__init__.py +0 -0
  24. {idun_agent_engine-0.2.1 → idun_agent_engine-0.2.3}/src/idun_agent_engine/core/__init__.py +0 -0
  25. {idun_agent_engine-0.2.1 → idun_agent_engine-0.2.3}/src/idun_agent_engine/core/app_factory.py +0 -0
  26. {idun_agent_engine-0.2.1 → idun_agent_engine-0.2.3}/src/idun_agent_engine/core/config_builder.py +0 -0
  27. {idun_agent_engine-0.2.1 → idun_agent_engine-0.2.3}/src/idun_agent_engine/core/engine_config.py +0 -0
  28. {idun_agent_engine-0.2.1 → idun_agent_engine-0.2.3}/src/idun_agent_engine/core/server_runner.py +0 -0
  29. {idun_agent_engine-0.2.1 → idun_agent_engine-0.2.3}/src/idun_agent_engine/observability/__init__.py +0 -0
  30. {idun_agent_engine-0.2.1 → idun_agent_engine-0.2.3}/src/idun_agent_engine/observability/base.py +0 -0
  31. {idun_agent_engine-0.2.1 → idun_agent_engine-0.2.3}/src/idun_agent_engine/observability/langfuse/__init__.py +0 -0
  32. {idun_agent_engine-0.2.1 → idun_agent_engine-0.2.3}/src/idun_agent_engine/observability/langfuse/langfuse_handler.py +0 -0
  33. {idun_agent_engine-0.2.1 → idun_agent_engine-0.2.3}/src/idun_agent_engine/observability/phoenix/__init__.py +0 -0
  34. {idun_agent_engine-0.2.1 → idun_agent_engine-0.2.3}/src/idun_agent_engine/observability/phoenix/phoenix_handler.py +0 -0
  35. {idun_agent_engine-0.2.1 → idun_agent_engine-0.2.3}/src/idun_agent_engine/observability/phoenix_local/__init__.py +0 -0
  36. {idun_agent_engine-0.2.1 → idun_agent_engine-0.2.3}/src/idun_agent_engine/py.typed +0 -0
  37. {idun_agent_engine-0.2.1 → idun_agent_engine-0.2.3}/src/idun_agent_engine/server/__init__.py +0 -0
  38. {idun_agent_engine-0.2.1 → idun_agent_engine-0.2.3}/src/idun_agent_engine/server/dependencies.py +0 -0
  39. {idun_agent_engine-0.2.1 → idun_agent_engine-0.2.3}/src/idun_agent_engine/server/lifespan.py +0 -0
  40. {idun_agent_engine-0.2.1 → idun_agent_engine-0.2.3}/src/idun_agent_engine/server/routers/__init__.py +0 -0
  41. {idun_agent_engine-0.2.1 → idun_agent_engine-0.2.3}/src/idun_agent_engine/server/routers/agent.py +0 -0
  42. {idun_agent_engine-0.2.1 → idun_agent_engine-0.2.3}/src/idun_agent_engine/server/routers/base.py +0 -0
  43. {idun_agent_engine-0.2.1 → idun_agent_engine-0.2.3}/src/idun_agent_engine/server/server_config.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: idun-agent-engine
3
- Version: 0.2.1
3
+ Version: 0.2.3
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
@@ -14,18 +14,20 @@ Classifier: Intended Audience :: Developers
14
14
  Classifier: License :: OSI Approved :: MIT License
15
15
  Classifier: Programming Language :: Python :: 3
16
16
  Classifier: Programming Language :: Python :: 3 :: Only
17
+ Classifier: Programming Language :: Python :: 3.12
17
18
  Classifier: Programming Language :: Python :: 3.13
18
19
  Classifier: Topic :: Software Development :: Libraries
19
20
  Classifier: Typing :: Typed
20
- Requires-Python: <3.14,>=3.13
21
+ Requires-Python: <3.14,>=3.12
21
22
  Requires-Dist: ag-ui-protocol<0.2.0,>=0.1.8
22
23
  Requires-Dist: aiosqlite<0.22.0,>=0.21.0
23
24
  Requires-Dist: arize-phoenix-otel<1.0.0,>=0.2.0
24
- Requires-Dist: arize-phoenix<12,>=11.22.0
25
+ Requires-Dist: arize-phoenix<12.0.0,>=11.22.0
26
+ Requires-Dist: click>=8.2.1
25
27
  Requires-Dist: fastapi<0.117.0,>=0.116.1
26
28
  Requires-Dist: google-adk<2.0.0,>=1.9.0
27
29
  Requires-Dist: httpx<0.29.0,>=0.28.1
28
- Requires-Dist: idun-agent-schema<0.3.0,>=0.2.0
30
+ Requires-Dist: idun-agent-schema<0.3.0,>=0.2.3
29
31
  Requires-Dist: langchain-core<0.4.0,>=0.3.72
30
32
  Requires-Dist: langchain-google-vertexai<3.0.0,>=2.0.27
31
33
  Requires-Dist: langchain<0.4,>=0.3.27
@@ -36,6 +38,7 @@ Requires-Dist: langgraph<0.7.0,>=0.6.3
36
38
  Requires-Dist: openinference-instrumentation-langchain<1.0.0,>=0.1.13
37
39
  Requires-Dist: pydantic<3.0.0,>=2.11.7
38
40
  Requires-Dist: python-dotenv>=1.1.1
41
+ Requires-Dist: sqlalchemy<3.0.0,>=2.0.36
39
42
  Requires-Dist: streamlit<2.0.0,>=1.47.1
40
43
  Requires-Dist: uvicorn<0.36.0,>=0.35.0
41
44
  Description-Content-Type: text/markdown
@@ -1,236 +1,236 @@
1
- # Idun Agent Engine
2
-
3
- Turn any LangGraph-based agent into a production-grade API in minutes.
4
-
5
- Idun Agent Engine is a lightweight runtime and SDK that wraps your agent with a FastAPI server, adds streaming, structured responses, config validation, and optional observability — with zero boilerplate. Use a YAML file or a fluent builder to configure and run.
6
-
7
- ## Installation
8
-
9
- ```bash
10
- pip install idun-agent-engine
11
- ```
12
-
13
- - Requires Python 3.13
14
- - Ships with FastAPI, Uvicorn, LangGraph, SQLite checkpointing, and optional observability hooks
15
-
16
- ## Quickstart
17
-
18
- ### 1) Minimal one-liner (from a YAML config)
19
-
20
- ```python
21
- from idun_agent_engine.core.server_runner import run_server_from_config
22
-
23
- run_server_from_config("config.yaml")
24
- ```
25
-
26
- Example `config.yaml`:
27
-
28
- ```yaml
29
- server:
30
- api:
31
- port: 8000
32
-
33
- agent:
34
- type: "langgraph"
35
- config:
36
- name: "My Example LangGraph Agent"
37
- graph_definition: "./examples/01_basic_config_file/example_agent.py:app"
38
- # Optional: conversation persistence
39
- checkpointer:
40
- type: "sqlite"
41
- db_url: "sqlite:///example_checkpoint.db"
42
- # Optional: provider-agnostic observability
43
- observability:
44
- provider: langfuse # or phoenix
45
- enabled: true
46
- options:
47
- host: ${LANGFUSE_HOST}
48
- public_key: ${LANGFUSE_PUBLIC_KEY}
49
- secret_key: ${LANGFUSE_SECRET_KEY}
50
- run_name: "idun-langgraph-run"
51
- ```
52
-
53
- Run and open docs at `http://localhost:8000/docs`.
54
-
55
- ### 2) Programmatic setup with the fluent builder
56
-
57
- ```python
58
- from pathlib import Path
59
- from idun_agent_engine import ConfigBuilder, create_app, run_server
60
-
61
- config = (
62
- ConfigBuilder()
63
- .with_api_port(8000)
64
- .with_langgraph_agent(
65
- name="Programmatic Example Agent",
66
- graph_definition=str(Path("./examples/02_programmatic_config/smart_agent.py:app")),
67
- sqlite_checkpointer="programmatic_example.db",
68
- )
69
- .build()
70
- )
71
-
72
- app = create_app(engine_config=config)
73
- run_server(app, reload=True)
74
- ```
75
-
76
- ## Endpoints
77
-
78
- All servers expose these by default:
79
-
80
- - POST `/agent/invoke`: single request/response
81
- - POST `/agent/stream`: server-sent events stream of `ag-ui` protocol events
82
- - GET `/health`: service health with engine version
83
- - GET `/`: root landing with links
84
-
85
- Invoke example:
86
-
87
- ```bash
88
- curl -X POST "http://localhost:8000/agent/invoke" \
89
- -H "Content-Type: application/json" \
90
- -d '{"query": "Hello!", "session_id": "user-123"}'
91
- ```
92
-
93
- Stream example:
94
-
95
- ```bash
96
- curl -N -X POST "http://localhost:8000/agent/stream" \
97
- -H "Content-Type: application/json" \
98
- -d '{"query": "Tell me a story", "session_id": "user-123"}'
99
- ```
100
-
101
- ## LangGraph integration
102
-
103
- Point the engine to a `StateGraph` variable in your file using `graph_definition`:
104
-
105
- ```python
106
- # examples/01_basic_config_file/example_agent.py
107
- import operator
108
- from typing import Annotated, TypedDict
109
- from langgraph.graph import END, StateGraph
110
-
111
- class AgentState(TypedDict):
112
- messages: Annotated[list, operator.add]
113
-
114
- def greeting_node(state):
115
- user_message = state["messages"][-1] if state["messages"] else ""
116
- return {"messages": [("ai", f"Hello! You said: '{user_message}'")]}
117
-
118
- graph = StateGraph(AgentState)
119
- graph.add_node("greet", greeting_node)
120
- graph.set_entry_point("greet")
121
- graph.add_edge("greet", END)
122
-
123
- # This variable name is referenced by graph_definition
124
- app = graph
125
- ```
126
-
127
- Then reference it in config:
128
-
129
- ```yaml
130
- agent:
131
- type: "langgraph"
132
- config:
133
- graph_definition: "./examples/01_basic_config_file/example_agent.py:app"
134
- ```
135
-
136
- Behind the scenes, the engine:
137
-
138
- - Validates config with Pydantic models
139
- - Loads your `StateGraph` from disk
140
- - Optionally wires a SQLite checkpointer via `langgraph.checkpoint.sqlite`
141
- - Exposes `invoke` and `stream` endpoints
142
- - Bridges LangGraph events to `ag-ui` stream events
143
-
144
- ## Observability (optional)
145
-
146
- Enable provider-agnostic observability via the `observability` block in your agent config. Today supports Langfuse and Arize Phoenix (OpenInference) patterns; more coming soon.
147
-
148
- ```yaml
149
- agent:
150
- type: "langgraph"
151
- config:
152
- observability:
153
- provider: langfuse # or phoenix
154
- enabled: true
155
- options:
156
- host: ${LANGFUSE_HOST}
157
- public_key: ${LANGFUSE_PUBLIC_KEY}
158
- secret_key: ${LANGFUSE_SECRET_KEY}
159
- run_name: "idun-langgraph-run"
160
- ```
161
-
162
- ## Configuration reference
163
-
164
- - `server.api.port` (int): HTTP port (default 8000)
165
- - `agent.type` (enum): currently `langgraph` (CrewAI placeholder exists but not implemented)
166
- - `agent.config.name` (str): human-readable name
167
- - `agent.config.graph_definition` (str): absolute or relative `path/to/file.py:variable`
168
- - `agent.config.checkpointer` (sqlite): `{ type: "sqlite", db_url: "sqlite:///file.db" }`
169
- - `agent.config.observability` (optional): provider options as shown above
170
-
171
- Config can be sourced by:
172
-
173
- - `engine_config` (preferred): pass a validated `EngineConfig` to `create_app`
174
- - `config_dict`: dict validated at runtime
175
- - `config_path`: path to YAML; defaults to `config.yaml`
176
-
177
- ## Examples
178
-
179
- The `examples/` folder contains complete projects:
180
-
181
- - `01_basic_config_file`: YAML config + simple agent
182
- - `02_programmatic_config`: `ConfigBuilder` usage and advanced flows
183
- - `03_minimal_setup`: one-line server from config
184
-
185
- Run any example with Python 3.13 installed.
186
-
187
- ## CLI and runtime helpers
188
-
189
- Top-level imports for convenience:
190
-
191
- ```python
192
- from idun_agent_engine import (
193
- create_app,
194
- run_server,
195
- run_server_from_config,
196
- run_server_from_builder,
197
- ConfigBuilder,
198
- )
199
- ```
200
-
201
- - `create_app(...)` builds the FastAPI app and registers routes
202
- - `run_server(app, ...)` runs with Uvicorn
203
- - `run_server_from_config(path, ...)` loads config, builds app, and runs
204
- - `run_server_from_builder(builder, ...)` builds from a builder and runs
205
-
206
- ## Production notes
207
-
208
- - Use a process manager (e.g., multiple Uvicorn workers behind a gateway). Note: `reload=True` is for development and incompatible with multi-worker mode.
209
- - Mount behind a reverse proxy and enable TLS where appropriate.
210
- - Persist conversations using the SQLite checkpointer in production or replace with a custom checkpointer when available.
211
-
212
- ## Roadmap
213
-
214
- - CrewAI adapter (placeholder exists, not yet implemented)
215
- - Additional stores and checkpointers
216
- - First-class CLI for `idun` commands
217
-
218
- ## Contributing
219
-
220
- Issues and PRs are welcome. See the repository:
221
-
222
- - Repo: `https://github.com/geoffreyharrazi/idun-agent-platform`
223
- - Package path: `libs/idun_agent_engine`
224
- - Open an issue: `https://github.com/geoffreyharrazi/idun-agent-platform/issues`
225
-
226
- Run locally:
227
-
228
- ```bash
229
- cd libs/idun_agent_engine
230
- poetry install
231
- poetry run pytest -q
232
- ```
233
-
234
- ## License
235
-
236
- MIT — see `LICENSE` in the repo root.
1
+ # Idun Agent Engine
2
+
3
+ Turn any LangGraph-based agent into a production-grade API in minutes.
4
+
5
+ Idun Agent Engine is a lightweight runtime and SDK that wraps your agent with a FastAPI server, adds streaming, structured responses, config validation, and optional observability — with zero boilerplate. Use a YAML file or a fluent builder to configure and run.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ pip install idun-agent-engine
11
+ ```
12
+
13
+ - Requires Python 3.13
14
+ - Ships with FastAPI, Uvicorn, LangGraph, SQLite checkpointing, and optional observability hooks
15
+
16
+ ## Quickstart
17
+
18
+ ### 1) Minimal one-liner (from a YAML config)
19
+
20
+ ```python
21
+ from idun_agent_engine.core.server_runner import run_server_from_config
22
+
23
+ run_server_from_config("config.yaml")
24
+ ```
25
+
26
+ Example `config.yaml`:
27
+
28
+ ```yaml
29
+ server:
30
+ api:
31
+ port: 8000
32
+
33
+ agent:
34
+ type: "langgraph"
35
+ config:
36
+ name: "My Example LangGraph Agent"
37
+ graph_definition: "./examples/01_basic_config_file/example_agent.py:app"
38
+ # Optional: conversation persistence
39
+ checkpointer:
40
+ type: "sqlite"
41
+ db_url: "sqlite:///example_checkpoint.db"
42
+ # Optional: provider-agnostic observability
43
+ observability:
44
+ provider: langfuse # or phoenix
45
+ enabled: true
46
+ options:
47
+ host: ${LANGFUSE_HOST}
48
+ public_key: ${LANGFUSE_PUBLIC_KEY}
49
+ secret_key: ${LANGFUSE_SECRET_KEY}
50
+ run_name: "idun-langgraph-run"
51
+ ```
52
+
53
+ Run and open docs at `http://localhost:8000/docs`.
54
+
55
+ ### 2) Programmatic setup with the fluent builder
56
+
57
+ ```python
58
+ from pathlib import Path
59
+ from idun_agent_engine import ConfigBuilder, create_app, run_server
60
+
61
+ config = (
62
+ ConfigBuilder()
63
+ .with_api_port(8000)
64
+ .with_langgraph_agent(
65
+ name="Programmatic Example Agent",
66
+ graph_definition=str(Path("./examples/02_programmatic_config/smart_agent.py:app")),
67
+ sqlite_checkpointer="programmatic_example.db",
68
+ )
69
+ .build()
70
+ )
71
+
72
+ app = create_app(engine_config=config)
73
+ run_server(app, reload=True)
74
+ ```
75
+
76
+ ## Endpoints
77
+
78
+ All servers expose these by default:
79
+
80
+ - POST `/agent/invoke`: single request/response
81
+ - POST `/agent/stream`: server-sent events stream of `ag-ui` protocol events
82
+ - GET `/health`: service health with engine version
83
+ - GET `/`: root landing with links
84
+
85
+ Invoke example:
86
+
87
+ ```bash
88
+ curl -X POST "http://localhost:8000/agent/invoke" \
89
+ -H "Content-Type: application/json" \
90
+ -d '{"query": "Hello!", "session_id": "user-123"}'
91
+ ```
92
+
93
+ Stream example:
94
+
95
+ ```bash
96
+ curl -N -X POST "http://localhost:8000/agent/stream" \
97
+ -H "Content-Type: application/json" \
98
+ -d '{"query": "Tell me a story", "session_id": "user-123"}'
99
+ ```
100
+
101
+ ## LangGraph integration
102
+
103
+ Point the engine to a `StateGraph` variable in your file using `graph_definition`:
104
+
105
+ ```python
106
+ # examples/01_basic_config_file/example_agent.py
107
+ import operator
108
+ from typing import Annotated, TypedDict
109
+ from langgraph.graph import END, StateGraph
110
+
111
+ class AgentState(TypedDict):
112
+ messages: Annotated[list, operator.add]
113
+
114
+ def greeting_node(state):
115
+ user_message = state["messages"][-1] if state["messages"] else ""
116
+ return {"messages": [("ai", f"Hello! You said: '{user_message}'")]}
117
+
118
+ graph = StateGraph(AgentState)
119
+ graph.add_node("greet", greeting_node)
120
+ graph.set_entry_point("greet")
121
+ graph.add_edge("greet", END)
122
+
123
+ # This variable name is referenced by graph_definition
124
+ app = graph
125
+ ```
126
+
127
+ Then reference it in config:
128
+
129
+ ```yaml
130
+ agent:
131
+ type: "langgraph"
132
+ config:
133
+ graph_definition: "./examples/01_basic_config_file/example_agent.py:app"
134
+ ```
135
+
136
+ Behind the scenes, the engine:
137
+
138
+ - Validates config with Pydantic models
139
+ - Loads your `StateGraph` from disk
140
+ - Optionally wires a SQLite checkpointer via `langgraph.checkpoint.sqlite`
141
+ - Exposes `invoke` and `stream` endpoints
142
+ - Bridges LangGraph events to `ag-ui` stream events
143
+
144
+ ## Observability (optional)
145
+
146
+ Enable provider-agnostic observability via the `observability` block in your agent config. Today supports Langfuse and Arize Phoenix (OpenInference) patterns; more coming soon.
147
+
148
+ ```yaml
149
+ agent:
150
+ type: "langgraph"
151
+ config:
152
+ observability:
153
+ provider: langfuse # or phoenix
154
+ enabled: true
155
+ options:
156
+ host: ${LANGFUSE_HOST}
157
+ public_key: ${LANGFUSE_PUBLIC_KEY}
158
+ secret_key: ${LANGFUSE_SECRET_KEY}
159
+ run_name: "idun-langgraph-run"
160
+ ```
161
+
162
+ ## Configuration reference
163
+
164
+ - `server.api.port` (int): HTTP port (default 8000)
165
+ - `agent.type` (enum): currently `langgraph` (CrewAI placeholder exists but not implemented)
166
+ - `agent.config.name` (str): human-readable name
167
+ - `agent.config.graph_definition` (str): absolute or relative `path/to/file.py:variable`
168
+ - `agent.config.checkpointer` (sqlite): `{ type: "sqlite", db_url: "sqlite:///file.db" }`
169
+ - `agent.config.observability` (optional): provider options as shown above
170
+
171
+ Config can be sourced by:
172
+
173
+ - `engine_config` (preferred): pass a validated `EngineConfig` to `create_app`
174
+ - `config_dict`: dict validated at runtime
175
+ - `config_path`: path to YAML; defaults to `config.yaml`
176
+
177
+ ## Examples
178
+
179
+ The `examples/` folder contains complete projects:
180
+
181
+ - `01_basic_config_file`: YAML config + simple agent
182
+ - `02_programmatic_config`: `ConfigBuilder` usage and advanced flows
183
+ - `03_minimal_setup`: one-line server from config
184
+
185
+ Run any example with Python 3.13 installed.
186
+
187
+ ## CLI and runtime helpers
188
+
189
+ Top-level imports for convenience:
190
+
191
+ ```python
192
+ from idun_agent_engine import (
193
+ create_app,
194
+ run_server,
195
+ run_server_from_config,
196
+ run_server_from_builder,
197
+ ConfigBuilder,
198
+ )
199
+ ```
200
+
201
+ - `create_app(...)` builds the FastAPI app and registers routes
202
+ - `run_server(app, ...)` runs with Uvicorn
203
+ - `run_server_from_config(path, ...)` loads config, builds app, and runs
204
+ - `run_server_from_builder(builder, ...)` builds from a builder and runs
205
+
206
+ ## Production notes
207
+
208
+ - Use a process manager (e.g., multiple Uvicorn workers behind a gateway). Note: `reload=True` is for development and incompatible with multi-worker mode.
209
+ - Mount behind a reverse proxy and enable TLS where appropriate.
210
+ - Persist conversations using the SQLite checkpointer in production or replace with a custom checkpointer when available.
211
+
212
+ ## Roadmap
213
+
214
+ - CrewAI adapter (placeholder exists, not yet implemented)
215
+ - Additional stores and checkpointers
216
+ - First-class CLI for `idun` commands
217
+
218
+ ## Contributing
219
+
220
+ Issues and PRs are welcome. See the repository:
221
+
222
+ - Repo: `https://github.com/geoffreyharrazi/idun-agent-platform`
223
+ - Package path: `libs/idun_agent_engine`
224
+ - Open an issue: `https://github.com/geoffreyharrazi/idun-agent-platform/issues`
225
+
226
+ Run locally:
227
+
228
+ ```bash
229
+ cd libs/idun_agent_engine
230
+ poetry install
231
+ poetry run pytest -q
232
+ ```
233
+
234
+ ## License
235
+
236
+ MIT — see `LICENSE` in the repo root.
@@ -1,9 +1,9 @@
1
1
  [project]
2
2
  name = "idun-agent-engine"
3
- version = "0.2.1"
3
+ version = "0.2.3"
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
- requires-python = ">=3.13,<3.14"
6
+ requires-python = ">=3.12,<3.14"
7
7
  readme = "README.md"
8
8
  license = "MIT"
9
9
  keywords = [
@@ -18,6 +18,7 @@ classifiers = [
18
18
  "License :: OSI Approved :: MIT License",
19
19
  "Programming Language :: Python :: 3",
20
20
  "Programming Language :: Python :: 3 :: Only",
21
+ "Programming Language :: Python :: 3.12",
21
22
  "Programming Language :: Python :: 3.13",
22
23
  "Framework :: FastAPI",
23
24
  "Intended Audience :: Developers",
@@ -41,14 +42,21 @@ dependencies = [
41
42
  "arize-phoenix-otel>=0.2.0,<1.0.0",
42
43
  "openinference-instrumentation-langchain>=0.1.13,<1.0.0",
43
44
  "langchain>=0.3.27,<0.4",
44
- "arize-phoenix>=11.22.0,<12",
45
- # Centralized schema package
46
- "idun-agent-schema>=0.2.0,<0.3.0",
47
- # "haystack-ai>=2.17.1",
45
+ # Pin <12: 12.x currently depends on placeholder jmespath==99.99.99 and fails in Docker
46
+ "arize-phoenix>=11.22.0,<12.0.0",
47
+ "idun-agent-schema>=0.2.3,<0.3.0",
48
48
  "langfuse-haystack>=2.3.0",
49
49
  "python-dotenv>=1.1.1",
50
+ "click>=8.2.1",
51
+ # Pin SQLAlchemy for Python 3.13 compatibility (required by Phoenix)
52
+ "sqlalchemy>=2.0.36,<3.0.0",
50
53
  ]
51
54
 
55
+
56
+ [project.scripts]
57
+ idun = "idun_platform_cli.main:cli"
58
+
59
+
52
60
  [project.urls]
53
61
  Homepage = "https://github.com/geoffreyharrazi/idun-agent-platform"
54
62
  Repository = "https://github.com/geoffreyharrazi/idun-agent-platform"
@@ -71,16 +79,11 @@ dev = [
71
79
  include = [
72
80
  "src/idun_agent_engine",
73
81
  "src/idun_agent_engine/py.typed",
82
+ "src/idun_platform_cli",
74
83
  ]
75
84
 
76
85
  [tool.hatch.build.targets.wheel]
77
- include = [
78
- "src/idun_agent_engine",
79
- "src/idun_agent_engine/py.typed",
80
- ]
81
-
82
- [tool.hatch.build.targets.wheel.sources]
83
- "src/idun_agent_engine" = "idun_agent_engine"
86
+ packages = ["src/idun_agent_engine", "src/idun_platform_cli"]
84
87
 
85
88
  [build-system]
86
89
  requires = ["hatchling"]
@@ -88,7 +91,7 @@ build-backend = "hatchling.build"
88
91
 
89
92
  [tool.black]
90
93
  line-length = 88
91
- target-version = ["py313"]
94
+ target-version = ["py312", "py313"]
92
95
  include = "\\.pyi?$"
93
96
  exclude = '''
94
97
  (
@@ -100,7 +103,7 @@ exclude = '''
100
103
  '''
101
104
 
102
105
  [tool.ruff]
103
- target-version = "py313"
106
+ target-version = "py312"
104
107
  line-length = 88
105
108
  extend-exclude = [
106
109
  "build",
@@ -132,7 +135,7 @@ extend-ignore = [
132
135
  convention = "google"
133
136
 
134
137
  [tool.mypy]
135
- python_version = "3.13"
138
+ python_version = "3.12"
136
139
  warn_return_any = true
137
140
  warn_unused_ignores = true
138
141
  ignore_missing_imports = true
@@ -1,3 +1,3 @@
1
1
  """Version information for Idun Agent Engine."""
2
2
 
3
- __version__ = "0.2.1"
3
+ __version__ = "0.2.3"
@@ -207,8 +207,10 @@ class LanggraphAgent(agent_base.BaseAgent):
207
207
  ) from None
208
208
 
209
209
  try:
210
+ from pathlib import Path
211
+ resolved_path = Path(module_path).resolve()
210
212
  spec = importlib.util.spec_from_file_location(
211
- graph_variable_name, module_path
213
+ graph_variable_name, str(resolved_path)
212
214
  )
213
215
  if spec is None or spec.loader is None:
214
216
  raise ImportError(f"Could not load spec for module at {module_path}")
@@ -23,7 +23,7 @@ class PhoenixLocalHandler(ObservabilityHandlerBase):
23
23
  def __init__(
24
24
  self,
25
25
  options: dict[str, Any] | None = None,
26
- default_endpoint: str = "http://127.0.0.1:6006",
26
+ default_endpoint: str = "http://0.0.0.0:6006",
27
27
  ):
28
28
  """Initialize handler, start Phoenix via CLI, and set up instrumentation.
29
29
 
@@ -0,0 +1,16 @@
1
+ """Manage, Deploy and package agents."""
2
+
3
+ import click
4
+
5
+ from .package import package_command
6
+ from .serve import serve_command
7
+
8
+
9
+ @click.group()
10
+ def agent():
11
+ """Agent command entrypoint."""
12
+ pass
13
+
14
+
15
+ agent.add_command(serve_command, name="serve")
16
+ agent.add_command(package_command, name="package")
@@ -0,0 +1,70 @@
1
+ import sys
2
+ from enum import StrEnum
3
+ from pathlib import Path
4
+
5
+ import click
6
+
7
+
8
+ class Dependency(StrEnum):
9
+ """Dependency Enum."""
10
+
11
+ REQUIREMENT = "requirements.txt"
12
+ PYPROJECT = "pyproject.toml"
13
+ NONE = "none"
14
+
15
+
16
+ def get_dependencies(path: str) -> Dependency:
17
+ """Verifies if the path folder contains a `requirements.txt` or `pyproject.toml`, and returns which."""
18
+ """:param path: Path pointing to the agent's folder."""
19
+ agent_path = Path(path).resolve()
20
+ if (agent_path / "requirements.txt").exists():
21
+ return Dependency.REQUIREMENT
22
+ elif (agent_path / "pyproject.toml").exists():
23
+ return Dependency.PYPROJECT
24
+ else:
25
+ return Dependency.NONE
26
+
27
+
28
+ def generate_dockerfile(dependency: Dependency) -> str:
29
+ # TODO: add envs vars based on source
30
+ """Generates Dockerfile based on given params."""
31
+ if dependency == Dependency.NONE:
32
+ print(
33
+ "[ERROR]: No pyproject.toml or requirements.txt found. Please make sure to include them."
34
+ )
35
+ sys.exit(1)
36
+ return "" # Unreachable, but satisfies type checker
37
+ if dependency == Dependency.REQUIREMENT:
38
+ # TODO: use from file
39
+ requirements_dockerfile = f"""FROM python:3.13-slim
40
+ RUN apt-get update && pip install uv
41
+
42
+ RUN uv pip install idun-agent-schema==0.2.3 --system
43
+ RUN uv pip install idun-agent-engine==0.2.3 --system
44
+
45
+ COPY . .
46
+ RUN uv pip install -r requirements.txt --system
47
+
48
+ CMD ["idun", "agent", "serve", "--source=manager"]
49
+ """
50
+ return requirements_dockerfile
51
+ if dependency == Dependency.PYPROJECT:
52
+ # TODO: implement pyproject.toml support
53
+ raise NotImplementedError("pyproject.toml support is not yet implemented")
54
+ raise ValueError(f"Unknown dependency type: {dependency}")
55
+
56
+
57
+ @click.command("package")
58
+ @click.argument("path", default=".")
59
+ @click.option("--target", required=False, default=".")
60
+ def package_command(path: str, target: str):
61
+ """Packages the agent and it's dependencies into a Dockerfile. You can specifiy the input path and the destination. Defaults to current directory."""
62
+ dependency = get_dependencies(path)
63
+ dockerfile = generate_dockerfile(dependency)
64
+ target_path = Path(target)
65
+ dockerfile_path = target_path / "Dockerfile"
66
+ try:
67
+ dockerfile_path.write_text(dockerfile)
68
+ print(f"Dockerfile generated in {target}")
69
+ except OSError as e:
70
+ print(f"[ERROR]: Cannot write dockerfile to path {target}: {e}")
@@ -0,0 +1,104 @@
1
+ import os
2
+ import sys
3
+ from enum import StrEnum
4
+
5
+ import click
6
+ from idun_agent_engine.core.app_factory import create_app
7
+ from idun_agent_engine.core.config_builder import ConfigBuilder
8
+ from idun_agent_engine.core.engine_config import EngineConfig
9
+ from idun_agent_engine.core.server_runner import run_server
10
+
11
+
12
+ class ServerSource(StrEnum):
13
+ """Enum for source types."""
14
+
15
+ MANAGER = "manager"
16
+ FILE = "file"
17
+
18
+
19
+ class Serve:
20
+ """Helper class to run the server."""
21
+
22
+ def __init__(self, source: ServerSource, path: str | None = None) -> None:
23
+ self._source: ServerSource = source
24
+ self._path: str | None = path or None
25
+
26
+ if self._source == ServerSource.MANAGER and (
27
+ not os.getenv("IDUN_AGENT_API_KEY") or not os.getenv("IDUN_MANAGER_HOST")
28
+ ):
29
+ print(
30
+ "[ERROR]: either IDUN_AGENT_API_KEY or IDUN_MANAGER_HOST are not found. Make sure you add them both to your env variables, as `manager` source requires both."
31
+ )
32
+ sys.exit(1)
33
+
34
+ if self._source == ServerSource.MANAGER:
35
+ self._url: str = os.environ["IDUN_MANAGER_HOST"]
36
+ self._agent_api_key: str = os.environ["IDUN_AGENT_API_KEY"]
37
+
38
+ self._config: EngineConfig | None = self._resolve_source()
39
+
40
+ def _resolve_source(self):
41
+ """Returns the EngineConfig based on the type of the source."""
42
+ if self._source == ServerSource.MANAGER:
43
+ print("Getting the config for the manager...")
44
+ return self._fetch_from_manager()
45
+ elif self._source == ServerSource.FILE:
46
+ print(f"Building config from: {self._path}")
47
+ return self._fetch_from_path()
48
+
49
+ def _fetch_from_path(self) -> EngineConfig | None:
50
+ try:
51
+ config = ConfigBuilder().load_from_file(self._path)
52
+ print(f"Successfully fetched and built config from {self._path}")
53
+ return config
54
+
55
+ except Exception as e:
56
+ print(f"[ERROR]: Cannot fetch config from {self._path}: {e} ")
57
+ sys.exit(1)
58
+
59
+ def _fetch_from_manager(self) -> EngineConfig | None:
60
+ """Fetches the config from the api."""
61
+ try:
62
+ config = (
63
+ ConfigBuilder()
64
+ .with_config_from_api(agent_api_key=self._agent_api_key, url=self._url)
65
+ .build()
66
+ )
67
+ print(f"Successfully fetched and built config from {self._url}")
68
+ return config
69
+ except Exception as e:
70
+ print(f"[ERROR]: Cannot fetch config from {self._url}: {e} ")
71
+ sys.exit(1)
72
+
73
+ def serve(self) -> None:
74
+ """Run the server using the idun engine."""
75
+ try:
76
+ app = create_app(engine_config=self._config)
77
+ run_server(app, port=self._config.server.api.port, reload=False) # pyright: ignore
78
+ except Exception as e:
79
+ raise ValueError(f"[ERROR]: Cannot start the agent server: {e}") from e
80
+
81
+
82
+ @click.command("serve")
83
+ @click.option("--source", required=True)
84
+ @click.option("--path")
85
+ def serve_command(source: str, path: str | None):
86
+ """Reads a config and exposes it's agent as an API. Config is either fetched from the manager, or from a path.
87
+
88
+ Note: Fetching from the manager requires env vars: IDUN_AGENT_API_KEY and IDUN_MANAGER_HOST.
89
+ """
90
+ match source:
91
+ case ServerSource.MANAGER:
92
+ s = Serve(source=source)
93
+ s.serve()
94
+
95
+ case ServerSource.FILE:
96
+ if not path:
97
+ print(
98
+ "[ERROR]: No config path provided. You need to specify the path of your config.yaml"
99
+ )
100
+ sys.exit(1)
101
+ s = Serve(source=source, path=path)
102
+ s.serve()
103
+ case _:
104
+ print(f"[ERROR]: Argument {source} not recognized.")
@@ -0,0 +1,14 @@
1
+ import click
2
+ from idun_platform_cli.groups.agent.main import agent
3
+
4
+
5
+ @click.group()
6
+ def cli():
7
+ """Entrypoint of the CLI."""
8
+ pass
9
+
10
+
11
+ cli.add_command(agent)
12
+
13
+ if __name__ == "__main__":
14
+ cli()