114 lines
6.2 KiB
Python
114 lines
6.2 KiB
Python
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
# pylint: disable=missing-module-docstring, missing-class-docstring
|
|
import json
|
|
from datetime import datetime
|
|
|
|
from flask_babel import gettext
|
|
|
|
from searx.plugins import Plugin, PluginInfo
|
|
|
|
|
|
class SXNGPlugin(Plugin):
|
|
id = "quick_answer"
|
|
default_on = False
|
|
|
|
def __init__(self):
|
|
super().__init__()
|
|
|
|
self.info = PluginInfo(
|
|
id=self.id,
|
|
name=gettext("Quick Answer"),
|
|
description=gettext("Use search results to obtain cited answers from LLMs by appending '?' to queries"),
|
|
examples=["Linear congruential generator?"],
|
|
preference_section="general/quick_answer",
|
|
)
|
|
|
|
def get_sys_prompt(self):
|
|
now = datetime.now()
|
|
return f"""
|
|
The current date is {now:%Y-%m-%d}
|
|
|
|
You ALWAYS follow these guidelines when writing your response:
|
|
- Use markdown formatting to enhance clarity and readability of your response.
|
|
- If you need to include mathematical expressions, use LaTeX to format them properly. Only use LaTeX when necessary for math.
|
|
- Delimit inline mathematical expressions with '$', for example: $y = mx + b$.
|
|
- Delimit block mathematical expressions with '$$', for example: $$F = ma$$.
|
|
- If you need to write code or program commands, format them as markdown code blocks.
|
|
- For all other output, use plain text formatting unless the user specifically requests otherwise.
|
|
- DO NOT include headers which only describe or rephrase the query before beginning your response.
|
|
- DO NOT include URLs or links in your response.
|
|
- ALWAYS enclose currency and price values in '**', for example: **$5.99**, to ensure they are formatted correctly.
|
|
|
|
The relevant available information is contained within the <information></information> tags. When a user asks a question, perform the following tasks:
|
|
0. Examine the available information and assess whether you can answer the question based on it, even if the answer is not explicitly stated. For example, if the question asks about a specific feature of a product and the available information discusses the product's features without mentioning the specific feature, you can infer that the product likely does not have that feature.
|
|
1. Use the available information to inform your answer.
|
|
2. When answering questions, provide inline citation references by putting their citation index delimited by 【 and 】 at end of sentence, example: This is a claim【1】."
|
|
3. If you need to cite multiple pieces of information inline, use separate 【 and 】 for each citation, example: "This is a claim【1】【2】."
|
|
4. Use citations most relevant to the query to augment your answer with informative supportive resources; do not create unhelpful, extended chains of citations.
|
|
5. DO NOT list URLs/links of the citation source or an aggregate list of citations at the end of the response. They would be automatically added by the system based on citation indices.
|
|
6. DO NOT provide inline citations inside or around code blocks, as they break formatting of output, only provide them to augment plaintext.
|
|
7. DO NOT use markdown to format your citations, always provide them in plaintext.
|
|
|
|
A few guidelines for you when answering questions:
|
|
- Highlight relevant entities/phrases with **, for example: "**Neil Armstrong** is known as the first person to land on the moon." (Do not apply this guideline to citations or in code blocks.)
|
|
- DO NOT talk about how you based your answer on the information provided to you as it may confuse the user.
|
|
- Don't copy-paste the information from the available information directly. Paraphrase the information in your own words.
|
|
- Even if the information is in another format, your output MUST follow the guidelines. for example: output O₁ instead of O<sub>1</sub>, output R⁷ instead of R<sup>7</sup>, etc.
|
|
- Be concise and informative in your answers.
|
|
"""
|
|
|
|
def format_sources(self, sources):
|
|
ret = "<available_information>\n"
|
|
for pos, source in enumerate(sources):
|
|
ret += "<datum>\n"
|
|
ret += f'<citation index="{pos}">\n'
|
|
ret += f"<source>\n{source.get('url', '')}\n</source>\n"
|
|
ret += f"<title>\n{source.get('title', '')}\n</title>\n"
|
|
ret += f"<content>\n{source.get('content', '')}\n</content>\n"
|
|
ret += "</datum>\n"
|
|
|
|
return ret + "</available_information>"
|
|
|
|
def post_search(self, request, search):
|
|
query = search.search_query
|
|
if query.pageno > 1 or not query.query.endswith("?"):
|
|
return
|
|
|
|
token = request.preferences.get_value("quick_answer_token")
|
|
if not token:
|
|
return
|
|
|
|
model = request.preferences.get_value("quick_answer_model")
|
|
providers = request.preferences.get_value("quick_answer_providers")
|
|
if providers:
|
|
providers = [provider.strip() for provider in providers.split(",")]
|
|
|
|
sources = search.result_container.get_ordered_results()
|
|
formatted_sources = self.format_sources(sources)
|
|
user = formatted_sources + f"\n\nUser query: {query.query}"
|
|
system = self.get_sys_prompt()
|
|
|
|
reference_map = {str(i): (source.get("url"), source.get("title")) for i, source in enumerate(sources)}
|
|
|
|
search.result_container.infoboxes.append(
|
|
{
|
|
"infobox": "Quick Answer",
|
|
"id": "quick_answer",
|
|
"content": f"""
|
|
<script>
|
|
window.systemPrompt = {json.dumps(system)};
|
|
window.userPrompt = {json.dumps(user)};
|
|
window.userToken = {json.dumps(token)};
|
|
window.userModel = {json.dumps(model)};
|
|
window.userProviders = {json.dumps(providers)};
|
|
window.referenceMap = {json.dumps(reference_map)};
|
|
</script>
|
|
""",
|
|
}
|
|
)
|
|
|
|
name = gettext("Quick Answer")
|
|
description = gettext("Use search results to obtain cited answers from LLMs by appending '?' to queries")
|
|
default_on = False
|
|
preference_section = "general"
|