Add the Open-WebUI tool that calls the retrieval bridge
Pasted into Open-WebUI rather than installed by a script: tools live in its own database, and writing there means guessing at the schema and access control of a live app. The secret is a Valve so this file stays committable.
This commit is contained in:
@@ -0,0 +1,129 @@
|
|||||||
|
"""
|
||||||
|
title: Varaverk Docs
|
||||||
|
description: Search Varaverk's own documentation and return grounding context.
|
||||||
|
author: Varaverk
|
||||||
|
version: 1.0.0
|
||||||
|
required_open_webui_version: 0.4.0
|
||||||
|
"""
|
||||||
|
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
# PURPOSE
|
||||||
|
# Open-WebUI tool that lets the chat model search Varaverk's documentation index and answer
|
||||||
|
# from it, instead of from whatever it happens to remember about a private project it has
|
||||||
|
# never seen.
|
||||||
|
#
|
||||||
|
# OPERATIONAL MODEL
|
||||||
|
# Not installed by any Varaverk script. Open-WebUI stores tools in its own database, and
|
||||||
|
# writing there directly would mean guessing at its schema, IDs and access control on a live
|
||||||
|
# app. Paste this into Open-WebUI → Workspace → Tools → +, then set the two Valves.
|
||||||
|
#
|
||||||
|
# Calls AI/ai_serve.js over HTTP because Open-WebUI runs in a container that cannot see
|
||||||
|
# Varaverk's filesystem and has no WebGUI session, so the plugin's PHP API is unreachable to
|
||||||
|
# it. The bridge returns chunks; the model already loaded in Open-WebUI does the generating.
|
||||||
|
#
|
||||||
|
# CONFIGURATION (Valves — set in the Open-WebUI tool editor, not here)
|
||||||
|
# base_url http://<host-lan-ip>:7822 — AI_HTTP_PORT from master.conf
|
||||||
|
# secret AI_HTTP_SECRET from master.conf
|
||||||
|
#
|
||||||
|
# The secret is a Valve rather than a constant so this file stays committable. Do not paste
|
||||||
|
# it into the code — this path is git-tracked and pushed to a remote.
|
||||||
|
#
|
||||||
|
# OPERATIONAL SAFEGUARDS
|
||||||
|
# Retrieval only. The bridge exposes one read-only verb over documentation already in git;
|
||||||
|
# this tool cannot write conf, run a script, or change anything.
|
||||||
|
#
|
||||||
|
# Returns "no relevant documentation found" rather than an empty string on a miss, so the
|
||||||
|
# model states that plainly instead of filling the silence from memory — the failure this
|
||||||
|
# whole retrieval path exists to prevent.
|
||||||
|
#
|
||||||
|
# Every failure is returned as readable text, never raised. An exception inside a tool call
|
||||||
|
# surfaces to the user as an opaque error; a sentence explaining that the bridge is
|
||||||
|
# unreachable is something they can act on.
|
||||||
|
#
|
||||||
|
# Time-boxed at 30s. A hung retrieval must not hold the chat turn open indefinitely.
|
||||||
|
# ═══════════════════════════════════════════════════════════════════════════════════════════════
|
||||||
|
|
||||||
|
import json
|
||||||
|
import urllib.parse
|
||||||
|
import urllib.request
|
||||||
|
|
||||||
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
|
|
||||||
|
class Tools:
|
||||||
|
class Valves(BaseModel):
|
||||||
|
base_url: str = Field(
|
||||||
|
default="http://192.168.50.2:7822",
|
||||||
|
description="AI retrieval bridge — host LAN IP and AI_HTTP_PORT. Not localhost: "
|
||||||
|
"Open-WebUI is a container and localhost is itself.",
|
||||||
|
)
|
||||||
|
secret: str = Field(
|
||||||
|
default="",
|
||||||
|
description="AI_HTTP_SECRET from Configurations/master.conf",
|
||||||
|
)
|
||||||
|
results: int = Field(
|
||||||
|
default=8,
|
||||||
|
description="Chunks to retrieve per query (1-25)",
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.valves = self.Valves()
|
||||||
|
|
||||||
|
def search_varaverk_docs(self, query: str, kind: str = "") -> str:
|
||||||
|
"""
|
||||||
|
Search the Varaverk documentation index for passages relevant to a question about
|
||||||
|
this specific home-media system: its scripts, configuration variables, safeguards,
|
||||||
|
orchestrators, rsync behaviour, watchdogs, fallback logic or plugin internals.
|
||||||
|
|
||||||
|
Always use this before answering any question about Varaverk. Varaverk is a private
|
||||||
|
project and is not in your training data; without this tool you do not know what it
|
||||||
|
is and must not guess.
|
||||||
|
|
||||||
|
:param query: The question or topic to search for, in natural language.
|
||||||
|
:param kind: Optional filter on where the text comes from. Use "readme" for
|
||||||
|
definitional or narrative questions such as "what is Varaverk" or "why does this
|
||||||
|
exist" — otherwise per-script header sections outrank the top-level prose and the
|
||||||
|
answer will look absent when it is not. Leave empty for specific technical
|
||||||
|
questions. One of: header, readme, manual, template, doc.
|
||||||
|
:return: Numbered passages with their source paths, or a message saying nothing matched.
|
||||||
|
"""
|
||||||
|
if not self.valves.secret:
|
||||||
|
return ("The Varaverk docs tool is not configured: its 'secret' Valve is empty. "
|
||||||
|
"Set it to AI_HTTP_SECRET from Configurations/master.conf.")
|
||||||
|
|
||||||
|
params = {
|
||||||
|
"key": self.valves.secret,
|
||||||
|
"q": query,
|
||||||
|
"k": max(1, min(int(self.valves.results), 25)),
|
||||||
|
}
|
||||||
|
if kind:
|
||||||
|
params["kind"] = kind
|
||||||
|
|
||||||
|
url = f"{self.valves.base_url.rstrip('/')}/search?" + urllib.parse.urlencode(params)
|
||||||
|
|
||||||
|
try:
|
||||||
|
with urllib.request.urlopen(url, timeout=30) as r:
|
||||||
|
data = json.load(r)
|
||||||
|
except Exception as e:
|
||||||
|
return (f"Could not reach the Varaverk retrieval bridge at "
|
||||||
|
f"{self.valves.base_url} ({e}). It is started by AI/start_ai_server.sh; "
|
||||||
|
f"check that AI_ENABLED and AI_HTTP_PORT are set and the index exists.")
|
||||||
|
|
||||||
|
if not data.get("ok"):
|
||||||
|
return f"Varaverk retrieval failed: {data.get('error', 'unknown error')}"
|
||||||
|
|
||||||
|
results = data.get("results") or []
|
||||||
|
if not results:
|
||||||
|
return (f"No relevant documentation found for '{query}'. Say so plainly rather "
|
||||||
|
f"than answering from general knowledge — Varaverk is private and is not "
|
||||||
|
f"in your training data.")
|
||||||
|
|
||||||
|
out = [f"{len(results)} passage(s) from the Varaverk documentation index:", ""]
|
||||||
|
for i, r in enumerate(results, 1):
|
||||||
|
label = " › ".join(x for x in (r.get("path"), r.get("section"), r.get("heading")) if x)
|
||||||
|
out.append(f"[{i}] {label} (score {r.get('score')})")
|
||||||
|
out.append(r.get("content", "").strip())
|
||||||
|
out.append("")
|
||||||
|
out.append("Answer only from the passages above, and cite them by their [n] markers. "
|
||||||
|
"If they do not contain the answer, say so and name what is missing.")
|
||||||
|
return "\n".join(out)
|
||||||
Reference in New Issue
Block a user