from pydantic import BaseModel, Field from pydantic_ai import Agent, RunContext from haiku.rag.client import HaikuRAG from haiku.rag.config import Config from haiku.rag.config.models import ModelConfig from haiku.rag.graph.common import get_model from haiku.rag.qa.prompts import QA_SYSTEM_PROMPT, QA_SYSTEM_PROMPT_WITH_CITATIONS class SearchResult(BaseModel): content: str = Field(description="The document text content") score: float = Field(description="Relevance score (higher is more relevant)") document_uri: str = Field( description="Source title (if available) or URI/path of the document" ) class Dependencies(BaseModel): model_config = {"arbitrary_types_allowed": True} client: HaikuRAG class QuestionAnswerAgent: def __init__( self, client: HaikuRAG, model_config: ModelConfig, use_citations: bool = False, q: float = 0.0, system_prompt: str | None = None, ): self._client = client if system_prompt is None: system_prompt = ( QA_SYSTEM_PROMPT_WITH_CITATIONS if use_citations else QA_SYSTEM_PROMPT ) model_obj = get_model(model_config, Config) self._agent = Agent( model=model_obj, deps_type=Dependencies, system_prompt=system_prompt, retries=3, ) @self._agent.tool async def search_documents( ctx: RunContext[Dependencies], query: str, limit: int = 5, ) -> list[SearchResult]: """Search the knowledge base for relevant documents.""" search_results = await ctx.deps.client.search(query, limit=limit) expanded_results = await ctx.deps.client.expand_context(search_results) return [ SearchResult( content=chunk.content, score=score, document_uri=(chunk.document_title or chunk.document_uri or ""), ) for chunk, score in expanded_results ] async def answer(self, question: str) -> str: """Answer a question using the RAG system.""" deps = Dependencies(client=self._client) result = await self._agent.run(question, deps=deps) return result.output