This commit is contained in:
+8
-94
@@ -45,10 +45,6 @@ def _get_streaming_connection(url: str):
|
|||||||
PLUGIN_NAME = "AI Answers"
|
PLUGIN_NAME = "AI Answers"
|
||||||
DEFAULT_TABS = "general,science,it,news"
|
DEFAULT_TABS = "general,science,it,news"
|
||||||
|
|
||||||
PROVIDER_PRESETS = {
|
|
||||||
'ollama': {'url': 'http://localhost:11434/v1/chat/completions', 'model': 'llama3.2'},
|
|
||||||
}
|
|
||||||
|
|
||||||
# UI assets
|
# UI assets
|
||||||
|
|
||||||
INTERACTIVE_CSS = '''
|
INTERACTIVE_CSS = '''
|
||||||
@@ -745,70 +741,17 @@ class SXNGPlugin(Plugin):
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
def _ollama_unload_model(self) -> None:
|
|
||||||
try:
|
|
||||||
if self.provider != 'ollama':
|
|
||||||
return
|
|
||||||
if not getattr(self, 'ollama_unload_after', False):
|
|
||||||
return
|
|
||||||
unload_url = (getattr(self, 'ollama_unload_url', '') or '').strip()
|
|
||||||
if not unload_url:
|
|
||||||
return
|
|
||||||
|
|
||||||
conn = None
|
|
||||||
try:
|
|
||||||
conn, path = _get_streaming_connection(unload_url)
|
|
||||||
conn.timeout = 2.0
|
|
||||||
payload = json.dumps({
|
|
||||||
"model": self.model,
|
|
||||||
"messages": [],
|
|
||||||
"keep_alive": 0
|
|
||||||
})
|
|
||||||
headers = {"Content-Type": "application/json"}
|
|
||||||
if self.api_key and self.api_key not in ('none', 'ollama'):
|
|
||||||
headers["Authorization"] = f"Bearer {self.api_key}"
|
|
||||||
conn.request("POST", path, body=payload, headers=headers)
|
|
||||||
res = conn.getresponse()
|
|
||||||
res.read()
|
|
||||||
if res.status >= 400:
|
|
||||||
logger.warning(f"{PLUGIN_NAME}: Ollama unload failed: {res.status} {res.reason}")
|
|
||||||
finally:
|
|
||||||
if conn:
|
|
||||||
conn.close()
|
|
||||||
except Exception as e:
|
|
||||||
logger.warning(f"{PLUGIN_NAME}: Ollama unload error: {e}")
|
|
||||||
|
|
||||||
def _load_config(self):
|
def _load_config(self):
|
||||||
self.interactive = os.getenv('LLM_INTERACTIVE', 'true').lower().strip() in ('true', '1', 'yes', 'on')
|
self.interactive = os.getenv('LLM_INTERACTIVE', 'true').lower().strip() in ('true', '1', 'yes', 'on')
|
||||||
self.question_mark_required = os.getenv('LLM_QUESTION_MARK_REQUIRED', 'false').lower().strip() in ('true', '1', 'yes', 'on')
|
self.question_mark_required = os.getenv('LLM_QUESTION_MARK_REQUIRED', 'false').lower().strip() in ('true', '1', 'yes', 'on')
|
||||||
raw_provider = os.getenv('LLM_PROVIDER', '').lower().strip()
|
|
||||||
|
|
||||||
raw_url = os.getenv('LLM_URL', '').strip()
|
|
||||||
if not raw_provider and raw_url:
|
|
||||||
url_lower = raw_url.lower()
|
|
||||||
if ':11434' in url_lower:
|
|
||||||
raw_provider = 'ollama'
|
|
||||||
else:
|
|
||||||
raw_provider = 'error'
|
|
||||||
logger.info(f"{raw_provider}: Ollama not detected")
|
|
||||||
|
|
||||||
if not raw_provider:
|
|
||||||
self.provider = ''
|
|
||||||
self.model = ''
|
|
||||||
self.api_key = ''
|
|
||||||
return
|
|
||||||
|
|
||||||
if raw_provider not in PROVIDER_PRESETS:
|
|
||||||
logger.warning(f"{PLUGIN_NAME}: Not Ollama '{raw_provider}', please correct.")
|
|
||||||
self.provider = raw_provider if raw_provider in PROVIDER_PRESETS else 'Error'
|
|
||||||
preset = PROVIDER_PRESETS[self.provider]
|
|
||||||
|
|
||||||
self.api_key = os.getenv('LLM_KEY', '')
|
raw_url = os.getenv('LLM_URL', 'http://ollama:11434/v1/chat/completions').strip()
|
||||||
if not self.api_key and self.provider == 'ollama':
|
if not raw_url.startswith(('http://', 'https://')):
|
||||||
self.api_key = 'none'
|
raw_url = f"http://{raw_url}"
|
||||||
self.api_key = self.api_key.strip()
|
self.endpoint_url = raw_url
|
||||||
|
|
||||||
self.model = os.getenv('LLM_MODEL', preset['model']).strip()
|
self.api_key = 'ollama'
|
||||||
|
self.model = os.getenv('LLM_MODEL', 'llama3.2').strip()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.max_tokens = max(1, int(os.getenv('LLM_MAX_TOKENS', 200)))
|
self.max_tokens = max(1, int(os.getenv('LLM_MAX_TOKENS', 200)))
|
||||||
@@ -832,31 +775,10 @@ class SXNGPlugin(Plugin):
|
|||||||
self.context_shallow_count = 15
|
self.context_shallow_count = 15
|
||||||
|
|
||||||
self.allowed_tabs = set(t.strip() for t in os.getenv('LLM_TABS', DEFAULT_TABS).split(','))
|
self.allowed_tabs = set(t.strip() for t in os.getenv('LLM_TABS', DEFAULT_TABS).split(','))
|
||||||
|
|
||||||
preset_url = preset['url']
|
|
||||||
if preset_url and '{model}' in preset_url:
|
|
||||||
preset_url = preset_url.format(model=self.model)
|
|
||||||
|
|
||||||
raw_url = os.getenv('LLM_URL', '').strip() or preset_url
|
|
||||||
if not raw_url.startswith(('http://', 'https://')):
|
|
||||||
raw_url = f"https://{raw_url}"
|
|
||||||
self.endpoint_url = raw_url
|
|
||||||
|
|
||||||
self.ollama_unload_after = os.getenv('LLM_OLLAMA_UNLOAD_AFTER', 'false').lower().strip() in ('true', '1', 'yes', 'on')
|
|
||||||
self.ollama_unload_url = ''
|
|
||||||
if self.provider == 'ollama' and self.ollama_unload_after:
|
|
||||||
try:
|
|
||||||
p = urlparse(self.endpoint_url)
|
|
||||||
scheme = p.scheme or 'http'
|
|
||||||
host = p.hostname or 'localhost'
|
|
||||||
port = p.port
|
|
||||||
netloc = f"{host}:{port}" if port else host
|
|
||||||
self.ollama_unload_url = f"{scheme}://{netloc}/api/chat"
|
|
||||||
except Exception:
|
|
||||||
self.ollama_unload_url = "http://localhost:11434/api/chat"
|
|
||||||
server_secret = settings.get('server', {}).get('secret_key', '')
|
server_secret = settings.get('server', {}).get('secret_key', '')
|
||||||
self.secret = hashlib.sha256(f"ai_answers_{server_secret}".encode()).hexdigest()
|
self.secret = hashlib.sha256(f"ai_answers_{server_secret}".encode()).hexdigest()
|
||||||
|
|
||||||
self.system_prompt = os.getenv('LLM_SYSTEM_PROMPT', '').strip()
|
self.system_prompt = os.getenv('LLM_SYSTEM_PROMPT', '').strip()
|
||||||
|
|
||||||
def _parse_aux_results(self, raw_results, raw_infoboxes, raw_answers):
|
def _parse_aux_results(self, raw_results, raw_infoboxes, raw_answers):
|
||||||
@@ -901,12 +823,7 @@ class SXNGPlugin(Plugin):
|
|||||||
|
|
||||||
return results, infoboxes, answers
|
return results, infoboxes, answers
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def init(self, app):
|
def init(self, app):
|
||||||
if not self.provider:
|
|
||||||
return
|
|
||||||
|
|
||||||
@app.route('/ai-auxiliary-search', methods=['POST'])
|
@app.route('/ai-auxiliary-search', methods=['POST'])
|
||||||
def ai_auxiliary_search():
|
def ai_auxiliary_search():
|
||||||
if not self.api_key:
|
if not self.api_key:
|
||||||
@@ -996,9 +913,6 @@ class SXNGPlugin(Plugin):
|
|||||||
except (ValueError, KeyError, AttributeError):
|
except (ValueError, KeyError, AttributeError):
|
||||||
abort(403)
|
abort(403)
|
||||||
|
|
||||||
if self.provider != 'ollama':
|
|
||||||
return jsonify({'models': [self.model] if self.model else []})
|
|
||||||
|
|
||||||
conn = None
|
conn = None
|
||||||
try:
|
try:
|
||||||
p = urlparse(self.endpoint_url)
|
p = urlparse(self.endpoint_url)
|
||||||
|
|||||||
Reference in New Issue
Block a user