Refactor everything graph-related under the graph module

This commit is contained in:
Yiorgis Gozadinos 2025-11-11 15:47:42 +02:00
parent 8f0597e89e
commit 032625b0bb
No known key found for this signature in database
46 changed files with 147 additions and 140 deletions

View file

@ -84,10 +84,10 @@ To customize settings, create a `haiku.rag.yaml` config file (see [Configuration
## Python Usage ## Python Usage
```python ```python
from haiku.rag.agui.stream import stream_graph
from haiku.rag.client import HaikuRAG from haiku.rag.client import HaikuRAG
from haiku.rag.config import Config from haiku.rag.config import Config
from haiku.rag.research import ( from haiku.rag.graph.agui import stream_graph
from haiku.rag.graph.research import (
ResearchContext, ResearchContext,
ResearchDeps, ResearchDeps,
ResearchState, ResearchState,

View file

@ -96,9 +96,9 @@ Python usage:
```python ```python
from haiku.rag.client import HaikuRAG from haiku.rag.client import HaikuRAG
from haiku.rag.config import Config from haiku.rag.config import Config
from haiku.rag.qa.deep.dependencies import DeepQAContext from haiku.rag.graph.deep_qa.dependencies import DeepQAContext
from haiku.rag.qa.deep.graph import build_deep_qa_graph from haiku.rag.graph.deep_qa.graph import build_deep_qa_graph
from haiku.rag.qa.deep.state import DeepQADeps, DeepQAState from haiku.rag.graph.deep_qa.state import DeepQADeps, DeepQAState
async with HaikuRAG(path_to_db) as client: async with HaikuRAG(path_to_db) as client:
# Use global config (recommended) # Use global config (recommended)
@ -205,9 +205,9 @@ Python usage (blocking result):
```python ```python
from haiku.rag.client import HaikuRAG from haiku.rag.client import HaikuRAG
from haiku.rag.config import Config from haiku.rag.config import Config
from haiku.rag.research.dependencies import ResearchContext from haiku.rag.graph.research.dependencies import ResearchContext
from haiku.rag.research.graph import build_research_graph from haiku.rag.graph.research.graph import build_research_graph
from haiku.rag.research.state import ResearchDeps, ResearchState from haiku.rag.graph.research.state import ResearchDeps, ResearchState
async with HaikuRAG(path_to_db) as client: async with HaikuRAG(path_to_db) as client:
# Use global config (recommended) # Use global config (recommended)
@ -253,12 +253,12 @@ result = await graph.run(state=state, deps=deps)
Python usage (streamed AG-UI events): Python usage (streamed AG-UI events):
```python ```python
from haiku.rag.agui.stream import stream_graph from haiku.rag.graph.agui import stream_graph
from haiku.rag.client import HaikuRAG from haiku.rag.client import HaikuRAG
from haiku.rag.config import Config from haiku.rag.config import Config
from haiku.rag.research.dependencies import ResearchContext from haiku.rag.graph.research.dependencies import ResearchContext
from haiku.rag.research.graph import build_research_graph from haiku.rag.graph.research.graph import build_research_graph
from haiku.rag.research.state import ResearchDeps, ResearchState from haiku.rag.graph.research.state import ResearchDeps, ResearchState
async with HaikuRAG(path_to_db) as client: async with HaikuRAG(path_to_db) as client:
graph = build_research_graph(config=Config) graph = build_research_graph(config=Config)

View file

@ -162,7 +162,7 @@ Research parameters like `max_iterations`, `confidence_threshold`, and `max_conc
When `--verbose` is set, the CLI consumes the research graph's AG-UI event stream, displaying step events and activity snapshots as agents progress through planning, search, evaluation, and synthesis. Without `--verbose`, only the final research report is displayed. When `--verbose` is set, the CLI consumes the research graph's AG-UI event stream, displaying step events and activity snapshots as agents progress through planning, search, evaluation, and synthesis. Without `--verbose`, only the final research report is displayed.
If you build your own integration, import `stream_graph` from `haiku.rag.agui.stream` to access AG-UI events (`STEP_STARTED`, `ACTIVITY_SNAPSHOT`, `STATE_SNAPSHOT`, `RUN_FINISHED`, etc.) and render them however you like while the graph is running. If you build your own integration, import `stream_graph` from `haiku.rag.graph.agui` to access AG-UI events (`STEP_STARTED`, `ACTIVITY_SNAPSHOT`, `STATE_SNAPSHOT`, `RUN_FINISHED`, etc.) and render them however you like while the graph is running.
## Server ## Server

View file

@ -6,7 +6,7 @@ import logfire
from pydantic_ai import Agent, RunContext from pydantic_ai import Agent, RunContext
from haiku.rag.config import AppConfig, Config from haiku.rag.config import AppConfig, Config
from haiku.rag.graph_common import get_model from haiku.rag.graph.common import get_model
from .context import load_message_history, save_message_history from .context import load_message_history, save_message_history
from .models import A2AConfig, AgentDependencies, SearchResult from .models import A2AConfig, AgentDependencies, SearchResult

View file

@ -8,7 +8,7 @@ from pydantic_ai.ag_ui import StateDeps
from haiku.rag.client import HaikuRAG from haiku.rag.client import HaikuRAG
from haiku.rag.config import Config from haiku.rag.config import Config
from haiku.rag.graph_common import get_model from haiku.rag.graph.common import get_model
class ResearchState(BaseModel): class ResearchState(BaseModel):

View file

@ -8,14 +8,14 @@ from rich.console import Console
from rich.markdown import Markdown from rich.markdown import Markdown
from rich.progress import Progress from rich.progress import Progress
from haiku.rag.agui import AGUIConsoleRenderer, stream_graph
from haiku.rag.client import HaikuRAG from haiku.rag.client import HaikuRAG
from haiku.rag.config import AppConfig, Config from haiku.rag.config import AppConfig, Config
from haiku.rag.graph.agui import AGUIConsoleRenderer, stream_graph
from haiku.rag.graph.research.dependencies import ResearchContext
from haiku.rag.graph.research.graph import build_research_graph
from haiku.rag.graph.research.state import ResearchDeps, ResearchState
from haiku.rag.mcp import create_mcp_server from haiku.rag.mcp import create_mcp_server
from haiku.rag.monitor import FileWatcher from haiku.rag.monitor import FileWatcher
from haiku.rag.research.dependencies import ResearchContext
from haiku.rag.research.graph import build_research_graph
from haiku.rag.research.state import ResearchDeps, ResearchState
from haiku.rag.store.models.chunk import Chunk from haiku.rag.store.models.chunk import Chunk
from haiku.rag.store.models.document import Document from haiku.rag.store.models.document import Document
@ -216,9 +216,9 @@ class HaikuRAGApp:
async with HaikuRAG(db_path=self.db_path, config=self.config) as self.client: async with HaikuRAG(db_path=self.db_path, config=self.config) as self.client:
try: try:
if deep: if deep:
from haiku.rag.qa.deep.dependencies import DeepQAContext from haiku.rag.graph.deep_qa.dependencies import DeepQAContext
from haiku.rag.qa.deep.graph import build_deep_qa_graph from haiku.rag.graph.deep_qa.graph import build_deep_qa_graph
from haiku.rag.qa.deep.state import DeepQADeps, DeepQAState from haiku.rag.graph.deep_qa.state import DeepQADeps, DeepQAState
graph = build_deep_qa_graph(config=self.config) graph = build_deep_qa_graph(config=self.config)
context = DeepQAContext( context = DeepQAContext(
@ -229,7 +229,7 @@ class HaikuRAGApp:
if verbose: if verbose:
# Use AG-UI renderer to process and display events # Use AG-UI renderer to process and display events
from haiku.rag.agui import AGUIConsoleRenderer from haiku.rag.graph.agui import AGUIConsoleRenderer
renderer = AGUIConsoleRenderer(self.console) renderer = AGUIConsoleRenderer(self.console)
result_dict = await renderer.render( result_dict = await renderer.render(
@ -287,7 +287,7 @@ class HaikuRAGApp:
return return
# Convert dict to ResearchReport model # Convert dict to ResearchReport model
from haiku.rag.research.models import ResearchReport from haiku.rag.graph.research.models import ResearchReport
report = ResearchReport.model_validate(report_dict) report = ResearchReport.model_validate(report_dict)
@ -497,7 +497,7 @@ class HaikuRAGApp:
async def run_agui(): async def run_agui():
import uvicorn import uvicorn
from haiku.rag.agui import create_agui_server from haiku.rag.graph.agui import create_agui_server
logger.info( logger.info(
f"Starting AG-UI server on {self.config.agui.host}:{self.config.agui.port}" f"Starting AG-UI server on {self.config.agui.host}:{self.config.agui.port}"

View file

@ -0,0 +1,26 @@
"""Graph module for haiku.rag.
This module contains all graph-related functionality including:
- AG-UI protocol for graph streaming
- Common graph utilities and models
- Research graph implementation
- Deep QA graph implementation
"""
from haiku.rag.graph.agui import (
AGUIConsoleRenderer,
AGUIEmitter,
create_agui_server,
stream_graph,
)
from haiku.rag.graph.deep_qa.graph import build_deep_qa_graph
from haiku.rag.graph.research.graph import build_research_graph
__all__ = [
"AGUIConsoleRenderer",
"AGUIEmitter",
"build_deep_qa_graph",
"build_research_graph",
"create_agui_server",
"stream_graph",
]

View file

@ -1,8 +1,8 @@
"""Generic AG-UI protocol support for haiku.rag graphs.""" """Generic AG-UI protocol support for haiku.rag graphs."""
from haiku.rag.agui.cli_renderer import AGUIConsoleRenderer from haiku.rag.graph.agui.cli_renderer import AGUIConsoleRenderer
from haiku.rag.agui.emitter import AGUIEmitter from haiku.rag.graph.agui.emitter import AGUIEmitter
from haiku.rag.agui.events import ( from haiku.rag.graph.agui.events import (
AGUIEvent, AGUIEvent,
emit_activity, emit_activity,
emit_activity_delta, emit_activity_delta,
@ -18,14 +18,14 @@ from haiku.rag.agui.events import (
emit_text_message_end, emit_text_message_end,
emit_text_message_start, emit_text_message_start,
) )
from haiku.rag.agui.server import ( from haiku.rag.graph.agui.server import (
RunAgentInput, RunAgentInput,
create_agui_app, create_agui_app,
create_agui_server, create_agui_server,
format_sse_event, format_sse_event,
) )
from haiku.rag.agui.state import compute_state_delta from haiku.rag.graph.agui.state import compute_state_delta
from haiku.rag.agui.stream import stream_graph from haiku.rag.graph.agui.stream import stream_graph
__all__ = [ __all__ = [
"AGUIConsoleRenderer", "AGUIConsoleRenderer",

View file

@ -5,7 +5,7 @@ from typing import Any
from rich.console import Console from rich.console import Console
from haiku.rag.agui.events import AGUIEvent from haiku.rag.graph.agui.events import AGUIEvent
class AGUIConsoleRenderer: class AGUIConsoleRenderer:

View file

@ -7,7 +7,7 @@ from uuid import uuid4
from pydantic import BaseModel from pydantic import BaseModel
from haiku.rag.agui.events import ( from haiku.rag.graph.agui.events import (
AGUIEvent, AGUIEvent,
emit_activity, emit_activity,
emit_run_error, emit_run_error,

View file

@ -5,7 +5,7 @@ from uuid import uuid4
from pydantic import BaseModel from pydantic import BaseModel
from haiku.rag.agui.state import compute_state_delta from haiku.rag.graph.agui.state import compute_state_delta
# Type aliases for AG-UI events (actual types from ag_ui.core will be used at runtime) # Type aliases for AG-UI events (actual types from ag_ui.core will be used at runtime)
AGUIEvent = dict[str, Any] AGUIEvent = dict[str, Any]

View file

@ -13,9 +13,9 @@ from starlette.requests import Request
from starlette.responses import JSONResponse, StreamingResponse from starlette.responses import JSONResponse, StreamingResponse
from starlette.routing import Route from starlette.routing import Route
from haiku.rag.agui.events import AGUIEvent
from haiku.rag.agui.stream import stream_graph
from haiku.rag.config.models import AGUIConfig from haiku.rag.config.models import AGUIConfig
from haiku.rag.graph.agui.events import AGUIEvent
from haiku.rag.graph.agui.stream import stream_graph
class GraphDeps(Protocol): class GraphDeps(Protocol):
@ -157,12 +157,12 @@ def create_agui_server(config: Any, db_path: Any | None = None) -> Starlette:
Starlette app with research and deep ask endpoints Starlette app with research and deep ask endpoints
""" """
from haiku.rag.client import HaikuRAG from haiku.rag.client import HaikuRAG
from haiku.rag.qa.deep.dependencies import DeepQAContext from haiku.rag.graph.deep_qa.dependencies import DeepQAContext
from haiku.rag.qa.deep.graph import build_deep_qa_graph from haiku.rag.graph.deep_qa.graph import build_deep_qa_graph
from haiku.rag.qa.deep.state import DeepQADeps, DeepQAState from haiku.rag.graph.deep_qa.state import DeepQADeps, DeepQAState
from haiku.rag.research.dependencies import ResearchContext from haiku.rag.graph.research.dependencies import ResearchContext
from haiku.rag.research.graph import build_research_graph from haiku.rag.graph.research.graph import build_research_graph
from haiku.rag.research.state import ResearchDeps, ResearchState from haiku.rag.graph.research.state import ResearchDeps, ResearchState
# Store client reference for proper lifecycle management # Store client reference for proper lifecycle management
_client_cache: dict[str, HaikuRAG] = {} _client_cache: dict[str, HaikuRAG] = {}

View file

@ -7,8 +7,8 @@ from typing import Any, Protocol
from pydantic import BaseModel from pydantic import BaseModel
from haiku.rag.agui.emitter import AGUIEmitter from haiku.rag.graph.agui.emitter import AGUIEmitter
from haiku.rag.agui.events import AGUIEvent from haiku.rag.graph.agui.events import AGUIEvent
class GraphDeps(Protocol): class GraphDeps(Protocol):

View file

@ -0,0 +1,5 @@
"""Common utilities for graph implementations."""
from haiku.rag.graph.common.utils import get_model
__all__ = ["get_model"]

View file

@ -1,7 +1,5 @@
"""Common utilities for all graph implementations.""" """Common utilities for all graph implementations."""
from typing import Any, Protocol
from pydantic_ai.models.openai import OpenAIChatModel from pydantic_ai.models.openai import OpenAIChatModel
from pydantic_ai.providers.ollama import OllamaProvider from pydantic_ai.providers.ollama import OllamaProvider
from pydantic_ai.providers.openai import OpenAIProvider from pydantic_ai.providers.openai import OpenAIProvider
@ -9,12 +7,6 @@ from pydantic_ai.providers.openai import OpenAIProvider
from haiku.rag.config import Config from haiku.rag.config import Config
class HasEmitLog(Protocol):
"""Protocol for objects that can emit log messages."""
def emit_log(self, message: str, state: Any = None) -> None: ...
def get_model(provider: str, model: str) -> OpenAIChatModel | str: def get_model(provider: str, model: str) -> OpenAIChatModel | str:
""" """
Get a model instance for the specified provider and model name. Get a model instance for the specified provider and model name.
@ -50,15 +42,3 @@ def get_model(provider: str, model: str) -> OpenAIChatModel | str:
f"Unknown model provider: {provider}. " f"Unknown model provider: {provider}. "
f"Supported providers: ollama, vllm, openai, anthropic, gemini, groq, bedrock" f"Supported providers: ollama, vllm, openai, anthropic, gemini, groq, bedrock"
) )
def log(deps: HasEmitLog, state: Any, message: str) -> None:
"""
Emit a log message through the dependencies.
Args:
deps: Dependencies object with emit_log method
state: Current state (passed to emit_log)
message: The message to log
"""
deps.emit_log(message, state)

View file

@ -0,0 +1 @@
from haiku.rag.graph.deep_qa.models import DeepQAAnswer

View file

@ -1,8 +1,7 @@
from pydantic import BaseModel, Field from pydantic import BaseModel, Field
from rich.console import Console
from haiku.rag.client import HaikuRAG from haiku.rag.client import HaikuRAG
from haiku.rag.graph_common.models import SearchAnswer from haiku.rag.graph.common.models import SearchAnswer
class DeepQAContext(BaseModel): class DeepQAContext(BaseModel):
@ -26,4 +25,3 @@ class DeepQADependencies(BaseModel):
client: HaikuRAG = Field(description="RAG client for document operations") client: HaikuRAG = Field(description="RAG client for document operations")
context: DeepQAContext = Field(description="Shared QA context") context: DeepQAContext = Field(description="Shared QA context")
console: Console | None = None

View file

@ -8,17 +8,17 @@ from pydantic_graph.beta.join import reduce_list_append
from haiku.rag.config import Config from haiku.rag.config import Config
from haiku.rag.config.models import AppConfig from haiku.rag.config.models import AppConfig
from haiku.rag.graph_common import get_model from haiku.rag.graph.common import get_model
from haiku.rag.graph_common.models import ResearchPlan, SearchAnswer from haiku.rag.graph.common.models import ResearchPlan, SearchAnswer
from haiku.rag.graph_common.prompts import PLAN_PROMPT, SEARCH_AGENT_PROMPT from haiku.rag.graph.common.prompts import PLAN_PROMPT, SEARCH_AGENT_PROMPT
from haiku.rag.qa.deep.dependencies import DeepQADependencies from haiku.rag.graph.deep_qa.dependencies import DeepQADependencies
from haiku.rag.qa.deep.models import DeepQAAnswer, DeepQAEvaluation from haiku.rag.graph.deep_qa.models import DeepQAAnswer, DeepQAEvaluation
from haiku.rag.qa.deep.prompts import ( from haiku.rag.graph.deep_qa.prompts import (
DECISION_PROMPT, DECISION_PROMPT,
SYNTHESIS_PROMPT, SYNTHESIS_PROMPT,
SYNTHESIS_PROMPT_WITH_CITATIONS, SYNTHESIS_PROMPT_WITH_CITATIONS,
) )
from haiku.rag.qa.deep.state import DeepQADeps, DeepQAState from haiku.rag.graph.deep_qa.state import DeepQADeps, DeepQAState
def build_deep_qa_graph( def build_deep_qa_graph(
@ -77,7 +77,6 @@ def build_deep_qa_graph(
agent_deps = DeepQADependencies( agent_deps = DeepQADependencies(
client=deps.client, client=deps.client,
context=state.context, context=state.context,
console=None,
) )
plan_result = await plan_agent.run(prompt, deps=agent_deps) plan_result = await plan_agent.run(prompt, deps=agent_deps)
state.context.sub_questions = list(plan_result.output.sub_questions) state.context.sub_questions = list(plan_result.output.sub_questions)
@ -151,7 +150,6 @@ def build_deep_qa_graph(
agent_deps = DeepQADependencies( agent_deps = DeepQADependencies(
client=deps.client, client=deps.client,
context=state.context, context=state.context,
console=None,
) )
try: try:
result = await agent.run(sub_q, deps=agent_deps) result = await agent.run(sub_q, deps=agent_deps)
@ -228,7 +226,6 @@ def build_deep_qa_graph(
agent_deps = DeepQADependencies( agent_deps = DeepQADependencies(
client=deps.client, client=deps.client,
context=state.context, context=state.context,
console=None,
) )
result = await agent.run(prompt, deps=agent_deps) result = await agent.run(prompt, deps=agent_deps)
evaluation = result.output evaluation = result.output
@ -302,7 +299,6 @@ def build_deep_qa_graph(
agent_deps = DeepQADependencies( agent_deps = DeepQADependencies(
client=deps.client, client=deps.client,
context=state.context, context=state.context,
console=None,
) )
result = await agent.run(prompt, deps=agent_deps) result = await agent.run(prompt, deps=agent_deps)

View file

@ -5,7 +5,7 @@ from typing import TYPE_CHECKING, Any
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.qa.deep.dependencies import DeepQAContext from haiku.rag.graph.deep_qa.dependencies import DeepQAContext
if TYPE_CHECKING: if TYPE_CHECKING:
from haiku.rag.config.models import AppConfig from haiku.rag.config.models import AppConfig

View file

@ -0,0 +1,3 @@
from haiku.rag.graph.common.models import SearchAnswer
from haiku.rag.graph.research.dependencies import ResearchContext, ResearchDependencies
from haiku.rag.graph.research.models import EvaluationResult, ResearchReport

View file

@ -1,7 +1,7 @@
from pydantic_ai import format_as_xml from pydantic_ai import format_as_xml
from haiku.rag.research.dependencies import ResearchContext from haiku.rag.graph.research.dependencies import ResearchContext
from haiku.rag.research.models import InsightAnalysis from haiku.rag.graph.research.models import InsightAnalysis
def format_context_for_prompt(context: ResearchContext) -> str: def format_context_for_prompt(context: ResearchContext) -> str:

View file

@ -3,8 +3,8 @@ from collections.abc import Iterable
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_common.models import SearchAnswer from haiku.rag.graph.common.models import SearchAnswer
from haiku.rag.research.models import ( from haiku.rag.graph.research.models import (
GapRecord, GapRecord,
InsightAnalysis, InsightAnalysis,
InsightRecord, InsightRecord,

View file

@ -8,25 +8,25 @@ from pydantic_graph.beta.join import reduce_list_append
from haiku.rag.config import Config from haiku.rag.config import Config
from haiku.rag.config.models import AppConfig from haiku.rag.config.models import AppConfig
from haiku.rag.graph_common import get_model from haiku.rag.graph.common import get_model
from haiku.rag.graph_common.models import ResearchPlan, SearchAnswer from haiku.rag.graph.common.models import ResearchPlan, SearchAnswer
from haiku.rag.graph_common.prompts import PLAN_PROMPT, SEARCH_AGENT_PROMPT from haiku.rag.graph.common.prompts import PLAN_PROMPT, SEARCH_AGENT_PROMPT
from haiku.rag.research.common import ( from haiku.rag.graph.research.common import (
format_analysis_for_prompt, format_analysis_for_prompt,
format_context_for_prompt, format_context_for_prompt,
) )
from haiku.rag.research.dependencies import ResearchDependencies from haiku.rag.graph.research.dependencies import ResearchDependencies
from haiku.rag.research.models import ( from haiku.rag.graph.research.models import (
EvaluationResult, EvaluationResult,
InsightAnalysis, InsightAnalysis,
ResearchReport, ResearchReport,
) )
from haiku.rag.research.prompts import ( from haiku.rag.graph.research.prompts import (
DECISION_AGENT_PROMPT, DECISION_AGENT_PROMPT,
INSIGHT_AGENT_PROMPT, INSIGHT_AGENT_PROMPT,
SYNTHESIS_AGENT_PROMPT, SYNTHESIS_AGENT_PROMPT,
) )
from haiku.rag.research.state import ResearchDeps, ResearchState from haiku.rag.graph.research.state import ResearchDeps, ResearchState
def build_research_graph( def build_research_graph(

View file

@ -5,12 +5,16 @@ 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.research.dependencies import ResearchContext from haiku.rag.graph.research.dependencies import ResearchContext
from haiku.rag.research.models import EvaluationResult, InsightAnalysis, ResearchReport from haiku.rag.graph.research.models import (
EvaluationResult,
InsightAnalysis,
ResearchReport,
)
if TYPE_CHECKING: if TYPE_CHECKING:
from haiku.rag.agui.emitter import AGUIEmitter
from haiku.rag.config.models import AppConfig from haiku.rag.config.models import AppConfig
from haiku.rag.graph.agui.emitter import AGUIEmitter
@dataclass @dataclass

View file

@ -1,5 +0,0 @@
"""Common utilities for graph implementations."""
from haiku.rag.graph_common.utils import get_model, log
__all__ = ["get_model", "log"]

View file

@ -6,7 +6,7 @@ from pydantic import BaseModel
from haiku.rag.client import HaikuRAG from haiku.rag.client import HaikuRAG
from haiku.rag.config import AppConfig, Config from haiku.rag.config import AppConfig, Config
from haiku.rag.research.models import ResearchReport from haiku.rag.graph.research.models import ResearchReport
class SearchResult(BaseModel): class SearchResult(BaseModel):
@ -191,9 +191,9 @@ def create_mcp_server(db_path: Path, config: AppConfig = Config) -> FastMCP:
try: try:
async with HaikuRAG(db_path, config=config) as rag: async with HaikuRAG(db_path, config=config) as rag:
if deep: if deep:
from haiku.rag.qa.deep.dependencies import DeepQAContext from haiku.rag.graph.deep_qa.dependencies import DeepQAContext
from haiku.rag.qa.deep.graph import build_deep_qa_graph from haiku.rag.graph.deep_qa.graph import build_deep_qa_graph
from haiku.rag.qa.deep.state import DeepQADeps, DeepQAState from haiku.rag.graph.deep_qa.state import DeepQADeps, DeepQAState
graph = build_deep_qa_graph(config=config) graph = build_deep_qa_graph(config=config)
context = DeepQAContext( context = DeepQAContext(
@ -226,9 +226,9 @@ def create_mcp_server(db_path: Path, config: AppConfig = Config) -> FastMCP:
A research report with findings, or None if an error occurred. A research report with findings, or None if an error occurred.
""" """
try: try:
from haiku.rag.research.dependencies import ResearchContext from haiku.rag.graph.research.dependencies import ResearchContext
from haiku.rag.research.graph import build_research_graph from haiku.rag.graph.research.graph import build_research_graph
from haiku.rag.research.state import ResearchDeps, ResearchState from haiku.rag.graph.research.state import ResearchDeps, ResearchState
async with HaikuRAG(db_path, config=config) as rag: async with HaikuRAG(db_path, config=config) as rag:
graph = build_research_graph(config=config) graph = build_research_graph(config=config)

View file

@ -1 +0,0 @@
from haiku.rag.qa.deep.models import DeepQAAnswer

View file

@ -1,3 +0,0 @@
from haiku.rag.graph_common.models import SearchAnswer
from haiku.rag.research.dependencies import ResearchContext, ResearchDependencies
from haiku.rag.research.models import EvaluationResult, ResearchReport

1
tests/graph/__init__.py Normal file
View file

@ -0,0 +1 @@
"""Tests for haiku.rag.graph module."""

View file

@ -4,8 +4,8 @@ import pytest
from pydantic import BaseModel from pydantic import BaseModel
from rich.console import Console from rich.console import Console
from haiku.rag.agui.cli_renderer import AGUIConsoleRenderer from haiku.rag.graph.agui.cli_renderer import AGUIConsoleRenderer
from haiku.rag.agui.events import ( from haiku.rag.graph.agui.events import (
emit_activity, emit_activity,
emit_run_error, emit_run_error,
emit_run_finished, emit_run_finished,

View file

@ -5,7 +5,7 @@ import asyncio
import pytest import pytest
from pydantic import BaseModel from pydantic import BaseModel
from haiku.rag.agui.emitter import AGUIEmitter from haiku.rag.graph.agui.emitter import AGUIEmitter
class TestState(BaseModel): class TestState(BaseModel):

View file

@ -2,7 +2,7 @@
from pydantic import BaseModel from pydantic import BaseModel
from haiku.rag.agui.events import ( from haiku.rag.graph.agui.events import (
emit_activity, emit_activity,
emit_run_error, emit_run_error,
emit_run_finished, emit_run_finished,

View file

@ -4,8 +4,8 @@ import pytest
from pydantic import BaseModel from pydantic import BaseModel
from starlette.testclient import TestClient from starlette.testclient import TestClient
from haiku.rag.agui.server import RunAgentInput, create_agui_app, format_sse_event
from haiku.rag.config.models import AGUIConfig from haiku.rag.config.models import AGUIConfig
from haiku.rag.graph.agui.server import RunAgentInput, create_agui_app, format_sse_event
class SimpleState(BaseModel): class SimpleState(BaseModel):

View file

@ -5,8 +5,8 @@ from dataclasses import dataclass
import pytest import pytest
from pydantic import BaseModel from pydantic import BaseModel
from haiku.rag.agui.emitter import AGUIEmitter from haiku.rag.graph.agui.emitter import AGUIEmitter
from haiku.rag.agui.stream import stream_graph from haiku.rag.graph.agui.stream import stream_graph
class TestState(BaseModel): class TestState(BaseModel):

View file

@ -2,10 +2,10 @@ import pytest
from pydantic_ai.models.test import TestModel from pydantic_ai.models.test import TestModel
from haiku.rag.client import HaikuRAG from haiku.rag.client import HaikuRAG
from haiku.rag.graph_common.models import SearchAnswer from haiku.rag.graph.common.models import SearchAnswer
from haiku.rag.qa.deep.dependencies import DeepQAContext from haiku.rag.graph.deep_qa.dependencies import DeepQAContext
from haiku.rag.qa.deep.graph import build_deep_qa_graph from haiku.rag.graph.deep_qa.graph import build_deep_qa_graph
from haiku.rag.qa.deep.state import DeepQADeps, DeepQAState from haiku.rag.graph.deep_qa.state import DeepQADeps, DeepQAState
@pytest.mark.asyncio @pytest.mark.asyncio
@ -16,8 +16,8 @@ async def test_deep_qa_graph_end_to_end(monkeypatch, temp_db_path):
def test_model_factory(provider, model): def test_model_factory(provider, model):
return TestModel() return TestModel()
monkeypatch.setattr("haiku.rag.graph_common.utils.get_model", test_model_factory) monkeypatch.setattr("haiku.rag.graph.common.utils.get_model", test_model_factory)
monkeypatch.setattr("haiku.rag.qa.deep.graph.get_model", test_model_factory) monkeypatch.setattr("haiku.rag.graph.deep_qa.graph.get_model", test_model_factory)
graph = build_deep_qa_graph() graph = build_deep_qa_graph()
@ -50,8 +50,8 @@ async def test_deep_qa_with_citations(monkeypatch, temp_db_path):
def test_model_factory(provider, model): def test_model_factory(provider, model):
return TestModel() return TestModel()
monkeypatch.setattr("haiku.rag.graph_common.utils.get_model", test_model_factory) monkeypatch.setattr("haiku.rag.graph.common.utils.get_model", test_model_factory)
monkeypatch.setattr("haiku.rag.qa.deep.graph.get_model", test_model_factory) monkeypatch.setattr("haiku.rag.graph.deep_qa.graph.get_model", test_model_factory)
graph = build_deep_qa_graph() graph = build_deep_qa_graph()

View file

@ -3,11 +3,11 @@ import asyncio
import pytest import pytest
from pydantic_ai.models.test import TestModel from pydantic_ai.models.test import TestModel
from haiku.rag.agui.stream import stream_graph
from haiku.rag.client import HaikuRAG from haiku.rag.client import HaikuRAG
from haiku.rag.research.dependencies import ResearchContext from haiku.rag.graph.agui.stream import stream_graph
from haiku.rag.research.graph import build_research_graph from haiku.rag.graph.research.dependencies import ResearchContext
from haiku.rag.research.state import ResearchDeps, ResearchState from haiku.rag.graph.research.graph import build_research_graph
from haiku.rag.graph.research.state import ResearchDeps, ResearchState
def test_build_graph_and_state(): def test_build_graph_and_state():
@ -39,8 +39,8 @@ async def test_graph_end_to_end_with_test_model(monkeypatch, temp_db_path):
def test_model_factory(_provider, _model): def test_model_factory(_provider, _model):
return TestModel() return TestModel()
monkeypatch.setattr("haiku.rag.graph_common.utils.get_model", test_model_factory) monkeypatch.setattr("haiku.rag.graph.common.utils.get_model", test_model_factory)
monkeypatch.setattr("haiku.rag.research.graph.get_model", test_model_factory) monkeypatch.setattr("haiku.rag.graph.research.graph.get_model", test_model_factory)
graph = build_research_graph() graph = build_research_graph()

View file

@ -343,7 +343,7 @@ async def test_ask_with_verbose(app: HaikuRAGApp, monkeypatch):
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_ask_with_deep(app: HaikuRAGApp, monkeypatch): async def test_ask_with_deep(app: HaikuRAGApp, monkeypatch):
"""Test asking a question with deep QA.""" """Test asking a question with deep QA."""
from haiku.rag.qa.deep.models import DeepQAAnswer from haiku.rag.graph.deep_qa.models import DeepQAAnswer
mock_output = DeepQAAnswer(answer="Deep QA answer", sources=["test.md"]) mock_output = DeepQAAnswer(answer="Deep QA answer", sources=["test.md"])
@ -358,7 +358,7 @@ async def test_ask_with_deep(app: HaikuRAGApp, monkeypatch):
with patch("haiku.rag.app.HaikuRAG", return_value=mock_client): with patch("haiku.rag.app.HaikuRAG", return_value=mock_client):
with patch( with patch(
"haiku.rag.qa.deep.graph.build_deep_qa_graph", return_value=mock_graph "haiku.rag.graph.deep_qa.graph.build_deep_qa_graph", return_value=mock_graph
): ):
await app.ask("test question", deep=True) await app.ask("test question", deep=True)
@ -371,7 +371,7 @@ async def test_ask_with_deep(app: HaikuRAGApp, monkeypatch):
@pytest.mark.asyncio @pytest.mark.asyncio
async def test_ask_with_deep_and_cite(app: HaikuRAGApp, monkeypatch): async def test_ask_with_deep_and_cite(app: HaikuRAGApp, monkeypatch):
"""Test asking a question with deep QA and citations.""" """Test asking a question with deep QA and citations."""
from haiku.rag.qa.deep.models import DeepQAAnswer from haiku.rag.graph.deep_qa.models import DeepQAAnswer
mock_output = DeepQAAnswer( mock_output = DeepQAAnswer(
answer="Deep QA answer with citations [test.md]", sources=["test.md"] answer="Deep QA answer with citations [test.md]", sources=["test.md"]
@ -388,7 +388,7 @@ async def test_ask_with_deep_and_cite(app: HaikuRAGApp, monkeypatch):
with patch("haiku.rag.app.HaikuRAG", return_value=mock_client): with patch("haiku.rag.app.HaikuRAG", return_value=mock_client):
with patch( with patch(
"haiku.rag.qa.deep.graph.build_deep_qa_graph", return_value=mock_graph "haiku.rag.graph.deep_qa.graph.build_deep_qa_graph", return_value=mock_graph
): ):
await app.ask("test question", deep=True, cite=True) await app.ask("test question", deep=True, cite=True)
@ -417,10 +417,10 @@ async def test_ask_with_deep_and_verbose(app: HaikuRAGApp, monkeypatch):
with patch("haiku.rag.app.HaikuRAG", return_value=mock_client): with patch("haiku.rag.app.HaikuRAG", return_value=mock_client):
with patch( with patch(
"haiku.rag.qa.deep.graph.build_deep_qa_graph", return_value=mock_graph "haiku.rag.graph.deep_qa.graph.build_deep_qa_graph", return_value=mock_graph
): ):
with patch( with patch(
"haiku.rag.agui.AGUIConsoleRenderer", return_value=mock_renderer "haiku.rag.graph.agui.AGUIConsoleRenderer", return_value=mock_renderer
): ):
await app.ask("test question", deep=True, verbose=True) await app.ask("test question", deep=True, verbose=True)

View file

@ -4,8 +4,8 @@ from unittest.mock import AsyncMock, patch
import pytest import pytest
from haiku.rag.graph.research.models import ResearchReport
from haiku.rag.mcp import create_mcp_server from haiku.rag.mcp import create_mcp_server
from haiku.rag.research.models import ResearchReport
from haiku.rag.store.models.document import Document from haiku.rag.store.models.document import Document
@ -249,7 +249,9 @@ async def test_mcp_ask_question_deep():
with ( with (
patch("haiku.rag.mcp.HaikuRAG") as mock_rag_class, patch("haiku.rag.mcp.HaikuRAG") as mock_rag_class,
patch("haiku.rag.qa.deep.graph.build_deep_qa_graph") as mock_graph_builder, patch(
"haiku.rag.graph.deep_qa.graph.build_deep_qa_graph"
) as mock_graph_builder,
): ):
mock_rag = AsyncMock() mock_rag = AsyncMock()
mock_rag_class.return_value.__aenter__ = AsyncMock(return_value=mock_rag) mock_rag_class.return_value.__aenter__ = AsyncMock(return_value=mock_rag)
@ -291,7 +293,7 @@ async def test_mcp_research_question():
with ( with (
patch("haiku.rag.mcp.HaikuRAG") as mock_rag_class, patch("haiku.rag.mcp.HaikuRAG") as mock_rag_class,
patch( patch(
"haiku.rag.research.graph.build_research_graph" "haiku.rag.graph.research.graph.build_research_graph"
) as mock_graph_builder, ) as mock_graph_builder,
): ):
mock_rag = AsyncMock() mock_rag = AsyncMock()