Functions in Open WebUI allow you to extend your language models with custom Python code that can be called during conversations. This enables your models to perform actions, fetch data, and integrate with external services.
Overview
Functions provide:
Native Python code execution
Built-in code editor in the workspace
Function calling and tool use capabilities
Filter functions to modify messages
Admin and user-level configuration
Functions are stored in the database and loaded dynamically at runtime. Admin users can create and manage functions through the web interface.
Creating Functions
Through the Web Interface
Navigate to Workspace
Go to Workspace → Functions in the Open WebUI interface
Create New Function
Click Create New Function and enter:
ID : Unique identifier (alphanumeric and underscores only)
Name : Display name for the function
Description : What the function does
Write Function Code
Use the built-in editor to write your Python function: """
title: Example Function
description: A simple example function
author: Your Name
version: 1.0.0
"""
class Function :
def __init__ ( self ):
pass
def __call__ ( self , user_message : str , ** kwargs ) -> str :
return f "Processed: { user_message } "
Save and Enable
Save the function and toggle it active to make it available
Via API
Create functions programmatically:
import requests
url = "http://localhost:8080/api/functions/create"
payload = {
"id" : "my_function" ,
"name" : "My Function" ,
"content" : '''
class Function:
def __call__(self, user_message: str) -> str:
return f"Echo: {user_message} "
''' ,
"meta" : {
"description" : "Echoes the user message" ,
"manifest" : {}
}
}
response = requests.post(url, json = payload)
Function Types
Open WebUI supports two main types of functions:
Action Functions
Perform operations and return results:
import requests
from datetime import datetime
class Function :
def __init__ ( self ):
self .name = "get_weather"
self .description = "Get current weather for a city"
def __call__ ( self , city : str , ** kwargs ) -> dict :
# Call weather API
response = requests.get(
f "https://api.weather.com/v1/current" ,
params = { "city" : city}
)
return response.json()
Filter Functions
Modify messages before and after model processing:
class Function :
type = "filter" # Mark as filter function
def inlet ( self , messages : list , user : dict , ** kwargs ) -> list :
"""Process messages before sending to model"""
# Add timestamp to each message
for msg in messages:
msg[ "timestamp" ] = datetime.now().isoformat()
return messages
def outlet ( self , messages : list , user : dict , ** kwargs ) -> list :
"""Process messages after model response"""
# Log the conversation
print ( f "Conversation for { user[ 'email' ] } : { len (messages) } messages" )
return messages
Valves Configuration
Valves allow functions to have configurable parameters:
from pydantic import BaseModel, Field
class Function :
class Valves ( BaseModel ):
api_key: str = Field(
default = "" ,
description = "API key for external service"
)
temperature: float = Field(
default = 0.7 ,
description = "Model temperature" ,
ge = 0.0 ,
le = 2.0
)
max_tokens: int = Field(
default = 1000 ,
description = "Maximum tokens"
)
def __init__ ( self ):
self .valves = self .Valves()
def __call__ ( self , prompt : str , ** kwargs ) -> str :
# Use valve values
api_key = self .valves.api_key
temp = self .valves.temperature
# Your function logic here
return f "Using temp= { temp } "
User Valves
Allow individual users to configure function parameters:
from pydantic import BaseModel, Field
class Function :
class UserValves ( BaseModel ):
personal_api_key: str = Field(
default = "" ,
description = "Your personal API key"
)
preferred_language: str = Field(
default = "en" ,
description = "Preferred response language"
)
def __init__ ( self ):
self .user_valves = {}
def __call__ ( self , user_message : str , __user__ : dict , ** kwargs ) -> str :
user_id = __user__[ "id" ]
valves = self .user_valves.get(user_id, self .UserValves())
# Use user-specific configuration
lang = valves.preferred_language
return f "Response in { lang } : { user_message } "
Managing Functions
List Functions
import requests
response = requests.get( "http://localhost:8080/api/functions/" )
functions = response.json()
Get Function by ID
response = requests.get( "http://localhost:8080/api/functions/id/my_function" )
function = response.json()
Update Function
import requests
url = "http://localhost:8080/api/functions/id/my_function/update"
payload = {
"content" : """# Updated code here""" ,
"meta" : { "description" : "Updated description" }
}
response = requests.post(url, json = payload)
Toggle Function
Enable or disable a function:
response = requests.post(
"http://localhost:8080/api/functions/id/my_function/toggle"
)
Delete Function
response = requests.delete(
"http://localhost:8080/api/functions/id/my_function/delete"
)
Import from URL
Load functions from GitHub or other sources:
import requests
url = "http://localhost:8080/api/functions/load/url"
payload = {
"url" : "https://github.com/user/repo/blob/main/my_function.py"
}
response = requests.post(url, json = payload)
function_data = response.json()
GitHub URLs are automatically converted to raw URLs. Both blob and tree URLs are supported.
Function Examples
import sqlite3
from typing import List, Dict
class Function :
def __init__ ( self ):
self .name = "query_database"
self .description = "Query the local database"
def __call__ ( self , query : str , ** kwargs ) -> List[Dict]:
conn = sqlite3.connect( 'app.db' )
cursor = conn.cursor()
cursor.execute(query)
columns = [desc[ 0 ] for desc in cursor.description]
results = [
dict ( zip (columns, row))
for row in cursor.fetchall()
]
conn.close()
return results
import requests
from pydantic import BaseModel, Field
class Function :
class Valves ( BaseModel ):
api_key: str = Field( default = "" , description = "API Key" )
base_url: str = Field(
default = "https://api.example.com" ,
description = "API Base URL"
)
def __init__ ( self ):
self .valves = self .Valves()
def __call__ ( self , endpoint : str , params : dict = None , ** kwargs ) -> dict :
headers = { "Authorization" : f "Bearer { self .valves.api_key } " }
url = f " { self .valves.base_url } / { endpoint } "
response = requests.get(url, headers = headers, params = params)
return response.json()
import re
from typing import List
class Function :
def __init__ ( self ):
self .name = "extract_emails"
self .description = "Extract email addresses from text"
def __call__ ( self , text : str , ** kwargs ) -> List[ str ]:
email_pattern = r ' \b [ A-Za-z0-9._%+- ] + @ [ A-Za-z0-9.- ] + \. [ A-Z|a-z ] {2,} \b '
emails = re.findall(email_pattern, text)
return list ( set (emails)) # Remove duplicates
Global vs Local Functions
Functions can be toggled between global and local scope:
import requests
# Toggle global scope (available to all users)
response = requests.post(
"http://localhost:8080/api/functions/id/my_function/toggle/global"
)
Global functions are available to all users. Ensure they don’t expose sensitive data or operations.
Best Practices
Error Handling : Always wrap function logic in try-except blocks
Type Hints : Use Python type hints for better documentation
Validation : Validate inputs before processing
Security : Never hardcode sensitive credentials
Performance : Keep functions lightweight and fast
Documentation : Include clear docstrings and metadata
Next Steps
Learn about Tools for extended functionality
Explore Pipelines for request/response filtering
Configure Skills for reusable capabilities