mirror of
https://github.com/open-webui/open-webui
synced 2025-03-23 06:17:24 +00:00
feat: tools integration
This commit is contained in:
parent
c5683dd24c
commit
b434ebf3ad
@ -41,7 +41,7 @@ class ToolModel(BaseModel):
|
||||
user_id: str
|
||||
name: str
|
||||
content: str
|
||||
specs: dict
|
||||
specs: List[dict]
|
||||
meta: ToolMeta
|
||||
updated_at: int # timestamp in epoch
|
||||
created_at: int # timestamp in epoch
|
||||
@ -74,7 +74,7 @@ class ToolsTable:
|
||||
self.db.create_tables([Tool])
|
||||
|
||||
def insert_new_tool(
|
||||
self, user_id: str, form_data: ToolForm, specs: dict
|
||||
self, user_id: str, form_data: ToolForm, specs: List[dict]
|
||||
) -> Optional[ToolModel]:
|
||||
tool = ToolModel(
|
||||
**{
|
||||
|
@ -52,7 +52,18 @@ def load_toolkit_module_from_path(tools_id, tools_path):
|
||||
|
||||
@router.get("/", response_model=List[ToolResponse])
|
||||
async def get_toolkits(user=Depends(get_current_user)):
|
||||
toolkits = [ToolResponse(**toolkit) for toolkit in Tools.get_tools()]
|
||||
toolkits = [toolkit for toolkit in Tools.get_tools()]
|
||||
return toolkits
|
||||
|
||||
|
||||
############################
|
||||
# ExportToolKits
|
||||
############################
|
||||
|
||||
|
||||
@router.get("/export", response_model=List[ToolModel])
|
||||
async def get_toolkits(user=Depends(get_current_user)):
|
||||
toolkits = [toolkit for toolkit in Tools.get_tools()]
|
||||
return toolkits
|
||||
|
||||
|
||||
@ -77,7 +88,7 @@ async def create_new_toolkit(form_data: ToolForm, user=Depends(get_admin_user)):
|
||||
toolkit = Tools.insert_new_tool(user.id, form_data, specs)
|
||||
|
||||
if toolkit:
|
||||
return ToolResponse(**toolkit)
|
||||
return toolkit
|
||||
else:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
@ -91,7 +102,7 @@ async def create_new_toolkit(form_data: ToolForm, user=Depends(get_admin_user)):
|
||||
else:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
detail=ERROR_MESSAGES.NAME_TAG_TAKEN,
|
||||
detail=ERROR_MESSAGES.ID_TAKEN,
|
||||
)
|
||||
|
||||
|
||||
@ -105,7 +116,7 @@ async def get_toolkit_by_id(id: str, user=Depends(get_admin_user)):
|
||||
toolkit = Tools.get_tool_by_id(id)
|
||||
|
||||
if toolkit:
|
||||
return ToolResponse(**toolkit)
|
||||
return toolkit
|
||||
else:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_401_UNAUTHORIZED,
|
||||
@ -137,7 +148,7 @@ async def update_toolkit_by_id(
|
||||
)
|
||||
|
||||
if toolkit:
|
||||
return ToolResponse(**toolkit)
|
||||
return toolkit
|
||||
else:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST,
|
||||
|
@ -32,6 +32,7 @@ class ERROR_MESSAGES(str, Enum):
|
||||
COMMAND_TAKEN = "Uh-oh! This command is already registered. Please choose another command string."
|
||||
FILE_EXISTS = "Uh-oh! This file is already registered. Please choose another file."
|
||||
|
||||
ID_TAKEN = "Uh-oh! This id is already registered. Please choose another id string."
|
||||
MODEL_ID_TAKEN = "Uh-oh! This model id is already registered. Please choose another model id string."
|
||||
|
||||
NAME_TAG_TAKEN = "Uh-oh! This name tag is already registered. Please choose another name tag string."
|
||||
|
@ -8,6 +8,7 @@
|
||||
import { createNewPrompt, deletePromptByCommand, getPrompts } from '$lib/apis/prompts';
|
||||
|
||||
import { goto } from '$app/navigation';
|
||||
import { deleteToolById, getTools } from '$lib/apis/tools';
|
||||
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
@ -78,7 +79,12 @@
|
||||
<div class=" flex flex-1 space-x-4 cursor-pointer w-full">
|
||||
<a href={`/workspace/tools/edit?id=${encodeURIComponent(tool.id)}`}>
|
||||
<div class=" flex-1 self-center pl-5">
|
||||
<div class=" font-bold">{tool.name}</div>
|
||||
<div class=" font-bold flex items-center gap-1.5">
|
||||
<div>
|
||||
{tool.name}
|
||||
</div>
|
||||
<div class=" text-gray-500 text-xs font-medium">{tool.id}</div>
|
||||
</div>
|
||||
<div class=" text-xs overflow-hidden text-ellipsis line-clamp-1">
|
||||
{tool.meta.description}
|
||||
</div>
|
||||
@ -89,7 +95,7 @@
|
||||
<a
|
||||
class="self-center w-fit text-sm px-2 py-2 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
|
||||
type="button"
|
||||
href={`/workspace/tools/edit?command=${encodeURIComponent(tool.id)}`}
|
||||
href={`/workspace/tools/edit?id=${encodeURIComponent(tool.id)}`}
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
@ -134,9 +140,16 @@
|
||||
<button
|
||||
class="self-center w-fit text-sm px-2 py-2 dark:text-gray-300 dark:hover:text-white hover:bg-black/5 dark:hover:bg-white/5 rounded-xl"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
// deletePrompt(prompt.command);
|
||||
// deleteTool
|
||||
on:click={async () => {
|
||||
const res = await deleteToolById(localStorage.token, tool.id).catch((error) => {
|
||||
toast.error(error);
|
||||
return null;
|
||||
});
|
||||
|
||||
if (res) {
|
||||
toast.success('Tool deleted successfully');
|
||||
tools.set(await getTools(localStorage.token));
|
||||
}
|
||||
}}
|
||||
>
|
||||
<svg
|
||||
|
@ -7,9 +7,10 @@
|
||||
export let value = '';
|
||||
|
||||
let codeEditor;
|
||||
let boilerplate = `from datetime import datetime
|
||||
let boilerplate = `import os
|
||||
import requests
|
||||
import os
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
class Tools:
|
||||
def __init__(self):
|
||||
@ -27,7 +28,9 @@ class Tools:
|
||||
"""
|
||||
value = os.getenv(variable_name)
|
||||
if value is not None:
|
||||
return f"The value of the environment variable '{variable_name}' is '{value}'"
|
||||
return (
|
||||
f"The value of the environment variable '{variable_name}' is '{value}'"
|
||||
)
|
||||
else:
|
||||
return f"The environment variable '{variable_name}' does not exist."
|
||||
|
||||
@ -62,38 +65,35 @@ class Tools:
|
||||
:param city: The name of the city to get the weather for.
|
||||
:return: The current weather information or an error message.
|
||||
"""
|
||||
api_key = os.getenv('OPENWEATHER_API_KEY')
|
||||
api_key = os.getenv("OPENWEATHER_API_KEY")
|
||||
if not api_key:
|
||||
return "API key is not set in the environment variable 'OPENWEATHER_API_KEY'."
|
||||
return (
|
||||
"API key is not set in the environment variable 'OPENWEATHER_API_KEY'."
|
||||
)
|
||||
|
||||
base_url = "http://api.openweathermap.org/data/2.5/weather"
|
||||
params = {
|
||||
'q': city,
|
||||
'appid': api_key,
|
||||
'units': 'metric' # Optional: Use 'imperial' for Fahrenheit
|
||||
"q": city,
|
||||
"appid": api_key,
|
||||
"units": "metric", # Optional: Use 'imperial' for Fahrenheit
|
||||
}
|
||||
|
||||
try:
|
||||
response = requests.get(base_url, params=params)
|
||||
response.raise_for_status() # Raise HTTPError for bad responses (4xx and 5xx)
|
||||
data = response.json()
|
||||
|
||||
if data.get('cod') != 200:
|
||||
|
||||
if data.get("cod") != 200:
|
||||
return f"Error fetching weather data: {data.get('message')}"
|
||||
|
||||
weather_description = data['weather'][0]['description']
|
||||
temperature = data['main']['temp']
|
||||
humidity = data['main']['humidity']
|
||||
wind_speed = data['wind']['speed']
|
||||
|
||||
return (f"Weather in {city}:\n"
|
||||
f"Description: {weather_description}\n"
|
||||
f"Temperature: {temperature}°C\n"
|
||||
f"Humidity: {humidity}%\n"
|
||||
f"Wind Speed: {wind_speed} m/s")
|
||||
|
||||
weather_description = data["weather"][0]["description"]
|
||||
temperature = data["main"]["temp"]
|
||||
humidity = data["main"]["humidity"]
|
||||
wind_speed = data["wind"]["speed"]
|
||||
|
||||
return f"Weather in {city}: {temperature}°C"
|
||||
except requests.RequestException as e:
|
||||
return f"Error fetching weather data: {str(e)}"
|
||||
|
||||
`;
|
||||
|
||||
export const formatHandler = async () => {
|
||||
|
@ -8,15 +8,18 @@
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
let formElement = null;
|
||||
|
||||
let loading = false;
|
||||
|
||||
let id = '';
|
||||
let name = '';
|
||||
let meta = {
|
||||
export let edit = false;
|
||||
|
||||
export let id = '';
|
||||
export let name = '';
|
||||
export let meta = {
|
||||
description: ''
|
||||
};
|
||||
|
||||
let content = '';
|
||||
export let content = '';
|
||||
|
||||
$: if (name) {
|
||||
id = name.replace(/\s+/g, '_').toLowerCase();
|
||||
@ -49,6 +52,7 @@
|
||||
<div class=" flex flex-col justify-between w-full overflow-y-auto h-full">
|
||||
<div class="mx-auto w-full md:px-0 h-full">
|
||||
<form
|
||||
bind:this={formElement}
|
||||
class=" flex flex-col max-h-[100dvh] h-full"
|
||||
on:submit|preventDefault={() => {
|
||||
submitHandler();
|
||||
@ -60,6 +64,7 @@
|
||||
on:click={() => {
|
||||
goto('/workspace/tools');
|
||||
}}
|
||||
type="button"
|
||||
>
|
||||
<div class=" self-center">
|
||||
<svg
|
||||
@ -96,6 +101,7 @@
|
||||
placeholder="Toolkit ID (e.g. my_toolkit)"
|
||||
bind:value={id}
|
||||
required
|
||||
disabled={edit}
|
||||
/>
|
||||
</div>
|
||||
<input
|
||||
@ -112,8 +118,9 @@
|
||||
bind:value={content}
|
||||
bind:this={codeEditor}
|
||||
on:save={() => {
|
||||
// submit form
|
||||
submitHandler();
|
||||
if (formElement) {
|
||||
formElement.requestSubmit();
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
@ -1,8 +1,28 @@
|
||||
<script>
|
||||
import { goto } from '$app/navigation';
|
||||
import { createNewTool, getTools } from '$lib/apis/tools';
|
||||
import ToolkitEditor from '$lib/components/workspace/Tools/ToolkitEditor.svelte';
|
||||
import { tools } from '$lib/stores';
|
||||
import { toast } from 'svelte-sonner';
|
||||
|
||||
const saveHandler = async (data) => {
|
||||
console.log(data);
|
||||
const res = await createNewTool(localStorage.token, {
|
||||
id: data.id,
|
||||
name: data.name,
|
||||
meta: data.meta,
|
||||
content: data.content
|
||||
}).catch((error) => {
|
||||
toast.error(error);
|
||||
return null;
|
||||
});
|
||||
|
||||
if (res) {
|
||||
toast.success('Tool created successfully');
|
||||
tools.set(await getTools(localStorage.token));
|
||||
|
||||
await goto('/workspace/tools');
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
|
@ -1,5 +1,57 @@
|
||||
<script>
|
||||
import { goto } from '$app/navigation';
|
||||
import { page } from '$app/stores';
|
||||
import { getToolById, getTools, updateToolById } from '$lib/apis/tools';
|
||||
import ToolkitEditor from '$lib/components/workspace/Tools/ToolkitEditor.svelte';
|
||||
import { tools } from '$lib/stores';
|
||||
import { onMount } from 'svelte';
|
||||
import { toast } from 'svelte-sonner';
|
||||
|
||||
let tool = null;
|
||||
|
||||
const saveHandler = async (data) => {
|
||||
console.log(data);
|
||||
const res = await updateToolById(localStorage.token, tool.id, {
|
||||
id: data.id,
|
||||
name: data.name,
|
||||
meta: data.meta,
|
||||
content: data.content
|
||||
}).catch((error) => {
|
||||
toast.error(error);
|
||||
return null;
|
||||
});
|
||||
|
||||
if (res) {
|
||||
toast.success('Tool updated successfully');
|
||||
tools.set(await getTools(localStorage.token));
|
||||
|
||||
await goto('/workspace/tools');
|
||||
}
|
||||
};
|
||||
|
||||
onMount(async () => {
|
||||
console.log('mounted');
|
||||
const id = $page.url.searchParams.get('id');
|
||||
|
||||
if (id) {
|
||||
tool = await getToolById(localStorage.token, id).catch((error) => {
|
||||
toast.error(error);
|
||||
goto('/workspace/tools');
|
||||
return null;
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<ToolkitEditor />
|
||||
{#if tool}
|
||||
<ToolkitEditor
|
||||
edit={true}
|
||||
id={tool.id}
|
||||
name={tool.name}
|
||||
meta={tool.meta}
|
||||
content={tool.content}
|
||||
on:save={(e) => {
|
||||
saveHandler(e.detail);
|
||||
}}
|
||||
/>
|
||||
{/if}
|
||||
|
Loading…
Reference in New Issue
Block a user