dp-zp-agent/tools/answers/answer_delivery.py
2026-10-26 19:23:02 +01:00

65 lines
3.3 KiB
Python

"""Production answer path: retrieval -> model selection -> guard -> rendered text."""
from __future__ import annotations
import json
import os
import threading
import urllib.request
from pathlib import Path
from tools.rag.common import DB_FILE
from tools.rag.rag_utils import build_rag_context
from tools.rag.llm_context import compact_rag_payload
from tools.graphrag.graph_search import search_graph
from tools.answer_evaluation.graph_context import graph_context
from tools.answer_evaluation.checked_generation import generate_checked_answer
from tools.answer_evaluation.verified_rag import generate_verified_rag
_capacity = threading.BoundedSemaphore(2)
def complete_with_configured_model(messages: list[dict]) -> dict:
key = os.getenv('OPENWEBUI_API_KEY', '').strip()
if not key:
raise RuntimeError('Answer model is not configured')
endpoint = os.getenv('ANSWER_LLM_URL', 'https://ui.tukekemt.xyz/api/chat/completions')
body = {'model': os.getenv('ANSWER_MODEL', 'model120-fast'), 'messages': messages,
'temperature': 0, 'stream': False}
request = urllib.request.Request(endpoint, data=json.dumps(body, ensure_ascii=False).encode(),
headers={'Content-Type': 'application/json', 'Authorization': 'Bearer ' + key})
with urllib.request.urlopen(request, timeout=120) as response:
data = response.read(2 * 1024 * 1024 + 1)
if len(data) > 2 * 1024 * 1024:
raise RuntimeError('Answer model output is too large')
return json.loads(data)
def deliver_answer(question: str, *, engine: str = 'rag', limit: int = 5,
complete=None, db_file: Path = DB_FILE) -> dict:
if not _capacity.acquire(blocking=False):
return {'status': 'error', 'reason': 'answer_capacity_exceeded',
'accepted_for_delivery': False, 'answer': 'Agent práve spracúva iné odpovede. Skúste to o chvíľu.'}
try:
model = complete or complete_with_configured_model
if engine == 'rag':
retrieved = compact_rag_payload(build_rag_context(db_file, question, limit=limit))
result = generate_verified_rag(question, retrieved, model)
elif engine == 'graphrag':
context = graph_context(search_graph(question))
prompt = (Path(__file__).resolve().parents[1] / 'answer_evaluation/graph_answer_prompt.txt').read_text()
generated = generate_checked_answer(question, context, prompt, model)
if generated.get('accepted_for_delivery'):
result = {**generated['answer'], 'accepted_for_delivery': True, 'reason': None,
'index_version': context.get('index_version'), 'complete': context.get('complete'),
'truncated': context.get('truncated'), 'truncation_reasons': context.get('truncation_reasons'),
'attempts': generated['attempts']}
else:
result = {'status': 'error', 'reason': 'answer_rejected', 'accepted_for_delivery': False,
'answer': 'Odpoveď neprešla kontrolou podkladov; neoverené údaje sa nezobrazia.',
'index_version': context.get('index_version'), 'attempts': generated['attempts']}
else:
raise ValueError('Unsupported answer engine')
return {**result, 'engine': engine}
finally:
_capacity.release()