dp-zp-agent/integrations/openwebui/verified_agent.py

64 lines
3.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
title: ZP Agent – overené odpovede
description: RAG/GraphRAG answers verified and rendered by ZP Agent before display.
version: 1.0.0
"""
import asyncio
import json
import urllib.request
from typing import Literal
from pydantic import BaseModel, Field
class Pipe:
class Valves(BaseModel):
BACKEND_URL: str = Field(default='', description='URL ZP Agent API reachable from the OpenWebUI server')
BACKEND_API_KEY: str = Field(default='', description='SEARCH_API_KEY of ZP Agent',
json_schema_extra={'input': {'type': 'password'}})
DEFAULT_ENGINE: Literal['rag', 'graphrag'] = 'rag'
TIMEOUT_SECONDS: int = Field(default=300, ge=10, le=600)
def __init__(self):
self.valves = self.Valves()
def pipes(self):
return [{'id': 'rag', 'name': 'ZP Agent / RAG – overené'},
{'id': 'graphrag', 'name': 'ZP Agent / GraphRAG – overené'}]
def _fetch(self, query: str, engine: str) -> dict:
url = self.valves.BACKEND_URL.rstrip('/')
if not url.startswith(('http://', 'https://')) or not self.valves.BACKEND_API_KEY:
raise ValueError('Missing backend configuration')
request = urllib.request.Request(url + '/answer', data=json.dumps(
{'query': query, 'engine': engine}, ensure_ascii=False).encode(),
headers={'Content-Type': 'application/json',
'Authorization': 'Bearer ' + self.valves.BACKEND_API_KEY})
with urllib.request.urlopen(request, timeout=self.valves.TIMEOUT_SECONDS) as response:
return json.load(response)
async def pipe(self, body: dict):
messages = [row for row in body.get('messages', []) if row.get('role') == 'user']
query = messages[-1].get('content', '') if messages else ''
if isinstance(query, list):
query = '\n'.join(row.get('text', '') for row in query if row.get('type') == 'text')
if not isinstance(query, str) or not query.strip():
return 'Zadajte textovú otázku.'
if len(query.strip()) > 500:
return 'Otázka môže mať najviac 500 znakov.'
model = str(body.get('model', '')).rsplit('.', 1)[-1]
engine = model if model in {'rag', 'graphrag'} else self.valves.DEFAULT_ENGINE
try:
result = await asyncio.to_thread(self._fetch, query.strip(), engine)
except Exception:
return 'Overovací backend nie je dostupný. Neoverená odpoveď sa nezobrazí.'
if not isinstance(result, dict):
return 'Backend neposkytol platnú overenú odpoveď.'
if result.get('accepted_for_delivery') is not True:
return 'Odpoveď neprešla overením podkladov. Neoverené údaje sa nezobrazia.'
if result.get('status') not in {'ok', 'no_answer', 'ambiguous'} or not isinstance(result.get('answer'), str):
return 'Backend neposkytol platnú overenú odpoveď.'
# Return the approved final text directly. Never send it to another LLM
# for paraphrasing: that would invalidate the fact/citation checks.
return result['answer'] or 'Backend neposkytol text odpovede.'