Better typing
This commit is contained in:
parent
68d1127e46
commit
0c8d2838b2
4 changed files with 29 additions and 16 deletions
|
|
@ -5,10 +5,11 @@
|
||||||
|
|
||||||
- **AG-UI Protocol Support**: Full AG-UI (Agent-UI) protocol implementation for graph execution with event streaming
|
- **AG-UI Protocol Support**: Full AG-UI (Agent-UI) protocol implementation for graph execution with event streaming
|
||||||
- New `AGUIEmitter` class for emitting AG-UI events from graphs
|
- New `AGUIEmitter` class for emitting AG-UI events from graphs
|
||||||
- Support for all AG-UI event types: lifecycle events (`RUN_STARTED`, `RUN_FINISHED`, `RUN_ERROR`), step events (`STEP_STARTED`, `STEP_FINISHED`), state updates (`STATE_SNAPSHOT`), activity narration (`ACTIVITY_SNAPSHOT`), and text messages (`TEXT_MESSAGE_CHUNK`)
|
- Support for all AG-UI event types: lifecycle events (`RUN_STARTED`, `RUN_FINISHED`, `RUN_ERROR`), step events (`STEP_STARTED`, `STEP_FINISHED`), state updates (`STATE_SNAPSHOT`, `STATE_DELTA`), activity narration (`ACTIVITY_SNAPSHOT`), and text messages (`TEXT_MESSAGE_CHUNK`)
|
||||||
- `AGUIConsoleRenderer` for rendering AG-UI event streams to terminal with Rich formatting
|
- `AGUIConsoleRenderer` for rendering AG-UI event streams to terminal with Rich formatting
|
||||||
- `stream_graph()` utility function for executing graphs with AG-UI event emission
|
- `stream_graph()` utility function for executing graphs with AG-UI event emission
|
||||||
- State diff computation for efficient state synchronization
|
- State diff computation for efficient state synchronization
|
||||||
|
- **Delta State Updates**: AG-UI emitter now supports incremental state updates via JSON Patch operations (`STATE_DELTA` events) to reduce bandwidth, configurable via `use_deltas` parameter (enabled by default)
|
||||||
- **AG-UI Server**: Starlette-based HTTP server for serving graphs via AG-UI protocol
|
- **AG-UI Server**: Starlette-based HTTP server for serving graphs via AG-UI protocol
|
||||||
- Server-Sent Events (SSE) streaming endpoint at `/v1/agent/stream`
|
- Server-Sent Events (SSE) streaming endpoint at `/v1/agent/stream`
|
||||||
- Health check endpoint at `/health`
|
- Health check endpoint at `/health`
|
||||||
|
|
@ -19,6 +20,7 @@
|
||||||
- Step-by-step execution visibility via AG-UI events
|
- Step-by-step execution visibility via AG-UI events
|
||||||
- **CLI AG-UI Flag**: New `--agui` flag for `serve` command to start AG-UI server
|
- **CLI AG-UI Flag**: New `--agui` flag for `serve` command to start AG-UI server
|
||||||
- **Graph Module**: New unified `haiku.rag.graph` module containing all graph-related functionality
|
- **Graph Module**: New unified `haiku.rag.graph` module containing all graph-related functionality
|
||||||
|
- **Common Graph Nodes**: New factory functions (`create_plan_node`, `create_search_node`) in `haiku.rag.graph.common.nodes` for reusable graph components
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,11 @@
|
||||||
|
|
||||||
import json
|
import json
|
||||||
from collections.abc import AsyncIterator, Callable
|
from collections.abc import AsyncIterator, Callable
|
||||||
from typing import Any, Protocol
|
from pathlib import Path
|
||||||
|
from typing import TYPE_CHECKING, Any, Protocol
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from haiku.rag.config.models import AppConfig
|
||||||
|
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
from pydantic_graph.beta import Graph
|
from pydantic_graph.beta import Graph
|
||||||
|
|
@ -14,6 +18,7 @@ from starlette.responses import JSONResponse, StreamingResponse
|
||||||
from starlette.routing import Route
|
from starlette.routing import Route
|
||||||
|
|
||||||
from haiku.rag.config.models import AGUIConfig
|
from haiku.rag.config.models import AGUIConfig
|
||||||
|
from haiku.rag.graph.agui.emitter import AGUIEmitter
|
||||||
from haiku.rag.graph.agui.events import AGUIEvent
|
from haiku.rag.graph.agui.events import AGUIEvent
|
||||||
from haiku.rag.graph.agui.stream import stream_graph
|
from haiku.rag.graph.agui.stream import stream_graph
|
||||||
|
|
||||||
|
|
@ -21,7 +26,7 @@ from haiku.rag.graph.agui.stream import stream_graph
|
||||||
class GraphDeps(Protocol):
|
class GraphDeps(Protocol):
|
||||||
"""Protocol for graph dependencies that support AG-UI emission."""
|
"""Protocol for graph dependencies that support AG-UI emission."""
|
||||||
|
|
||||||
agui_emitter: Any | None
|
agui_emitter: AGUIEmitter[Any, Any] | None
|
||||||
|
|
||||||
|
|
||||||
class RunAgentInput(BaseModel):
|
class RunAgentInput(BaseModel):
|
||||||
|
|
@ -146,7 +151,7 @@ def format_sse_event(event: AGUIEvent) -> str:
|
||||||
return f"data: {event_json}\n\n"
|
return f"data: {event_json}\n\n"
|
||||||
|
|
||||||
|
|
||||||
def create_agui_server(config: Any, db_path: Any | None = None) -> Starlette:
|
def create_agui_server(config: "AppConfig", db_path: Path | None = None) -> Starlette:
|
||||||
"""Create AG-UI server with both research and deep ask endpoints.
|
"""Create AG-UI server with both research and deep ask endpoints.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
|
|
@ -167,7 +172,7 @@ def create_agui_server(config: Any, db_path: Any | None = None) -> Starlette:
|
||||||
# Store client reference for proper lifecycle management
|
# Store client reference for proper lifecycle management
|
||||||
_client_cache: dict[str, HaikuRAG] = {}
|
_client_cache: dict[str, HaikuRAG] = {}
|
||||||
|
|
||||||
def get_client(effective_db_path: Any) -> HaikuRAG:
|
def get_client(effective_db_path: Path) -> HaikuRAG:
|
||||||
"""Get or create cached client."""
|
"""Get or create cached client."""
|
||||||
path_key = str(effective_db_path)
|
path_key = str(effective_db_path)
|
||||||
if path_key not in _client_cache:
|
if path_key not in _client_cache:
|
||||||
|
|
|
||||||
|
|
@ -3,24 +3,28 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
from collections.abc import AsyncIterator
|
from collections.abc import AsyncIterator
|
||||||
from contextlib import suppress
|
from contextlib import suppress
|
||||||
from typing import Any, Protocol
|
from typing import Protocol, TypeVar
|
||||||
|
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
from pydantic_graph.beta import Graph
|
||||||
|
|
||||||
from haiku.rag.graph.agui.emitter import AGUIEmitter
|
from haiku.rag.graph.agui.emitter import AGUIEmitter
|
||||||
from haiku.rag.graph.agui.events import AGUIEvent
|
from haiku.rag.graph.agui.events import AGUIEvent
|
||||||
|
|
||||||
|
StateT = TypeVar("StateT", bound=BaseModel)
|
||||||
|
ResultT = TypeVar("ResultT")
|
||||||
|
|
||||||
class GraphDeps(Protocol):
|
|
||||||
|
class GraphDeps[StateT: BaseModel, ResultT](Protocol):
|
||||||
"""Protocol for graph dependencies that support AG-UI emission."""
|
"""Protocol for graph dependencies that support AG-UI emission."""
|
||||||
|
|
||||||
agui_emitter: AGUIEmitter[Any, Any] | None
|
agui_emitter: AGUIEmitter[StateT, ResultT] | None
|
||||||
|
|
||||||
|
|
||||||
async def stream_graph(
|
async def stream_graph[StateT: BaseModel, DepsT: GraphDeps, ResultT](
|
||||||
graph: Any,
|
graph: Graph[StateT, DepsT, None, ResultT],
|
||||||
state: BaseModel,
|
state: StateT,
|
||||||
deps: GraphDeps,
|
deps: DepsT,
|
||||||
use_deltas: bool = True,
|
use_deltas: bool = True,
|
||||||
) -> AsyncIterator[AGUIEvent]:
|
) -> AsyncIterator[AGUIEvent]:
|
||||||
"""Run a graph and yield AG-UI events as they occur.
|
"""Run a graph and yield AG-UI events as they occur.
|
||||||
|
|
@ -48,8 +52,8 @@ async def stream_graph(
|
||||||
raise TypeError("deps must have an 'agui_emitter' attribute")
|
raise TypeError("deps must have an 'agui_emitter' attribute")
|
||||||
|
|
||||||
# Create AG-UI emitter
|
# Create AG-UI emitter
|
||||||
emitter: AGUIEmitter[Any, Any] = AGUIEmitter(use_deltas=use_deltas)
|
emitter: AGUIEmitter[StateT, ResultT] = AGUIEmitter(use_deltas=use_deltas)
|
||||||
deps.agui_emitter = emitter
|
deps.agui_emitter = emitter # type: ignore[assignment]
|
||||||
|
|
||||||
async def _execute() -> None:
|
async def _execute() -> None:
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,22 @@
|
||||||
import asyncio
|
import asyncio
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import TYPE_CHECKING, Any
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
from haiku.rag.client import HaikuRAG
|
from haiku.rag.client import HaikuRAG
|
||||||
from haiku.rag.graph.deep_qa.dependencies import DeepQAContext
|
from haiku.rag.graph.deep_qa.dependencies import DeepQAContext
|
||||||
|
from haiku.rag.graph.deep_qa.models import DeepQAAnswer
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from haiku.rag.config.models import AppConfig
|
from haiku.rag.config.models import AppConfig
|
||||||
|
from haiku.rag.graph.agui.emitter import AGUIEmitter
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class DeepQADeps:
|
class DeepQADeps:
|
||||||
client: HaikuRAG
|
client: HaikuRAG
|
||||||
agui_emitter: Any | None = None
|
agui_emitter: "AGUIEmitter[DeepQAState, DeepQAAnswer] | None" = None
|
||||||
semaphore: asyncio.Semaphore | None = None
|
semaphore: asyncio.Semaphore | None = None
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue