Skip to content

Commit 3ddcae0

Browse files
authored
groundwork (#1185)
* groundwork for tools * remove optional text * polish sidebar * slight style change * add tests
1 parent 8d01050 commit 3ddcae0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+9555
-4759
lines changed
+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { openapiToFunctions } from "@/lib/openapi-to-functions"
2+
3+
describe("openapiToFunctions", () => {
4+
const validSchema = JSON.stringify({
5+
openapi: "3.1.0",
6+
info: {
7+
title: "Get weather data",
8+
description: "Retrieves current weather data for a location.",
9+
version: "v1.0.0"
10+
},
11+
servers: [
12+
{
13+
url: "https://weather.example.com"
14+
}
15+
],
16+
paths: {
17+
"/location": {
18+
get: {
19+
description: "Get temperature for a specific location",
20+
operationId: "GetCurrentWeather",
21+
parameters: [
22+
{
23+
name: "location",
24+
in: "query",
25+
description: "The city and state to retrieve the weather for",
26+
required: true,
27+
schema: {
28+
type: "string"
29+
}
30+
}
31+
]
32+
}
33+
}
34+
}
35+
})
36+
37+
const invalidSchema = JSON.stringify({
38+
openapi: "2.0" // Invalid version
39+
// ... rest of the schema
40+
})
41+
42+
it("should parse a valid OpenAPI schema", () => {
43+
const result = openapiToFunctions(validSchema)
44+
expect(result.title).toBe("Get weather data")
45+
expect(result.description).toBe(
46+
"Retrieves current weather data for a location."
47+
)
48+
expect(result.url).toBe("https://weather.example.com")
49+
expect(result.routes).toHaveLength(1)
50+
expect(result.routes[0].path).toBe("/location")
51+
// Add more assertions as needed
52+
})
53+
54+
it("should throw an error for an invalid OpenAPI schema", () => {
55+
expect(() => openapiToFunctions(invalidSchema)).toThrow(
56+
"Invalid OpenAPI schema. Only version 3.1.0 is supported."
57+
)
58+
})
59+
60+
// Add more tests as needed
61+
})

app/login/page.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ export default async function Login({
6363
email,
6464
password,
6565
options: {
66-
// TODO: USE IF YOU WANT TO SEND EMAIL VERIFICATION, ALSO CHANGE TOML FILE
66+
// USE IF YOU WANT TO SEND EMAIL VERIFICATION, ALSO CHANGE TOML FILE
6767
// emailRedirectTo: `${origin}/auth/callback`
6868
}
6969
})
@@ -75,7 +75,7 @@ export default async function Login({
7575

7676
return redirect("/setup")
7777

78-
// TODO: USE IF YOU WANT TO SEND EMAIL VERIFICATION, ALSO CHANGE TOML FILE
78+
// USE IF YOU WANT TO SEND EMAIL VERIFICATION, ALSO CHANGE TOML FILE
7979
// return redirect("/login?message=Check email to continue sign in process")
8080
}
8181

components/chat/file-picker.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -27,14 +27,14 @@ export const FilePicker: FC<FilePickerProps> = ({
2727
}) => {
2828
const { files, collections, setIsAtPickerOpen } = useContext(ChatbotUIContext)
2929

30+
const itemsRef = useRef<(HTMLDivElement | null)[]>([])
31+
3032
useEffect(() => {
3133
if (isFocused && itemsRef.current[0]) {
3234
itemsRef.current[0].focus()
3335
}
3436
}, [isFocused])
3537

36-
const itemsRef = useRef<(HTMLDivElement | null)[]>([])
37-
3838
const filteredFiles = files.filter(
3939
file =>
4040
file.name.toLowerCase().includes(searchQuery.toLowerCase()) &&

components/chat/prompt-picker.tsx

+24-7
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export const PromptPicker: FC<PromptPickerProps> = ({
2020
const { prompts, isPromptPickerOpen, setIsPromptPickerOpen } =
2121
useContext(ChatbotUIContext)
2222

23-
const firstPromptRef = useRef<HTMLDivElement>(null)
23+
const itemsRef = useRef<(HTMLDivElement | null)[]>([])
2424

2525
const [promptVariables, setPromptVariables] = useState<
2626
{
@@ -31,6 +31,12 @@ export const PromptPicker: FC<PromptPickerProps> = ({
3131
>([])
3232
const [showPromptVariables, setShowPromptVariables] = useState(false)
3333

34+
useEffect(() => {
35+
if (isFocused && itemsRef.current[0]) {
36+
itemsRef.current[0].focus()
37+
}
38+
}, [isFocused])
39+
3440
const filteredPrompts = prompts.filter(prompt =>
3541
prompt.name.toLowerCase().includes(searchQuery.toLowerCase())
3642
)
@@ -72,7 +78,20 @@ export const PromptPicker: FC<PromptPickerProps> = ({
7278
index === filteredPrompts.length - 1
7379
) {
7480
e.preventDefault()
75-
firstPromptRef.current?.focus()
81+
itemsRef.current[0]?.focus()
82+
} else if (e.key === "ArrowUp" && !e.shiftKey && index === 0) {
83+
// go to last element if arrow up is pressed on first element
84+
e.preventDefault()
85+
itemsRef.current[itemsRef.current.length - 1]?.focus()
86+
} else if (e.key === "ArrowUp") {
87+
e.preventDefault()
88+
const prevIndex =
89+
index - 1 >= 0 ? index - 1 : itemsRef.current.length - 1
90+
itemsRef.current[prevIndex]?.focus()
91+
} else if (e.key === "ArrowDown") {
92+
e.preventDefault()
93+
const nextIndex = index + 1 < itemsRef.current.length ? index + 1 : 0
94+
itemsRef.current[nextIndex]?.focus()
7695
}
7796
}
7897

@@ -112,10 +131,6 @@ export const PromptPicker: FC<PromptPickerProps> = ({
112131
}
113132
}
114133

115-
useEffect(() => {
116-
firstPromptRef.current?.focus()
117-
}, [isFocused])
118-
119134
return (
120135
<>
121136
{isPromptPickerOpen && (
@@ -173,7 +188,9 @@ export const PromptPicker: FC<PromptPickerProps> = ({
173188
filteredPrompts.map((prompt, index) => (
174189
<div
175190
key={prompt.id}
176-
ref={index === 0 ? firstPromptRef : null}
191+
ref={ref => {
192+
itemsRef.current[index] = ref
193+
}}
177194
tabIndex={0}
178195
className="hover:bg-accent focus:bg-accent flex cursor-pointer flex-col rounded p-2 focus:outline-none"
179196
onClick={() => handleSelectPrompt(prompt)}

components/sharing/add-to-workspace.tsx

+11-2
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ export const AddToWorkspace: FC<AddToWorkspaceProps> = ({
3535
setPrompts,
3636
setFiles,
3737
setCollections,
38-
setAssistants
38+
setAssistants,
39+
setTools
3940
} = useContext(ChatbotUIContext)
4041

4142
const router = useRouter()
@@ -149,6 +150,13 @@ export const AddToWorkspace: FC<AddToWorkspaceProps> = ({
149150
)
150151

151152
return createdAssistant
153+
},
154+
tools: async (
155+
item: Tables<"tools">,
156+
workspaceId: string,
157+
userId: string
158+
) => {
159+
// TODO
152160
}
153161
}
154162

@@ -158,7 +166,8 @@ export const AddToWorkspace: FC<AddToWorkspaceProps> = ({
158166
prompts: setPrompts,
159167
files: setFiles,
160168
collections: setCollections,
161-
assistants: setAssistants
169+
assistants: setAssistants,
170+
tools: setTools
162171
}
163172

164173
const handleAddToWorkspace = async (workspaceId: string) => {

components/sharing/share-item.tsx

+7
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,13 @@ export const ShareItem: FC<ShareItemProps> = ({
106106
}
107107
break
108108

109+
case "tools":
110+
data = {
111+
...data,
112+
name: item.name
113+
}
114+
break
115+
109116
default:
110117
break
111118
}

components/sidebar/items/all/sidebar-create-item.tsx

+56-41
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { Button } from "@/components/ui/button"
22
import {
3-
Dialog,
4-
DialogContent,
5-
DialogFooter,
6-
DialogHeader,
7-
DialogTitle
8-
} from "@/components/ui/dialog"
3+
Sheet,
4+
SheetContent,
5+
SheetFooter,
6+
SheetHeader,
7+
SheetTitle
8+
} from "@/components/ui/sheet"
99
import { ChatbotUIContext } from "@/context/context"
1010
import { createAssistant, updateAssistant } from "@/db/assistants"
1111
import { createChat } from "@/db/chats"
@@ -18,6 +18,7 @@ import {
1818
getAssistantImageFromStorage,
1919
uploadAssistantImage
2020
} from "@/db/storage/assistant-images"
21+
import { createTool } from "@/db/tools"
2122
import { convertBlobToBase64 } from "@/lib/blob-to-b64"
2223
import { Tables, TablesInsert } from "@/supabase/types"
2324
import { ContentType } from "@/types"
@@ -47,7 +48,8 @@ export const SidebarCreateItem: FC<SidebarCreateItemProps> = ({
4748
setFiles,
4849
setCollections,
4950
setAssistants,
50-
setAssistantImages
51+
setAssistantImages,
52+
setTools
5153
} = useContext(ChatbotUIContext)
5254

5355
const buttonRef = useRef<HTMLButtonElement>(null)
@@ -105,32 +107,37 @@ export const SidebarCreateItem: FC<SidebarCreateItemProps> = ({
105107

106108
const createdAssistant = await createAssistant(rest, workspaceId)
107109

108-
const filePath = await uploadAssistantImage(createdAssistant, image)
110+
let updatedAssistant = createdAssistant
109111

110-
const updatedAssistant = await updateAssistant(createdAssistant.id, {
111-
image_path: filePath
112-
})
112+
if (image) {
113+
const filePath = await uploadAssistantImage(createdAssistant, image)
113114

114-
const url = (await getAssistantImageFromStorage(filePath)) || ""
115+
const updatedAssistant = await updateAssistant(createdAssistant.id, {
116+
image_path: filePath
117+
})
115118

116-
if (url) {
117-
const response = await fetch(url)
118-
const blob = await response.blob()
119-
const base64 = await convertBlobToBase64(blob)
119+
const url = (await getAssistantImageFromStorage(filePath)) || ""
120120

121-
setAssistantImages(prev => [
122-
...prev,
123-
{
124-
assistantId: updatedAssistant.id,
125-
path: filePath,
126-
base64,
127-
url
128-
}
129-
])
121+
if (url) {
122+
const response = await fetch(url)
123+
const blob = await response.blob()
124+
const base64 = await convertBlobToBase64(blob)
125+
126+
setAssistantImages(prev => [
127+
...prev,
128+
{
129+
assistantId: updatedAssistant.id,
130+
path: filePath,
131+
base64,
132+
url
133+
}
134+
])
135+
}
130136
}
131137

132138
return updatedAssistant
133-
}
139+
},
140+
tools: createTool
134141
}
135142

136143
const stateUpdateFunctions = {
@@ -139,7 +146,8 @@ export const SidebarCreateItem: FC<SidebarCreateItemProps> = ({
139146
prompts: setPrompts,
140147
files: setFiles,
141148
collections: setCollections,
142-
assistants: setAssistants
149+
assistants: setAssistants,
150+
tools: setTools
143151
}
144152

145153
const handleCreate = async () => {
@@ -173,17 +181,24 @@ export const SidebarCreateItem: FC<SidebarCreateItemProps> = ({
173181
}
174182

175183
return (
176-
<Dialog open={isOpen} onOpenChange={onOpenChange}>
177-
<DialogContent onKeyDown={handleKeyDown}>
178-
<DialogHeader>
179-
<DialogTitle className="text-2xl font-bold">
180-
Create {contentType.slice(0, -1)}
181-
</DialogTitle>
182-
</DialogHeader>
183-
184-
<div className="space-y-3">{renderInputs()}</div>
185-
186-
<DialogFooter className="mt-2 flex justify-between">
184+
<Sheet open={isOpen} onOpenChange={onOpenChange}>
185+
<SheetContent
186+
className="flex flex-col justify-between"
187+
side="left"
188+
onKeyDown={handleKeyDown}
189+
>
190+
<div className="grow">
191+
<SheetHeader>
192+
<SheetTitle className="text-2xl font-bold">
193+
Create{" "}
194+
{contentType.charAt(0).toUpperCase() + contentType.slice(1, -1)}
195+
</SheetTitle>
196+
</SheetHeader>
197+
198+
<div className="mt-4 space-y-3">{renderInputs()}</div>
199+
</div>
200+
201+
<SheetFooter className="mt-2 flex justify-between">
187202
<div className="flex grow justify-end space-x-2">
188203
<Button
189204
disabled={creating}
@@ -197,8 +212,8 @@ export const SidebarCreateItem: FC<SidebarCreateItemProps> = ({
197212
{creating ? "Creating..." : "Create"}
198213
</Button>
199214
</div>
200-
</DialogFooter>
201-
</DialogContent>
202-
</Dialog>
215+
</SheetFooter>
216+
</SheetContent>
217+
</Sheet>
203218
)
204219
}

0 commit comments

Comments
 (0)