"""Provide service-layer logic for qa agent.""" from __future__ import annotations from dataclasses import dataclass, field from typing import Dict, Generator, List, Optional from app.config.settings import settings from app.shared.bootstrap import get_agent_conversation_service # Keep service responsibilities explicit so downstream behavior stays predictable. @dataclass class AgentResponse: """Represent the Agent Response type.""" answer: str sources: List[Dict] = field(default_factory=list) model: str = "" latency_ms: int = 0 retrieved_count: int = 0 context_tokens: int = 0 truncated: bool = False error: Optional[str] = None @property def is_success(self) -> bool: """Return whether success for the Agent Response instance.""" return self.error is None @dataclass class AgentConfig: """Define configuration for agent config.""" llm_provider: str = settings.llm_provider llm_model: str = settings.llm_model top_k: int = settings.rag_top_k min_score: float = 0.0 max_context_tokens: int = settings.rag_max_context_tokens temperature: float = settings.llm_temperature prompt_template: str = "compliance_qa" include_metadata: bool = True class QAAgent: """Represent the Q A Agent type.""" def __init__(self, config: Optional[AgentConfig] = None): """Initialize the Q A Agent instance.""" self.config = config or AgentConfig() def ask( self, query: str, filters: Optional[str] = None, prompt_template: Optional[str] = None, ) -> AgentResponse: """Handle ask for the Q A Agent instance.""" _, result = get_agent_conversation_service().ask( query=query, filters=filters, provider=self.config.llm_provider, model=self.config.llm_model, top_k=self.config.top_k, prompt_template=prompt_template or self.config.prompt_template, ) return AgentResponse( answer=result.answer, sources=[source.__dict__ for source in result.sources], model=result.model, latency_ms=result.latency_ms, retrieved_count=result.retrieved_count, context_tokens=result.context_tokens, truncated=result.truncated, error=result.error, ) def ask_stream(self, query: str, filters: Optional[str] = None) -> Generator[dict, None, None]: """Handle ask stream for the Q A Agent instance.""" _, stream = get_agent_conversation_service().stream_chat( query=query, filters=filters, provider=self.config.llm_provider, model=self.config.llm_model, top_k=self.config.top_k, prompt_template=self.config.prompt_template, ) for event in stream: yield event def close(self): """Release the resources held by this component.""" return None def ask_compliance_question(query: str, top_k: int = 5) -> AgentResponse: """Handle ask compliance question.""" return QAAgent(AgentConfig(top_k=top_k)).ask(query)