From 7253830b10b6f9d62d56b29daec9d1961621e7f7 Mon Sep 17 00:00:00 2001 From: thiswillbeyourgithub <26625900+thiswillbeyourgithub@users.noreply.github.com> Date: Fri, 9 May 2025 10:35:20 +0200 Subject: [PATCH 1/7] new: mention Literal to give choice to the user Signed-off-by: thiswillbeyourgithub <26625900+thiswillbeyourgithub@users.noreply.github.com> --- docs/features/plugin/functions/tab-shared/Common.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/features/plugin/functions/tab-shared/Common.md b/docs/features/plugin/functions/tab-shared/Common.md index f48d29b7..1919897f 100644 --- a/docs/features/plugin/functions/tab-shared/Common.md +++ b/docs/features/plugin/functions/tab-shared/Common.md @@ -11,6 +11,7 @@ Valves are configurable by admins alone via the Tools or Functions menus. On the ``` from pydantic import BaseModel, Field +from typing import Literal # Define and Valves class Filter: @@ -27,6 +28,11 @@ class Filter: 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" From de65056c074b49bc8551a8c88e912c93f61048f4 Mon Sep 17 00:00:00 2001 From: thiswillbeyourgithub <26625900+thiswillbeyourgithub@users.noreply.github.com> Date: Fri, 9 May 2025 10:35:29 +0200 Subject: [PATCH 2/7] fix: indentation Signed-off-by: thiswillbeyourgithub <26625900+thiswillbeyourgithub@users.noreply.github.com> --- docs/features/plugin/functions/tab-shared/Common.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/features/plugin/functions/tab-shared/Common.md b/docs/features/plugin/functions/tab-shared/Common.md index 1919897f..51b7d8ab 100644 --- a/docs/features/plugin/functions/tab-shared/Common.md +++ b/docs/features/plugin/functions/tab-shared/Common.md @@ -27,7 +27,7 @@ class Filter: 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", @@ -36,10 +36,10 @@ class Filter: 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 + pass # Note that this 'pass' helps for parsing and is recommended. # UserValves are defined the same way. From dea3f578de7af0a3f87f6bfda37207028c6ed0d3 Mon Sep 17 00:00:00 2001 From: thiswillbeyourgithub <26625900+thiswillbeyourgithub@users.noreply.github.com> Date: Fri, 9 May 2025 10:50:44 +0200 Subject: [PATCH 3/7] new: move valve to its own section Currently, the tabs-shared folder mechanism seems broken: the valve documentation does not appear on the website. Additionaly, valves are relevant to tools too. Signed-off-by: thiswillbeyourgithub <26625900+thiswillbeyourgithub@users.noreply.github.com> --- docs/features/plugin/migration/index.mdx | 2 +- .../tab-shared/Common.md => valves/index.mdx} | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) rename docs/features/plugin/{functions/tab-shared/Common.md => valves/index.mdx} (96%) diff --git a/docs/features/plugin/migration/index.mdx b/docs/features/plugin/migration/index.mdx index e0454390..575515f0 100644 --- a/docs/features/plugin/migration/index.mdx +++ b/docs/features/plugin/migration/index.mdx @@ -1,5 +1,5 @@ --- -sidebar_position: 3 +sidebar_position: 4 title: "🚚 Migrating Tools & Functions: 0.4 to 0.5" --- diff --git a/docs/features/plugin/functions/tab-shared/Common.md b/docs/features/plugin/valves/index.mdx similarity index 96% rename from docs/features/plugin/functions/tab-shared/Common.md rename to docs/features/plugin/valves/index.mdx index 51b7d8ab..272c192a 100644 --- a/docs/features/plugin/functions/tab-shared/Common.md +++ b/docs/features/plugin/valves/index.mdx @@ -1,8 +1,14 @@ -## 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. From 0ceb244e739b092860c90e254114e0e3939333a3 Mon Sep 17 00:00:00 2001 From: thiswillbeyourgithub <26625900+thiswillbeyourgithub@users.noreply.github.com> Date: Fri, 9 May 2025 10:58:53 +0200 Subject: [PATCH 4/7] fix: deduplicate the valve page and linke to the dedicated page Signed-off-by: thiswillbeyourgithub <26625900+thiswillbeyourgithub@users.noreply.github.com> --- docs/features/plugin/tools/development.mdx | 184 +-------------------- 1 file changed, 1 insertion(+), 183 deletions(-) diff --git a/docs/features/plugin/tools/development.mdx b/docs/features/plugin/tools/development.mdx index 87f2c1bd..7e8d85e6 100644 --- a/docs/features/plugin/tools/development.mdx +++ b/docs/features/plugin/tools/development.mdx @@ -54,190 +54,8 @@ Each tool must have type hints for arguments. As of version Open WebUI version 0 ### 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. - -
-Commented example - - ``` -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}" - -``` -
- -### 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 - } - ) -``` - -
-Example - -``` -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}" -``` -
- -#### 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 - } - ) -``` - -
-Example - -``` -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}" -``` -
#### 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: From 90badfc70871d0be4f1b9cbdfa3ea8f2240881e5 Mon Sep 17 00:00:00 2001 From: thiswillbeyourgithub <26625900+thiswillbeyourgithub@users.noreply.github.com> Date: Fri, 9 May 2025 11:00:44 +0200 Subject: [PATCH 5/7] fix: remove very old versioned message Signed-off-by: thiswillbeyourgithub <26625900+thiswillbeyourgithub@users.noreply.github.com> --- docs/features/plugin/tools/development.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/features/plugin/tools/development.mdx b/docs/features/plugin/tools/development.mdx index 7e8d85e6..aea8a7fb 100644 --- a/docs/features/plugin/tools/development.mdx +++ b/docs/features/plugin/tools/development.mdx @@ -50,7 +50,7 @@ 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) From 88e11a49bf47c2e966dbc045b78bef91d541d5c5 Mon Sep 17 00:00:00 2001 From: thiswillbeyourgithub <26625900+thiswillbeyourgithub@users.noreply.github.com> Date: Fri, 9 May 2025 11:15:01 +0200 Subject: [PATCH 6/7] ran prettier write Signed-off-by: thiswillbeyourgithub <26625900+thiswillbeyourgithub@users.noreply.github.com> --- docs/features/plugin/valves/index.mdx | 111 ++++++++++++++------------ 1 file changed, 58 insertions(+), 53 deletions(-) diff --git a/docs/features/plugin/valves/index.mdx b/docs/features/plugin/valves/index.mdx index 272c192a..bee7bc3c 100644 --- a/docs/features/plugin/valves/index.mdx +++ b/docs/features/plugin/valves/index.mdx @@ -5,7 +5,6 @@ title: "🔄 Valves & UserValves" ## 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. They are always optional, but HIGHLY encouraged. Hence, Valves and UserValves class can be defined in either a `Pipe`, `Pipeline`, `Filter` or `Tools` class. @@ -15,79 +14,82 @@ Valves are configurable by admins alone via the Tools or Functions menus. On the
Commented example - ``` +``` 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 - ) - # 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. - - # 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! ``` +
### 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 } ) @@ -109,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 } ) @@ -133,9 +135,11 @@ async def test_function( return f"Tell the user: {e}" ``` + #### 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. ``` @@ -179,4 +183,5 @@ async def test_function( return f"Tell the user: {e}" ``` + From adfa5ed0cf8cf9291c4cd396e242eccfbf3f69a9 Mon Sep 17 00:00:00 2001 From: thiswillbeyourgithub <26625900+thiswillbeyourgithub@users.noreply.github.com> Date: Fri, 9 May 2025 11:22:24 +0200 Subject: [PATCH 7/7] fix: update the other valves page for pipelines it was not making it clear that valves are not a pipeline only feature Signed-off-by: thiswillbeyourgithub <26625900+thiswillbeyourgithub@users.noreply.github.com> --- docs/pipelines/valves.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/pipelines/valves.md b/docs/pipelines/valves.md index b0334d57..de4bbc40 100644 --- a/docs/pipelines/valves.md +++ b/docs/pipelines/valves.md @@ -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: