Spaces:
Running
Running
feat: new tool UI (#1630)
Browse files* feat: new tool UI
* wip community tools support
* fix: community tools support
* fix: line heights
* fix: MIME types
* fix: remove debug logs
* fix: make sure file upload is always last
* fix: always enable document parser when a pdf is present in the conversation
* fix: use correct icon for document upload
* fix: make sure community tools use their custom icons correctly
* feat: add button to browse community tools
* tweak buttons
* add some tooltips
* fix: bug in file upload tooltips
* fix: lint
---------
Co-authored-by: Victor Mustar <[email protected]>
- chart/env/prod.yaml +2 -1
- src/lib/components/HoverTooltip.svelte +29 -2
- src/lib/components/ToolLogo.svelte +3 -1
- src/lib/components/chat/ChatInput.svelte +239 -26
- src/lib/components/chat/ChatWindow.svelte +26 -27
- src/lib/components/chat/FileDropzone.svelte +13 -1
- src/lib/components/icons/IconImageGen.svelte +23 -0
- src/lib/components/icons/IconInternet.svelte +6 -11
- src/lib/components/icons/IconPaperclip.svelte +20 -0
- src/lib/server/textGeneration/tools.ts +10 -7
- src/lib/types/Tool.ts +4 -1
- src/lib/utils/toolIds.ts +4 -0
- src/routes/+layout.server.ts +2 -0
- src/routes/conversation/[id]/+server.ts +13 -1
chart/env/prod.yaml
CHANGED
@@ -511,7 +511,8 @@ envVars:
|
|
511 |
],
|
512 |
"outputComponent": "textbox",
|
513 |
"outputComponentIdx": 0,
|
514 |
-
"showOutput": false
|
|
|
515 |
},
|
516 |
{
|
517 |
"_id": "000000000000000000000003",
|
|
|
511 |
],
|
512 |
"outputComponent": "textbox",
|
513 |
"outputComponentIdx": 0,
|
514 |
+
"showOutput": false,
|
515 |
+
"isHidden": true
|
516 |
},
|
517 |
{
|
518 |
"_id": "000000000000000000000003",
|
src/lib/components/HoverTooltip.svelte
CHANGED
@@ -1,11 +1,38 @@
|
|
1 |
<script lang="ts">
|
2 |
export let label = "";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3 |
</script>
|
4 |
|
5 |
-
<div class="group/tooltip md:relative">
|
6 |
<slot />
|
|
|
7 |
<div
|
8 |
-
class="
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
9 |
>
|
10 |
{label}
|
11 |
</div>
|
|
|
1 |
<script lang="ts">
|
2 |
export let label = "";
|
3 |
+
export let position: "top" | "bottom" | "left" | "right" = "bottom";
|
4 |
+
export let TooltipClassNames = "";
|
5 |
+
|
6 |
+
const positionClasses = {
|
7 |
+
top: "bottom-full mb-2",
|
8 |
+
bottom: "top-full mt-2",
|
9 |
+
left: "right-full mr-2 top-1/2 -translate-y-1/2",
|
10 |
+
right: "left-full ml-2 top-1/2 -translate-y-1/2",
|
11 |
+
};
|
12 |
</script>
|
13 |
|
14 |
+
<div class="group/tooltip inline-block md:relative">
|
15 |
<slot />
|
16 |
+
|
17 |
<div
|
18 |
+
class="
|
19 |
+
invisible
|
20 |
+
absolute
|
21 |
+
z-10
|
22 |
+
w-64
|
23 |
+
whitespace-normal
|
24 |
+
rounded-md
|
25 |
+
bg-black
|
26 |
+
p-2
|
27 |
+
text-center
|
28 |
+
text-white
|
29 |
+
group-hover/tooltip:visible
|
30 |
+
group-active/tooltip:visible
|
31 |
+
max-sm:left-1/2
|
32 |
+
max-sm:-translate-x-1/2
|
33 |
+
{positionClasses[position]}
|
34 |
+
{TooltipClassNames}
|
35 |
+
"
|
36 |
>
|
37 |
{label}
|
38 |
</div>
|
src/lib/components/ToolLogo.svelte
CHANGED
@@ -13,7 +13,7 @@
|
|
13 |
|
14 |
export let color: string;
|
15 |
export let icon: string;
|
16 |
-
export let size: "sm" | "md" | "lg" = "md";
|
17 |
|
18 |
$: gradientColor = (() => {
|
19 |
switch (color) {
|
@@ -72,6 +72,8 @@
|
|
72 |
|
73 |
$: sizeClass = (() => {
|
74 |
switch (size) {
|
|
|
|
|
75 |
case "sm":
|
76 |
return "size-8";
|
77 |
case "md":
|
|
|
13 |
|
14 |
export let color: string;
|
15 |
export let icon: string;
|
16 |
+
export let size: "xs" | "sm" | "md" | "lg" = "md";
|
17 |
|
18 |
$: gradientColor = (() => {
|
19 |
switch (color) {
|
|
|
72 |
|
73 |
$: sizeClass = (() => {
|
74 |
switch (size) {
|
75 |
+
case "xs":
|
76 |
+
return "size-4";
|
77 |
case "sm":
|
78 |
return "size-8";
|
79 |
case "md":
|
src/lib/components/chat/ChatInput.svelte
CHANGED
@@ -2,12 +2,51 @@
|
|
2 |
import { browser } from "$app/environment";
|
3 |
import { createEventDispatcher, onMount } from "svelte";
|
4 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5 |
export let value = "";
|
6 |
-
export let minRows = 1;
|
7 |
-
export let maxRows: null | number = null;
|
8 |
export let placeholder = "";
|
|
|
9 |
export let disabled = false;
|
10 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
11 |
let textareaElement: HTMLTextAreaElement;
|
12 |
let isCompositionOn = false;
|
13 |
|
@@ -28,8 +67,14 @@
|
|
28 |
return /android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(userAgent);
|
29 |
}
|
30 |
|
31 |
-
|
32 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
33 |
|
34 |
function handleKeydown(event: KeyboardEvent) {
|
35 |
if (event.key === "Enter" && !event.shiftKey && !isCompositionOn) {
|
@@ -48,41 +93,209 @@
|
|
48 |
}
|
49 |
}
|
50 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
51 |
onMount(() => {
|
52 |
if (!isVirtualKeyboard()) {
|
53 |
textareaElement.focus();
|
54 |
}
|
|
|
55 |
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
56 |
</script>
|
57 |
|
58 |
-
<div class="
|
59 |
-
<
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
79 |
</div>
|
80 |
|
81 |
-
<style>
|
82 |
pre,
|
83 |
textarea {
|
84 |
font-family: inherit;
|
85 |
box-sizing: border-box;
|
86 |
line-height: 1.5;
|
87 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
88 |
</style>
|
|
|
2 |
import { browser } from "$app/environment";
|
3 |
import { createEventDispatcher, onMount } from "svelte";
|
4 |
|
5 |
+
import HoverTooltip from "$lib/components/HoverTooltip.svelte";
|
6 |
+
import IconInternet from "$lib/components/icons/IconInternet.svelte";
|
7 |
+
import IconImageGen from "$lib/components/icons/IconImageGen.svelte";
|
8 |
+
import IconPaperclip from "$lib/components/icons/IconPaperclip.svelte";
|
9 |
+
import { useSettingsStore } from "$lib/stores/settings";
|
10 |
+
import { webSearchParameters } from "$lib/stores/webSearchParameters";
|
11 |
+
import {
|
12 |
+
documentParserToolId,
|
13 |
+
fetchUrlToolId,
|
14 |
+
imageGenToolId,
|
15 |
+
webSearchToolId,
|
16 |
+
} from "$lib/utils/toolIds";
|
17 |
+
import type { Assistant } from "$lib/types/Assistant";
|
18 |
+
import { page } from "$app/stores";
|
19 |
+
import type { ToolFront } from "$lib/types/Tool";
|
20 |
+
import ToolLogo from "../ToolLogo.svelte";
|
21 |
+
import { goto } from "$app/navigation";
|
22 |
+
import { base } from "$app/paths";
|
23 |
+
import IconAdd from "~icons/carbon/add";
|
24 |
+
|
25 |
+
export let files: File[] = [];
|
26 |
+
export let mimeTypes: string[] = [];
|
27 |
+
|
28 |
export let value = "";
|
|
|
|
|
29 |
export let placeholder = "";
|
30 |
+
export let loading = false;
|
31 |
export let disabled = false;
|
32 |
|
33 |
+
export let assistant: Assistant | undefined = undefined;
|
34 |
+
|
35 |
+
export let modelHasTools = false;
|
36 |
+
export let modelIsMultimodal = false;
|
37 |
+
|
38 |
+
const onFileChange = async (e: Event) => {
|
39 |
+
if (!e.target) return;
|
40 |
+
const target = e.target as HTMLInputElement;
|
41 |
+
files = [...files, ...(target.files ?? [])];
|
42 |
+
|
43 |
+
if (files.some((file) => file.type.startsWith("application/"))) {
|
44 |
+
await settings.instantSet({
|
45 |
+
tools: [...($settings.tools ?? []), documentParserToolId],
|
46 |
+
});
|
47 |
+
}
|
48 |
+
};
|
49 |
+
|
50 |
let textareaElement: HTMLTextAreaElement;
|
51 |
let isCompositionOn = false;
|
52 |
|
|
|
67 |
return /android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(userAgent);
|
68 |
}
|
69 |
|
70 |
+
function adjustTextareaHeight() {
|
71 |
+
if (!textareaElement) return;
|
72 |
+
textareaElement.style.height = "auto";
|
73 |
+
const newHeight = Math.min(textareaElement.scrollHeight, parseInt("96em"));
|
74 |
+
textareaElement.style.height = `${newHeight}px`;
|
75 |
+
if (!textareaElement.parentElement) return;
|
76 |
+
textareaElement.parentElement.style.height = `${newHeight}px`;
|
77 |
+
}
|
78 |
|
79 |
function handleKeydown(event: KeyboardEvent) {
|
80 |
if (event.key === "Enter" && !event.shiftKey && !isCompositionOn) {
|
|
|
93 |
}
|
94 |
}
|
95 |
|
96 |
+
const settings = useSettingsStore();
|
97 |
+
|
98 |
+
// tool section
|
99 |
+
|
100 |
+
$: webSearchIsOn = modelHasTools
|
101 |
+
? ($settings.tools?.includes(webSearchToolId) ?? false) ||
|
102 |
+
($settings.tools?.includes(fetchUrlToolId) ?? false)
|
103 |
+
: $webSearchParameters.useSearch;
|
104 |
+
$: imageGenIsOn = $settings.tools?.includes(imageGenToolId) ?? false;
|
105 |
+
|
106 |
+
$: documentParserIsOn =
|
107 |
+
modelHasTools && files.length > 0 && files.some((file) => file.type.startsWith("application/"));
|
108 |
+
|
109 |
onMount(() => {
|
110 |
if (!isVirtualKeyboard()) {
|
111 |
textareaElement.focus();
|
112 |
}
|
113 |
+
adjustTextareaHeight();
|
114 |
});
|
115 |
+
|
116 |
+
$: extraTools = $page.data.tools
|
117 |
+
.filter((t: ToolFront) => $settings.tools?.includes(t._id))
|
118 |
+
.filter(
|
119 |
+
(t: ToolFront) =>
|
120 |
+
![documentParserToolId, imageGenToolId, webSearchToolId, fetchUrlToolId].includes(t._id)
|
121 |
+
) satisfies ToolFront[];
|
122 |
</script>
|
123 |
|
124 |
+
<div class="min-h-full flex-1" on:paste>
|
125 |
+
<div class="relative w-full min-w-0">
|
126 |
+
<textarea
|
127 |
+
enterkeyhint={!isVirtualKeyboard() ? "enter" : "send"}
|
128 |
+
tabindex="0"
|
129 |
+
rows="1"
|
130 |
+
class="scrollbar-custom max-h-[96em] w-full resize-none scroll-p-3 overflow-y-auto overflow-x-hidden border-0 bg-transparent px-3 py-2.5 outline-none focus:ring-0 focus-visible:ring-0 max-sm:p-2.5 max-sm:text-[16px]"
|
131 |
+
class:text-gray-400={disabled}
|
132 |
+
bind:value
|
133 |
+
bind:this={textareaElement}
|
134 |
+
{disabled}
|
135 |
+
on:keydown={handleKeydown}
|
136 |
+
on:compositionstart={() => (isCompositionOn = true)}
|
137 |
+
on:compositionend={() => (isCompositionOn = false)}
|
138 |
+
on:input={adjustTextareaHeight}
|
139 |
+
on:beforeinput
|
140 |
+
{placeholder}
|
141 |
+
/>
|
142 |
+
</div>
|
143 |
+
{#if !assistant}
|
144 |
+
<div
|
145 |
+
class="-ml-0.5 flex flex-wrap items-center justify-start gap-2.5 px-3 pb-2.5 text-gray-500 dark:text-gray-400"
|
146 |
+
>
|
147 |
+
<HoverTooltip
|
148 |
+
label="Search the web"
|
149 |
+
position="top"
|
150 |
+
TooltipClassNames="text-xs !text-left !w-auto whitespace-nowrap !py-1 !mb-0 max-sm:hidden {webSearchIsOn
|
151 |
+
? 'hidden'
|
152 |
+
: ''}"
|
153 |
+
>
|
154 |
+
<button
|
155 |
+
class="base-tool"
|
156 |
+
class:active-tool={webSearchIsOn}
|
157 |
+
disabled={loading}
|
158 |
+
on:click|preventDefault={async () => {
|
159 |
+
if (modelHasTools) {
|
160 |
+
if (webSearchIsOn) {
|
161 |
+
await settings.instantSet({
|
162 |
+
tools: ($settings.tools ?? []).filter(
|
163 |
+
(t) => t !== webSearchToolId && t !== fetchUrlToolId
|
164 |
+
),
|
165 |
+
});
|
166 |
+
} else {
|
167 |
+
await settings.instantSet({
|
168 |
+
tools: [...($settings.tools ?? []), webSearchToolId, fetchUrlToolId],
|
169 |
+
});
|
170 |
+
}
|
171 |
+
} else {
|
172 |
+
$webSearchParameters.useSearch = !webSearchIsOn;
|
173 |
+
}
|
174 |
+
}}
|
175 |
+
>
|
176 |
+
<IconInternet classNames="text-xl" />
|
177 |
+
{#if webSearchIsOn}
|
178 |
+
Search
|
179 |
+
{/if}
|
180 |
+
</button>
|
181 |
+
</HoverTooltip>
|
182 |
+
{#if modelHasTools}
|
183 |
+
<HoverTooltip
|
184 |
+
label="Generate images"
|
185 |
+
position="top"
|
186 |
+
TooltipClassNames="text-xs !text-left !w-auto whitespace-nowrap !py-1 !mb-0 max-sm:hidden {imageGenIsOn
|
187 |
+
? 'hidden'
|
188 |
+
: ''}"
|
189 |
+
>
|
190 |
+
<button
|
191 |
+
class="base-tool"
|
192 |
+
class:active-tool={imageGenIsOn}
|
193 |
+
disabled={loading}
|
194 |
+
on:click|preventDefault={async () => {
|
195 |
+
if (modelHasTools) {
|
196 |
+
if (imageGenIsOn) {
|
197 |
+
await settings.instantSet({
|
198 |
+
tools: ($settings.tools ?? []).filter((t) => t !== imageGenToolId),
|
199 |
+
});
|
200 |
+
} else {
|
201 |
+
await settings.instantSet({
|
202 |
+
tools: [...($settings.tools ?? []), imageGenToolId],
|
203 |
+
});
|
204 |
+
}
|
205 |
+
}
|
206 |
+
}}
|
207 |
+
>
|
208 |
+
<IconImageGen classNames="text-xl" />
|
209 |
+
{#if imageGenIsOn}
|
210 |
+
Image Gen
|
211 |
+
{/if}
|
212 |
+
</button>
|
213 |
+
</HoverTooltip>
|
214 |
+
{/if}
|
215 |
+
{#if modelHasTools}
|
216 |
+
{#each extraTools as tool}
|
217 |
+
<button
|
218 |
+
class="active-tool base-tool"
|
219 |
+
disabled={loading}
|
220 |
+
on:click|preventDefault={async () => {
|
221 |
+
goto(`${base}/tools/${tool._id}`);
|
222 |
+
}}
|
223 |
+
>
|
224 |
+
<ToolLogo icon={tool.icon} color={tool.color} size="xs" />
|
225 |
+
{tool.displayName}
|
226 |
+
</button>
|
227 |
+
{/each}
|
228 |
+
{/if}
|
229 |
+
{#if modelIsMultimodal || modelHasTools}
|
230 |
+
{@const mimeTypesString = mimeTypes
|
231 |
+
.map((m) => {
|
232 |
+
// if the mime type ends in *, grab the first part so image/* becomes image
|
233 |
+
if (m.endsWith("*")) {
|
234 |
+
return m.split("/")[0];
|
235 |
+
}
|
236 |
+
// otherwise, return the second part for example application/pdf becomes pdf
|
237 |
+
return m.split("/")[1];
|
238 |
+
})
|
239 |
+
.join(", ")}
|
240 |
+
<form class="flex items-center">
|
241 |
+
<HoverTooltip
|
242 |
+
label={`Upload ${mimeTypesString} files`}
|
243 |
+
position="top"
|
244 |
+
TooltipClassNames="text-xs !text-left !w-auto whitespace-nowrap !py-1 !mb-0 max-sm:hidden"
|
245 |
+
>
|
246 |
+
<button
|
247 |
+
class="base-tool relative"
|
248 |
+
class:active-tool={documentParserIsOn}
|
249 |
+
disabled={loading}
|
250 |
+
>
|
251 |
+
<input
|
252 |
+
class="absolute w-full cursor-pointer opacity-0"
|
253 |
+
aria-label="Upload file"
|
254 |
+
type="file"
|
255 |
+
on:change={onFileChange}
|
256 |
+
accept={mimeTypes.join(",")}
|
257 |
+
/>
|
258 |
+
<IconPaperclip classNames="text-xl" />
|
259 |
+
{#if documentParserIsOn}
|
260 |
+
Document Parser
|
261 |
+
{/if}
|
262 |
+
</button>
|
263 |
+
</HoverTooltip>
|
264 |
+
</form>
|
265 |
+
{/if}
|
266 |
+
{#if modelHasTools}
|
267 |
+
<HoverTooltip
|
268 |
+
label="Browse more tools"
|
269 |
+
position="right"
|
270 |
+
TooltipClassNames="text-xs !text-left !w-auto whitespace-nowrap !py-1 max-sm:hidden"
|
271 |
+
>
|
272 |
+
<a
|
273 |
+
class="base-tool flex !size-[20px] items-center justify-center rounded-full bg-white/10"
|
274 |
+
href={`${base}/tools`}
|
275 |
+
title="Browse more tools"
|
276 |
+
>
|
277 |
+
<IconAdd class="text-sm" />
|
278 |
+
</a>
|
279 |
+
</HoverTooltip>
|
280 |
+
{/if}
|
281 |
+
</div>
|
282 |
+
{/if}
|
283 |
+
<slot />
|
284 |
</div>
|
285 |
|
286 |
+
<style lang="postcss">
|
287 |
pre,
|
288 |
textarea {
|
289 |
font-family: inherit;
|
290 |
box-sizing: border-box;
|
291 |
line-height: 1.5;
|
292 |
}
|
293 |
+
|
294 |
+
.base-tool {
|
295 |
+
@apply flex h-[1.6rem] items-center gap-[.2rem] whitespace-nowrap text-xs outline-none transition-all hover:text-purple-600 focus:outline-none active:outline-none dark:hover:text-gray-300;
|
296 |
+
}
|
297 |
+
|
298 |
+
.active-tool {
|
299 |
+
@apply rounded-full bg-purple-500/15 pl-1 pr-2 text-purple-600 hover:text-purple-600 dark:bg-purple-600/40 dark:text-purple-300;
|
300 |
+
}
|
301 |
</style>
|
src/lib/components/chat/ChatWindow.svelte
CHANGED
@@ -13,13 +13,10 @@
|
|
13 |
import ChatInput from "./ChatInput.svelte";
|
14 |
import StopGeneratingBtn from "../StopGeneratingBtn.svelte";
|
15 |
import type { Model } from "$lib/types/Model";
|
16 |
-
import WebSearchToggle from "../WebSearchToggle.svelte";
|
17 |
-
import ToolsMenu from "../ToolsMenu.svelte";
|
18 |
import LoginModal from "../LoginModal.svelte";
|
19 |
import { page } from "$app/stores";
|
20 |
import FileDropzone from "./FileDropzone.svelte";
|
21 |
import RetryBtn from "../RetryBtn.svelte";
|
22 |
-
import UploadBtn from "../UploadBtn.svelte";
|
23 |
import file2base64 from "$lib/utils/file2base64";
|
24 |
import type { Assistant } from "$lib/types/Assistant";
|
25 |
import { base } from "$app/paths";
|
@@ -35,11 +32,11 @@
|
|
35 |
import { useConvTreeStore } from "$lib/stores/convTree";
|
36 |
import UploadedFile from "./UploadedFile.svelte";
|
37 |
import { useSettingsStore } from "$lib/stores/settings";
|
38 |
-
import type { ToolFront } from "$lib/types/Tool";
|
39 |
import ModelSwitch from "./ModelSwitch.svelte";
|
40 |
|
41 |
import { fly } from "svelte/transition";
|
42 |
import { cubicInOut } from "svelte/easing";
|
|
|
43 |
|
44 |
export let messages: Message[] = [];
|
45 |
export let loading = false;
|
@@ -224,18 +221,25 @@
|
|
224 |
|
225 |
const settings = useSettingsStore();
|
226 |
|
227 |
-
|
228 |
-
|
229 |
-
|
230 |
-
|
231 |
-
|
232 |
-
|
233 |
-
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
239 |
$: isFileUploadEnabled = activeMimeTypes.length > 0;
|
240 |
</script>
|
241 |
|
@@ -382,13 +386,6 @@
|
|
382 |
|
383 |
<div class="w-full">
|
384 |
<div class="flex w-full pb-3">
|
385 |
-
{#if !assistant}
|
386 |
-
{#if currentModel.tools}
|
387 |
-
<ToolsMenu {loading} />
|
388 |
-
{:else if $page.data.settings?.searchEnabled}
|
389 |
-
<WebSearchToggle />
|
390 |
-
{/if}
|
391 |
-
{/if}
|
392 |
{#if loading}
|
393 |
<StopGeneratingBtn classNames="ml-auto" on:click={() => dispatch("stop")} />
|
394 |
{:else if lastIsError}
|
@@ -404,9 +401,6 @@
|
|
404 |
/>
|
405 |
{:else}
|
406 |
<div class="ml-auto gap-2">
|
407 |
-
{#if isFileUploadEnabled}
|
408 |
-
<UploadBtn bind:files mimeTypes={activeMimeTypes} classNames="ml-auto" />
|
409 |
-
{/if}
|
410 |
{#if messages && lastMessage && lastMessage.interrupted && !isReadOnly}
|
411 |
<ContinueBtn
|
412 |
on:click={() => {
|
@@ -439,8 +433,12 @@
|
|
439 |
<ChatInput value="Sorry, something went wrong. Please try again." disabled={true} />
|
440 |
{:else}
|
441 |
<ChatInput
|
|
|
442 |
placeholder={isReadOnly ? "This conversation is read-only." : "Ask anything"}
|
|
|
443 |
bind:value={message}
|
|
|
|
|
444 |
on:submit={handleSubmit}
|
445 |
on:beforeinput={(ev) => {
|
446 |
if ($page.data.loginRequired) {
|
@@ -449,8 +447,9 @@
|
|
449 |
}
|
450 |
}}
|
451 |
on:paste={onPaste}
|
452 |
-
maxRows={6}
|
453 |
disabled={isReadOnly || lastIsError}
|
|
|
|
|
454 |
/>
|
455 |
{/if}
|
456 |
|
|
|
13 |
import ChatInput from "./ChatInput.svelte";
|
14 |
import StopGeneratingBtn from "../StopGeneratingBtn.svelte";
|
15 |
import type { Model } from "$lib/types/Model";
|
|
|
|
|
16 |
import LoginModal from "../LoginModal.svelte";
|
17 |
import { page } from "$app/stores";
|
18 |
import FileDropzone from "./FileDropzone.svelte";
|
19 |
import RetryBtn from "../RetryBtn.svelte";
|
|
|
20 |
import file2base64 from "$lib/utils/file2base64";
|
21 |
import type { Assistant } from "$lib/types/Assistant";
|
22 |
import { base } from "$app/paths";
|
|
|
32 |
import { useConvTreeStore } from "$lib/stores/convTree";
|
33 |
import UploadedFile from "./UploadedFile.svelte";
|
34 |
import { useSettingsStore } from "$lib/stores/settings";
|
|
|
35 |
import ModelSwitch from "./ModelSwitch.svelte";
|
36 |
|
37 |
import { fly } from "svelte/transition";
|
38 |
import { cubicInOut } from "svelte/easing";
|
39 |
+
import type { ToolFront } from "$lib/types/Tool";
|
40 |
|
41 |
export let messages: Message[] = [];
|
42 |
export let loading = false;
|
|
|
221 |
|
222 |
const settings = useSettingsStore();
|
223 |
|
224 |
+
$: mimeTypesFromActiveTools = $page.data.tools
|
225 |
+
.filter((tool: ToolFront) => {
|
226 |
+
if ($page.data?.assistant) {
|
227 |
+
return $page.data.assistant.tools?.includes(tool._id);
|
228 |
+
}
|
229 |
+
if (currentModel.tools) {
|
230 |
+
return $settings?.tools?.includes(tool._id) ?? tool.isOnByDefault;
|
231 |
+
}
|
232 |
+
return false;
|
233 |
+
})
|
234 |
+
.flatMap((tool: ToolFront) => tool.mimeTypes ?? []);
|
235 |
+
|
236 |
+
$: activeMimeTypes = Array.from(
|
237 |
+
new Set([
|
238 |
+
...mimeTypesFromActiveTools, // fetch mime types from active tools either from tool settings or active assistant
|
239 |
+
...(currentModel.tools && !$page.data.assistant ? ["application/pdf"] : []), // if its a tool model, we can always enable document parser so we always accept pdfs
|
240 |
+
...(currentModel.multimodal ? currentModel.multimodalAcceptedMimetypes ?? ["image/*"] : []), // if its a multimodal model, we always accept images
|
241 |
+
])
|
242 |
+
);
|
243 |
$: isFileUploadEnabled = activeMimeTypes.length > 0;
|
244 |
</script>
|
245 |
|
|
|
386 |
|
387 |
<div class="w-full">
|
388 |
<div class="flex w-full pb-3">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
389 |
{#if loading}
|
390 |
<StopGeneratingBtn classNames="ml-auto" on:click={() => dispatch("stop")} />
|
391 |
{:else if lastIsError}
|
|
|
401 |
/>
|
402 |
{:else}
|
403 |
<div class="ml-auto gap-2">
|
|
|
|
|
|
|
404 |
{#if messages && lastMessage && lastMessage.interrupted && !isReadOnly}
|
405 |
<ContinueBtn
|
406 |
on:click={() => {
|
|
|
433 |
<ChatInput value="Sorry, something went wrong. Please try again." disabled={true} />
|
434 |
{:else}
|
435 |
<ChatInput
|
436 |
+
{assistant}
|
437 |
placeholder={isReadOnly ? "This conversation is read-only." : "Ask anything"}
|
438 |
+
{loading}
|
439 |
bind:value={message}
|
440 |
+
bind:files
|
441 |
+
mimeTypes={activeMimeTypes}
|
442 |
on:submit={handleSubmit}
|
443 |
on:beforeinput={(ev) => {
|
444 |
if ($page.data.loginRequired) {
|
|
|
447 |
}
|
448 |
}}
|
449 |
on:paste={onPaste}
|
|
|
450 |
disabled={isReadOnly || lastIsError}
|
451 |
+
modelHasTools={currentModel.tools}
|
452 |
+
modelIsMultimodal={currentModel.multimodal}
|
453 |
/>
|
454 |
{/if}
|
455 |
|
src/lib/components/chat/FileDropzone.svelte
CHANGED
@@ -1,4 +1,6 @@
|
|
1 |
<script lang="ts">
|
|
|
|
|
2 |
import CarbonImage from "~icons/carbon/image";
|
3 |
// import EosIconsLoading from "~icons/eos-icons/loading";
|
4 |
|
@@ -8,6 +10,8 @@
|
|
8 |
export let onDrag = false;
|
9 |
export let onDragInner = false;
|
10 |
|
|
|
|
|
11 |
async function dropHandle(event: DragEvent) {
|
12 |
event.preventDefault();
|
13 |
if (event.dataTransfer && event.dataTransfer.items) {
|
@@ -32,7 +36,11 @@
|
|
32 |
);
|
33 |
})
|
34 |
) {
|
35 |
-
setErrorMsg(
|
|
|
|
|
|
|
|
|
36 |
files = [];
|
37 |
return;
|
38 |
}
|
@@ -46,6 +54,10 @@
|
|
46 |
|
47 |
// add the file to the files array
|
48 |
files = [...files, file];
|
|
|
|
|
|
|
|
|
49 |
}
|
50 |
}
|
51 |
onDrag = false;
|
|
|
1 |
<script lang="ts">
|
2 |
+
import { useSettingsStore } from "$lib/stores/settings";
|
3 |
+
import { documentParserToolId } from "$lib/utils/toolIds";
|
4 |
import CarbonImage from "~icons/carbon/image";
|
5 |
// import EosIconsLoading from "~icons/eos-icons/loading";
|
6 |
|
|
|
10 |
export let onDrag = false;
|
11 |
export let onDragInner = false;
|
12 |
|
13 |
+
const settings = useSettingsStore();
|
14 |
+
|
15 |
async function dropHandle(event: DragEvent) {
|
16 |
event.preventDefault();
|
17 |
if (event.dataTransfer && event.dataTransfer.items) {
|
|
|
36 |
);
|
37 |
})
|
38 |
) {
|
39 |
+
setErrorMsg(
|
40 |
+
`Some file type not supported. Only allowed: ${mimeTypes.join(
|
41 |
+
", "
|
42 |
+
)}. Uploaded document is of type ${file.type}`
|
43 |
+
);
|
44 |
files = [];
|
45 |
return;
|
46 |
}
|
|
|
54 |
|
55 |
// add the file to the files array
|
56 |
files = [...files, file];
|
57 |
+
|
58 |
+
settings.instantSet({
|
59 |
+
tools: [...($settings.tools ?? []), documentParserToolId],
|
60 |
+
});
|
61 |
}
|
62 |
}
|
63 |
onDrag = false;
|
src/lib/components/icons/IconImageGen.svelte
ADDED
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
export let classNames = "";
|
3 |
+
</script>
|
4 |
+
|
5 |
+
<svg
|
6 |
+
class={classNames}
|
7 |
+
xmlns="http://www.w3.org/2000/svg"
|
8 |
+
aria-hidden="true"
|
9 |
+
focusable="false"
|
10 |
+
role="img"
|
11 |
+
width="1em"
|
12 |
+
height="1em"
|
13 |
+
fill="currentColor"
|
14 |
+
preserveAspectRatio="xMidYMid meet"
|
15 |
+
viewBox="0 0 32 32"
|
16 |
+
>
|
17 |
+
<path
|
18 |
+
fill-rule="evenodd"
|
19 |
+
clip-rule="evenodd"
|
20 |
+
d="M6 4H26C26.5304 4 27.0391 4.21071 27.4142 4.58579C27.7893 4.96086 28 5.46957 28 6V26C28 26.5304 27.7893 27.0391 27.4142 27.4142C27.0391 27.7893 26.5304 28 26 28H17C16.4477 28 16 27.5523 16 27C16 26.4477 16.4477 26 17 26H26V24L21 19L19.41 20.59C19.0353 20.9625 18.5284 21.1716 18 21.1716C17.4716 21.1716 16.9647 20.9625 16.59 20.59L14 18L11.1229 15.122C10.7324 14.7315 10.7323 14.0984 11.1227 13.7078C11.5133 13.3169 12.1468 13.3168 12.5375 13.7075L18 19.17L19.59 17.58C19.9647 17.2075 20.4716 16.9984 21 16.9984C21.5284 16.9984 22.0353 17.2075 22.41 17.58L26 21.17V6H6V15C6 15.5523 5.55228 16 5 16C4.44772 16 4 15.5523 4 15V6C4 5.46957 4.21071 4.96086 4.58579 4.58579C4.96086 4.21071 5.46957 4 6 4ZM20.6667 13.4944C20.1734 13.8241 19.5933 14 19 14C18.2044 14 17.4413 13.6839 16.8787 13.1213C16.3161 12.5587 16 11.7957 16 11C16 10.4067 16.1759 9.82664 16.5056 9.33329C16.8352 8.83994 17.3038 8.45543 17.8519 8.22836C18.4001 8.0013 19.0033 7.94189 19.5853 8.05765C20.1672 8.1734 20.7018 8.45912 21.1213 8.87868C21.5409 9.29824 21.8266 9.83279 21.9424 10.4147C22.0581 10.9967 21.9987 11.5999 21.7716 12.1481C21.5446 12.6962 21.1601 13.1648 20.6667 13.4944ZM19.5556 10.1685C19.3911 10.0587 19.1978 10 19 10C18.7348 10 18.4804 10.1054 18.2929 10.2929C18.1054 10.4804 18 10.7348 18 11C18 11.1978 18.0586 11.3911 18.1685 11.5556C18.2784 11.72 18.4346 11.8482 18.6173 11.9239C18.8 11.9996 19.0011 12.0194 19.1951 11.9808C19.3891 11.9422 19.5673 11.847 19.7071 11.7071C19.847 11.5673 19.9422 11.3891 19.9808 11.1951C20.0194 11.0011 19.9996 10.8 19.9239 10.6173C19.8482 10.4346 19.72 10.2784 19.5556 10.1685ZM15.741 24.7381C15.739 24.886 15.6911 25.0295 15.6039 25.149C15.5167 25.2684 15.3946 25.3578 15.2543 25.4048L14.001 25.8181C13.6058 25.9535 13.2491 26.1822 12.961 26.4848C12.6607 26.7773 12.4325 27.1356 12.2943 27.5314L11.861 28.7781C11.8116 28.9156 11.7237 29.036 11.6077 29.1248C11.4847 29.2112 11.338 29.2578 11.1877 29.2581C11.0389 29.261 10.8934 29.2141 10.7743 29.1248C10.6527 29.0372 10.5617 28.9136 10.5143 28.7714L10.0943 27.5181C9.96432 27.1241 9.74415 26.7659 9.45134 26.4719C9.15854 26.1779 8.80117 25.9563 8.40768 25.8248L7.15435 25.4114C7.01338 25.3617 6.89039 25.2712 6.80101 25.1514C6.73433 25.0607 6.6903 24.9553 6.6726 24.8441C6.6549 24.7328 6.66404 24.619 6.69927 24.512C6.73449 24.405 6.79478 24.308 6.8751 24.2291C6.95542 24.1501 7.05345 24.0915 7.16101 24.0581L8.40768 23.6448C8.80559 23.5152 9.16742 23.2938 9.46391 22.9985C9.7604 22.7031 9.98322 22.3422 10.1143 21.9448L10.5277 20.7048C10.5681 20.5663 10.6523 20.4446 10.7677 20.3581C10.8823 20.263 11.0255 20.209 11.1743 20.2048C11.3261 20.1949 11.4766 20.2373 11.601 20.3248C11.7289 20.4059 11.8253 20.5282 11.8743 20.6714L12.2943 21.9381C12.4239 22.336 12.6453 22.6978 12.9407 22.9943C13.236 23.2908 13.5969 23.5136 13.9943 23.6448L15.241 24.0781C15.3813 24.122 15.5032 24.2111 15.5877 24.3314C15.6787 24.4484 15.7322 24.5902 15.741 24.7381ZM9.21774 20.2143C9.21583 20.351 9.17195 20.4838 9.09206 20.5947C9.01216 20.7056 8.90011 20.7892 8.77108 20.8343L7.87774 21.1277C7.62559 21.2166 7.39742 21.3626 7.21108 21.5543C7.02115 21.7421 6.87541 21.9699 6.78441 22.221L6.47108 23.1143C6.42814 23.2389 6.34927 23.3479 6.24441 23.4277C6.13091 23.5072 5.99633 23.5513 5.85774 23.5543C5.72027 23.5519 5.58689 23.5071 5.4759 23.4259C5.36491 23.3448 5.28172 23.2313 5.23774 23.101L4.94441 22.2077C4.85551 21.9555 4.70948 21.7273 4.51774 21.541C4.32993 21.3511 4.1022 21.2053 3.85108 21.1143L2.95108 20.8143C2.82059 20.7723 2.70793 20.6878 2.63108 20.5743C2.54797 20.4652 2.50343 20.3315 2.50441 20.1943C2.50531 20.0554 2.54959 19.9202 2.63108 19.8077C2.71438 19.7006 2.82833 19.6216 2.95774 19.581L3.85108 19.281C4.1022 19.19 4.32993 19.0443 4.51774 18.8543C4.71108 18.6697 4.85708 18.441 4.94441 18.1877L5.24441 17.3077C5.28436 17.1815 5.36095 17.0701 5.46441 16.9877C5.57091 16.9036 5.70206 16.8567 5.83774 16.8543C5.97522 16.8428 6.11231 16.8806 6.22441 16.961C6.33915 17.0368 6.42764 17.1463 6.47774 17.2743L6.77774 18.1877C6.86774 18.4397 7.01374 18.6677 7.20441 18.8543C7.39222 19.0443 7.61995 19.19 7.87108 19.281L8.77108 19.5943C8.89656 19.6412 9.00502 19.7248 9.08232 19.8342C9.15961 19.9436 9.20216 20.0737 9.20441 20.2077L9.21774 20.2143Z"
|
21 |
+
fill="currentColor"
|
22 |
+
/>
|
23 |
+
</svg>
|
src/lib/components/icons/IconInternet.svelte
CHANGED
@@ -12,17 +12,12 @@
|
|
12 |
height="1em"
|
13 |
fill="currentColor"
|
14 |
preserveAspectRatio="xMidYMid meet"
|
15 |
-
viewBox="0 0
|
16 |
>
|
17 |
-
|
18 |
fill-rule="evenodd"
|
19 |
-
d="M1.5 10a8.5 8.5 0 1 0 17 0a8.5 8.5 0 0 0-17 0m16 0a7.5 7.5 0 1 1-15 0a7.5 7.5 0 0 1 15 0"
|
20 |
clip-rule="evenodd"
|
21 |
-
|
22 |
-
fill
|
23 |
-
|
24 |
-
|
25 |
-
/><path
|
26 |
-
d="m3.735 5.312l.67-.742c.107.096.221.19.343.281c1.318.988 3.398 1.59 5.665 1.59c1.933 0 3.737-.437 5.055-1.19a5.59 5.59 0 0 0 .857-.597l.65.76c-.298.255-.636.49-1.01.704c-1.477.845-3.452 1.323-5.552 1.323c-2.47 0-4.762-.663-6.265-1.79a5.81 5.81 0 0 1-.413-.34m0 9.389l.67.74c.107-.096.221-.19.343-.28c1.318-.988 3.398-1.59 5.665-1.59c1.933 0 3.737.436 5.055 1.19c.321.184.608.384.857.596l.65-.76a6.583 6.583 0 0 0-1.01-.704c-1.477-.844-3.452-1.322-5.552-1.322c-2.47 0-4.762.663-6.265 1.789c-.146.11-.284.223-.413.34M2 10.5v-1h16v1z"
|
27 |
-
/></svg
|
28 |
-
>
|
|
|
12 |
height="1em"
|
13 |
fill="currentColor"
|
14 |
preserveAspectRatio="xMidYMid meet"
|
15 |
+
viewBox="0 0 32 32"
|
16 |
>
|
17 |
+
<path
|
18 |
fill-rule="evenodd"
|
|
|
19 |
clip-rule="evenodd"
|
20 |
+
d="M10.5169 17.0606C10.6548 20.6396 11.4841 23.7792 12.7062 25.8143C8.92688 24.5465 6.12113 21.1568 5.70417 17.0606H10.5169ZM12.4184 17.0606C12.5317 19.7958 13.0765 22.1801 13.8416 23.8831C14.7847 25.9823 15.6952 26.3495 15.9998 26.3495C16.3044 26.3495 17.215 25.9823 18.158 23.8831C18.9231 22.1801 19.4679 19.7958 19.5812 17.0606H12.4184ZM19.5894 15.1606H12.4102C12.5026 12.3328 13.0559 9.86568 13.8416 8.11691C14.7847 6.0177 15.6952 5.65049 15.9998 5.65049C16.3044 5.65049 17.215 6.0177 18.158 8.11691C18.9437 9.86568 19.4971 12.3328 19.5894 15.1606ZM21.4827 17.0606C21.3448 20.6397 20.5154 23.7793 19.2934 25.8145C23.0729 24.5468 25.8789 21.157 26.2958 17.0606H21.4827ZM26.316 15.1606H21.4903C21.3787 11.4898 20.5406 8.2625 19.2934 6.1855C23.1407 7.47598 25.9792 10.9654 26.316 15.1606ZM10.5093 15.1606H5.68404C6.02075 10.9655 8.85906 7.47627 12.7062 6.18566C11.459 8.26267 10.6209 11.4899 10.5093 15.1606ZM3.75049 16C3.75049 9.23633 9.23227 3.753 15.9954 3.75049H15.9998H16C22.7652 3.75049 28.2495 9.23478 28.2495 16C28.2495 22.7652 22.7652 28.2495 16 28.2495C9.23478 28.2495 3.75049 22.7652 3.75049 16Z"
|
21 |
+
fill="currentColor"
|
22 |
+
/>
|
23 |
+
</svg>
|
|
|
|
|
|
|
|
src/lib/components/icons/IconPaperclip.svelte
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
export let classNames = "";
|
3 |
+
</script>
|
4 |
+
|
5 |
+
<svg
|
6 |
+
class={classNames}
|
7 |
+
xmlns="http://www.w3.org/2000/svg"
|
8 |
+
aria-hidden="true"
|
9 |
+
focusable="false"
|
10 |
+
role="img"
|
11 |
+
width="1em"
|
12 |
+
height="1em"
|
13 |
+
fill="currentColor"
|
14 |
+
preserveAspectRatio="xMidYMid meet"
|
15 |
+
viewBox="0 0 32 32"
|
16 |
+
><path
|
17 |
+
d="M19.02 5.57a5.77 5.77 0 1 1 8.56 7.74L16.6 25.45l-.02.01v.01A7.87 7.87 0 0 1 4.92 14.9L12.95 6A1.18 1.18 0 0 1 14.7 7.6l-8.03 8.87a5.51 5.51 0 1 0 8.19 7.4l10.97-12.14a3.41 3.41 0 1 0-5.06-4.58l-9.32 10.3a1.27 1.27 0 1 0 1.88 1.7l6.28-6.94a1.18 1.18 0 0 1 1.75 1.59l-6.28 6.94a3.63 3.63 0 0 1-5.41-4.83l.02-.02 9.33-10.32Z"
|
18 |
+
fill="currentColor"
|
19 |
+
/></svg
|
20 |
+
>
|
src/lib/server/textGeneration/tools.ts
CHANGED
@@ -185,18 +185,21 @@ export async function* runTools(
|
|
185 |
: messages;
|
186 |
|
187 |
let rawText = "";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
188 |
// do the function calling bits here
|
189 |
for await (const output of await endpoint({
|
190 |
messages: formattedMessages,
|
191 |
preprompt,
|
192 |
generateSettings: { temperature: 0.1, ...assistant?.generateSettings },
|
193 |
-
tools:
|
194 |
-
...tool,
|
195 |
-
inputs: tool.inputs.map((input) => ({
|
196 |
-
...input,
|
197 |
-
type: input.type === "file" ? "str" : input.type,
|
198 |
-
})),
|
199 |
-
})),
|
200 |
conversationId: conv._id,
|
201 |
})) {
|
202 |
// model natively supports tool calls
|
|
|
185 |
: messages;
|
186 |
|
187 |
let rawText = "";
|
188 |
+
|
189 |
+
const mappedTools = tools.map((tool) => ({
|
190 |
+
...tool,
|
191 |
+
inputs: tool.inputs.map((input) => ({
|
192 |
+
...input,
|
193 |
+
type: input.type === "file" ? "str" : input.type,
|
194 |
+
})),
|
195 |
+
}));
|
196 |
+
|
197 |
// do the function calling bits here
|
198 |
for await (const output of await endpoint({
|
199 |
messages: formattedMessages,
|
200 |
preprompt,
|
201 |
generateSettings: { temperature: 0.1, ...assistant?.generateSettings },
|
202 |
+
tools: mappedTools,
|
|
|
|
|
|
|
|
|
|
|
|
|
203 |
conversationId: conv._id,
|
204 |
})) {
|
205 |
// model natively supports tool calls
|
src/lib/types/Tool.ts
CHANGED
@@ -146,7 +146,10 @@ export type CommunityToolEditable = Omit<
|
|
146 |
|
147 |
export type Tool = ConfigTool | CommunityTool;
|
148 |
|
149 |
-
export type ToolFront = Pick<
|
|
|
|
|
|
|
150 |
_id: string;
|
151 |
isOnByDefault: boolean;
|
152 |
isLocked: boolean;
|
|
|
146 |
|
147 |
export type Tool = ConfigTool | CommunityTool;
|
148 |
|
149 |
+
export type ToolFront = Pick<
|
150 |
+
Tool,
|
151 |
+
"type" | "name" | "displayName" | "description" | "color" | "icon"
|
152 |
+
> & {
|
153 |
_id: string;
|
154 |
isOnByDefault: boolean;
|
155 |
isLocked: boolean;
|
src/lib/utils/toolIds.ts
ADDED
@@ -0,0 +1,4 @@
|
|
|
|
|
|
|
|
|
|
|
1 |
+
export const webSearchToolId = "00000000000000000000000a";
|
2 |
+
export const fetchUrlToolId = "00000000000000000000000b";
|
3 |
+
export const imageGenToolId = "000000000000000000000001";
|
4 |
+
export const documentParserToolId = "000000000000000000000002";
|
src/routes/+layout.server.ts
CHANGED
@@ -250,6 +250,8 @@ export const load: LayoutServerLoad = async ({ locals, depends }) => {
|
|
250 |
toolUseDuration.find(
|
251 |
(el) => el.labels.tool === tool._id.toString() && el.labels.quantile === 0.9
|
252 |
)?.value ?? 15_000,
|
|
|
|
|
253 |
} satisfies ToolFront)
|
254 |
),
|
255 |
communityToolCount: await collections.tools.countDocuments({
|
|
|
250 |
toolUseDuration.find(
|
251 |
(el) => el.labels.tool === tool._id.toString() && el.labels.quantile === 0.9
|
252 |
)?.value ?? 15_000,
|
253 |
+
color: tool.color,
|
254 |
+
icon: tool.icon,
|
255 |
} satisfies ToolFront)
|
256 |
),
|
257 |
communityToolCount: await collections.tools.countDocuments({
|
src/routes/conversation/[id]/+server.ts
CHANGED
@@ -25,6 +25,7 @@ import { MetricsServer } from "$lib/server/metrics";
|
|
25 |
import { textGeneration } from "$lib/server/textGeneration";
|
26 |
import type { TextGenerationContext } from "$lib/server/textGeneration/types";
|
27 |
import { logger } from "$lib/server/logger.js";
|
|
|
28 |
|
29 |
export async function POST({ request, locals, params, getClientAddress }) {
|
30 |
const id = z.string().parse(params.id);
|
@@ -190,6 +191,14 @@ export async function POST({ request, locals, params, getClientAddress }) {
|
|
190 |
})
|
191 |
);
|
192 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
193 |
if (usageLimits?.messageLength && (newPrompt?.length ?? 0) > usageLimits.messageLength) {
|
194 |
error(400, "Message too long.");
|
195 |
}
|
@@ -442,7 +451,10 @@ export async function POST({ request, locals, params, getClientAddress }) {
|
|
442 |
assistant: undefined,
|
443 |
isContinue: isContinue ?? false,
|
444 |
webSearch: webSearch ?? false,
|
445 |
-
toolsPreference:
|
|
|
|
|
|
|
446 |
promptedAt,
|
447 |
ip: getClientAddress(),
|
448 |
username: locals.user?.username,
|
|
|
25 |
import { textGeneration } from "$lib/server/textGeneration";
|
26 |
import type { TextGenerationContext } from "$lib/server/textGeneration/types";
|
27 |
import { logger } from "$lib/server/logger.js";
|
28 |
+
import { documentParserToolId } from "$lib/utils/toolIds.js";
|
29 |
|
30 |
export async function POST({ request, locals, params, getClientAddress }) {
|
31 |
const id = z.string().parse(params.id);
|
|
|
191 |
})
|
192 |
);
|
193 |
|
194 |
+
// Check for PDF files in the input
|
195 |
+
const hasPdfFiles = inputFiles?.some((file) => file.mime === "application/pdf") ?? false;
|
196 |
+
|
197 |
+
// Check for existing PDF files in the conversation
|
198 |
+
const hasPdfInConversation =
|
199 |
+
conv.messages?.some((msg) => msg.files?.some((file) => file.mime === "application/pdf")) ??
|
200 |
+
false;
|
201 |
+
|
202 |
if (usageLimits?.messageLength && (newPrompt?.length ?? 0) > usageLimits.messageLength) {
|
203 |
error(400, "Message too long.");
|
204 |
}
|
|
|
451 |
assistant: undefined,
|
452 |
isContinue: isContinue ?? false,
|
453 |
webSearch: webSearch ?? false,
|
454 |
+
toolsPreference: [
|
455 |
+
...(toolsPreferences ?? []),
|
456 |
+
...(hasPdfFiles || hasPdfInConversation ? [documentParserToolId] : []), // Add document parser tool if PDF files are present
|
457 |
+
],
|
458 |
promptedAt,
|
459 |
ip: getClientAddress(),
|
460 |
username: locals.user?.username,
|