Communitygithub.com

gdevenyi/openwebui-tool-skill

Habilidade de agente para escrever e verificar ferramentas Python do Open WebUI

O que é openwebui-tool-skill?

Esta skill capacita assistentes de IA (como Claude Code ou Cursor) a escrever, verificar e depurar ferramentas Python para o Open WebUI. Ela define regras precisas de formatação, que vão desde a estrutura obrigatória do arquivo (frontmatter YAML, classe Tools, métodos async) até detalhes de docstring no estilo reST, garantindo que apenas os métodos públicos desejados sejam expostos ao modelo de linguagem. Além disso, a skill orienta o processo de verificação local com o script `scripts/check_tool.py`, que reproduz a geração de esquemas do servidor, e explica como configurar válvulas (valves) e eventos de progresso. É útil para desenvolvedores que criam ou mantêm integrações no ecossistema Open WebUI, suportando as plataformas Claude Code, Cursor, Codex e similares.

Funciona com~Claude Code~Codex CLI~Cursor
npx skills add gdevenyi/openwebui-tool-skill

Perguntar na sua IA favorita

Abre um novo chat com esta habilidade de agente já pré-carregada.

Documentação

Ferramentas Python do Open WebUI

Uma ferramenta é um arquivo Python. O Open WebUI executa o arquivo, instancia Tools(), e transforma cada método público em uma função que o modelo pode chamar. Esse conjunto de métodos públicos é a superfície da ferramenta — um auxiliar que você esqueceu de prefixar com _ se torna uma função que o modelo chamará.

A docstring não é um comentário. Seu primeiro bloco se torna a descrição da função enviada ao modelo, e cada linha :param nome: se torna uma descrição de parâmetro. A docstring é o prompt.

O arquivo

"""
title: Consulta de Pacote
author: você
description: Consulta um pacote no 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="Raiz da API JSON do PyPI",
        )

    class UserValves(BaseModel):
        include_yanked: bool = Field(default=False, description="Incluir versões yanked")

    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:
        """
        Consulta um pacote Python no PyPI e retorna seus metadados.

        :param name: O nome do pacote, ex. `httpx`
        :param version: Uma versão específica; omita para a versão mais recente
        :return: JSON com resumo, versão mais recente, página inicial e licença
        """
        if __event_emitter__:
            await __event_emitter__(
                {"type": "status", "data": {"description": f"Buscando {name}...", "done": False}}
            )

        try:
            data = await self._fetch(name, version)
        except httpx.HTTPStatusError as e:
            return f"PyPI retornou {e.response.status_code} para o pacote '{name}'."
        except Exception as e:
            return f"Não foi possível acessar o 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()

Esse arquivo gera exatamente esta especificação para o modelo — nada mais no arquivo é acessível:

{"name": "get_package",
 "description": "Consulta um pacote Python no PyPI e retorna seus metadados.",
 "parameters": {"type": "object", "required": ["name"], "properties": {
   "name": {"type": "string", "description": "O nome do pacote, ex. `httpx`"},
   "version": {"type": "string", "description": "Uma versão específica; omita para a versão mais recente"}}}}

Regras que o esqueleto não mostra

  • A linha 1 é exatamente """. Um comentário, um import ou uma linha em branco acima dela torna todo o frontmatter invisível — requirements então nunca é instalado, silenciosamente.
  • Mantenha auxiliares fora da superfície com um _ inicial. Qualquer outra coisa pública em Tools — incluindo qualquer coisa herdada — é uma função que o modelo pode chamar.
  • Apenas docstrings reST. :param nome: texto alcança o modelo; um bloco Args: no estilo Google não alcança nada.
  • Adicione dicas de tipo a todo parâmetro voltado ao modelo. As dicas geram o esquema JSON. As dicas devem ser resolvíveis no momento da importação; uma anotação não resolvível faz com que salvar a ferramenta falhe imediatamente.
  • Dê um padrão a cada parâmetro __dunder__ injetado. O Open WebUI os fornece apenas onde os possui, e o modelo nunca os vê — eles são removidos do esquema.
  • __init__ não aceita argumentos e define self.valves = self.Valves(). Sem essa linha, os valores de válvula configurados pelo administrador são descartados.
  • Todo método é async def. Um método síncrono é executado inline no loop de eventos e bloqueia o servidor; empurre trabalhos bloqueantes através de await asyncio.to_thread(...).
  • Retorne uma string (um dict ou list é convertido para JSON automaticamente). Retorne uma mensagem de erro legível em vez de levantar uma exceção — uma exceção levantada chega ao modelo como seu str() simples.
  • O progresso passa por eventos status; a resposta passa pelo valor de retorno. Os quatro eventos de conteúdo (message, chat:message, chat:message:delta, replace) são sobrescritos por snapshots de conclusão no modo Nativo, que é o único modo suportado.

Verificar

python scripts/check_tool.py mytool.py

O script depende apenas da biblioteca padrão, mas executa o arquivo da ferramenta, portanto as importações da ferramenta precisam ser resolvíveis — uv run --with httpx --with pydantic python scripts/check_tool.py mytool.py para o arquivo acima.

Ele reproduz a carga do servidor e a geração de esquemas. Duas barras, ambas devem ser cumpridas: a saída é limpa, e cada nome na superfície da ferramenta impressa é um que você pretendia que o modelo chamasse.

Em seguida, cole o arquivo em Espaço de Trabalho > Ferramentas. O id da ferramenta é definido lá, não no arquivo, e deve ser um identificador Python em minúsculas.

Referência

Leia references/REFERENCE.md para a lista completa de parâmetros injetados, o catálogo do emissor de eventos (status, citation, notification, confirmation, input, arquivos), tipos de entrada de válvula (senha, seleção, multisseleção, opções dinâmicas), retorno de arquivos ou HTML embutido, e as flags herdadas que não fazem mais nada.

Habilidades Relacionadas