Files
Alfredv2/alfred/app/services/alfred_context_images.py
T

84 lines
3.0 KiB
Python

"""Illustrations contextuelles Wikipédia déclenchées APRÈS une réponse Hermes.
Pas de modification des prompts Hermes ou de /api/render. Échec silencieux.
"""
from __future__ import annotations
import re
import threading
import time
import unicodedata
from urllib.parse import urlencode, quote, urlparse
import requests
CACHE = {}
LOCK = threading.Lock()
def _norm(s):
s = unicodedata.normalize('NFKD', str(s)).casefold()
return re.sub(r'[^a-z0-9 ]+', '', ''.join(c for c in s if not unicodedata.combining(c)))
def entity_question(s):
text = ' '.join(str(s or '').split())
m = re.match(r'^(?:alfred[ ,.:!?]*)?(?:qui\s+(?:est|etait|était|sont)\s+|c.?est\s+qui\s+)([^?!.]{3,95})', text, re.I)
if not m:
return None
entity = m.group(1).strip(' «»\'".,?!')
entity = re.sub(r'\s+(?:au juste|exactement|s.il te plait)$', '', entity, flags=re.I)
if len(entity.split()) > 7 or len(entity) < 3:
return None
return entity
def _query_wikipedia(name):
# PageImages retourne une illustration publique et l'URL de la page source.
params = {'action':'query','generator':'search','gsrsearch':name,'gsrlimit':1,
'prop':'pageimages','piprop':'thumbnail','pithumbsize':500,
'format':'json','formatversion':'2','redirects':'1'}
headers = {'User-Agent':'AlfredAssistant/1.0 (personal home assistant; image source: Wikipedia)'}
response = requests.get('https://fr.wikipedia.org/w/api.php', params=params,
headers=headers, timeout=4, allow_redirects=False)
response.raise_for_status()
pages = response.json().get('query', {}).get('pages', [])
if not pages:
return None
page = pages[0]
title = str(page.get('title',''))
if not title or _norm(name) not in _norm(title) and _norm(title) not in _norm(name):
return None
url = str(page.get('thumbnail', {}).get('source', ''))
parsed = urlparse(url)
if parsed.scheme != 'https' or parsed.hostname not in ('upload.wikimedia.org','commons.wikimedia.org'):
return None
return {'title': title, 'image_url':url,
'source_url':'https://fr.wikipedia.org/wiki/'+quote(title.replace(' ','_')),
'credit':'Image : Wikipédia / Wikimedia Commons'}
def image_for_question(name):
key = _norm(name)
with LOCK:
old = CACHE.get(key)
if old and time.monotonic()-old[0] < 4*3600:
return old[1]
try:
result = _query_wikipedia(name)
except (requests.RequestException, ValueError, KeyError):
result = None
with LOCK:
CACHE[key] = (time.monotonic(), result)
return result
def schedule_context_image(question, answer, socketio, still_current):
name = entity_question(question)
if not name:
return
def worker():
info = image_for_question(name)
if info and still_current():
info['text'] = str(answer or '')[:500]
socketio.emit('alfred_context_image', info)
threading.Thread(target=worker, name='alfred-context-image', daemon=True).start()