Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
7 changes: 7 additions & 0 deletions packages/sdk/server-ai/src/ldai/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,17 @@
ProviderConfig,
)
from ldai.providers.types import EvalScore, JudgeResponse
from ldai.runners import AgentGraphRunner, AgentRunner
from ldai.runners.types import AgentGraphResult, AgentResult, ToolRegistry
from ldai.tracker import AIGraphTracker

__all__ = [
'LDAIClient',
'AgentRunner',
'AgentGraphRunner',
'AgentResult',
'AgentGraphResult',
'ToolRegistry',
'AIAgentConfig',
'AIAgentConfigDefault',
'AIAgentConfigRequest',
Expand Down
13 changes: 13 additions & 0 deletions packages/sdk/server-ai/src/ldai/runners/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
"""Runner ABCs and result types for LaunchDarkly AI SDK."""

from ldai.runners.agent_graph_runner import AgentGraphRunner
from ldai.runners.agent_runner import AgentRunner
from ldai.runners.types import AgentGraphResult, AgentResult, ToolRegistry

__all__ = [
'AgentRunner',
'AgentGraphRunner',
'AgentResult',
'AgentGraphResult',
'ToolRegistry',
]
26 changes: 26 additions & 0 deletions packages/sdk/server-ai/src/ldai/runners/agent_graph_runner.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"""Abstract base class for agent graph runners."""

from abc import ABC, abstractmethod
from typing import Any

from ldai.runners.types import AgentGraphResult


class AgentGraphRunner(ABC):
Comment thread
jsonbailey marked this conversation as resolved.
Outdated
"""
Abstract base class for agent graph runners.

An AgentGraphRunner encapsulates multi-agent graph execution.
Provider-specific implementations (e.g. OpenAIAgentGraphRunner) are
returned by RunnerFactory.create_agent_graph() and hold all provider
wiring internally.
"""

@abstractmethod
async def run(self, input: Any) -> AgentGraphResult:
"""
Run the agent graph with the given input.

:param input: The input to the agent graph (string prompt or structured input)
:return: AgentGraphResult containing the output, raw response, and metrics
"""
25 changes: 25 additions & 0 deletions packages/sdk/server-ai/src/ldai/runners/agent_runner.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
"""Abstract base class for agent runners."""

from abc import ABC, abstractmethod
from typing import Any

from ldai.runners.types import AgentResult


class AgentRunner(ABC):
"""
Abstract base class for single-agent runners.

An AgentRunner encapsulates the execution of a single AI agent.
Provider-specific implementations (e.g. OpenAIAgentRunner) are returned
by RunnerFactory.create_agent() and hold all provider wiring internally.
"""

@abstractmethod
async def run(self, input: Any) -> AgentResult:
"""
Run the agent with the given input.

:param input: The input to the agent (string prompt or structured input)
:return: AgentResult containing the output, raw response, and metrics
"""
32 changes: 32 additions & 0 deletions packages/sdk/server-ai/src/ldai/runners/types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
"""Result types and type aliases for agent and agent graph runners."""

from __future__ import annotations

from dataclasses import dataclass
from typing import Any, Callable, Dict

from ldai.providers.types import LDAIMetrics

# Type alias for a registry of tools available to an agent.
# Keys are tool names; values are the callable implementations.
ToolRegistry = Dict[str, Callable]


@dataclass
class AgentResult:
"""
Result from a single-agent run.
"""
output: str
raw: Any
metrics: LDAIMetrics


@dataclass
class AgentGraphResult:
"""
Result from an agent graph run.
"""
output: str
raw: Any
metrics: LDAIMetrics
Comment thread
jsonbailey marked this conversation as resolved.
Outdated
100 changes: 100 additions & 0 deletions packages/sdk/server-ai/tests/test_runner_abcs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import pytest

from ldai.providers.types import LDAIMetrics
from ldai.runners.agent_graph_runner import AgentGraphRunner
from ldai.runners.agent_runner import AgentRunner
from ldai.runners.types import AgentGraphResult, AgentResult, ToolRegistry


# --- Concrete test doubles ---

class ConcreteAgentRunner(AgentRunner):
async def run(self, input):
return AgentResult(
output=f"agent response to: {input}",
raw={"raw": input},
metrics=LDAIMetrics(success=True),
)


class ConcreteAgentGraphRunner(AgentGraphRunner):
async def run(self, input):
return AgentGraphResult(
output=f"graph response to: {input}",
raw={"raw": input},
metrics=LDAIMetrics(success=True),
)


# --- AgentRunner ---

def test_agent_runner_is_abstract():
with pytest.raises(TypeError):
AgentRunner() # type: ignore[abstract]


@pytest.mark.asyncio
async def test_agent_runner_run_returns_agent_result():
runner = ConcreteAgentRunner()
result = await runner.run("hello")
assert isinstance(result, AgentResult)
assert result.output == "agent response to: hello"
assert result.raw == {"raw": "hello"}
assert result.metrics.success is True


@pytest.mark.asyncio
async def test_agent_result_fields():
metrics = LDAIMetrics(success=True)
result = AgentResult(output="done", raw={"key": "val"}, metrics=metrics)
assert result.output == "done"
assert result.raw == {"key": "val"}
assert result.metrics is metrics


# --- AgentGraphRunner ---

def test_agent_graph_runner_is_abstract():
with pytest.raises(TypeError):
AgentGraphRunner() # type: ignore[abstract]


@pytest.mark.asyncio
async def test_agent_graph_runner_run_returns_agent_graph_result():
runner = ConcreteAgentGraphRunner()
result = await runner.run("hello graph")
assert isinstance(result, AgentGraphResult)
assert result.output == "graph response to: hello graph"
assert result.raw == {"raw": "hello graph"}
assert result.metrics.success is True


@pytest.mark.asyncio
async def test_agent_graph_result_fields():
metrics = LDAIMetrics(success=False)
result = AgentGraphResult(output="", raw=None, metrics=metrics)
assert result.output == ""
assert result.raw is None
assert result.metrics.success is False


# --- ToolRegistry ---

def test_tool_registry_is_dict_of_callables():
tools: ToolRegistry = {
"search": lambda q: f"results for {q}",
"calculator": lambda x: x * 2,
}
assert tools["search"]("python") == "results for python"
assert tools["calculator"](21) == 42


# --- Top-level exports ---

def test_top_level_exports():
import ldai
assert hasattr(ldai, 'AgentRunner')
assert hasattr(ldai, 'AgentGraphRunner')
assert hasattr(ldai, 'AgentResult')
assert hasattr(ldai, 'AgentGraphResult')
assert hasattr(ldai, 'ToolRegistry')
Loading