Communitygithub.com

gdevenyi/openwebui-tool-skill

Habilidad de agente para escribir y verificar herramientas Python de Open WebUI

¿Qué es openwebui-tool-skill?

Esta habilidad para agentes de IA está diseñada para ayudar a desarrolladores a crear y probar herramientas Python para Open WebUI de manera eficiente. Open WebUI permite que herramientas Python se conviertan en funciones invocables por modelos de lenguaje, y esta habilidad guía al agente en la estructura correcta de archivos, incluyendo el formato de docstrings, la definición de superficie de herramientas, y la gestión de válvulas de configuración. Soporta la plataforma Open WebUI y se basa en principios clave como mantener las funciones de ayuda privadas con guion bajo, usar solo docstrings reST, escribir métodos asíncronos y devolver cadenas de texto. Incluye un script de verificación (check_tool.py) para validar que no haya funciones expuestas no deseadas. Es útil para cualquier desarrollador que integre herramientas personalizadas en Open WebUI, asegurando que la herramienta se cargue correctamente y que el modelo solo vea las funciones previstas.

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

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

Herramientas Python de Open WebUI

Una herramienta es un archivo Python. Open WebUI exec el archivo, instancia Tools() y convierte cada método público en una función que el modelo puede llamar. Ese conjunto de métodos públicos es la superficie de la herramienta — un ayudante que olvidaste prefijar con _ es una función que el modelo llamará.

El docstring no es un comentario. Su primer bloque se convierte en la descripción de la función enviada al modelo, y cada línea :param nombre: se convierte en una descripción de parámetro. El docstring es el prompt.

El archivo

"""
title: Búsqueda de Paquetes
author: tú
description: Busca un paquete en 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="Raíz de la API JSON de PyPI",
        )

    class UserValves(BaseModel):
        include_yanked: bool = Field(default=False, description="Incluir versiones retiradas")

    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:
        """
        Busca un paquete Python en PyPI y devuelve sus metadatos.

        :param name: El nombre del paquete, p.ej. `httpx`
        :param version: Una versión específica; omitir para la última versión
        :return: JSON con resumen, última versión, página de inicio y licencia
        """
        if __event_emitter__:
            await __event_emitter__(
                {"type": "status", "data": {"description": f"Obteniendo {name}...", "done": False}}
            )

        try:
            data = await self._fetch(name, version)
        except httpx.HTTPStatusError as e:
            return f"PyPI devolvió {e.response.status_code} para el paquete '{name}'."
        except Exception as e:
            return f"No se pudo contactar 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 cliente:
            response = await cliente.get(f"{self.valves.base_url}/{path}")
            response.raise_for_status()
            return response.json()

Ese archivo genera exactamente esta especificación para el modelo — nada más en el archivo es accesible:

{"name": "get_package",
 "description": "Busca un paquete Python en PyPI y devuelve sus metadatos.",
 "parameters": {"type": "object", "required": ["name"], "properties": {
   "name": {"type": "string", "description": "El nombre del paquete, p.ej. `httpx`"},
   "version": {"type": "string", "description": "Una versión específica; omitir para la última versión"}}}}

Reglas que el esqueleto no puede mostrar

  • La línea 1 es exactamente """. Un comentario, una importación o una línea en blanco arriba hace que todo el frontmatter sea invisible — entonces requirements nunca se instala, silenciosamente.
  • Mantén los ayudantes fuera de la superficie con un guion bajo _ inicial. Todo lo demás público en Tools — incluyendo cualquier cosa heredada — es una función que el modelo puede llamar.
  • Solo docstrings reST. :param nombre: texto llega al modelo; un bloque estilo Google Args: no llega a nada.
  • Añade tipo a cada parámetro que ve el modelo. Las anotaciones generan el esquema JSON. Las anotaciones deben resolverse en tiempo de importación; una anotación irresoluble hace que guardar la herramienta falle por completo.
  • Asigna un valor predeterminado a cada parámetro __dunder__ inyectado. Open WebUI los proporciona solo donde los tiene, y el modelo nunca los ve — se eliminan del esquema.
  • __init__ no toma argumentos y establece self.valves = self.Valves(). Sin esa línea, los valores de válvula configurados por el administrador se pierden.
  • Cada método es async def. Un método síncrono se ejecuta en el bucle de eventos y bloquea el servidor; empuja trabajo bloqueante a través de await asyncio.to_thread(...).
  • Devuelve una cadena (un dict o list se convierte a JSON por ti). Devuelve un mensaje de error legible en lugar de lanzar una excepción — una excepción lanzada llega al modelo como su str() simple.
  • El progreso va a través de eventos status; la respuesta va a través del valor de retorno. Los cuatro eventos de contenido (message, chat:message, chat:message:delta, replace) son sobrescritos por instantáneas de finalización en modo Nativo, que es el único modo compatible.

Verificar

python scripts/check_tool.py mi_herramienta.py

El script es solo de la biblioteca estándar, pero ejecuta el archivo de la herramienta, por lo que las importaciones propias de la herramienta deben resolverse — uv run --with httpx --with pydantic python scripts/check_tool.py mi_herramienta.py para el archivo anterior.

Reproduce la carga del servidor y la generación de especificaciones. Dos barras, ambas deben cumplirse: sale limpio, y cada nombre en la superficie de la herramienta impresa es uno que pretendes que el modelo llame.

Luego pega el archivo en Workspace > Tools. El id de la herramienta se establece allí, no en el archivo, y debe ser un identificador Python en minúsculas.

Referencia

Lee references/REFERENCE.md para la lista completa de parámetros inyectados, el catálogo de emisores de eventos (status, citation, notification, confirmation, input, archivos), tipos de entrada de válvulas (contraseña, selección, selección múltiple, opciones dinámicas), devolución de archivos o HTML incrustado y los indicadores heredados que ya no hacen nada.

Skills relacionados