Wraps odootrain RAG API (http://192.168.2.9:8000) as a BaseAgent so any specialist agent can query Odoo 18 docs mid-execution via PeerBus request_type=query_docs. Participates in sweep health checks. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
129 lines
4.6 KiB
Python
129 lines
4.6 KiB
Python
from __future__ import annotations
|
|
import logging
|
|
import httpx
|
|
from .base_agent import BaseAgent, AgentReport, SweepReport
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
RAG_URL = "http://192.168.2.9:8000"
|
|
RAG_TIMEOUT = 60
|
|
|
|
|
|
class OdooDocAgent(BaseAgent):
|
|
"""
|
|
Read-only knowledge agent backed by the odootrain RAG stack.
|
|
Answers questions about Odoo 18 workflows using the indexed documentation.
|
|
|
|
Other agents query it via PeerBus:
|
|
response = await self._peer_bus.request(
|
|
from_agent=self.name,
|
|
to_agent='odoo_doc_agent',
|
|
request_type='query_docs',
|
|
params={'question': '...', 'module': 'accounting'}, # module optional
|
|
reason='Need Odoo workflow guidance',
|
|
)
|
|
guidance = response.data.get('answer', '')
|
|
"""
|
|
|
|
name = 'odoo_doc_agent'
|
|
domain = 'documentation'
|
|
required_odoo_module = 'base'
|
|
system_prompt_file = ''
|
|
tools = []
|
|
|
|
async def _plan(self) -> dict:
|
|
return {
|
|
'question': self._directive.task,
|
|
'module': self._directive.params.get('module'),
|
|
'top_k': self._directive.params.get('top_k', 6),
|
|
}
|
|
|
|
async def _gather(self, plan: dict) -> None:
|
|
payload = {
|
|
'question': plan['question'],
|
|
'top_k': plan['top_k'],
|
|
}
|
|
if plan.get('module'):
|
|
payload['module'] = plan['module']
|
|
|
|
try:
|
|
async with httpx.AsyncClient(timeout=RAG_TIMEOUT) as client:
|
|
resp = await client.post(f"{RAG_URL}/ask", json=payload)
|
|
resp.raise_for_status()
|
|
self._gathered = resp.json()
|
|
except Exception as exc:
|
|
logger.error('odoo_doc_agent RAG call failed: %s', exc)
|
|
self._gathered = {'answer': '', 'sources': [], 'error': str(exc)}
|
|
|
|
async def _reason(self) -> dict:
|
|
return {}
|
|
|
|
async def _act(self, reasoning: dict) -> None:
|
|
pass
|
|
|
|
async def _report(self) -> AgentReport:
|
|
answer = self._gathered.get('answer', '')
|
|
sources = self._gathered.get('sources', [])
|
|
error = self._gathered.get('error')
|
|
|
|
status = 'failed' if error and not answer else 'complete'
|
|
summary = answer or f'RAG lookup failed: {error}'
|
|
|
|
return AgentReport(
|
|
directive_id=self._directive.directive_id,
|
|
agent=self.name,
|
|
status=status,
|
|
summary=summary,
|
|
data={
|
|
'answer': answer,
|
|
'sources': sources,
|
|
'model': self._gathered.get('model', ''),
|
|
},
|
|
error=error,
|
|
)
|
|
|
|
async def handle_peer_request(self, request_type: str, params: dict, directive_id: str) -> dict:
|
|
if request_type != 'query_docs':
|
|
return {'success': False, 'error': f'Unknown request type: {request_type}'}
|
|
|
|
question = params.get('question', '')
|
|
if not question:
|
|
return {'success': False, 'error': 'question is required'}
|
|
|
|
payload = {'question': question, 'top_k': params.get('top_k', 6)}
|
|
if params.get('module'):
|
|
payload['module'] = params['module']
|
|
|
|
try:
|
|
async with httpx.AsyncClient(timeout=RAG_TIMEOUT) as client:
|
|
resp = await client.post(f"{RAG_URL}/ask", json=payload)
|
|
resp.raise_for_status()
|
|
data = resp.json()
|
|
return {
|
|
'success': True,
|
|
'answer': data.get('answer', ''),
|
|
'sources': data.get('sources', []),
|
|
}
|
|
except Exception as exc:
|
|
logger.error('odoo_doc_agent peer request failed: %s', exc)
|
|
return {'success': False, 'error': str(exc)}
|
|
|
|
async def sweep(self) -> SweepReport:
|
|
try:
|
|
async with httpx.AsyncClient(timeout=10) as client:
|
|
resp = await client.get(f"{RAG_URL}/health")
|
|
health = resp.json()
|
|
qdrant_ok = 'ok' in str(health.get('qdrant', ''))
|
|
ollama_ok = 'ok' in str(health.get('ollama', ''))
|
|
findings = []
|
|
if not qdrant_ok:
|
|
findings.append({'issue': 'Qdrant unhealthy', 'severity': 'high', 'detail': health})
|
|
if not ollama_ok:
|
|
findings.append({'issue': 'Ollama unhealthy', 'severity': 'high', 'detail': health})
|
|
return SweepReport(agent=self.name, findings=findings)
|
|
except Exception as exc:
|
|
return SweepReport(
|
|
agent=self.name,
|
|
findings=[{'issue': 'RAG API unreachable', 'severity': 'high', 'detail': str(exc)}],
|
|
)
|