Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add websearch controls for assistants #812

Merged
merged 50 commits into from Mar 14, 2024
Merged
Show file tree
Hide file tree
Changes from 40 commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
6f7b200
remove query modifiers from generateQuery
nsarrazin Feb 10, 2024
6ba10db
Add backend for assistant RAG
nsarrazin Feb 10, 2024
a200884
Add front-end for updating RAG assistant
nsarrazin Feb 10, 2024
59066f2
enable web parser to return plaintext directly for matching headers
nsarrazin Feb 10, 2024
42b3d29
Update websearch flow for handling assistant rag preferences
nsarrazin Feb 10, 2024
df0db81
Add our old blocklist to .env.template
nsarrazin Feb 10, 2024
384b52c
Enable websearch to run on messages depending on assistant requirements
nsarrazin Feb 10, 2024
6778a6e
Merge branch 'main' into feature/rag_on_assistant
nsarrazin Feb 10, 2024
ba39acb
Merge branch 'main' into feature/rag_on_assistant
mishig25 Feb 15, 2024
059795d
reorganized imports
nsarrazin Feb 15, 2024
fff5e21
Rename vars
nsarrazin Feb 19, 2024
465b817
use projection
nsarrazin Feb 26, 2024
873133c
Add environment variable for assistant rag
nsarrazin Feb 26, 2024
268b8f4
Merge branch 'main' into feature/rag_on_assistant
nsarrazin Feb 26, 2024
620b58e
fix assistant rag on runwebsearch
nsarrazin Feb 26, 2024
5eb3c03
Merge branch 'main' into feature/rag_on_assistant
nsarrazin Feb 26, 2024
af8b3bf
fix styling if rag is disabled
nsarrazin Feb 26, 2024
2fbb28f
make sure we always omit credentials when fetching web pages
nsarrazin Feb 26, 2024
920a72e
Add new checks for SSRF, with a new env var `ENABLE_LOCAL_FETCH`
nsarrazin Feb 26, 2024
fc29655
Use DNS to check if the links are local or not
nsarrazin Mar 4, 2024
37fb70b
Merge branch 'main' into feature/rag_on_assistant
nsarrazin Mar 4, 2024
70d7c16
Merge branch 'main' into feature/rag_on_assistant
nsarrazin Mar 6, 2024
52e010c
Merge branch 'main' into feature/rag_on_assistant
nsarrazin Mar 6, 2024
b73a99f
Add a websearch indicator
nsarrazin Mar 7, 2024
c4eb1d4
Merge branch 'main' into feature/rag_on_assistant
nsarrazin Mar 11, 2024
9be64c6
Add more tags to parser
nsarrazin Mar 12, 2024
58dc4ec
Add indicators
nsarrazin Mar 12, 2024
eb23dc7
Display RAG options in settings view
nsarrazin Mar 12, 2024
0398ec6
ui
gary149 Mar 13, 2024
c6ffb01
fix rag detection
nsarrazin Mar 13, 2024
877d3cb
bit more spacing
nsarrazin Mar 13, 2024
5164deb
fix button position in assistant form
nsarrazin Mar 13, 2024
f34bed4
wording (mainly)
gary149 Mar 13, 2024
dfe3481
reduce number of tags
nsarrazin Mar 13, 2024
6a7c3b0
Bump max URLs from 3 to 10
nsarrazin Mar 14, 2024
6f822dc
add ul and ol to parseWeb
gary149 Mar 14, 2024
b8ba360
change splitting string
nsarrazin Mar 14, 2024
e6cd56d
link style
gary149 Mar 14, 2024
aab0c39
wording
gary149 Mar 14, 2024
039e3c4
add feedback link
gary149 Mar 14, 2024
822c3f3
Update src/routes/settings/(nav)/assistants/[assistantId]/+page.svelte
nsarrazin Mar 14, 2024
2c44a6f
Update src/routes/settings/(nav)/assistants/[assistantId]/+page.svelte
nsarrazin Mar 14, 2024
2314b24
Update src/routes/assistants/+page.svelte
nsarrazin Mar 14, 2024
940edd0
Update src/routes/settings/(nav)/assistants/[assistantId]/+page.svelte
nsarrazin Mar 14, 2024
a693c07
Update src/lib/components/chat/ChatWindow.svelte
nsarrazin Mar 14, 2024
a68ae9f
Update src/routes/settings/(nav)/assistants/[assistantId]/+page.svelte
nsarrazin Mar 14, 2024
ebaafaa
Update src/lib/components/AssistantSettings.svelte
nsarrazin Mar 14, 2024
bf2cb52
lint
nsarrazin Mar 14, 2024
1aac902
throw error if not a string
nsarrazin Mar 14, 2024
0760cbf
simplify rag check
nsarrazin Mar 14, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion .env
Expand Up @@ -135,7 +135,8 @@ EXPOSE_API=true
# PUBLIC_APP_DISCLAIMER=1

ENABLE_ASSISTANTS=false #set to true to enable assistants feature

ENABLE_ASSISTANTS_RAG=false # /!\ This will let users specify arbitrary URLs that the server will then request. Make sure you have the proper firewall rules in place.
ENABLE_LOCAL_FETCH=false #set to true to disable the blocklist for local fetches. Only enable this if you have the proper firewall rules to prevent SSRF attacks and understand the implications.
ALTERNATIVE_REDIRECT_URLS=`[]` #valide alternative redirect URL for OAuth

WEBHOOK_URL_REPORT_ASSISTANT=#provide webhook url to get notified when an assistant gets reported
Expand Down
3 changes: 3 additions & 0 deletions .env.template
Expand Up @@ -277,8 +277,11 @@ PUBLIC_PLAUSIBLE_SCRIPT_URL="/js/script.js"
# XFF_DEPTH=2

ENABLE_ASSISTANTS=true
ENABLE_ASSISTANTS_RAG=true
EXPOSE_API=true

ALTERNATIVE_REDIRECT_URLS=`[
huggingchat://login/callback
]`

WEBSEARCH_BLOCKLIST=`["youtube.com", "twitter.com"]`
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Expand Up @@ -62,6 +62,7 @@
"handlebars": "^4.7.8",
"highlight.js": "^11.7.0",
"image-size": "^1.0.2",
"ip-address": "^9.0.5",
"jsdom": "^22.0.0",
"json5": "^2.2.3",
"marked": "^4.3.0",
Expand Down
145 changes: 139 additions & 6 deletions src/lib/components/AssistantSettings.svelte
Expand Up @@ -5,11 +5,13 @@

import { onMount } from "svelte";
import { applyAction, enhance } from "$app/forms";
import { page } from "$app/stores";
import { base } from "$app/paths";
import CarbonPen from "~icons/carbon/pen";
import CarbonUpload from "~icons/carbon/upload";

import { useSettingsStore } from "$lib/stores/settings";
import { isHuggingChat } from "$lib/utils/isHuggingChat";

type ActionData = {
error: boolean;
Expand Down Expand Up @@ -71,6 +73,14 @@
let deleteExistingAvatar = false;

let loading = false;

let ragMode: false | "links" | "domains" | "all" = assistant?.rag?.allowAllDomains
? "all"
: assistant?.rag?.allowedLinks?.length ?? 0 > 0
? "links"
: (assistant?.rag?.allowedDomains?.length ?? 0) > 0
? "domains"
: false;
</script>

<form
Expand Down Expand Up @@ -103,6 +113,24 @@
}
}

formData.delete("ragMode");

if (ragMode === false || !$page.data.enableAssistantsRAG) {
formData.set("ragAllowAll", "false");
formData.set("ragLinkList", "");
formData.set("ragDomainList", "");
} else if (ragMode === "all") {
formData.set("ragAllowAll", "true");
formData.set("ragLinkList", "");
formData.set("ragDomainList", "");
} else if (ragMode === "links") {
formData.set("ragAllowAll", "false");
formData.set("ragDomainList", "");
} else if (ragMode === "domains") {
formData.set("ragAllowAll", "false");
formData.set("ragLinkList", "");
}

return async ({ result }) => {
loading = false;
await applyAction(result);
Expand All @@ -126,7 +154,7 @@
{/if}

<div class="grid h-full w-full flex-1 grid-cols-2 gap-6 text-sm max-sm:grid-cols-1">
<div class="flex flex-col gap-4">
<div class="col-span-1 flex flex-col gap-4">
<div>
<div class="mb-1 block pb-2 text-sm font-semibold">Avatar</div>
<input
Expand Down Expand Up @@ -255,21 +283,126 @@
</div>
<p class="text-xs text-red-500">{getError("inputMessage1", form)}</p>
</label>
{#if $page.data.enableAssistantsRAG}
<div class="mb-4 flex flex-col flex-nowrap">
<span class="mt-2 text-smd font-semibold"
>Internet access <span
class="ml-1 rounded bg-gray-100 px-1 py-0.5 text-xxs font-normal text-gray-600"
>Experimental</span
>

{#if isHuggingChat}
<a
href="https://huggingface.co/spaces/huggingchat/chat-ui/discussions/385"
target="_blank"
class="ml-0.5 rounded bg-gray-100 px-1 py-0.5 text-xxs font-normal text-gray-700 underline decoration-gray-400"
>Give feedback</a
>
{/if}
</span>

<label class="mt-1">
<input
checked={!ragMode}
on:change={() => (ragMode = false)}
type="radio"
name="ragMode"
value={false}
/>
<span class="my-2 text-sm" class:font-semibold={!ragMode}> Disabled </span>
{#if !ragMode}
<span class="block text-xs text-gray-500">
Assistant won't look for information on the web and will be faster to answer.
nsarrazin marked this conversation as resolved.
Show resolved Hide resolved
</span>
{/if}
</label>

<label class="mt-1">
<input
checked={ragMode === "all"}
on:change={() => (ragMode = "all")}
type="radio"
name="ragMode"
value={"all"}
/>
<span class="my-2 text-sm" class:font-semibold={ragMode === "all"}> Enabled </span>
{#if ragMode === "all"}
<span class="block text-xs text-gray-500">
Assistant will do a web search on each user request to find information.
</span>
{/if}
</label>

<label class="mt-1">
<input
checked={ragMode === "domains"}
on:change={() => (ragMode = "domains")}
type="radio"
name="ragMode"
value={false}
/>
<span class="my-2 text-sm" class:font-semibold={ragMode === "domains"}>
Domains search
</span>
</label>
{#if ragMode === "domains"}
<span class="mb-2 text-xs text-gray-500">
Specify domains and URLs that the application can search, separated by commas.
</span>

<input
name="ragDomainList"
class="w-full rounded-lg border-2 border-gray-200 bg-gray-100 p-2"
placeholder="wikipedia.org,bbc.com"
value={assistant?.rag?.allowedDomains?.join(",") ?? ""}
/>
<p class="text-xs text-red-500">{getError("ragDomainList", form)}</p>
{/if}

<label class="mt-1">
<input
checked={ragMode === "links"}
on:change={() => (ragMode = "links")}
type="radio"
name="ragMode"
value={false}
/>
<span class="my-2 text-sm" class:font-semibold={ragMode === "links"}>
Specific Links
</span>
</label>
{#if ragMode === "links"}
<span class="mb-2 text-xs text-gray-500">
Specify a maximum of 10 direct URLs that the Assistant will access. HTML & Plain Text
only, separated by commas
</span>
<input
name="ragLinkList"
class="w-full rounded-lg border-2 border-gray-200 bg-gray-100 p-2"
placeholder="https://raw.githubusercontent.com/huggingface/chat-ui/main/README.md"
value={assistant?.rag?.allowedLinks.join(",") ?? ""}
/>
<p class="text-xs text-red-500">{getError("ragLinkList", form)}</p>
{/if}
</div>
{/if}
</div>

<label class="flex flex-col">
<div class="mb-1 text-sm font-semibold">Instructions (system prompt)</div>
<div class="col-span-1 flex h-full flex-col">
<span class="mb-1 text-sm font-semibold"> Instructions (system prompt) </span>
<textarea
name="preprompt"
class="min-h-[8lh] flex-1 rounded-lg border-2 border-gray-200 bg-gray-100 p-2 text-sm"
class="mb-20 min-h-[8lh] flex-1 rounded-lg border-2 border-gray-200 bg-gray-100 p-2 text-sm"
placeholder="You'll act as..."
value={assistant?.preprompt ?? ""}
/>
<p class="text-xs text-red-500">{getError("preprompt", form)}</p>
</label>
</div>
</div>

<div class="mt-6 flex justify-end gap-2">
<div
class="ml-auto mt-6 flex w-fit justify-end gap-2 max-sm:fixed max-sm:bottom-6 max-sm:right-6"
>
<a
href={assistant ? `${base}/settings/assistants/${assistant?._id}` : `${base}/settings`}
class="flex items-center justify-center rounded-full bg-gray-200 px-5 py-2 font-semibold text-gray-600"
Expand Down
36 changes: 31 additions & 5 deletions src/lib/components/chat/AssistantIntroduction.svelte
Expand Up @@ -3,21 +3,34 @@
import IconGear from "~icons/bi/gear-fill";
import { base } from "$app/paths";
import type { Assistant } from "$lib/types/Assistant";
import IconInternet from "../icons/IconInternet.svelte";

export let assistant: Pick<
Assistant,
"avatar" | "name" | "modelId" | "createdByName" | "exampleInputs" | "_id" | "description"
| "avatar"
| "name"
| "rag"
| "modelId"
| "createdByName"
| "exampleInputs"
| "_id"
| "description"
>;

const dispatch = createEventDispatcher<{ message: string }>();

$: hasRag =
assistant?.rag?.allowAllDomains ||
(assistant?.rag?.allowedDomains?.length ?? 0) > 0 ||
(assistant?.rag?.allowedLinks?.length ?? 0) > 0;
</script>

<div class="flex h-full w-full flex-col content-center items-center justify-center pb-52">
<div
class="relative mt-auto rounded-2xl bg-gray-100 text-gray-600 dark:border-gray-800 dark:bg-gray-800/60 dark:text-gray-300"
>
<div
class="flex min-w-[80dvw] items-center gap-4 p-4 pr-1 sm:min-w-[440px] md:p-8 md:pt-10 xl:gap-8"
class="mt-3 flex min-w-[80dvw] items-center gap-4 p-4 pr-1 sm:min-w-[440px] md:p-8 md:pt-10 xl:gap-8"
>
{#if assistant.avatar}
<img
Expand All @@ -39,9 +52,21 @@
<p class="-mb-1">Assistant</p>

<p class="text-xl font-bold sm:text-2xl">{assistant.name}</p>
<p class="line-clamp-6 text-sm text-gray-500 dark:text-gray-400">
{assistant.description}
</p>
{#if assistant.description}
<p class="line-clamp-6 text-sm text-gray-500 dark:text-gray-400">
{assistant.description}
</p>
{/if}

{#if hasRag}
<div
class="flex h-5 w-fit items-center gap-1 rounded-full bg-blue-500/10 pl-1 pr-2 text-xs"
title="This assistant uses the websearch."
>
<IconInternet classNames="text-sm text-blue-600" />
Has internet access
</div>
{/if}

{#if assistant.createdByName}
<p class="pt-2 text-sm text-gray-400 dark:text-gray-500">
Expand All @@ -55,6 +80,7 @@
{/if}
</div>
</div>

<div class="absolute right-3 top-3 md:right-4 md:top-4">
<a
href="{base}/settings/assistants/{assistant._id.toString()}"
Expand Down
2 changes: 1 addition & 1 deletion src/lib/components/chat/ChatWindow.svelte
Expand Up @@ -138,7 +138,7 @@
bind:this={chatContainer}
>
<div class="mx-auto flex h-full max-w-3xl flex-col gap-6 px-5 pt-6 sm:gap-8 xl:max-w-4xl">
{#if $page.data?.assistant}
{#if $page.data?.assistant && messages.length > 0}
nsarrazin marked this conversation as resolved.
Show resolved Hide resolved
<a
class="mx-auto flex items-center gap-1.5 rounded-full border border-gray-100 bg-gray-50 py-1 pl-1 pr-3 text-sm text-gray-800 hover:bg-gray-100 dark:border-gray-800 dark:bg-gray-800 dark:text-gray-200 dark:hover:bg-gray-700"
href="{base}/settings/assistants/{$page.data.assistant._id}"
Expand Down
1 change: 1 addition & 0 deletions src/lib/components/icons/IconInternet.svelte
Expand Up @@ -10,6 +10,7 @@
role="img"
width="1em"
height="1em"
fill="currentColor"
preserveAspectRatio="xMidYMid meet"
viewBox="0 0 20 20"
>
Expand Down
31 changes: 31 additions & 0 deletions src/lib/server/isURLLocal.spec.ts
@@ -0,0 +1,31 @@
import { isURLLocal } from "./isURLLocal";
import { describe, expect, it } from "vitest";

describe("isURLLocal", async () => {
it("should return true for localhost", async () => {
expect(await isURLLocal(new URL("http://localhost"))).toBe(true);
});
it("should return true for 127.0.0.1", async () => {
expect(await isURLLocal(new URL("http://127.0.0.1"))).toBe(true);
});
it("should return true for 127.254.254.254", async () => {
expect(await isURLLocal(new URL("http://127.254.254.254"))).toBe(true);
});
it("should return false for huggingface.co", async () => {
expect(await isURLLocal(new URL("https://huggingface.co/"))).toBe(false);
});
it("should return true for 127.0.0.1.nip.io", async () => {
expect(await isURLLocal(new URL("http://127.0.0.1.nip.io"))).toBe(true);
});
it("should fail on ipv6", async () => {
await expect(isURLLocal(new URL("http://[::1]"))).rejects.toThrow();
});
it("should fail on ipv6 --1.sslip.io", async () => {
await expect(isURLLocal(new URL("http://--1.sslip.io"))).rejects.toThrow();
});
it("should fail on invalid domain names", async () => {
await expect(
isURLLocal(new URL("http://34329487239847329874923948732984.com/"))
).rejects.toThrow();
});
});
26 changes: 26 additions & 0 deletions src/lib/server/isURLLocal.ts
@@ -0,0 +1,26 @@
import { Address6, Address4 } from "ip-address";

import dns from "node:dns";

export async function isURLLocal(URL: URL): Promise<boolean> {
const isLocal = new Promise<boolean>((resolve, reject) => {
dns.lookup(URL.hostname, (err, address, family) => {
if (err) {
reject(err);
}
if (family === 4) {
const addr = new Address4(address);
resolve(addr.isInSubnet(new Address4("127.0.0.0/8")));
} else if (family === 6) {
const addr = new Address6(address);
resolve(
addr.isLoopback() || addr.isInSubnet(new Address6("::1/128")) || addr.isLinkLocal()
);
} else {
reject(new Error("Unknown IP family"));
}
});
});

return isLocal;
}