Merge pull request #537 from thiswillbeyourgithub/enh_valves
Some checks are pending
Deploy site to Pages / build (push) Waiting to run
Deploy site to Pages / deploy (push) Blocked by required conditions

Fix valves documentation
This commit is contained in:
Tim Jaeryang Baek 2025-05-09 13:31:05 +04:00 committed by GitHub
commit 38bd96315f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 73 additions and 236 deletions

View File

@ -1,5 +1,5 @@
---
sidebar_position: 3
sidebar_position: 4
title: "🚚 Migrating Tools & Functions: 0.4 to 0.5"
---

View File

@ -50,194 +50,12 @@ class Tools:
```
### Type Hints
Each tool must have type hints for arguments. As of version Open WebUI version 0.4.3, the types may also be nested, such as `queries_and_docs: list[tuple[str, int]]`. Those type hints are used to generate the JSON schema that is sent to the model. Tools without type hints will work with a lot less consistency.
Each tool must have type hints for arguments. The types may also be nested, such as `queries_and_docs: list[tuple[str, int]]`. Those type hints are used to generate the JSON schema that is sent to the model. Tools without type hints will work with a lot less consistency.
### Valves and UserValves - (optional, but HIGHLY encouraged)
Valves and UserValves are used to allow users to provide dynamic 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 and UserValves are used for specifying customizable settings of the Tool, you can read more on the dedicated [Valves & UserValves](../valves/index.mdx) page.
Valves are configurable by admins alone and UserValves are configurable by any users.
<details>
<summary>Commented example</summary>
```
from pydantic import BaseModel, Field
class Tools:
# Notice the current indentation: Valves and UserValves must be declared as
# attributes of a Tools, Filter or Pipe class. Here we take the
# example of a Tool.
class Valves(BaseModel):
# Valves and UserValves inherit from pydantic's BaseModel. This
# enables complex use cases like model validators etc.
test_valve: int = Field( # Notice the type hint: it is used to
# choose the kind of UI element to show the user (buttons,
# texts, etc).
default=4,
description="A valve controlling a numberical value"
# required=False, # you can enforce fields using True
)
pass
# Note that this 'pass' helps for parsing and is recommended.
# UserValves are defined the same way.
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()
# Because they are set by the admin, they are accessible directly
# upon code execution.
pass
# The __user__ handling is the same for Filters, Tools and Functions.
def test_the_tool(self, message: str, __user__: dict):
"""
This is a test tool. If the user asks you to test the tools, put any
string you want in the message argument.
:param message: Any string you want.
:return: The same string as input.
"""
# Because UserValves are defined per user they are only available
# on use.
# Note that although __user__ is a dict, __user__["valves"] is a
# UserValves object. Hence you can access values like that:
test_user_valve = __user__["valves"].test_user_valve
# Or:
test_user_valve = dict(__user__["valves"])["test_user_valve"]
# But this will return the default value instead of the actual value:
# test_user_valve = __user__["valves"]["test_user_valve"] # Do not do that!
return message + f"\nThe user valve set value is: {test_user_valve}"
```
</details>
### Optional Arguments
Below is a list of optional arguments your tools can depend on:
- `__event_emitter__`: Emit events (see following section)
- `__event_call__`: Same as event emitter but can be used for user interactions
- `__user__`: A dictionary with user information. It also contains the `UserValves` object in `__user__["valves"]`.
- `__metadata__`: Dictionary with chat metadata
- `__messages__`: List of previous messages
- `__files__`: Attached files
- `__model__`: Model name
Just add them as argument to any method of your Tool class just like `__user__` in the example above.
### 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 Tool.
There are two different types of Event Emitters:
If the model seems to be unable to call the tool, make sure it is enabled (either via the Model page or via the `+` sign next to the chat input field). You can also turn the `Function Calling` argument of the `Advanced Params` section of the Model page from `Default` to `Native`.
#### Status
This is used to add statuses to a message while it is performing steps. These can be done at any stage during the Tool. These statuses appear right above the message content. These are very useful for Tools 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, "hidden": 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, "hidden": False},
# Note done is True here indicating we are done emitting statuses
# You can also set "hidden": True if you want to remove the status once the message is returned
}
)
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 Tool. 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>
#### Citations
This type is used to provide citations or references in the chat. You can utilize it to specify the content, the source, and any relevant metadata. Below is an example of how to emit a citation event:

View File

@ -1,81 +1,95 @@
## Shared Function Components
---
sidebar_position: 3
title: "🔄 Valves & UserValves"
---
### Valves and UserValves - (optional, but HIGHLY encouraged)
## Valves & UserValves
Valves and UserValves are used to allow users to provide dynamic 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 and UserValves are used to allow users to provide dynamic 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. They are always optional, but HIGHLY encouraged.
Hence, Valves and UserValves class can be defined in either a `Pipe`, `Pipeline`, `Filter` or `Tools` class.
Valves are configurable by admins alone via the Tools or Functions menus. On the other hand UserValves are configurable by any users directly from a chat session.
<details>
<summary>Commented example</summary>
```
```
from pydantic import BaseModel, Field
from typing import Literal
# Define and Valves
class Filter:
# Notice the current indentation: Valves and UserValves must be declared as
# attributes of a Tools, Filter or Pipe class. Here we take the
# example of a Filter.
class Valves(BaseModel):
# Valves and UserValves inherit from pydantic's BaseModel. This
# enables complex use cases like model validators etc.
test_valve: int = Field( # Notice the type hint: it is used to
# choose the kind of UI element to show the user (buttons,
# texts, etc).
default=4,
description="A valve controlling a numberical value"
# required=False, # you can enforce fields using True
)
priority: int = Field(
default=0,
description="Priority level for the filter operations. Lower values are passed through first"
)
# The priority field is optional but if present will be used to
# order the Filters.
pass
# Note that this 'pass' helps for parsing and is recommended.
# UserValves are defined the same way.
class UserValves(BaseModel):
test_user_valve: bool = Field(
default=False, description="A user valve controlling a True/False (on/off) switch"
)
pass
# Notice the current indentation: Valves and UserValves must be declared as
# attributes of a Tools, Filter or Pipe class. Here we take the
# example of a Filter.
class Valves(BaseModel):
# Valves and UserValves inherit from pydantic's BaseModel. This
# enables complex use cases like model validators etc.
test_valve: int = Field( # Notice the type hint: it is used to
# choose the kind of UI element to show the user (buttons,
# texts, etc).
default=4,
description="A valve controlling a numberical value"
# required=False, # you can enforce fields using True
)
# To give the user the choice between multiple strings, you can use Literal from typing:
choice_option: Literal["choiceA", "choiceB"] = Field(
default="choiceA",
description="An example of a multi choice valve",
)
priority: int = Field(
default=0,
description="Priority level for the filter operations. Lower values are passed through first"
)
# The priority field is optional but if present will be used to
# order the Filters.
pass
# Note that this 'pass' helps for parsing and is recommended.
def __init__(self):
self.valves = self.Valves()
# Because they are set by the admin, they are accessible directly
# upon code execution.
pass
# UserValves are defined the same way.
class UserValves(BaseModel):
test_user_valve: bool = Field(
default=False, description="A user valve controlling a True/False (on/off) switch"
)
pass
# The inlet method is only used for Filter but the __user__ handling is the same
def inlet(self, body: dict, __user__: dict):
# Because UserValves are defined per user they are only available
# on use.
# Note that although __user__ is a dict, __user__["valves"] is a
# UserValves object. Hence you can access values like that:
test_user_valve = __user__["valves"].test_user_valve
# Or:
test_user_valve = dict(__user__["valves"])["test_user_valve"]
# But this will return the default value instead of the actual value:
# test_user_valve = __user__["valves"]["test_user_valve"] # Do not do that!
def __init__(self):
self.valves = self.Valves()
# Because they are set by the admin, they are accessible directly
# upon code execution.
pass
# The inlet method is only used for Filter but the __user__ handling is the same
def inlet(self, body: dict, __user__: dict):
# Because UserValves are defined per user they are only available
# on use.
# Note that although __user__ is a dict, __user__["valves"] is a
# UserValves object. Hence you can access values like that:
test_user_valve = __user__["valves"].test_user_valve
# Or:
test_user_valve = dict(__user__["valves"])["test_user_valve"]
# But this will return the default value instead of the actual value:
# test_user_valve = __user__["valves"]["test_user_valve"] # Do not do that!
```
</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},
"data": {"description": "Message that shows up in the chat", "done": False},
# Note done is False here indicating we are still emitting statuses
}
)
@ -97,7 +111,7 @@ async def test_function(
await __event_emitter__(
{
"type": "status", # We set the type here
"data": {"description": "Message that shows up in the chat", "done": False},
"data": {"description": "Message that shows up in the chat", "done": False},
# Note done is False here indicating we are still emitting statuses
}
)
@ -121,9 +135,11 @@ async def test_function(
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.
```
@ -167,4 +183,5 @@ async def test_function(
return f"Tell the user: {e}"
```
</details>

View File

@ -5,7 +5,9 @@ title: "⚙️ Valves"
# Valves
Valves are input variables that are set per pipeline. Valves are set as a subclass of the `Pipeline` class, and initialized as part of the `__init__` method of the `Pipeline` class.
`Valves` (see the dedicated [Valves & UserValves](../features/plugin/valves/index.mdx) page) can also be set for `Pipeline`. In short, `Valves` are input variables that are set per pipeline.
`Valves` are set as a subclass of the `Pipeline` class, and initialized as part of the `__init__` method of the `Pipeline` class.
When adding valves to your pipeline, include a way to ensure that valves can be reconfigured by admins in the web UI. There are a few options for this: