feat: tools integration

This commit is contained in:
Timothy J. Baek 2024-06-10 21:33:46 -07:00
parent c5683dd24c
commit b434ebf3ad
8 changed files with 146 additions and 42 deletions

View File

@ -41,7 +41,7 @@ class ToolModel(BaseModel):
user_id: str user_id: str
name: str name: str
content: str content: str
specs: dict specs: List[dict]
meta: ToolMeta meta: ToolMeta
updated_at: int # timestamp in epoch updated_at: int # timestamp in epoch
created_at: int # timestamp in epoch created_at: int # timestamp in epoch
@ -74,7 +74,7 @@ class ToolsTable:
self.db.create_tables([Tool]) self.db.create_tables([Tool])
def insert_new_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]: ) -> Optional[ToolModel]:
tool = ToolModel( tool = ToolModel(
**{ **{

View File

@ -52,7 +52,18 @@ def load_toolkit_module_from_path(tools_id, tools_path):
@router.get("/", response_model=List[ToolResponse]) @router.get("/", response_model=List[ToolResponse])
async def get_toolkits(user=Depends(get_current_user)): 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 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) toolkit = Tools.insert_new_tool(user.id, form_data, specs)
if toolkit: if toolkit:
return ToolResponse(**toolkit) return toolkit
else: else:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST, 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: else:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST, 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) toolkit = Tools.get_tool_by_id(id)
if toolkit: if toolkit:
return ToolResponse(**toolkit) return toolkit
else: else:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED, status_code=status.HTTP_401_UNAUTHORIZED,
@ -137,7 +148,7 @@ async def update_toolkit_by_id(
) )
if toolkit: if toolkit:
return ToolResponse(**toolkit) return toolkit
else: else:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST, status_code=status.HTTP_400_BAD_REQUEST,

View File

@ -32,6 +32,7 @@ class ERROR_MESSAGES(str, Enum):
COMMAND_TAKEN = "Uh-oh! This command is already registered. Please choose another command string." 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." 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." 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." NAME_TAG_TAKEN = "Uh-oh! This name tag is already registered. Please choose another name tag string."

View File

@ -8,6 +8,7 @@
import { createNewPrompt, deletePromptByCommand, getPrompts } from '$lib/apis/prompts'; import { createNewPrompt, deletePromptByCommand, getPrompts } from '$lib/apis/prompts';
import { goto } from '$app/navigation'; import { goto } from '$app/navigation';
import { deleteToolById, getTools } from '$lib/apis/tools';
const i18n = getContext('i18n'); const i18n = getContext('i18n');
@ -78,7 +79,12 @@
<div class=" flex flex-1 space-x-4 cursor-pointer w-full"> <div class=" flex flex-1 space-x-4 cursor-pointer w-full">
<a href={`/workspace/tools/edit?id=${encodeURIComponent(tool.id)}`}> <a href={`/workspace/tools/edit?id=${encodeURIComponent(tool.id)}`}>
<div class=" flex-1 self-center pl-5"> <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"> <div class=" text-xs overflow-hidden text-ellipsis line-clamp-1">
{tool.meta.description} {tool.meta.description}
</div> </div>
@ -89,7 +95,7 @@
<a <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" 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" type="button"
href={`/workspace/tools/edit?command=${encodeURIComponent(tool.id)}`} href={`/workspace/tools/edit?id=${encodeURIComponent(tool.id)}`}
> >
<svg <svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
@ -134,9 +140,16 @@
<button <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" 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" type="button"
on:click={() => { on:click={async () => {
// deletePrompt(prompt.command); const res = await deleteToolById(localStorage.token, tool.id).catch((error) => {
// deleteTool toast.error(error);
return null;
});
if (res) {
toast.success('Tool deleted successfully');
tools.set(await getTools(localStorage.token));
}
}} }}
> >
<svg <svg

View File

@ -7,9 +7,10 @@
export let value = ''; export let value = '';
let codeEditor; let codeEditor;
let boilerplate = `from datetime import datetime let boilerplate = `import os
import requests import requests
import os from datetime import datetime
class Tools: class Tools:
def __init__(self): def __init__(self):
@ -27,7 +28,9 @@ class Tools:
""" """
value = os.getenv(variable_name) value = os.getenv(variable_name)
if value is not None: 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: else:
return f"The environment variable '{variable_name}' does not exist." return f"The environment variable '{variable_name}' does not exist."
@ -62,15 +65,17 @@ class Tools:
:param city: The name of the city to get the weather for. :param city: The name of the city to get the weather for.
:return: The current weather information or an error message. :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: 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" base_url = "http://api.openweathermap.org/data/2.5/weather"
params = { params = {
'q': city, "q": city,
'appid': api_key, "appid": api_key,
'units': 'metric' # Optional: Use 'imperial' for Fahrenheit "units": "metric", # Optional: Use 'imperial' for Fahrenheit
} }
try: try:
@ -78,22 +83,17 @@ class Tools:
response.raise_for_status() # Raise HTTPError for bad responses (4xx and 5xx) response.raise_for_status() # Raise HTTPError for bad responses (4xx and 5xx)
data = response.json() data = response.json()
if data.get('cod') != 200: if data.get("cod") != 200:
return f"Error fetching weather data: {data.get('message')}" return f"Error fetching weather data: {data.get('message')}"
weather_description = data['weather'][0]['description'] weather_description = data["weather"][0]["description"]
temperature = data['main']['temp'] temperature = data["main"]["temp"]
humidity = data['main']['humidity'] humidity = data["main"]["humidity"]
wind_speed = data['wind']['speed'] wind_speed = data["wind"]["speed"]
return (f"Weather in {city}:\n" return f"Weather in {city}: {temperature}°C"
f"Description: {weather_description}\n"
f"Temperature: {temperature}°C\n"
f"Humidity: {humidity}%\n"
f"Wind Speed: {wind_speed} m/s")
except requests.RequestException as e: except requests.RequestException as e:
return f"Error fetching weather data: {str(e)}" return f"Error fetching weather data: {str(e)}"
`; `;
export const formatHandler = async () => { export const formatHandler = async () => {

View File

@ -8,15 +8,18 @@
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
let formElement = null;
let loading = false; let loading = false;
let id = ''; export let edit = false;
let name = '';
let meta = { export let id = '';
export let name = '';
export let meta = {
description: '' description: ''
}; };
export let content = '';
let content = '';
$: if (name) { $: if (name) {
id = name.replace(/\s+/g, '_').toLowerCase(); 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=" flex flex-col justify-between w-full overflow-y-auto h-full">
<div class="mx-auto w-full md:px-0 h-full"> <div class="mx-auto w-full md:px-0 h-full">
<form <form
bind:this={formElement}
class=" flex flex-col max-h-[100dvh] h-full" class=" flex flex-col max-h-[100dvh] h-full"
on:submit|preventDefault={() => { on:submit|preventDefault={() => {
submitHandler(); submitHandler();
@ -60,6 +64,7 @@
on:click={() => { on:click={() => {
goto('/workspace/tools'); goto('/workspace/tools');
}} }}
type="button"
> >
<div class=" self-center"> <div class=" self-center">
<svg <svg
@ -96,6 +101,7 @@
placeholder="Toolkit ID (e.g. my_toolkit)" placeholder="Toolkit ID (e.g. my_toolkit)"
bind:value={id} bind:value={id}
required required
disabled={edit}
/> />
</div> </div>
<input <input
@ -112,8 +118,9 @@
bind:value={content} bind:value={content}
bind:this={codeEditor} bind:this={codeEditor}
on:save={() => { on:save={() => {
// submit form if (formElement) {
submitHandler(); formElement.requestSubmit();
}
}} }}
/> />
</div> </div>

View File

@ -1,8 +1,28 @@
<script> <script>
import { goto } from '$app/navigation';
import { createNewTool, getTools } from '$lib/apis/tools';
import ToolkitEditor from '$lib/components/workspace/Tools/ToolkitEditor.svelte'; import ToolkitEditor from '$lib/components/workspace/Tools/ToolkitEditor.svelte';
import { tools } from '$lib/stores';
import { toast } from 'svelte-sonner';
const saveHandler = async (data) => { const saveHandler = async (data) => {
console.log(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> </script>

View File

@ -1,5 +1,57 @@
<script> <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 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> </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}