Communitygithub.com

gdevenyi/openwebui-tool-skill

Agent-Fähigkeit zum Schreiben und Verifizieren von Open WebUI Python-Tools.

Was ist openwebui-tool-skill?

Diese Fähigkeit ist speziell für KI-Agenten entwickelt, die Open WebUI Python-Tools erstellen, testen und validieren. Ein Tool ist eine einzelne Python-Datei, die von Open WebUI ausgeführt wird, um eine `Tools()`-Klasse zu instanziieren und jede öffentliche Methode als aufrufbare Funktion für das Modell bereitzustellen. Der Docstring der Methode fungiert als Prompt: Sein erster Block wird zur Funktionsbeschreibung und jede `:param name:`-Zeile zu einer Parameterbeschreibung. Die Fähigkeit beschreibt detailliert die Regeln, die ein Tool namens „Package Lookup“ einhalten muss, darunter den korrekten Dateikopf, die Verwendung von reST-Docstrings, die Typannotation aller modellseitigen Parameter, die Standardwerte für injizierte `__dunder__`-Parameter und die Rückgabe von Strings anstelle von Dicts. Zudem enthält sie einen lokalen Verifikationsbefehl (`scripts/check_tool.py`), der ohne externe Abhängigkeiten auskommt. Der Fokus liegt auf plattformspezifischen Details wie Valves, UserValves, Event-Emittern und der Unterscheidung zwischen öffentlichen und privaten Methoden, was diese Fähigkeit besonders für Entwickler nützlich macht, die nahtlos in Open WebUI integrierte Tools bereitstellen möchten.

Funktioniert mit~Claude Code~Codex CLI~Cursor
npx skills add gdevenyi/openwebui-tool-skill

In Ihrer bevorzugten KI fragen

Öffnet einen neuen Chat, in dem dieser Agent-Skill bereits geladen ist.

Dokumentation

Open WebUI Python-Tools

Ein Tool ist eine Python-Datei. Open WebUI führt die Datei mit exec aus, instanziiert Tools() und macht jede öffentliche Methode zu einer Funktion, die das Modell aufrufen kann. Diese Menge öffentlicher Methoden ist die Oberfläche des Tools – ein Helfer, den Sie mit _ zu versehen vergessen haben, wird zu einer vom Modell aufrufbaren Funktion.

Der Docstring ist kein Kommentar. Sein erster Block wird zur Funktionsbeschreibung, die an das Modell gesendet wird, und jede :param name:-Zeile wird zur Parameterbeschreibung. Der Docstring ist der Prompt.

Die Datei

"""
title: Paketabfrage
author: du
description: Ein Paket auf PyPI nachschlagen
requirements: httpx
version: 0.1.0
licence: MIT
"""

import json
from typing import Any, Callable, Optional

import httpx
from pydantic import BaseModel, Field


class Tools:
    class Valves(BaseModel):
        base_url: str = Field(
            default="https://pypi.org/pypi",
            description="PyPI JSON-API-Wurzel",
        )

    class UserValves(BaseModel):
        include_yanked: bool = Field(default=False, description="Zurückgezogene Veröffentlichungen einbeziehen")

    def __init__(self):
        self.valves = self.Valves()

    async def get_package(
        self,
        name: str,
        version: Optional[str] = None,
        __user__: Optional[dict] = None,
        __event_emitter__: Optional[Callable[[dict], Any]] = None,
    ) -> str:
        """
        Suche ein Python-Paket auf PyPI und gib seine Metadaten zurück.

        :param name: Der Paketname, z. B. `httpx`
        :param version: Eine bestimmte Version; weglassen für die neueste Version
        :return: JSON mit Zusammenfassung, neuester Version, Homepage und Lizenz
        """
        if __event_emitter__:
            await __event_emitter__(
                {"type": "status", "data": {"description": f"Rufe {name} ab...", "done": False}}
            )

        try:
            data = await self._fetch(name, version)
        except httpx.HTTPStatusError as e:
            return f"PyPI gab Status {e.response.status_code} für Paket '{name}' zurück."
        except Exception as e:
            return f"PyPI nicht erreichbar: {e}"

        info = data.get("info", {})
        return json.dumps(
            {"name": info.get("name"), "version": info.get("version"), "summary": info.get("summary")},
            ensure_ascii=False,
        )

    async def _fetch(self, name: str, version: Optional[str]) -> dict:
        path = f"{name}/{version}/json" if version else f"{name}/json"
        async with httpx.AsyncClient(timeout=15) as client:
            response = await client.get(f"{self.valves.base_url}/{path}")
            response.raise_for_status()
            return response.json()

Diese Datei erzeugt genau diese Spezifikation für das Modell – nichts anderes in der Datei ist erreichbar:

{"name": "get_package",
 "description": "Suche ein Python-Paket auf PyPI und gib seine Metadaten zurück.",
 "parameters": {"type": "object", "required": ["name"], "properties": {
   "name": {"type": "string", "description": "Der Paketname, z. B. `httpx`"},
   "version": {"type": "string", "description": "Eine bestimmte Version; weglassen für die neueste Version"}}}}

Regeln, die das Skelett nicht zeigen kann

  • Zeile 1 ist genau """. Ein Kommentar, ein Import oder eine Leerzeile darüber macht die gesamte Frontmatter unsichtbar – dann installiert sich requirements niemals, und zwar lautlos.
  • Halte Helfer von der Oberfläche fern mit einem führenden _. Alles andere Öffentliche an Tools – einschließlich geerbter Methoden – ist eine Funktion, die das Modell aufrufen kann.
  • Nur reST-Docstrings. :param name: text erreicht das Modell; ein Google-style Args:-Block erreicht nichts.
  • Typisiere jeden modellseitigen Parameter. Die Typannotationen erzeugen das JSON-Schema. Annotationen müssen zum Importzeitpunkt auflösbar sein; eine nicht auflösbare Annotation führt dazu, dass das Speichern des Tools sofort fehlschlägt.
  • Gib jedem injizierten __dunder__-Parameter einen Standardwert. Open WebUI liefert sie nur dort, wo sie vorhanden sind, und das Modell sieht sie nie – sie werden aus dem Schema entfernt.
  • __init__ nimmt keine Argumente entgegen und setzt self.valves = self.Valves(). Ohne diese Zeile gehen die vom Administrator konfigurierten Ventilwerte verloren.
  • Jede Methode ist async def. Eine synchrone Methode läuft inline auf der Ereignisschleife und blockiert den Server; verschiebe blockierende Arbeit durch await asyncio.to_thread(...).
  • Gib einen String zurück (ein dict oder list wird für dich in JSON umgewandelt). Gib eine lesbare Fehlermeldung zurück, anstatt eine Exception zu werfen – eine ausgelöste Exception erreicht das Modell als deren rohe str()-Darstellung.
  • Fortschritt erfolgt über status-Ereignisse; die Antwort erfolgt über den Rückgabewert. Die vier Inhalt-Ereignisse (message, chat:message, chat:message:delta, replace) werden durch Vervollständigungs-Schnappschüsse im nativen Modus überschrieben, dem einzigen unterstützten Modus.

Verifizieren

python scripts/check_tool.py meinwerkzeug.py

Das Skript benötigt nur die Standardbibliothek, führt aber die Tool-Datei aus, daher müssen die eigenen Importe des Tools aufgelöst werden können – uv run --with httpx --with pydantic python scripts/check_tool.py meinwerkzeug.py für die obige Datei.

Es reproduziert das Laden des Servers und die Spezifikationserzeugung. Zwei Balken, beide müssen halten: Es beendet sich sauber, und jeder Name in der gedruckten Tool-Oberfläche ist einer, den das Modell aufrufen soll.

Füge dann die Datei in Arbeitsbereich > Tools ein. Die Tool-ID wird dort festgelegt, nicht in der Datei, und muss ein kleingeschriebener Python-Bezeichner sein.

Referenz

Lies references/REFERENCE.md für die vollständige Liste der injizierten Parameter, den Ereignis-Emitter-Katalog (status, citation, notification, confirmation, input, Dateien), Ventil-Eingabetypen (Passwort, Auswahl, Mehrfachauswahl, dynamische Optionen), das Zurückgeben von Dateien oder eingebettetem HTML und die geerbten Flags, die nichts mehr bewirken.

Verwandte Skills