mirror of
https://github.com/open-webui/functions.git
synced 2026-07-01 17:58:56 -04:00
feat: functions
This commit is contained in:
@@ -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
|
||||
@@ -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
|
||||
@@ -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}"
|
||||
Reference in New Issue
Block a user