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

Dynamic system instructions #949

Merged
merged 28 commits into from Mar 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
9dd5042
Dynamic system instructions
nsarrazin Mar 21, 2024
3a34536
Merge branch 'main' into feat/dynamic_preprompt
nsarrazin Mar 21, 2024
079f55a
disable tag rendering if the feature is disabled
nsarrazin Mar 21, 2024
b1fc61f
make time & date tags bold
nsarrazin Mar 21, 2024
3602e45
fix bug enabling dynamic prompt on assistant creation
nsarrazin Mar 22, 2024
33916ae
Move fetching at prompt building time
nsarrazin Mar 22, 2024
59cef7f
get rid of date & time tags
nsarrazin Mar 22, 2024
40ea890
use `url=` for tags
nsarrazin Mar 22, 2024
4ec3cbf
add env flag check
nsarrazin Mar 22, 2024
8de7d78
Merge branch 'main' into feat/dynamic_preprompt
nsarrazin Mar 22, 2024
4f5141e
Add more detailed errors in prompt
nsarrazin Mar 22, 2024
072baa7
wording
gary149 Mar 24, 2024
74624de
token counter
gary149 Mar 24, 2024
73baffc
modal update
gary149 Mar 24, 2024
9c61907
wording
gary149 Mar 24, 2024
d083192
hide disabled if dynamic prompt is enabled
gary149 Mar 25, 2024
b1a769f
add template variables parsing
gary149 Mar 25, 2024
aae4b83
regex update
gary149 Mar 25, 2024
2b45025
same regex
gary149 Mar 25, 2024
346b5c6
regex again
gary149 Mar 25, 2024
0a24e63
sys prompt max height
gary149 Mar 25, 2024
80e31c2
rm unused
gary149 Mar 25, 2024
61d19d4
Always use absolute URL in links
nsarrazin Mar 25, 2024
e30ab33
trying something
gary149 Mar 25, 2024
f0a3399
Revert "trying something"
gary149 Mar 25, 2024
d2e1e1f
wording
gary149 Mar 25, 2024
d2ffb5e
remove debug log
nsarrazin Mar 25, 2024
671dcee
last wording tweak
gary149 Mar 25, 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
150 changes: 97 additions & 53 deletions src/lib/components/AssistantSettings.svelte
Expand Up @@ -12,6 +12,7 @@

import { useSettingsStore } from "$lib/stores/settings";
import { isHuggingChat } from "$lib/utils/isHuggingChat";
import IconInternet from "./icons/IconInternet.svelte";
import TokensCounter from "./TokensCounter.svelte";

type ActionData = {
Expand All @@ -33,6 +34,7 @@
let modelId =
assistant?.modelId ?? models.find((_model) => _model.id === $settings.activeModel)?.name;
let systemPrompt = assistant?.preprompt ?? "";
let dynamicPrompt = assistant?.dynamicPrompt ?? false;

let compress: typeof readAndCompressImage | null = null;

Expand Down Expand Up @@ -84,6 +86,9 @@
: (assistant?.rag?.allowedDomains?.length ?? 0) > 0
? "domains"
: false;

const regex = /{{\s?url=(.+?)\s?}}/g;
$: templateVariables = [...systemPrompt.matchAll(regex)].map((match) => match[1]);
</script>

<form
Expand Down Expand Up @@ -142,10 +147,10 @@
>
{#if assistant}
<h2 class="text-xl font-semibold">
Edit {assistant?.name ?? "assistant"}
Edit Assistant: {assistant?.name ?? "assistant"}
</h2>
<p class="mb-6 text-sm text-gray-500">
Modifying an existing assistant will propagate those changes to all users.
Modifying an existing assistant will propagate the changes to all users.
</p>
{:else}
<h2 class="text-xl font-semibold">Create new assistant</h2>
Expand Down Expand Up @@ -222,7 +227,7 @@
<input
name="name"
class="w-full rounded-lg border-2 border-gray-200 bg-gray-100 p-2"
placeholder="My awesome model"
placeholder="Assistant Name"
value={assistant?.name ?? ""}
/>
<p class="text-xs text-red-500">{getError("name", form)}</p>
Expand Down Expand Up @@ -260,41 +265,42 @@

<label>
<div class="mb-1 font-semibold">User start messages</div>
<div class="flex flex-col gap-2">
<div class="grid gap-1.5 text-sm md:grid-cols-2">
<input
name="exampleInput1"
placeholder="Start Message 1"
bind:value={inputMessage1}
class="w-full rounded-lg border-2 border-gray-200 bg-gray-100 p-2"
/>
{#if !!inputMessage1 || !!inputMessage2}
<input
name="exampleInput2"
bind:value={inputMessage2}
class="w-full rounded-lg border-2 border-gray-200 bg-gray-100 p-2"
/>
{/if}
{#if !!inputMessage2 || !!inputMessage3}
<input
name="exampleInput3"
bind:value={inputMessage3}
class="w-full rounded-lg border-2 border-gray-200 bg-gray-100 p-2"
/>
{/if}
{#if !!inputMessage3 || !!inputMessage4}
<input
name="exampleInput4"
bind:value={inputMessage4}
class="w-full rounded-lg border-2 border-gray-200 bg-gray-100 p-2"
/>
{/if}
<input
name="exampleInput2"
placeholder="Start Message 2"
bind:value={inputMessage2}
class="w-full rounded-lg border-2 border-gray-200 bg-gray-100 p-2"
/>

<input
name="exampleInput3"
placeholder="Start Message 3"
bind:value={inputMessage3}
class="w-full rounded-lg border-2 border-gray-200 bg-gray-100 p-2"
/>
<input
name="exampleInput4"
placeholder="Start Message 4"
bind:value={inputMessage4}
class="w-full rounded-lg border-2 border-gray-200 bg-gray-100 p-2"
/>
</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"
>Internet access
<IconInternet classNames="inline text-sm text-blue-600" />

<span class="ml-1 rounded bg-gray-100 px-1 py-0.5 text-xxs font-normal text-gray-600"
>Experimental</span
>

Expand All @@ -316,10 +322,11 @@
name="ragMode"
value={false}
/>
<span class="my-2 text-sm" class:font-semibold={!ragMode}> Disabled </span>
<span class="my-2 text-sm" class:font-semibold={!ragMode}> Default </span>
{#if !ragMode}
<span class="block text-xs text-gray-500">
Assistant won't look for information from Internet and will be faster to answer.
Assistant will not use internet to do information retrieval and will respond faster.
Recommended for most Assistants.
</span>
{/if}
</label>
Expand Down Expand Up @@ -391,52 +398,89 @@
/>
<p class="text-xs text-red-500">{getError("ragLinkList", form)}</p>
{/if}

<!-- divider -->
<div class="my-3 ml-0 mr-6 w-full border border-gray-200" />

<label class="text-sm has-[:checked]:font-semibold">
<input type="checkbox" name="dynamicPrompt" bind:checked={dynamicPrompt} />
Dynamic Prompt
<p class="mb-2 text-xs font-normal text-gray-500">
Allow the use of template variables {"{{url=https://example.com/path}}"}
to insert dynamic content into your prompt by making GET requests to specified URLs on
each inference.
</p>
</label>
</div>
{/if}
</div>

<div class="col-span-1 flex h-full flex-col">
<span class="mb-1 text-sm font-semibold"> Instructions (system prompt) </span>
<div class="relative mb-20 flex min-h-[8lh] flex-1 grow flex-col">
<div class="mb-1 flex justify-between text-sm">
<span class="font-semibold"> Instructions (System Prompt) </span>
{#if dynamicPrompt && templateVariables.length}
<div class="relative">
<button
type="button"
class="peer rounded bg-blue-500/20 px-1 text-xs text-blue-600 focus:bg-blue-500/30 focus:text-blue-800 sm:text-sm"
>
{templateVariables.length} template variable{templateVariables.length > 1 ? "s" : ""}
</button>
<div
class="invisible absolute right-0 top-6 z-10 rounded-lg border bg-white p-2 text-xs shadow-lg peer-focus:visible hover:visible sm:w-96"
>
Will performs a GET request and injects the response into the prompt. Works better
with plain text, csv or json content.
{#each templateVariables as match}
<a href={match} target="_blank" class="text-gray-500 underline decoration-gray-300"
>{match}</a
>
{/each}
</div>
</div>
{/if}
</div>
<div class="relative mb-20 flex h-full flex-col gap-2">
<textarea
name="preprompt"
class="flex-1 rounded-lg border-2 border-gray-200 bg-gray-100 p-2 text-sm"
class="min-h-[8lh] flex-1 rounded-lg border-2 border-gray-200 bg-gray-100 p-2 text-sm"
placeholder="You'll act as..."
bind:value={systemPrompt}
/>
{#if modelId}
{@const model = models.find((_model) => _model.id === modelId)}
{#if model?.tokenizer && systemPrompt}
<TokensCounter
classNames="absolute bottom-2 right-2"
classNames="absolute bottom-4 right-4"
prompt={systemPrompt}
modelTokenizer={model.tokenizer}
truncate={model?.parameters?.truncate}
/>
{/if}
{/if}

<p class="text-xs text-red-500">{getError("preprompt", form)}</p>
</div>
<p class="text-xs text-red-500">{getError("preprompt", form)}</p>
</div>
</div>

<div class="fixed bottom-6 right-6 ml-auto mt-6 flex w-fit justify-end gap-2 sm:absolute">
<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"
>
Cancel
</a>
<button
type="submit"
disabled={loading}
aria-disabled={loading}
class="flex items-center justify-center rounded-full bg-black px-8 py-2 font-semibold"
class:bg-gray-200={loading}
class:text-gray-600={loading}
class:text-white={!loading}
>
{assistant ? "Save" : "Create"}
</button>
<div class="fixed bottom-6 right-6 ml-auto mt-6 flex w-fit justify-end gap-2 sm:absolute">
<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"
>
Cancel
</a>
<button
type="submit"
disabled={loading}
aria-disabled={loading}
class="flex items-center justify-center rounded-full bg-black px-8 py-2 font-semibold"
class:bg-gray-200={loading}
class:text-gray-600={loading}
class:text-white={!loading}
>
{assistant ? "Save" : "Create"}
</button>
</div>
</div>
</form>
18 changes: 15 additions & 3 deletions src/lib/components/TokensCounter.svelte
Expand Up @@ -41,8 +41,20 @@

{#if tokenizer}
{#await tokenizeText(prompt) then nTokens}
<p class="text-sm opacity-60 hover:opacity-80 {classNames}">
{nTokens}{truncate ? `/${truncate}` : ""}
</p>
{@const exceedLimit = nTokens > (truncate || Infinity)}
<div class={classNames}>
<p
class="peer text-sm {exceedLimit
? 'text-red-500 opacity-100'
: 'opacity-60 hover:opacity-90'}"
>
{nTokens}{truncate ? `/${truncate}` : ""}
</p>
<div
class="invisible absolute -top-6 right-0 whitespace-nowrap rounded bg-black px-1 text-sm text-white peer-hover:visible"
>
Tokens usage
</div>
</div>
{/await}
{/if}
4 changes: 3 additions & 1 deletion src/lib/components/chat/AssistantIntroduction.svelte
Expand Up @@ -10,6 +10,7 @@
| "avatar"
| "name"
| "rag"
| "dynamicPrompt"
| "modelId"
| "createdByName"
| "exampleInputs"
Expand All @@ -22,7 +23,8 @@
$: hasRag =
assistant?.rag?.allowAllDomains ||
(assistant?.rag?.allowedDomains?.length ?? 0) > 0 ||
(assistant?.rag?.allowedLinks?.length ?? 0) > 0;
(assistant?.rag?.allowedLinks?.length ?? 0) > 0 ||
assistant?.dynamicPrompt;
</script>

<div class="flex h-full w-full flex-col content-center items-center justify-center pb-52">
Expand Down
1 change: 1 addition & 0 deletions src/lib/types/Assistant.ts
Expand Up @@ -19,5 +19,6 @@ export interface Assistant extends Timestamps {
allowedDomains: string[];
allowedLinks: string[];
};
dynamicPrompt?: boolean;
searchTokens: string[];
}
3 changes: 2 additions & 1 deletion src/routes/assistants/+page.svelte
Expand Up @@ -205,7 +205,8 @@
{@const hasRag =
assistant?.rag?.allowAllDomains ||
!!assistant?.rag?.allowedDomains?.length ||
!!assistant?.rag?.allowedLinks?.length}
!!assistant?.rag?.allowedLinks?.length ||
!!assistant?.dynamicPrompt}

<button
class="relative flex flex-col items-center justify-center overflow-hidden text-balance rounded-xl border bg-gray-50/50 px-4 py-6 text-center shadow hover:bg-gray-50 hover:shadow-inner max-sm:px-4 sm:h-64 sm:pb-4 xl:pt-8 dark:border-gray-800/70 dark:bg-gray-950/20 dark:hover:bg-gray-950/40"
Expand Down
24 changes: 12 additions & 12 deletions src/routes/conversation/+server.ts
Expand Up @@ -45,22 +45,11 @@ export const POST: RequestHandler = async ({ locals, request }) => {
throw error(400, "Invalid model");
}

// get preprompt from assistant if it exists
const assistant = await collections.assistants.findOne({
_id: new ObjectId(values.assistantId),
});

if (assistant) {
values.preprompt = assistant.preprompt;
} else {
values.preprompt ??= model?.preprompt ?? "";
}

let messages: Message[] = [
{
id: v4(),
from: "system",
content: values.preprompt,
content: values.preprompt ?? "",
createdAt: new Date(),
updatedAt: new Date(),
children: [],
Expand Down Expand Up @@ -95,6 +84,17 @@ export const POST: RequestHandler = async ({ locals, request }) => {
throw error(400, "Can't start a conversation with an unlisted model");
}

// get preprompt from assistant if it exists
const assistant = await collections.assistants.findOne({
_id: new ObjectId(values.assistantId),
});

if (assistant) {
values.preprompt = assistant.preprompt;
} else {
values.preprompt ??= model?.preprompt ?? "";
}

const res = await collections.conversations.insertOne({
_id: new ObjectId(),
title: title || "New Chat",
Expand Down