from pydantic import BaseModel, Field from pydantic_ai import Agent, RunContext from pydantic_ai.models.openai import OpenAIModel from pydantic_ai.providers.ollama import OllamaProvider from haiku.rag.client import HaikuRAG from haiku.rag.config import Config from haiku.rag.qa.prompts import SYSTEM_PROMPT, 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 URI/path of the document") class Dependencies(BaseModel): model_config = {"arbitrary_types_allowed": True} client: HaikuRAG class QuestionAnswerAgent: def __init__( self, client: HaikuRAG, provider: str, model: str, use_citations: bool = False, q: float = 0.0, ): self._client = client system_prompt = SYSTEM_PROMPT_WITH_CITATIONS if use_citations else SYSTEM_PROMPT model_obj = self._get_model(provider, model) self._agent = Agent( model=model_obj, deps_type=Dependencies, system_prompt=system_prompt, ) @self._agent.tool async def search_documents( ctx: RunContext[Dependencies], query: str, limit: int = 3, ) -> 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_uri or "", ) for chunk, score in expanded_results ] def _get_model(self, provider: str, model: str): """Get the appropriate model object for the provider.""" if provider == "openai": return f"openai:{model}" elif provider == "anthropic": return f"anthropic:{model}" elif provider == "ollama": return OpenAIModel( model_name=model, provider=OllamaProvider(base_url=f"{Config.OLLAMA_BASE_URL}/v1"), ) else: raise ValueError(f"Unsupported provider: {provider}") 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