Adrien Denat coyotte508 HF staff commited on
Commit
b7b2c8c
1 Parent(s): 2e28042

Disable models (#187)

Browse files

* add ability for a model to be disabled

* fallback to default model if user has invalid/disabled model in settings

* set conversations to read-only if conv model is disabled

* add better disabled state on chat input

* fix inverted if statement

* remove bigcode from .env

* fix not updating settings

Co-authored-by: Eliott C. <coyotte508@gmail.com>

* refactor solution to use an OLD_MODELS env var instead

* OLD_MODELS needs to be in .env but can be empty

* fix typing issue + typo

* refactor models validation

* old models displayName is optional

* set read only prop on user message not assistant messages

* remove console.log

* ♻️ Refacto isReadOnly

* 🩹 Fluff: use 410 http code

---------

Co-authored-by: Eliott C. <coyotte508@gmail.com>

.env CHANGED
@@ -41,38 +41,9 @@ MODELS=`[
41
  "truncate": 1000,
42
  "max_new_tokens": 1024
43
  }
44
- },
45
- {
46
- "name":"bigcode/starcoderbase",
47
- "displayName":"BigCode/StarCoderBase",
48
- "datasetName":"bigcode/the-stack-dedup",
49
- "description": "A good model for answering technical questions",
50
- "websiteUrl":"https://huggingface.co/bigcode/",
51
- "prepromptUrl": "https://huggingface.co/datasets/coyotte508/bigcodeprompt/raw/main/prompt.txt",
52
- "promptExamples": [
53
- {
54
- "title": "Write a code snippet",
55
- "prompt": "Write a function that loads a file and filters line starting with \"Star\"?"
56
- }, {
57
- "title": "Explain a technical concept",
58
- "prompt": "What is a Dockerfile?"
59
- }, {
60
- "title": "Solve a technical task",
61
- "prompt": "How to install pytorch with cuda?"
62
- }
63
- ],
64
- "userMessageToken": "\n\nHuman: ",
65
- "assistantMessageToken": "\n\nAssistant:",
66
- "parameters": {
67
- "temperature": 0.1,
68
- "top_p": 0.9,
69
- "repetition_penalty": 1.2,
70
- "truncate": 8000,
71
- "max_new_tokens": 2000,
72
- "stop": ["Human:", "-----", "Assistant:"]
73
- }
74
  }
75
  ]`
 
76
 
77
  PUBLIC_ORIGIN=#https://hf.co
78
  PUBLIC_GOOGLE_ANALYTICS_ID=#G-XXXXXXXX / Leave empty to disable
 
41
  "truncate": 1000,
42
  "max_new_tokens": 1024
43
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
  }
45
  ]`
46
+ OLD_MODELS=`[]`# any removed models, `{ name: string, displayName?: string, id?: string }`
47
 
48
  PUBLIC_ORIGIN=#https://hf.co
49
  PUBLIC_GOOGLE_ANALYTICS_ID=#G-XXXXXXXX / Leave empty to disable
src/lib/buildPrompt.ts CHANGED
@@ -26,5 +26,11 @@ export function buildPrompt(
26
  .join("") + model.assistantMessageToken;
27
 
28
  // Not super precise, but it's truncated in the model's backend anyway
29
- return model.preprompt + prompt.split(" ").slice(-model.parameters.truncate).join(" ");
 
 
 
 
 
 
30
  }
 
26
  .join("") + model.assistantMessageToken;
27
 
28
  // Not super precise, but it's truncated in the model's backend anyway
29
+ return (
30
+ model.preprompt +
31
+ prompt
32
+ .split(" ")
33
+ .slice(-(model.parameters?.truncate ?? 0))
34
+ .join(" ")
35
+ );
36
  }
src/lib/components/chat/ChatIntroduction.svelte CHANGED
@@ -3,7 +3,6 @@
3
  import Logo from "$lib/components/icons/Logo.svelte";
4
  import { createEventDispatcher } from "svelte";
5
  import IconChevron from "$lib/components/icons/IconChevron.svelte";
6
- import AnnouncementBanner from "../AnnouncementBanner.svelte";
7
  import ModelsModal from "../ModelsModal.svelte";
8
  import type { Model } from "$lib/types/Model";
9
  import ModelCardMetadata from "../ModelCardMetadata.svelte";
@@ -39,14 +38,6 @@
39
  </div>
40
  </div>
41
  <div class="lg:col-span-2 lg:pl-24">
42
- <AnnouncementBanner classNames="mb-4" title="BigCode/StarCoderBase is now available">
43
- <button
44
- type="button"
45
- on:click={() => (isModelsModalOpen = true)}
46
- class="mr-2 flex items-center underline hover:no-underline"
47
- ><IconChevron classNames="mr-1" /> Switch model</button
48
- >
49
- </AnnouncementBanner>
50
  {#if isModelsModalOpen}
51
  <ModelsModal {settings} {models} on:close={() => (isModelsModalOpen = false)} />
52
  {/if}
@@ -56,12 +47,14 @@
56
  <div class="text-sm text-gray-600 dark:text-gray-400">Current Model</div>
57
  <div class="font-semibold">{currentModel.displayName}</div>
58
  </div>
59
- <button
60
- type="button"
61
- on:click={() => (isModelsModalOpen = true)}
62
- class="btn ml-auto flex h-7 w-7 self-start rounded-full bg-gray-100 p-1 text-xs hover:bg-gray-100 dark:border-gray-600 dark:bg-gray-800 dark:hover:bg-gray-600"
63
- ><IconChevron /></button
64
- >
 
 
65
  </div>
66
  <ModelCardMetadata variant="dark" model={currentModel} />
67
  </div>
 
3
  import Logo from "$lib/components/icons/Logo.svelte";
4
  import { createEventDispatcher } from "svelte";
5
  import IconChevron from "$lib/components/icons/IconChevron.svelte";
 
6
  import ModelsModal from "../ModelsModal.svelte";
7
  import type { Model } from "$lib/types/Model";
8
  import ModelCardMetadata from "../ModelCardMetadata.svelte";
 
38
  </div>
39
  </div>
40
  <div class="lg:col-span-2 lg:pl-24">
 
 
 
 
 
 
 
 
41
  {#if isModelsModalOpen}
42
  <ModelsModal {settings} {models} on:close={() => (isModelsModalOpen = false)} />
43
  {/if}
 
47
  <div class="text-sm text-gray-600 dark:text-gray-400">Current Model</div>
48
  <div class="font-semibold">{currentModel.displayName}</div>
49
  </div>
50
+ {#if models.length > 1}
51
+ <button
52
+ type="button"
53
+ on:click={() => (isModelsModalOpen = true)}
54
+ class="btn ml-auto flex h-7 w-7 self-start rounded-full bg-gray-100 p-1 text-xs hover:bg-gray-100 dark:border-gray-600 dark:bg-gray-800 dark:hover:bg-gray-600"
55
+ ><IconChevron /></button
56
+ >
57
+ {/if}
58
  </div>
59
  <ModelCardMetadata variant="dark" model={currentModel} />
60
  </div>
src/lib/components/chat/ChatMessage.svelte CHANGED
@@ -23,7 +23,7 @@
23
  .replaceAll("<", "&lt;")
24
  .trim();
25
 
26
- for (const stop of [...(model.parameters.stop ?? []), "<|endoftext|>"]) {
27
  if (ret.endsWith(stop)) {
28
  ret = ret.slice(0, -stop.length).trim();
29
  }
@@ -38,6 +38,7 @@
38
  export let model: Model;
39
  export let message: Message;
40
  export let loading = false;
 
41
 
42
  const dispatch = createEventDispatcher<{ retry: void }>();
43
 
@@ -131,14 +132,16 @@
131
  <CarbonDownload />
132
  </a>
133
  {/if}
134
- <button
135
- class="cursor-pointer rounded-lg border border-gray-100 p-1 text-xs text-gray-400 group-hover:block hover:text-gray-500 dark:border-gray-800 dark:text-gray-400 dark:hover:text-gray-300 md:hidden lg:-right-2"
136
- title="Retry"
137
- type="button"
138
- on:click={() => dispatch("retry")}
139
- >
140
- <CarbonRotate360 />
141
- </button>
 
 
142
  </div>
143
  {/if}
144
  </div>
 
23
  .replaceAll("<", "&lt;")
24
  .trim();
25
 
26
+ for (const stop of [...(model.parameters?.stop ?? []), "<|endoftext|>"]) {
27
  if (ret.endsWith(stop)) {
28
  ret = ret.slice(0, -stop.length).trim();
29
  }
 
38
  export let model: Model;
39
  export let message: Message;
40
  export let loading = false;
41
+ export let readOnly = false;
42
 
43
  const dispatch = createEventDispatcher<{ retry: void }>();
44
 
 
132
  <CarbonDownload />
133
  </a>
134
  {/if}
135
+ {#if !readOnly}
136
+ <button
137
+ class="cursor-pointer rounded-lg border border-gray-100 p-1 text-xs text-gray-400 group-hover:block hover:text-gray-500 dark:border-gray-800 dark:text-gray-400 dark:hover:text-gray-300 md:hidden lg:-right-2"
138
+ title="Retry"
139
+ type="button"
140
+ on:click={() => dispatch("retry")}
141
+ >
142
+ <CarbonRotate360 />
143
+ </button>
144
+ {/if}
145
  </div>
146
  {/if}
147
  </div>
src/lib/components/chat/ChatMessages.svelte CHANGED
@@ -17,7 +17,8 @@
17
  export let pending: boolean;
18
  export let currentModel: Model;
19
  export let settings: LayoutData["settings"];
20
- export let models: Model[] | undefined;
 
21
 
22
  let chatContainer: HTMLElement;
23
 
@@ -43,12 +44,11 @@
43
  loading={loading && i === messages.length - 1}
44
  {message}
45
  model={currentModel}
 
46
  on:retry={() => dispatch("retry", { id: message.id, content: message.content })}
47
  />
48
  {:else}
49
- {#if models}
50
- <ChatIntroduction {settings} {models} {currentModel} on:message />
51
- {/if}
52
  {/each}
53
  {#if pending}
54
  <ChatMessage
 
17
  export let pending: boolean;
18
  export let currentModel: Model;
19
  export let settings: LayoutData["settings"];
20
+ export let models: Model[];
21
+ export let readOnly: boolean;
22
 
23
  let chatContainer: HTMLElement;
24
 
 
44
  loading={loading && i === messages.length - 1}
45
  {message}
46
  model={currentModel}
47
+ {readOnly}
48
  on:retry={() => dispatch("retry", { id: message.id, content: message.content })}
49
  />
50
  {:else}
51
+ <ChatIntroduction {settings} {models} {currentModel} on:message />
 
 
52
  {/each}
53
  {#if pending}
54
  <ChatMessage
src/lib/components/chat/ChatWindow.svelte CHANGED
@@ -12,13 +12,14 @@
12
  import type { LayoutData } from "../../../routes/$types";
13
 
14
  export let messages: Message[] = [];
15
- export let disabled = false;
16
  export let loading = false;
17
  export let pending = false;
18
  export let currentModel: Model;
19
- export let models: Model[] | undefined = undefined;
20
  export let settings: LayoutData["settings"];
21
 
 
 
22
  let message: string;
23
 
24
  const dispatch = createEventDispatcher<{
@@ -43,6 +44,7 @@
43
  {currentModel}
44
  {models}
45
  {messages}
 
46
  on:message
47
  on:retry={(ev) => {
48
  if (!loading) dispatch("retry", ev.detail);
@@ -58,7 +60,8 @@
58
  />
59
  <form
60
  on:submit|preventDefault={handleSubmit}
61
- class="relative flex w-full max-w-4xl flex-1 items-center rounded-xl border bg-gray-100 focus-within:border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:focus-within:border-gray-500 "
 
62
  >
63
  <div class="flex w-full flex-1 border-none bg-transparent">
64
  <ChatInput
@@ -66,10 +69,11 @@
66
  bind:value={message}
67
  on:submit={handleSubmit}
68
  maxRows={4}
 
69
  />
70
  <button
71
  class="btn mx-1 my-1 h-[2.4rem] self-end rounded-lg bg-transparent p-1 px-[0.7rem] text-gray-400 disabled:opacity-60 enabled:hover:text-gray-700 dark:disabled:opacity-40 enabled:dark:hover:text-gray-100"
72
- disabled={!message || loading || disabled}
73
  type="submit"
74
  >
75
  <CarbonSendAltFilled />
 
12
  import type { LayoutData } from "../../../routes/$types";
13
 
14
  export let messages: Message[] = [];
 
15
  export let loading = false;
16
  export let pending = false;
17
  export let currentModel: Model;
18
+ export let models: Model[];
19
  export let settings: LayoutData["settings"];
20
 
21
+ $: isReadOnly = !models.some((model) => model.id === currentModel.id);
22
+
23
  let message: string;
24
 
25
  const dispatch = createEventDispatcher<{
 
44
  {currentModel}
45
  {models}
46
  {messages}
47
+ readOnly={isReadOnly}
48
  on:message
49
  on:retry={(ev) => {
50
  if (!loading) dispatch("retry", ev.detail);
 
60
  />
61
  <form
62
  on:submit|preventDefault={handleSubmit}
63
+ class="relative flex w-full max-w-4xl flex-1 items-center rounded-xl border bg-gray-100 focus-within:border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:focus-within:border-gray-500
64
+ {isReadOnly ? 'opacity-30' : ''}"
65
  >
66
  <div class="flex w-full flex-1 border-none bg-transparent">
67
  <ChatInput
 
69
  bind:value={message}
70
  on:submit={handleSubmit}
71
  maxRows={4}
72
+ disabled={isReadOnly}
73
  />
74
  <button
75
  class="btn mx-1 my-1 h-[2.4rem] self-end rounded-lg bg-transparent p-1 px-[0.7rem] text-gray-400 disabled:opacity-60 enabled:hover:text-gray-700 dark:disabled:opacity-40 enabled:dark:hover:text-gray-100"
76
+ disabled={!message || loading || isReadOnly}
77
  type="submit"
78
  >
79
  <CarbonSendAltFilled />
src/lib/server/models.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { HF_ACCESS_TOKEN, MODELS } from "$env/static/private";
2
  import { z } from "zod";
3
 
4
  const modelsRaw = z
@@ -41,7 +41,8 @@ const modelsRaw = z
41
  max_new_tokens: z.number().int().positive(),
42
  stop: z.array(z.string()).optional(),
43
  })
44
- .passthrough(),
 
45
  })
46
  )
47
  .parse(JSON.parse(MODELS));
@@ -55,6 +56,25 @@ export const models = await Promise.all(
55
  }))
56
  );
57
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
  export type BackendModel = (typeof models)[0];
59
 
60
  export const defaultModel = models[0];
 
 
 
 
 
 
1
+ import { HF_ACCESS_TOKEN, MODELS, OLD_MODELS } from "$env/static/private";
2
  import { z } from "zod";
3
 
4
  const modelsRaw = z
 
41
  max_new_tokens: z.number().int().positive(),
42
  stop: z.array(z.string()).optional(),
43
  })
44
+ .passthrough()
45
+ .optional(),
46
  })
47
  )
48
  .parse(JSON.parse(MODELS));
 
56
  }))
57
  );
58
 
59
+ // Models that have been deprecated
60
+ export const oldModels = OLD_MODELS
61
+ ? z
62
+ .array(
63
+ z.object({
64
+ id: z.string().optional(),
65
+ name: z.string().min(1),
66
+ displayName: z.string().min(1).optional(),
67
+ })
68
+ )
69
+ .parse(JSON.parse(OLD_MODELS))
70
+ .map((m) => ({ ...m, id: m.id || m.name, displayName: m.displayName || m.name }))
71
+ : [];
72
+
73
  export type BackendModel = (typeof models)[0];
74
 
75
  export const defaultModel = models[0];
76
+
77
+ export const validateModel = (_models: BackendModel[]) => {
78
+ // Zod enum function requires 2 parameters
79
+ return z.enum([_models[0].id, ..._models.slice(1).map((m) => m.id)]);
80
+ };
src/lib/utils/models.ts CHANGED
@@ -1,10 +1,4 @@
1
  import type { Model } from "$lib/types/Model";
2
- import { z } from "zod";
3
 
4
- export const findCurrentModel = (models: Model[], name?: string) =>
5
- models.find((m) => m.id === name) ?? models[0];
6
-
7
- export const validateModel = (models: Model[]) => {
8
- // Zod enum function requires 2 parameters
9
- return z.enum([models[0].id, ...models.slice(1).map((m) => m.id)]);
10
- };
 
1
  import type { Model } from "$lib/types/Model";
 
2
 
3
+ export const findCurrentModel = (models: Model[], id?: string) =>
4
+ models.find((m) => m.id === id) ?? models[0];
 
 
 
 
 
src/routes/+layout.server.ts CHANGED
@@ -3,8 +3,7 @@ import type { LayoutServerLoad } from "./$types";
3
  import { collections } from "$lib/server/database";
4
  import type { Conversation } from "$lib/types/Conversation";
5
  import { UrlDependency } from "$lib/types/UrlDependency";
6
- import { defaultModel, models } from "$lib/server/models";
7
- import { validateModel } from "$lib/utils/models";
8
  import { authCondition } from "$lib/server/auth";
9
 
10
  export const load: LayoutServerLoad = async ({ locals, depends, url }) => {
@@ -29,6 +28,15 @@ export const load: LayoutServerLoad = async ({ locals, depends, url }) => {
29
 
30
  const settings = await collections.settings.findOne(authCondition(locals));
31
 
 
 
 
 
 
 
 
 
 
32
  return {
33
  conversations: await conversations
34
  .find(authCondition(locals))
@@ -61,5 +69,6 @@ export const load: LayoutServerLoad = async ({ locals, depends, url }) => {
61
  promptExamples: model.promptExamples,
62
  parameters: model.parameters,
63
  })),
 
64
  };
65
  };
 
3
  import { collections } from "$lib/server/database";
4
  import type { Conversation } from "$lib/types/Conversation";
5
  import { UrlDependency } from "$lib/types/UrlDependency";
6
+ import { defaultModel, models, oldModels, validateModel } from "$lib/server/models";
 
7
  import { authCondition } from "$lib/server/auth";
8
 
9
  export const load: LayoutServerLoad = async ({ locals, depends, url }) => {
 
28
 
29
  const settings = await collections.settings.findOne(authCondition(locals));
30
 
31
+ // If the active model in settings is not valid, set it to the default model. This can happen if model was disabled.
32
+ if (settings && !validateModel(models).safeParse(settings?.activeModel).success) {
33
+ settings.activeModel = defaultModel.id;
34
+ await collections.settings.updateOne(
35
+ { sessionId: locals.sessionId },
36
+ { $set: { activeModel: defaultModel.id } }
37
+ );
38
+ }
39
+
40
  return {
41
  conversations: await conversations
42
  .find(authCondition(locals))
 
69
  promptExamples: model.promptExamples,
70
  parameters: model.parameters,
71
  })),
72
+ oldModels,
73
  };
74
  };
src/routes/+page.svelte CHANGED
@@ -45,7 +45,7 @@
45
  <ChatWindow
46
  on:message={(ev) => createConversation(ev.detail)}
47
  {loading}
48
- currentModel={findCurrentModel(data.models, data.settings.activeModel)}
49
  models={data.models}
50
  settings={data.settings}
51
  />
 
45
  <ChatWindow
46
  on:message={(ev) => createConversation(ev.detail)}
47
  {loading}
48
+ currentModel={findCurrentModel([...data.models, ...data.oldModels], data.settings.activeModel)}
49
  models={data.models}
50
  settings={data.settings}
51
  />
src/routes/conversation/+server.ts CHANGED
@@ -5,8 +5,7 @@ import { error, redirect } from "@sveltejs/kit";
5
  import { base } from "$app/paths";
6
  import { z } from "zod";
7
  import type { Message } from "$lib/types/Message";
8
- import { models } from "$lib/server/models";
9
- import { validateModel } from "$lib/utils/models";
10
  import { authCondition } from "$lib/server/auth";
11
 
12
  export const POST: RequestHandler = async ({ locals, request }) => {
 
5
  import { base } from "$app/paths";
6
  import { z } from "zod";
7
  import type { Message } from "$lib/types/Message";
8
+ import { models, validateModel } from "$lib/server/models";
 
9
  import { authCondition } from "$lib/server/auth";
10
 
11
  export const POST: RequestHandler = async ({ locals, request }) => {
src/routes/conversation/[id]/+page.svelte CHANGED
@@ -173,6 +173,7 @@
173
  on:retry={(message) => writeMessage(message.detail.content, message.detail.id)}
174
  on:share={() => shareConversation($page.params.id, data.title)}
175
  on:stop={() => (isAborted = true)}
176
- currentModel={findCurrentModel(data.models, data.model)}
 
177
  settings={data.settings}
178
  />
 
173
  on:retry={(message) => writeMessage(message.detail.content, message.detail.id)}
174
  on:share={() => shareConversation($page.params.id, data.title)}
175
  on:stop={() => (isAborted = true)}
176
+ models={data.models}
177
+ currentModel={findCurrentModel([...data.models, ...data.oldModels], data.model)}
178
  settings={data.settings}
179
  />
src/routes/conversation/[id]/+server.ts CHANGED
@@ -16,8 +16,8 @@ import { ObjectId } from "mongodb";
16
  import { z } from "zod";
17
 
18
  export async function POST({ request, fetch, locals, params }) {
19
- // todo: add validation on params.id
20
- const convId = new ObjectId(params.id);
21
  const date = new Date();
22
 
23
  const conv = await collections.conversations.findOne({
@@ -32,7 +32,7 @@ export async function POST({ request, fetch, locals, params }) {
32
  const model = models.find((m) => m.id === conv.model);
33
 
34
  if (!model) {
35
- throw error(400, "Model not availalbe anymore");
36
  }
37
 
38
  const json = await request.json();
 
16
  import { z } from "zod";
17
 
18
  export async function POST({ request, fetch, locals, params }) {
19
+ const id = z.string().parse(params.id);
20
+ const convId = new ObjectId(id);
21
  const date = new Date();
22
 
23
  const conv = await collections.conversations.findOne({
 
32
  const model = models.find((m) => m.id === conv.model);
33
 
34
  if (!model) {
35
+ throw error(410, "Model not available anymore");
36
  }
37
 
38
  const json = await request.json();
src/routes/r/[id]/+page.svelte CHANGED
@@ -72,6 +72,7 @@
72
  })
73
  .finally(() => (loading = false))}
74
  messages={data.messages}
 
75
  currentModel={findCurrentModel(data.models, data.model)}
76
  settings={data.settings}
77
  {loading}
 
72
  })
73
  .finally(() => (loading = false))}
74
  messages={data.messages}
75
+ models={data.models}
76
  currentModel={findCurrentModel(data.models, data.model)}
77
  settings={data.settings}
78
  {loading}
src/routes/settings/+page.server.ts CHANGED
@@ -2,8 +2,7 @@ import { base } from "$app/paths";
2
  import { collections } from "$lib/server/database";
3
  import { redirect } from "@sveltejs/kit";
4
  import { z } from "zod";
5
- import { defaultModel, models } from "$lib/server/models";
6
- import { validateModel } from "$lib/utils/models";
7
  import { authCondition } from "$lib/server/auth";
8
 
9
  export const actions = {
 
2
  import { collections } from "$lib/server/database";
3
  import { redirect } from "@sveltejs/kit";
4
  import { z } from "zod";
5
+ import { defaultModel, models, validateModel } from "$lib/server/models";
 
6
  import { authCondition } from "$lib/server/auth";
7
 
8
  export const actions = {