[feat] plugin: AI summary of search results via local Ollama server
Add an optional, disabled-by-default plugin that shows an AI generated summary at the top of the result page, generated by a (local) Ollama server: - async: the result page is never delayed; a client plugin streams the answer (NDJSON over a new /ai_summary endpoint) into a placeholder answer with a typing indicator, collapsed behind a More button, with an inline follow-up chat - trigger: first page of general searches only, skipped when an infobox or instant answer already answers the query - grounding (per-user preference): send the top result snippets as context, the model answers from them instead of its own knowledge - configuration: new AI Summary preferences tab (server URL, model, grounding) with instance defaults in a new ai_summary: settings section; all three preferences can be locked for public instances Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
b060c780d0
commit
b47fd08cb7
@@ -19,6 +19,7 @@ __all__ = [
|
||||
"EngineResults",
|
||||
"AnswerSet",
|
||||
"Answer",
|
||||
"AiSummary",
|
||||
"Translations",
|
||||
"WeatherAnswer",
|
||||
"Code",
|
||||
@@ -32,7 +33,7 @@ import typing as t
|
||||
import abc
|
||||
|
||||
from ._base import Result, MainResult, LegacyResult
|
||||
from .answer import AnswerSet, Answer, Translations, WeatherAnswer
|
||||
from .answer import AnswerSet, Answer, AiSummary, Translations, WeatherAnswer
|
||||
from .keyvalue import KeyValue
|
||||
from .code import Code
|
||||
from .paper import Paper
|
||||
@@ -49,6 +50,7 @@ class ResultList(list[Result | LegacyResult], abc.ABC):
|
||||
implemented)."""
|
||||
|
||||
Answer = Answer
|
||||
AiSummary = AiSummary
|
||||
KeyValue = KeyValue
|
||||
Code = Code
|
||||
Paper = Paper
|
||||
|
||||
@@ -14,6 +14,10 @@ template.
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
.. autoclass:: AiSummary
|
||||
:members:
|
||||
:show-inheritance:
|
||||
|
||||
.. autoclass:: Translations
|
||||
:members:
|
||||
:show-inheritance:
|
||||
@@ -29,7 +33,7 @@ template.
|
||||
# pylint: disable=too-few-public-methods
|
||||
|
||||
|
||||
__all__ = ["AnswerSet", "Answer", "Translations", "WeatherAnswer"]
|
||||
__all__ = ["AnswerSet", "Answer", "AiSummary", "Translations", "WeatherAnswer"]
|
||||
|
||||
from flask_babel import gettext
|
||||
import msgspec
|
||||
@@ -92,6 +96,28 @@ class Answer(BaseAnswer, kw_only=True):
|
||||
return hash(self.answer)
|
||||
|
||||
|
||||
class AiSummary(BaseAnswer, kw_only=True):
|
||||
"""Placeholder for an AI generated summary of the search query
|
||||
(:py:obj:`searx.plugins.ai_summary`). The summary itself is fetched
|
||||
asynchronously by the client after the result page has been rendered."""
|
||||
|
||||
# The answers of an AnswerSet are sorted by their template name; this
|
||||
# template sorts before the other answer templates, the AI summary is
|
||||
# therefore rendered first in the answer area.
|
||||
template: str = "answer/ai_summary.html"
|
||||
|
||||
query: str
|
||||
"""The search query the summary is generated for."""
|
||||
|
||||
grounding: bool = False
|
||||
"""Ground the summary on the search results (user's preference)."""
|
||||
|
||||
def __hash__(self):
|
||||
"""The hash value of field *query* is the hash value of the
|
||||
:py:obj:`AiSummary` object."""
|
||||
return hash(self.query)
|
||||
|
||||
|
||||
class Translations(BaseAnswer, kw_only=True):
|
||||
"""Answer type with a list of translations.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user