refac: functions

This commit is contained in:
Timothy Jaeryang Baek 2024-12-28 20:54:14 -08:00
parent 8ff61b5c35
commit e8c0f9ddb0
7 changed files with 491 additions and 377 deletions

View File

@ -0,0 +1,52 @@
---
sidebar_position: 3
title: "🎬 Action Function"
---
Action functions allow you to write custom buttons to the message toolbar for end users to interact
with. This feature enables more interactive messaging, enabling users to grant permission before a
task is performed, generate visualizations of structured data, download an audio snippet of chats,
and many other use cases.
A scaffold of Action code can be found [in the community section](https://openwebui.com/f/hub/custom_action/).
An example of a graph visualization Action can be seen in the video below.
<p align="center">
<a href="#">
<img src="/img/pipelines/graph-viz-action.gif" alt="Graph Visualization Action" />
</a>
</p>
### Action
Actions are used to create a button in the Message UI (the small buttons found directly underneath individual chat messages).
Actions have a single main component called an action function. This component takes an object defining the type of action and the data being processed.
<details>
<summary>Example</summary>
```
async def action(
self,
body: dict,
__user__=None,
__event_emitter__=None,
__event_call__=None,
) -> Optional[dict]:
print(f"action:{__name__}")
response = await __event_call__(
{
"type": "input",
"data": {
"title": "write a message",
"message": "here write a message to append",
"placeholder": "enter your message",
},
}
)
print(response)
```
</details>

View File

@ -1,19 +0,0 @@
---
sidebar_position: 6
title: "🎬 Actions"
---
Action functions allow you to write custom buttons to the message toolbar for end users to interact
with. This feature enables more interactive messaging, enabling users to grant permission before a
task is performed, generate visualizations of structured data, download an audio snippet of chats,
and many other use cases.
A scaffold of Action code can be found [in the community section](https://openwebui.com/f/hub/custom_action/).
An example of a graph visualization Action can be seen in the video below.
<p align="center">
<a href="#">
<img src="/img/pipelines/graph-viz-action.gif" alt="Graph Visualization Action" />
</a>
</p>

View File

@ -0,0 +1,62 @@
---
sidebar_position: 2
title: "🪄 Filter Function"
---
### Filter
Filters are used to manipulate the user input and/or the LLM output to add, remove, format, or otherwise adjust the content of the body object.
Filters have a few main components:
#### Inlet Function
The inlet is user to pre-process a user input before it is send to the LLM for processing.
#### Outlet Function
The outlet is used to post-process the output from the LLM. It is important to note that when you perform actions such as stripping/replacing content, this will happen after the output is rendered to the UI.
<details>
<summary>Example</summary>
```
class Filter:
# Define and Valves
class Valves(BaseModel):
priority: int = Field(
default=0, description="Priority level for the filter operations."
)
test_valve: int = Field(
default=4, description="A valve controlling a numberical value"
)
pass
# Define any UserValves
class UserValves(BaseModel):
test_user_valve: bool = Field(
default=False, description="A user valve controlling a True/False (on/off) switch"
)
pass
def __init__(self):
self.valves = self.Valves()
pass
def inlet(self, body: dict, __user__: Optional[dict] = None) -> dict:
print(f"inlet:{__name__}")
print(f"inlet:body:{body}")
print(f"inlet:user:{__user__}")
# Pre-processing logic here
return body
def outlet(self, body: dict, __user__: Optional[dict] = None) -> dict:
print(f"outlet:{__name__}")
print(f"outlet:body:{body}")
print(f"outlet:user:{__user__}")
# Post-processing logic here
return body
```
</details>

View File

@ -3,361 +3,131 @@ sidebar_position: 1
title: "🧰 Functions"
---
## What are Functions?
Functions are modular operations that allow users to enhance the capabilities of the AI by embedding specific logic or actions directly into workflows. Unlike tools, which operate as external utilities, functions run natively within the OpenWebUI environment and handle tasks such as data processing, visualization, and interactive messaging. Functions are lightweight and designed to execute efficiently on the same server as the WebUI, enabling quick responses without the need for external dependencies.
## 🚀 What Are Functions?
## How can I use Functions?
Functions can be used, [once installed](#how-to-install-functions), by assigning them to an LLM or enabling them globally. Some function types will always be enabled globally, such as manifolds. To assign a function to a model, you simply need to navigate to Workspace => Models. Here you can select the model for which youd like to enable any Functinos.
Once you click the pencil icon to edit the model settings, scroll down to the Functions section and check any Functions you wish to enable. Once done you must click save.
You also have the ability to enable Functions globally for ALL models. In order to do this, navigate to Workspace => Functions and click the "..." menu. Once the menu opens, simply enable the "Global" switch and your function will be enabled for every model in your OpenWebUI instance.
## How to install Functions
The Functions import process is quite simple. You will have two options:
### Download and import manually
Navigate to the community site: https://openwebui.com/functions/
1) Click on the Function you wish to import
2) Click the blue “Get” button in the top right-hand corner of the page
3) Click “Download as JSON export”
4) You can now upload the Funtion into OpenWebUI by navigating to Workspace => Functions and clicking “Import Functions
### Import via your OpenWebUI URL
1) Navigate to the community site: https://openwebui.com/functions/
2) Click on the Function you wish to import
3) Click the blue “Get” button in the top right-hand corner of the page
4) Enter the IP address of your OpenWebUI instance and click “Import to WebUI” which will automatically open your instance and allow you to import the Function.
Note: You can install your own Function and other Functions not tracked on the community site using the manual import method. Please do not import Functions you do not understand or are not from a trustworthy source. Running unknown code is ALWAYS a risk.
## What are the support types of functions
### Filter
Filters are used to manipulate the user input and/or the LLM output to add, remove, format, or otherwise adjust the content of the body object.
Filters have a few main components:
#### Inlet Function
The inlet is user to pre-process a user input before it is send to the LLM for processing.
#### Outlet Function
The outlet is used to post-process the output from the LLM. It is important to note that when you perform actions such as stripping/replacing content, this will happen after the output is rendered to the UI.
<details>
<summary>Example</summary>
```
class Filter:
# Define and Valves
class Valves(BaseModel):
priority: int = Field(
default=0, description="Priority level for the filter operations."
)
test_valve: int = Field(
default=4, description="A valve controlling a numberical value"
)
pass
# Define any UserValves
class UserValves(BaseModel):
test_user_valve: bool = Field(
default=False, description="A user valve controlling a True/False (on/off) switch"
)
pass
def __init__(self):
self.valves = self.Valves()
pass
def inlet(self, body: dict, __user__: Optional[dict] = None) -> dict:
print(f"inlet:{__name__}")
print(f"inlet:body:{body}")
print(f"inlet:user:{__user__}")
# Pre-processing logic here
return body
def outlet(self, body: dict, __user__: Optional[dict] = None) -> dict:
print(f"outlet:{__name__}")
print(f"outlet:body:{body}")
print(f"outlet:user:{__user__}")
# Post-processing logic here
return body
```
</details>
### Action
Actions are used to create a button in the Message UI (the small buttons found directly underneath individual chat messages).
Actions have a single main component called an action function. This component takes an object defining the type of action and the data being processed.
<details>
<summary>Example</summary>
```
async def action(
self,
body: dict,
__user__=None,
__event_emitter__=None,
__event_call__=None,
) -> Optional[dict]:
print(f"action:{__name__}")
response = await __event_call__(
{
"type": "input",
"data": {
"title": "write a message",
"message": "here write a message to append",
"placeholder": "enter your message",
},
}
)
print(response)
```
</details>
#### Pipes
#### Pipe
A Pipe is used to create a "Model" with custom logic and processing. A Pipe will always show up as it's own singular model in the OpenWebUI interface and will, much like a filter
A Pipe has a single main component called a pipe function. This component encapsulates all of the primary logic that the Pipe will perform.
<details>
<summary>Example</summary>
```
class Pipe:
class Valves(BaseModel):
RANDOM_CONFIG_OPTION: str = Field(default="")
def __init__(self):
self.type = "pipe"
self.id = "blah"
self.name = "Testing"
self.valves = self.Valves(
**{"RANDOM_CONFIG_OPTION": os.getenv("RANDOM_CONFIG_OPTION", "")}
)
pass
def get_provider_models(self):
return [
{"id": "model_id_1", "name": "model_1"},
{"id": "model_id_2", "name": "model_2"},
{"id": "model_id_3", "name": "model_3"},
]
def pipe(self, body: dict) -> Union[str, Generator, Iterator]:
# Logic goes here
return body
```
</details>
#### Manifold
A Manifold is used to create a collection of Pipes. If a Pipe creates a singular "Model", a Manifold creates a set of "Models." Manifolds are typically used to create integrations with other providers.
A Manifold has two main components:
##### Pipes Function
This is used to simply initiate a dictionary to hold all of the Pipes created by the manifold
##### Pipe Function
As referenced above, this component encapsulates all of the primary logic that the Pipe will perform.
<details>
<summary>Example</summary>
```
class Pipe:
class Valves(BaseModel):
PROVIDER_API_KEY: str = Field(default="")
def __init__(self):
self.type = "manifold"
self.id = "blah"
self.name = "Testing"
self.valves = self.Valves(
**{"PROVIDER_API_KEY": os.getenv("PROVIDER_API_KEY", "")}
)
pass
def get_provider_models(self):
return [
{"id": "model_id_1", "name": "model_1"},
{"id": "model_id_2", "name": "model_2"},
{"id": "model_id_3", "name": "model_3"},
]
def pipes(self) -> List[dict]:
return self.get_provider_models()
def pipe(self, body: dict) -> Union[str, Generator, Iterator]:
# Logic goes here
return body
```
</details>
Note: To differentiate between a Pipe and a Manifold you will need to specify the type in def init:
```
def __init__(self):
self.type = "pipe"
self.id = "blah"
self.name = "Testing"
pass
```
or
```
def __init__(self):
self.type = "manifold"
self.id = "blah"
self.name = "Testing/"
pass
```
## Shared Function Components
### Valves and UserValves - (optional, but HIGHLY encouraged)
Valves and UserValves are used to allow users to provide dyanmic details such as an API key or a configuration option. These will create a fillable field or a bool switch in the GUI menu for the given function.
Valves are configurable by admins alone and UserValves are configurable by any users.
<details>
<summary>Example</summary>
```
# Define and Valves
class Valves(BaseModel):
priority: int = Field(
default=0, description="Priority level for the filter operations."
)
test_valve: int = Field(
default=4, description="A valve controlling a numberical value"
)
pass
# Define any UserValves
class UserValves(BaseModel):
test_user_valve: bool = Field(
default=False, description="A user valve controlling a True/False (on/off) switch"
)
pass
def __init__(self):
self.valves = self.Valves()
pass
```
</details>
### Event Emitters
Event Emitters are used to add additional information to the chat interface. Similarly to Filter Outlets, Event Emitters are capable of appending content to the chat. Unlike Filter Outlets, they are not capable of stripping information. Additionally, emitters can be activated at any stage during the function.
There are two different types of Event Emitters:
#### Status
This is used to add statuses to a message while it is performing steps. These can be done at any stage during the Function. These statuses appear right above the message content. These are very useful for Functions that delay the LLM response or process large amounts of information. This allows you to inform users what is being processed in real-time.
```
await __event_emitter__(
{
"type": "status", # We set the type here
"data": {"description": "Message that shows up in the chat", "done": False},
# Note done is False here indicating we are still emitting statuses
}
)
```
<details>
<summary>Example</summary>
```
async def test_function(
self, prompt: str, __user__: dict, __event_emitter__=None
) -> str:
"""
This is a demo
:param test: this is a test parameter
"""
await __event_emitter__(
{
"type": "status", # We set the type here
"data": {"description": "Message that shows up in the chat", "done": False},
# Note done is False here indicating we are still emitting statuses
}
)
# Do some other logic here
await __event_emitter__(
{
"type": "status",
"data": {"description": "Completed a task message", "done": True},
# Note done is True here indicating we are done emitting statuses
}
)
except Exception as e:
await __event_emitter__(
{
"type": "status",
"data": {"description": f"An error occured: {e}", "done": True},
}
)
return f"Tell the user: {e}"
```
</details>
#### Message
This type is used to append a message to the LLM at any stage in the Function. This means that you can append messages, embed images, and even render web pages before, or after, or during the LLM response.
```
await __event_emitter__(
{
"type": "message", # We set the type here
"data": {"content": "This message will be appended to the chat."},
# Note that with message types we do NOT have to set a done condition
}
)
```
<details>
<summary>Example</summary>
```
async def test_function(
self, prompt: str, __user__: dict, __event_emitter__=None
) -> str:
"""
This is a demo
:param test: this is a test parameter
"""
await __event_emitter__(
{
"type": "message", # We set the type here
"data": {"content": "This message will be appended to the chat."},
# Note that with message types we do NOT have to set a done condition
}
)
except Exception as e:
await __event_emitter__(
{
"type": "status",
"data": {"description": f"An error occured: {e}", "done": True},
}
)
return f"Tell the user: {e}"
```
</details>
Functions are like **plugins** for OpenWebUI. They help you **extend its capabilities**—whether its adding support for new AI models like Anthropic or Vertex AI, tweaking how messages are processed, or introducing custom buttons to the interface for better usability.
Unlike external tools that may require complex integrations, **Functions are built-in and run within the OpenWebUI environment.** That means they are fast, modular, and dont rely on external dependencies.
Think of Functions as **modular building blocks** that let you enhance how the WebUI works, tailored exactly to what you need. Theyre lightweight, highly customizable, and written in **pure Python**, so you have the freedom to create anything—from new AI-powered workflows to integrations with anything you use, like Google Search or Home Assistant.
---
## 🏗️ Types of Functions
There are **three types of Functions** in OpenWebUI, each with a specific purpose. Lets break them down and explain exactly what they do:
---
### 1. [**Pipe Function** Create Custom "Models" ](./pipe.mdx)
A **Pipe Function** is how you create **custom models** or integrations, which then appear in the interface as if they were standalone models.
**What does it do?**
- Pipes let you define complex workflows. For instance, you could create a Pipe that sends data to **Model A** and **Model B**, processes their outputs, and combines the results into one finalized answer.
- Pipes dont even have to use AI! They can be setups for **search APIs**, **weather data**, or even systems like **Home Assistant**. Basically, anything youd like to interact with can become part of OpenWebUI.
**Use case example:**
Imagine you want to query Google Search directly from OpenWebUI. You can create a Pipe Function that:
1. Takes your input.
2. Sends the query to Google Searchs API.
3. Processes the response and returns it to you inside the WebUI like a normal "model" response.
When enabled, **Pipe Functions show up as their own selectable model**. Use Pipes whenever you need custom functionality that works like a model in the interface.
For a detailed guide, see [**Pipe Functions**](./pipe.mdx).
---
### 2. [**Filter Function** Modify Inputs and Outputs](./filter.mdx)
A **Filter Function** is like a tool for tweaking data before it gets sent to the AI **or** after it comes back.
**What does it do?**
Filters act as "hooks" in the workflow and have two main parts:
- **Inlet**: Adjust the input that is sent to the model. For example, adding additional instructions, keywords, or formatting tweaks.
- **Outlet**: Modify the output that you receive from the model. For instance, cleaning up the response, adjusting tone, or formatting data into a specific style.
**Use case example:**
Suppose youre working on a project that needs precise formatting. You can use a Filter to ensure:
1. Your input is always transformed into the required format.
2. The output from the model is cleaned up before being displayed.
Filters are **linked to specific models** or can be enabled for all models **globally**, depending on your needs.
Check out the full guide for more examples and instructions: [**Filter Functions**](./filter.mdx).
---
### 3. [**Action Function** Add Custom Buttons](./action.mdx)
An **Action Function** is used to add **custom buttons** to the chat interface.
**What does it do?**
Actions allow you to define **interactive shortcuts** that trigger specific functionality directly from the chat. These buttons appear underneath individual chat messages, giving you convenient, one-click access to the actions you define.
**Use case example:**
Lets say you often need to summarize long messages or generate specific outputs like translations. You can create an Action Function to:
1. Add a “Summarize” button under every incoming message.
2. When clicked, it triggers your custom function to process that message and return the summary.
Buttons provide a **clean and user-friendly way** to interact with extended functionality you define.
Learn how to set them up in the [**Action Functions Guide**](./action.mdx).
---
## 🛠️ How to Use Functions
Here's how to put Functions to work in OpenWebUI:
### 1. **Install Functions**
You can install Functions via the OpenWebUI interface or by importing them manually.
⚠️ **Be cautious.** Only install Functions from trusted sources. Running unknown code poses security risks.
---
### 2. **Enable Functions**
Functions must be explicitly enabled after installation:
- When you enable a **Pipe Function**, it becomes available as its own **model** in the interface.
- For **Filter** and **Action Functions**, enabling them isnt enough—you also need to assign them to specific models or enable them globally for all models.
---
### 3. **Assign Filters or Actions to Models**
- Navigate to `Workspace => Models` and assign your Filter or Action to the relevant model there.
- Alternatively, enable Functions for **all models globally** by going to `Workspace => Functions`, selecting the "..." menu, and toggling the **Global** switch.
---
### Quick Summary
- **Pipes** appear as standalone models you can interact with.
- **Filters** modify inputs/outputs for smoother AI interactions.
- **Actions** add clickable buttons to individual chat messages.
Once youve followed the setup process, Functions will seamlessly enhance your workflows.
---
## ✅ Why Use Functions?
Functions are designed for anyone who wants to **unlock new possibilities** with OpenWebUI:
- **Extend**: Add new models or integrate with non-AI tools like APIs, databases, or smart devices.
- **Optimize**: Tweak inputs and outputs to fit your use case perfectly.
- **Simplify**: Add buttons or shortcuts to make the interface intuitive and efficient.
Whether youre customizing workflows for specific projects, integrating external data, or just making OpenWebUI easier to use, Functions are the key to taking control of your instance.
---
### 📝 Final Notes:
1. Always install Functions from **trusted sources only**.
2. Make sure you understand the difference between Pipe, Filter, and Action Functions to use them effectively.
3. Explore the official guides:
- [Pipe Functions Guide](./pipe.mdx)
- [Filter Functions Guide](./filter.mdx)
- [Action Functions Guide](./action.mdx)
By leveraging Functions, youll bring entirely new capabilities to your OpenWebUI setup. Start experimenting today! 🚀

View File

@ -0,0 +1,109 @@
---
sidebar_position: 1
title: "🚰 Pipe Function"
---
import Common from './tab-shared/Common.md';
#### Pipe
A Pipe is used to create a "Model" with custom logic and processing. A Pipe will always show up as it's own singular model in the OpenWebUI interface and will, much like a filter
A Pipe has a single main component called a pipe function. This component encapsulates all of the primary logic that the Pipe will perform.
<details>
<summary>Example</summary>
```
class Pipe:
class Valves(BaseModel):
RANDOM_CONFIG_OPTION: str = Field(default="")
def __init__(self):
self.type = "pipe"
self.id = "blah"
self.name = "Testing"
self.valves = self.Valves(
**{"RANDOM_CONFIG_OPTION": os.getenv("RANDOM_CONFIG_OPTION", "")}
)
pass
def get_provider_models(self):
return [
{"id": "model_id_1", "name": "model_1"},
{"id": "model_id_2", "name": "model_2"},
{"id": "model_id_3", "name": "model_3"},
]
def pipe(self, body: dict) -> Union[str, Generator, Iterator]:
# Logic goes here
return body
```
</details>
#### Manifold
A Manifold is used to create a collection of Pipes. If a Pipe creates a singular "Model", a Manifold creates a set of "Models." Manifolds are typically used to create integrations with other providers.
A Manifold has two main components:
##### Pipes Function
This is used to simply initiate a dictionary to hold all of the Pipes created by the manifold
##### Pipe Function
As referenced above, this component encapsulates all of the primary logic that the Pipe will perform.
<details>
<summary>Example</summary>
```
class Pipe:
class Valves(BaseModel):
PROVIDER_API_KEY: str = Field(default="")
def __init__(self):
self.type = "manifold"
self.id = "blah"
self.name = "Testing"
self.valves = self.Valves(
**{"PROVIDER_API_KEY": os.getenv("PROVIDER_API_KEY", "")}
)
pass
def get_provider_models(self):
return [
{"id": "model_id_1", "name": "model_1"},
{"id": "model_id_2", "name": "model_2"},
{"id": "model_id_3", "name": "model_3"},
]
def pipes(self) -> List[dict]:
return self.get_provider_models()
def pipe(self, body: dict) -> Union[str, Generator, Iterator]:
# Logic goes here
return body
```
</details>
Note: To differentiate between a Pipe and a Manifold you will need to specify the type in def init:
```
def __init__(self):
self.type = "pipe"
self.id = "blah"
self.name = "Testing"
pass
```
or
```
def __init__(self):
self.type = "manifold"
self.id = "blah"
self.name = "Testing/"
pass
```
<Common/>

View File

@ -0,0 +1,140 @@
## Shared Function Components
### Valves and UserValves - (optional, but HIGHLY encouraged)
Valves and UserValves are used to allow users to provide dyanmic details such as an API key or a configuration option. These will create a fillable field or a bool switch in the GUI menu for the given function.
Valves are configurable by admins alone and UserValves are configurable by any users.
<details>
<summary>Example</summary>
```
# Define and Valves
class Valves(BaseModel):
priority: int = Field(
default=0, description="Priority level for the filter operations."
)
test_valve: int = Field(
default=4, description="A valve controlling a numberical value"
)
pass
# Define any UserValves
class UserValves(BaseModel):
test_user_valve: bool = Field(
default=False, description="A user valve controlling a True/False (on/off) switch"
)
pass
def __init__(self):
self.valves = self.Valves()
pass
```
</details>
### Event Emitters
Event Emitters are used to add additional information to the chat interface. Similarly to Filter Outlets, Event Emitters are capable of appending content to the chat. Unlike Filter Outlets, they are not capable of stripping information. Additionally, emitters can be activated at any stage during the function.
There are two different types of Event Emitters:
#### Status
This is used to add statuses to a message while it is performing steps. These can be done at any stage during the Function. These statuses appear right above the message content. These are very useful for Functions that delay the LLM response or process large amounts of information. This allows you to inform users what is being processed in real-time.
```
await __event_emitter__(
{
"type": "status", # We set the type here
"data": {"description": "Message that shows up in the chat", "done": False},
# Note done is False here indicating we are still emitting statuses
}
)
```
<details>
<summary>Example</summary>
```
async def test_function(
self, prompt: str, __user__: dict, __event_emitter__=None
) -> str:
"""
This is a demo
:param test: this is a test parameter
"""
await __event_emitter__(
{
"type": "status", # We set the type here
"data": {"description": "Message that shows up in the chat", "done": False},
# Note done is False here indicating we are still emitting statuses
}
)
# Do some other logic here
await __event_emitter__(
{
"type": "status",
"data": {"description": "Completed a task message", "done": True},
# Note done is True here indicating we are done emitting statuses
}
)
except Exception as e:
await __event_emitter__(
{
"type": "status",
"data": {"description": f"An error occured: {e}", "done": True},
}
)
return f"Tell the user: {e}"
```
</details>
#### Message
This type is used to append a message to the LLM at any stage in the Function. This means that you can append messages, embed images, and even render web pages before, or after, or during the LLM response.
```
await __event_emitter__(
{
"type": "message", # We set the type here
"data": {"content": "This message will be appended to the chat."},
# Note that with message types we do NOT have to set a done condition
}
)
```
<details>
<summary>Example</summary>
```
async def test_function(
self, prompt: str, __user__: dict, __event_emitter__=None
) -> str:
"""
This is a demo
:param test: this is a test parameter
"""
await __event_emitter__(
{
"type": "message", # We set the type here
"data": {"content": "This message will be appended to the chat."},
# Note that with message types we do NOT have to set a done condition
}
)
except Exception as e:
await __event_emitter__(
{
"type": "status",
"data": {"description": f"An error occured: {e}", "done": True},
}
)
return f"Tell the user: {e}"
```
</details>

View File

@ -1,5 +1,5 @@
---
sidebar_position: 0
sidebar_position: 2
title: "⚙️ Tools"
---