feat: functions

This commit is contained in:
Timothy Jaeryang Baek
2025-05-26 18:51:57 +04:00
parent 3967a3508a
commit 987519ec1a
4 changed files with 362 additions and 0 deletions
+47
View File
@@ -0,0 +1,47 @@
"""
title: Context Clip Filter
author: open-webui
author_url: https://github.com/open-webui
funding_url: https://github.com/open-webui
version: 0.1
"""
from pydantic import BaseModel, Field
from typing import Optional
class Filter:
class Valves(BaseModel):
priority: int = Field(
default=0, description="Priority level for the filter operations."
)
n_last_messages: int = Field(
default=4, description="Number of last messages to retain."
)
pass
class UserValves(BaseModel):
pass
def __init__(self):
self.valves = self.Valves()
pass
def inlet(self, body: dict, __user__: Optional[dict] = None) -> dict:
messages = body["messages"]
# Ensure we always keep the system prompt
system_prompt = next(
(message for message in messages if message.get("role") == "system"), None
)
if system_prompt:
messages = [
message for message in messages if message.get("role") != "system"
]
messages = messages[-self.valves.n_last_messages :]
messages.insert(0, system_prompt)
else: # If no system prompt, simply truncate to the last n_last_messages
messages = messages[-self.valves.n_last_messages :]
body["messages"] = messages
return body
@@ -0,0 +1,111 @@
"""
title: Dynamic Vision Router
author: open-webui, atgehrhardt,
credits to @iamg30 for v0.1.5-v0.1.7 updates
author_url: https://github.com/open-webui
funding_url: https://github.com/open-webui
version: 0.1.7
required_open_webui_version: 0.3.8
"""
from pydantic import BaseModel, Field
from typing import Callable, Awaitable, Any, Optional, Literal
import json
from open_webui.utils.misc import get_last_user_message_item
class Filter:
class Valves(BaseModel):
priority: int = Field(
default=0,
description="Priority level for the filter operations.",
)
vision_model_id: str = Field(
default="",
description="The identifier of the vision model to be used for processing images. Note: Compatibility is provider-specific; ollama models can only route to ollama models, and OpenAI models to OpenAI models respectively.",
)
skip_reroute_models: list[str] = Field(
default_factory=list,
description="A list of model identifiers that should not be re-routed to the chosen vision model.",
)
enabled_for_admins: bool = Field(
default=False,
description="Whether dynamic vision routing is enabled for admin users.",
)
enabled_for_users: bool = Field(
default=True,
description="Whether dynamic vision routing is enabled for regular users.",
)
status: bool = Field(
default=False,
description="A flag to enable or disable the status indicator. Set to True to enable status updates.",
)
pass
def __init__(self):
self.valves = self.Valves()
pass
async def inlet(
self,
body: dict,
__event_emitter__: Callable[[Any], Awaitable[None]],
__model__: Optional[dict] = None,
__user__: Optional[dict] = None,
) -> dict:
if __model__["id"] in self.valves.skip_reroute_models:
return body
if __model__["id"] == self.valves.vision_model_id:
return body
if __user__ is not None:
if __user__.get("role") == "admin" and not self.valves.enabled_for_admins:
return body
elif __user__.get("role") == "user" and not self.valves.enabled_for_users:
return body
messages = body.get("messages")
if messages is None:
# Handle the case where messages is None
return body
user_message = get_last_user_message_item(messages)
if user_message is None:
# Handle the case where user_message is None
return body
has_images = user_message.get("images") is not None
if not has_images:
user_message_content = user_message.get("content")
if user_message_content is not None and isinstance(
user_message_content, list
):
has_images = any(
item.get("type") == "image_url" for item in user_message_content
)
if has_images:
if self.valves.vision_model_id:
body["model"] = self.valves.vision_model_id
if self.valves.status:
await __event_emitter__(
{
"type": "status",
"data": {
"description": f"Request routed to {self.valves.vision_model_id}",
"done": True,
},
}
)
else:
if self.valves.status:
await __event_emitter__(
{
"type": "status",
"data": {
"description": "No vision model ID provided, routing could not be completed.",
"done": True,
},
}
)
return body
+86
View File
@@ -0,0 +1,86 @@
"""
title: Max Turns Filter
author: open-webui
author_url: https://github.com/open-webui
funding_url: https://github.com/open-webui
version: 0.1.1
"""
from pydantic import BaseModel, Field
from typing import Optional
class Filter:
class Valves(BaseModel):
priority: int = Field(
default=0, description="Priority level for the filter operations."
)
max_turns_for_users: int = Field(
default=8,
description="Maximum allowable conversation turns for a non-admin user.",
)
max_turns_for_admins: int = Field(
default=8,
description="Maximum allowable conversation turns for an admin user.",
)
enabled_for_admins: bool = Field(
default=True,
description="Whether the max turns limit is enabled for admins.",
)
pass
class UserValves(BaseModel):
max_turns: int = Field(
default=4, description="Maximum allowable conversation turns for a user."
)
pass
def __init__(self):
# Indicates custom file handling logic. This flag helps disengage default routines in favor of custom
# implementations, informing the WebUI to defer file-related operations to designated methods within this class.
# Alternatively, you can remove the files directly from the body in from the inlet hook
# self.file_handler = True
# Initialize 'valves' with specific configurations. Using 'Valves' instance helps encapsulate settings,
# which ensures settings are managed cohesively and not confused with operational flags like 'file_handler'.
self.valves = self.Valves()
pass
def inlet(self, body: dict, __user__: Optional[dict] = None) -> dict:
# Modify the request body or validate it before processing by the chat completion API.
# This function is the pre-processor for the API where various checks on the input can be performed.
# It can also modify the request before sending it to the API.
print(f"inlet:{__name__}")
print(f"inlet:body:{body}")
print(f"inlet:user:{__user__}")
if __user__ is not None:
messages = body.get("messages", [])
if __user__.get("role") == "admin" and not self.valves.enabled_for_admins:
max_turns = float("inf")
else:
max_turns = (
self.valves.max_turns_for_admins
if __user__.get("role") == "admin"
else self.valves.max_turns_for_users
)
current_turns = (
len(messages) // 2
) # Each turn consists of a user message and an assistant response
if current_turns >= max_turns:
raise Exception(
f"Conversation turn limit exceeded. The maximum turns allowed is {max_turns}."
)
return body
def outlet(self, body: dict, __user__: Optional[dict] = None) -> dict:
# Modify or analyze the response body after processing by the API.
# This function is the post-processor for the API, which can be used to modify the response
# or perform additional checks and analytics.
print(f"outlet:{__name__}")
print(f"outlet:body:{body}")
print(f"outlet:user:{__user__}")
return body
+118
View File
@@ -0,0 +1,118 @@
"""
title: OpenAI Manifold Pipe
author: open-webui
author_url: https://github.com/open-webui
funding_url: https://github.com/open-webui
version: 0.1.2
"""
from pydantic import BaseModel, Field
from typing import Optional, Union, Generator, Iterator
from open_webui.utils.misc import get_last_user_message
import os
import requests
class Pipe:
class Valves(BaseModel):
NAME_PREFIX: str = Field(
default="OPENAI/",
description="The prefix applied before the model names.",
)
OPENAI_API_BASE_URL: str = Field(
default="https://api.openai.com/v1",
description="The base URL for OpenAI API endpoints.",
)
OPENAI_API_KEY: str = Field(
default="",
description="Required API key to retrieve the model list.",
)
pass
class UserValves(BaseModel):
OPENAI_API_KEY: str = Field(
default="",
description="User-specific API key for accessing OpenAI services.",
)
def __init__(self):
self.type = "manifold"
self.valves = self.Valves()
pass
def pipes(self):
if self.valves.OPENAI_API_KEY:
try:
headers = {}
headers["Authorization"] = f"Bearer {self.valves.OPENAI_API_KEY}"
headers["Content-Type"] = "application/json"
r = requests.get(
f"{self.valves.OPENAI_API_BASE_URL}/models", headers=headers
)
models = r.json()
return [
{
"id": model["id"],
"name": f'{self.valves.NAME_PREFIX}{model["name"] if "name" in model else model["id"]}',
}
for model in models["data"]
if "gpt" in model["id"]
]
except Exception as e:
print(f"Error: {e}")
return [
{
"id": "error",
"name": "Could not fetch models from OpenAI, please update the API Key in the valves.",
},
]
else:
return [
{
"id": "error",
"name": "Global API Key not provided.",
},
]
def pipe(self, body: dict, __user__: dict) -> Union[str, Generator, Iterator]:
# This is where you can add your custom pipelines like RAG.
print(f"pipe:{__name__}")
print(__user__)
user_valves = __user__.get("valves")
if not user_valves:
raise Exception("User Valves not configured.")
if not user_valves.OPENAI_API_KEY:
raise Exception("OPENAI_API_KEY not provided by the user.")
headers = {}
headers["Authorization"] = f"Bearer {user_valves.OPENAI_API_KEY}"
headers["Content-Type"] = "application/json"
model_id = body["model"][body["model"].find(".") + 1 :]
payload = {**body, "model": model_id}
print(payload)
try:
r = requests.post(
url=f"{self.valves.OPENAI_API_BASE_URL}/chat/completions",
json=payload,
headers=headers,
stream=True,
)
r.raise_for_status()
if body["stream"]:
return r.iter_lines()
else:
return r.json()
except Exception as e:
return f"Error: {e}"