Est-ce qu'une fonction Open WebUI peut importer une autre fonction Open WebUI ?

Journal du mercredi 25 juin 2025 à 17:39

J'ai essayé de comprendre si une fonction Open WebUI pouvait importer le code d'une autre fonction Open WebUI.
La réponse est non. Je vais tenter dans cette note d'expliquer pourquoi.

(j'ai aussi publié une version de cette note en anglais dans la section "discussions" de Open WebUI)

Open WebUI propose de méthode pour créer ou mettre à jour une fonction Open WebUI sur une instance en production : via l'interface web d'administration, ou via l'API REST.

Une instance production fait référence à Open WebUI hébergé sur une Virtual machine ou un Cluster Kubernetes, par opposition à une instance locale lancée en mode développement.

Dans un premier temps, j'ai essayé d'importer dans Open WebUI les deux fichiers suivants :

# utils.py
def add(a, b):
    return a + b
# hello_world.py
from pydantic import BaseModel, Field

from .utils import add

class Pipe:
    class Valves(BaseModel):
        pass

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

    def pipe(self, body: dict):
        print("body", body)

        return f"Hello, World! {add(1, 2)}"

Le fichier hello_world.py contient un import de utils.add implémenté dans le premier fichier.

L'importation du premier fichier est refusée par Open WebUI parce que class Pipe: est absent de utils.py.

J'ai ensuite trompé Open WebUI en ajoutant une classe Pipe fictive das le fichier utils.py et l'importation a réussi.

Ensuite l'import de hello_world.py a échoué parce que Open WebUI n'arrive pas a effectué l'import from .utils import add. J'ai ensuite effectué plusieurs tentatives d'import absolut, par exemple from open_webui.utils import add… mais sans succès.

J'ai pris un peu de temps pour étudier l'implémentation d'Open WebUI et j'ai identifié cette section de code :

module_name = f"tool_{tool_id}"
module = types.ModuleType(module_name)
sys.modules[module_name] = module

Ce code permet à Open WebUI de charger dynamiquement le code source des modules qui sont stockés dans la base de données.

Un esprit tordu pourrait en pratique importer une fonction chargé dynamiquement dans un autre module dynamique, par exemple :

from tool_utils import add

Mais cette méthode ne correspond pas à l'usage normal d'Open WebUI.

Pour implémenter des fonctions "modulaires", Open WebUI conseille d'utiliser la fonctionnalité "Pipelines" :

Welcome to Pipelines, an Open WebUI initiative. Pipelines bring modular, customizable workflows to any UI client supporting OpenAI API specs – and much more! Easily extend functionalities, integrate unique logic, and create dynamic workflows with just a few lines of code.

source

Pour les personnes qui souhaitent vraiment effectuer des imports dans des fonctions Open WebUI sans utiliser la fonction Pipelines, il existe tout de même une solution que j'ai implémentée dans la branche test-if-openwebui-function-support-import.

Voici le contenu de /functions/hello_world.py :

from pydantic import BaseModel, Field

from open_webui.shared.utils import add

class Pipe:
    class Valves(BaseModel):
        pass

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

    def pipe(self, body: dict):
        print("body", body)

        return f"Hello, World! {add(1, 2)}"

Le contenu de /shared/utils.py

def add(a, b):
    return a + b

Pour rendre accessible /shared/utils.py dans l'instance d'Open WebUI lancé loculement, j'ai configuré de volume mounts suivante dans mon /docker-compose.yml :

  openwebui:
    image: ghcr.io/open-webui/open-webui:0.6.15
    restart: unless-stopped
    volumes:
      - ./shared/:/app/backend/open_webui/shared/
    ports:
      - "3000:8080"

Ensuite, si je souhaite pouvoir déployer en production cette fonction Open WebUI et le module utils.py, il sera nécessaire de build une image Docker customisé d'Open WebUI pour y inclure le fichier /shared/utils.py.

Cette méthode peut fonctionner, mais cela reste un "hack" non conseillé. Il est préférable d'utiliser la méthode "Pipelines".