Communitygithub.com

gdevenyi/openwebui-tool-skill

Compétence d'agent pour écrire et vérifier des outils Python dans Open WebUI

Qu'est-ce que openwebui-tool-skill ?

Cette compétence d'agent est spécialisée dans la création et la validation d'outils Python pour Open WebUI. Elle permet de générer un fichier Python unique, conforme aux règles strictes d'Open WebUI, où chaque méthode publique d'une classe `Tools` devient une fonction appelable par le modèle. La compétence garantit que les docstrings sont formatées en reST, que les paramètres sont correctement typés et que les méthodes asynchrones sont utilisées pour éviter de bloquer le serveur. Elle inclut également un script de vérification (`check_tool.py`) qui reproduit le chargement du serveur et valide que seules les méthodes intentionnées sont exposées. Idéale pour les développeurs intégrant des outils personnalisés dans Open WebUI, elle prend en charge les valves de configuration, les événements de progression (status, citation, notification) et les retours sous forme de chaînes de caractères. Plateformes cibles : Open WebUI, et tout environnement exploitant des outils Python pour modèles de langage.

Compatible avec~Claude Code~Codex CLI~Cursor
npx skills add gdevenyi/openwebui-tool-skill

Demander à votre IA préférée

Ouvre une nouvelle conversation avec cette compétence d'agent déjà préchargée.

Documentation

Outils Python Open WebUI

Un outil est un fichier Python unique. Open WebUI exécute le fichier, instancie Tools(), et transforme chaque méthode publique en une fonction que le modèle peut appeler. Cet ensemble de méthodes publiques constitue la surface de l'outil — une aide que vous avez oublié de préfixer par _ devient une fonction que le modèle appellera.

La docstring n'est pas un commentaire. Son premier bloc devient la description de la fonction envoyée au modèle, et chaque ligne :param nom: devient une description de paramètre. La docstring est l'invite.

Le fichier

"""
title: Recherche de paquet
author: vous
description: Rechercher un paquet sur PyPI
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="Racine de l'API JSON PyPI",
        )

    class UserValves(BaseModel):
        include_yanked: bool = Field(default=False, description="Inclure les versions supprimées")

    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:
        """
        Rechercher un paquet Python sur PyPI et retourner ses métadonnées.

        :param name: Le nom du paquet, ex. `httpx`
        :param version: Une version spécifique ; omettre pour la dernière version
        :return: JSON avec résumé, dernière version, page d'accueil et licence
        """
        if __event_emitter__:
            await __event_emitter__(
                {"type": "status", "data": {"description": f"Récupération de {name}...", "done": False}}
            )

        try:
            data = await self._fetch(name, version)
        except httpx.HTTPStatusError as e:
            return f"PyPI a retourné {e.response.status_code} pour le paquet '{name}'."
        except Exception as e:
            return f"Impossible de contacter PyPI : {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()

Ce fichier génère exactement cette spécification pour le modèle — rien d'autre dans le fichier n'est accessible :

{"name": "get_package",
 "description": "Rechercher un paquet Python sur PyPI et retourner ses métadonnées.",
 "parameters": {"type": "object", "required": ["name"], "properties": {
   "name": {"type": "string", "description": "Le nom du paquet, ex. `httpx`"},
   "version": {"type": "string", "description": "Une version spécifique ; omettre pour la dernière version"}}}}

Règles que le squelette ne peut pas montrer

  • La ligne 1 est exactement """. Un commentaire, une importation ou une ligne vide au-dessus rend tout le frontmatter invisible — requirements ne s'installe alors jamais, en silence.
  • Gardez les aides hors de la surface avec un préfixe _. Tout le reste de ce qui est public sur Tools — y compris tout ce qui est hérité — est une fonction que le modèle peut appeler.
  • Docstrings reST uniquement. :param nom: texte atteint le modèle ; un bloc Args: de style Google n'atteint rien.
  • Type-hint chaque paramètre exposé au modèle. Les indications génèrent le schéma JSON. Les indications doivent être résolubles au moment de l'importation ; une annotation insoluble rend l'enregistrement de l'outil immédiatement impossible.
  • Donnez à chaque paramètre __dunder__ injecté une valeur par défaut. Open WebUI ne les fournit que là où il les a, et le modèle ne les voit jamais — ils sont retirés du schéma.
  • __init__ ne prend aucun argument et définit self.valves = self.Valves(). Sans cette ligne, les valeurs de valve configurées par l'administrateur sont perdues.
  • Chaque méthode est async def. Une méthode synchrone s'exécute en ligne sur la boucle d'événements et bloque le serveur ; poussez le travail bloquant via await asyncio.to_thread(...).
  • Retournez une chaîne (un dict ou une list est converti en JSON pour vous). Retournez un message d'erreur lisible plutôt que de lever une exception — une exception levée atteint le modèle sous forme de son simple str().
  • La progression passe par les événements status ; la réponse passe par la valeur de retour. Les quatre événements de contenu (message, chat:message, chat:message:delta, replace) sont écrasés par les instantanés de complétion en mode Natif, qui est le seul mode pris en charge.

Vérifier

python scripts/check_tool.py moutil.py

Le script ne dépend que de la bibliothèque standard, mais il exécute le fichier outil, donc les imports propres à l'outil doivent être résolus — uv run --with httpx --with pydantic python scripts/check_tool.py moutil.py pour le fichier ci-dessus.

Il reproduit le chargement et la génération de spécifications du serveur. Deux barres, les deux doivent tenir : il se termine proprement, et chaque nom dans la surface de l'outil imprimée est celui que vous vouliez que le modèle appelle.

Ensuite, collez le fichier dans Workspace > Tools. L'identifiant de l'outil est défini là, pas dans le fichier, et doit être un identifiant Python en minuscules.

Référence

Lisez references/REFERENCE.md pour la liste complète des paramètres injectés, le catalogue d'émetteurs d'événements (status, citation, notification, confirmation, input, fichiers), les types d'entrée de valve (mot de passe, sélection, sélection multiple, options dynamiques), le retour de fichiers ou de HTML intégré, et les indicateurs hérités qui ne font plus rien.

Skills associés