""" 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://: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)