187 lines
5.6 KiB
Python
187 lines
5.6 KiB
Python
"""
|
|
AI Answers Plugin - One-Shot Test
|
|
Comprehensive test that outputs everything: config, injection, LLM response.
|
|
|
|
Usage: python test.py
|
|
Requires: pip install flask flask-babel python-dotenv
|
|
"""
|
|
|
|
import sys
|
|
import os
|
|
import re
|
|
import time
|
|
import logging
|
|
from types import ModuleType
|
|
from dotenv import load_dotenv
|
|
|
|
load_dotenv()
|
|
|
|
# Suppress Flask noise during test
|
|
logging.getLogger('werkzeug').setLevel(logging.ERROR)
|
|
logging.basicConfig(level=logging.INFO, format='%(message)s')
|
|
|
|
# Mock SearXNG modules
|
|
searx = ModuleType("searx")
|
|
searx_plugins = ModuleType("searx.plugins")
|
|
searx_results = ModuleType("searx.result_types")
|
|
|
|
class MockPlugin:
|
|
def __init__(self, cfg):
|
|
self.active = getattr(cfg, 'active', True)
|
|
|
|
class MockPluginInfo:
|
|
def __init__(self, **kwargs):
|
|
self.meta = kwargs
|
|
|
|
class MockEngineResults:
|
|
def __init__(self):
|
|
self.types = ModuleType("types")
|
|
self.types.Answer = lambda *args, **kwargs: kwargs.get('answer', args[0] if args else "")
|
|
self._results = []
|
|
|
|
def add(self, res):
|
|
self._results.append(res)
|
|
|
|
searx_plugins.Plugin = MockPlugin
|
|
searx_plugins.PluginInfo = MockPluginInfo
|
|
searx_results.EngineResults = MockEngineResults
|
|
|
|
sys.modules["searx"] = searx
|
|
sys.modules["searx.plugins"] = searx_plugins
|
|
sys.modules["searx.result_types"] = searx_results
|
|
|
|
from flask import Flask
|
|
from flask_babel import Babel
|
|
from ai_answers import SXNGPlugin
|
|
|
|
def run_tests():
|
|
print()
|
|
print("=" * 60)
|
|
print(" AI Answers Plugin - Comprehensive Test")
|
|
print("=" * 60)
|
|
|
|
# === CONFIG TEST ===
|
|
print("\n[1/4] Configuration")
|
|
print("-" * 40)
|
|
|
|
app = Flask(__name__)
|
|
Babel(app)
|
|
|
|
class MockConfig:
|
|
active = True
|
|
|
|
plugin = SXNGPlugin(MockConfig())
|
|
plugin.init(app)
|
|
|
|
print(f" Provider: {plugin.provider or 'NOT SET'}")
|
|
print(f" Model: {plugin.model or 'N/A'}")
|
|
print(f" API Key: {'[OK]' if plugin.api_key else '[MISSING]'}")
|
|
print(f" Max Tokens: {getattr(plugin, 'max_tokens', 'N/A')}")
|
|
print(f" Temperature: {getattr(plugin, 'temperature', 'N/A')}")
|
|
print(f" Context Count: {getattr(plugin, 'context_count', 'N/A')}")
|
|
print(f" Allowed Tabs: {getattr(plugin, 'allowed_tabs', 'N/A')}")
|
|
|
|
if not plugin.api_key:
|
|
print("\n" + "=" * 60)
|
|
print(" SKIPPED: No LLM_KEY configured")
|
|
print(" Set LLM_PROVIDER and LLM_KEY in .env to run full test")
|
|
print("=" * 60)
|
|
return False
|
|
|
|
# === INJECTION TEST ===
|
|
print("\n[2/4] HTML Injection")
|
|
print("-" * 40)
|
|
|
|
class MockSearchQuery:
|
|
pageno = 1
|
|
query = "why is the sky blue"
|
|
lang = 'en'
|
|
categories = ['general']
|
|
|
|
class MockSearch:
|
|
search_query = MockSearchQuery()
|
|
class MockResultContainer:
|
|
def __init__(self):
|
|
self.answers = set()
|
|
def get_ordered_results(self):
|
|
return [
|
|
{"title": "Wikipedia", "content": "The sky appears blue due to Rayleigh scattering.", "url": "https://example.com/1", "publishedDate": "2026-01-15"},
|
|
{"title": "NASA", "content": "Blue wavelengths scatter more than red.", "url": "https://example.com/2", "publishedDate": "2026-01-10"},
|
|
]
|
|
result_container = MockResultContainer()
|
|
|
|
search = MockSearch()
|
|
plugin.post_search(None, search)
|
|
|
|
if not search.result_container.answers:
|
|
print(" FAIL: No HTML injected")
|
|
return False
|
|
|
|
html = str(list(search.result_container.answers)[0])
|
|
|
|
has_box = 'id="sxng-stream-box"' in html
|
|
has_endpoint = '/ai-stream' in html
|
|
|
|
token_match = re.search(r'const tk = "(.*?)";', html)
|
|
has_token = bool(token_match)
|
|
|
|
print(f" Stream box: {'[OK]' if has_box else '[FAIL]'}")
|
|
print(f" Endpoint ref: {'[OK]' if has_endpoint else '[FAIL]'}")
|
|
print(f" Auth token: {'[OK]' if has_token else '[FAIL]'}")
|
|
print(f" HTML size: {len(html):,} bytes")
|
|
|
|
if not (has_box and has_endpoint and has_token):
|
|
print(" FAIL: Missing required elements")
|
|
return False
|
|
|
|
# === STREAM ENDPOINT TEST ===
|
|
print("\n[3/4] Stream Endpoint")
|
|
print("-" * 40)
|
|
|
|
with app.test_client() as client:
|
|
payload = {
|
|
"q": "why is the sky blue",
|
|
"context": "[1] Wikipedia: The sky appears blue due to Rayleigh scattering.",
|
|
"lang": "en",
|
|
"tk": token_match.group(1)
|
|
}
|
|
|
|
start = time.time()
|
|
response = client.post('/ai-stream', json=payload)
|
|
elapsed = time.time() - start
|
|
|
|
print(f" Status: {response.status_code}")
|
|
print(f" Time: {elapsed:.2f}s")
|
|
|
|
if response.status_code != 200:
|
|
print(f" FAIL: Expected 200, got {response.status_code}")
|
|
return False
|
|
|
|
# === LLM RESPONSE TEST ===
|
|
print("\n[4/4] LLM Response")
|
|
print("-" * 40)
|
|
|
|
data = response.data.decode('utf-8')
|
|
print(f" Bytes: {len(data):,}")
|
|
print(f" Words: ~{len(data.split())}")
|
|
|
|
if len(data) < 10:
|
|
print(" FAIL: Response too short (API error?)")
|
|
return False
|
|
|
|
print("\n --- Response Preview ---")
|
|
preview = data[:500] + ("..." if len(data) > 500 else "")
|
|
for line in preview.split('\n'):
|
|
print(f" {line}")
|
|
print(" --- End Preview ---")
|
|
|
|
# === SUMMARY ===
|
|
print("\n" + "=" * 60)
|
|
print(" ALL TESTS PASSED")
|
|
print("=" * 60)
|
|
return True
|
|
|
|
if __name__ == "__main__":
|
|
success = run_tests()
|
|
sys.exit(0 if success else 1)
|