Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 16 additions & 9 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,25 @@
"type": "debugpy",
"request": "launch",
"console": "integratedTerminal",
"module": "uvicorn",
"module": "idun_platform_cli.main",
// "args": [
// "main:app",
// "--host",
// "0.0.0.0",
// "--port",
// "8100",
// "--reload"
// ],
"args": [
"main:app",
"--host",
"0.0.0.0",
"--port",
"8100",
"--reload"
"agent", "serve",
"--source",
"file",
"--path",
"${workspaceFolder}/libs/idun_agent_engine/examples/01_basic_config_file/config.yaml",
],
"cwd": "${workspaceFolder}/libs/idun_agent_engine/examples/01_basic_config_file",
"cwd": "${workspaceFolder}/libs/idun_agent_engine",
"justMyCode": false,
"envFile": "${workspaceFolder}/libs/idun_agent_engine/examples/01_basic_config_file/.env"
"envFile": "${workspaceFolder}/libs/idun_agent_engine/.env"
},
],
"compounds": [
Expand Down
19 changes: 19 additions & 0 deletions bruno/idun_agent_engine/Copilotkit stream.bru
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
meta {
name: Copilotkit stream
type: http
seq: 4
}

post {
url: http://0.0.0.0:8001/agent/copilotkit/stream
body: json
auth: none
}

headers {
content-type: application/json
}

body:json {
{"threadId":"thread-001","runId":"run-001","state":{},"messages":[{"id":"msg-001","role":"user","content":"Hello! boss"}],"tools":[],"context":[],"forwardedProps":{}}
}
18 changes: 18 additions & 0 deletions docker-compose.dev.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,24 @@ services:
depends_on:
- manager

web:
image: node:lts-alpine
container_name: idun-web-dev
working_dir: /app
command: sh -lc "npm install && npm run dev -- --host 0.0.0.0 --port 3000"
ports:
- "3000:3000"
volumes:
- ./services/idun_agent_web:/app
- /app/node_modules
environment:
- VITE_API_URL=http://localhost:8000 # http://host.docker.internal:8000
- CHOKIDAR_USEPOLLING=true
networks:
- idun_dev_network
depends_on:
- manager

volumes:
postgres_data:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ RUN apt-get update && pip install uv
# --extra-index-url https://pypi.org/simple idun-agent-engine==0.2.1.dev20251118083707 \
# --index-strategy unsafe-best-match --prerelease=allow

RUN uv pip install idun-agent-schema==0.2.6 --system
RUN uv pip install idun-agent-engine==0.2.6 --system
RUN uv pip install idun-agent-schema==0.2.7 --system
RUN uv pip install idun-agent-engine==0.2.7 --system

COPY requirements.txt .
COPY config.yaml .
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@
# Server Configuration - Controls the server and platform behavior
server:
api:
port: 8000 # Port where the server will run
port: 8001 # Port where the server will run

# Agent Configuration - Defines which agent to load and how
agent:
type: "LANGGRAPH" # Agent framework type
# Agent-specific configuration (passed to the LangGraph adapter)
config:
name: "My Example LangGraph Agent"
graph_definition: "agent/example_agent.py:app" # Path to your graph definition
graph_definition: "./examples/01_basic_config_file/example_agent.py:app" # Path to your graph definition
# Observability (provider-agnostic). Example with Langfuse or Phoenix
observability:
provider: langfuse #langfuse # or phoenix
Expand Down
10 changes: 6 additions & 4 deletions libs/idun_agent_engine/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "idun-agent-engine"
version = "0.2.6"
version = "0.2.7"
description = "Python SDK and runtime to serve AI agents with FastAPI, LangGraph, and observability."
authors = [{ name = "Geoffrey HARRAZI", email = "[email protected]" }]
requires-python = ">=3.12,<3.14"
Expand All @@ -26,7 +26,7 @@ classifiers = [
"Typing :: Typed",
]
dependencies = [
"fastapi>=0.116.1,<0.117.0",
"fastapi>=0.115.0,<0.116.0",
"uvicorn>=0.35.0,<0.36.0",
"langgraph>=0.6.3,<0.7.0",
"langgraph-checkpoint-sqlite>=2.0.11,<3.0.0",
Expand All @@ -38,18 +38,20 @@ dependencies = [
"google-adk>=1.9.0,<2.0.0",
"ag-ui-protocol>=0.1.8,<0.2.0",
"aiosqlite>=0.21.0,<0.22.0",
"langfuse==2.60.8",
"langfuse==3.10.1",
"arize-phoenix-otel>=0.2.0,<1.0.0",
"openinference-instrumentation-langchain>=0.1.13,<1.0.0",
"langchain>=0.3.27,<0.4",
# Pin <12: 12.x currently depends on placeholder jmespath==99.99.99 and fails in Docker
"arize-phoenix>=11.22.0,<12.0.0",
"idun-agent-schema>=0.2.6,<0.3.0",
"idun-agent-schema>=0.2.7,<0.3.0",
"langfuse-haystack>=2.3.0",
"python-dotenv>=1.1.1",
"click>=8.2.1",
# Pin SQLAlchemy for Python 3.13 compatibility (required by Phoenix)
"sqlalchemy>=2.0.36,<3.0.0",
"ag-ui-langgraph>=0.0.20,<0.1.0",
"copilotkit>=0.1.72,<0.2.0",
]


Expand Down
2 changes: 1 addition & 1 deletion libs/idun_agent_engine/src/idun_agent_engine/_version.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
"""Version information for Idun Agent Engine."""

__version__ = "0.2.6"
__version__ = "0.2.7"
9 changes: 9 additions & 0 deletions libs/idun_agent_engine/src/idun_agent_engine/agent/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,15 @@ def agent_instance(self) -> Any:
"""
pass

@property
@abstractmethod
def copilotkit_agent_instance(self) -> Any:
"""Get the CopilotKit agent instance.

This might be set after initialization.
"""
pass

@property
def configuration(self) -> ConfigType:
"""Return current configuration settings for the agent.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,15 @@ def agent_instance(self) -> Any:
raise RuntimeError("Agent not initialized. Call initialize() first.")
return self._agent_instance

@property
def copilotkit_agent_instance(self) -> Any:
"""Return the CopilotKit agent instance.

Raises:
RuntimeError: If the CopilotKit agent is not yet initialized.
"""
raise NotImplementedError("CopilotKit agent instance not supported yet for Haystack agent.")

@property
def configuration(self) -> HaystackAgentConfig:
"""Return validated configuration.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

from idun_agent_engine import observability
from idun_agent_engine.agent import base as agent_base
from copilotkit import LangGraphAGUIAgent


class LanggraphAgent(agent_base.BaseAgent):
Expand All @@ -29,6 +30,7 @@ def __init__(self):
self._input_schema: Any = None
self._output_schema: Any = None
self._agent_instance: Any = None
self._copilotkit_agent_instance: LangGraphAGUIAgent | None = None
self._checkpointer: Any = None
self._store: Any = None
self._connection: Any = None
Expand Down Expand Up @@ -79,6 +81,17 @@ def agent_instance(self) -> Any:
raise RuntimeError("Agent not initialized. Call initialize() first.")
return self._agent_instance

@property
def copilotkit_agent_instance(self) -> LangGraphAGUIAgent:
"""Return the CopilotKit agent instance.

Raises:
RuntimeError: If the CopilotKit agent is not yet initialized.
"""
if self._copilotkit_agent_instance is None:
raise RuntimeError("CopilotKit agent not initialized. Call initialize() first.")
return self._copilotkit_agent_instance

@property
def configuration(self) -> LangGraphAgentConfig:
"""Return validated configuration.
Expand Down Expand Up @@ -158,6 +171,12 @@ async def initialize(self, config: LangGraphAgentConfig) -> None:
checkpointer=self._checkpointer, store=self._store
)

self._copilotkit_agent_instance = LangGraphAGUIAgent(
name=self._name,
description="Agent description", # TODO: add agent description
graph=self._agent_instance,
)

if self._agent_instance:
self._input_schema = self._agent_instance.input_schema
self._output_schema = self._agent_instance.output_schema
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
from ..server.routers.base import base_router
from .config_builder import ConfigBuilder
from .engine_config import EngineConfig
from .._version import __version__


def create_app(
Expand Down Expand Up @@ -48,7 +49,7 @@ def create_app(
lifespan=lifespan,
title="Idun Agent Engine Server",
description="A production-ready server for conversational AI agents",
version="0.1.0",
version=__version__,
docs_url="/docs",
redoc_url="/redoc",
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,20 @@ async def get_agent(request: Request):
app_config = ConfigBuilder.load_from_file()
agent = await ConfigBuilder.initialize_agent_from_config(app_config)
return agent

async def get_copilotkit_agent(request: Request):
"""Return the pre-initialized agent instance from the app state.

Falls back to loading from the default config if not present (e.g., tests).
"""
if hasattr(request.app.state, "copilotkit_agent"):
return request.app.state.copilotkit_agent
else:
# This is a fallback for cases where the lifespan event did not run,
# like in some testing scenarios.
# Consider logging a warning here.
print("⚠️ CopilotKit agent not found in app state, initializing fallback agent...")

app_config = ConfigBuilder.load_from_file()
copilotkit_agent = await ConfigBuilder.initialize_agent_from_config(app_config)
return copilotkit_agent
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@
from idun_agent_schema.engine.api import ChatRequest, ChatResponse

from idun_agent_engine.agent.base import BaseAgent
from idun_agent_engine.server.dependencies import get_agent
from idun_agent_engine.server.dependencies import get_agent, get_copilotkit_agent

from ag_ui.core.types import RunAgentInput
from ag_ui.encoder import EventEncoder
from copilotkit import LangGraphAGUIAgent

logging.basicConfig(
format="%(asctime)s %(levelname)-8s %(message)s",
Expand Down Expand Up @@ -57,7 +61,6 @@ async def stream(
):
"""Process a message with the agent, streaming ag-ui events."""
try:

async def event_stream():
message = {"query": request.query, "session_id": request.session_id}
async for event in agent.stream(message):
Expand All @@ -66,3 +69,28 @@ async def event_stream():
return StreamingResponse(event_stream(), media_type="text/event-stream")
except Exception as e: # noqa: BLE001
raise HTTPException(status_code=500, detail=str(e)) from e

@agent_router.post("/copilotkit/stream")
async def copilotkit_stream(
input_data: RunAgentInput,
request: Request,
copilotkit_agent: Annotated[LangGraphAGUIAgent, Depends(get_copilotkit_agent)],
):
"""Process a message with the agent, streaming ag-ui events."""
try:
# Get the accept header from the request
accept_header = request.headers.get("accept")

# Create an event encoder to properly format SSE events
encoder = EventEncoder(accept=accept_header or "") # type: ignore[arg-type]

async def event_generator():
async for event in copilotkit_agent.run(input_data):
yield encoder.encode(event)

return StreamingResponse(
event_generator(), # type: ignore[arg-type]
media_type=encoder.get_content_type()
)
except Exception as e: # noqa: BLE001
raise HTTPException(status_code=500, detail=str(e)) from e
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ def generate_dockerfile(dependency: Dependency) -> str:
requirements_dockerfile = f"""FROM python:3.13-slim
RUN apt-get update && pip install uv

RUN uv pip install idun-agent-schema==0.2.6 --system
RUN uv pip install idun-agent-engine==0.2.6 --system
RUN uv pip install idun-agent-schema==0.2.7 --system
RUN uv pip install idun-agent-engine==0.2.7 --system

COPY . .
RUN uv pip install -r requirements.txt --system
Expand Down
Loading
Loading