mishig HF staff commited on
Commit
15bbf38
2 Parent(s): d36fc40 77100da

New model selector (design only) (#39)

Browse files
src/lib/components/Icons/IconSearch.svelte ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ xmlns:xlink="http://www.w3.org/1999/xlink"
9
+ aria-hidden="true"
10
+ focusable="false"
11
+ role="img"
12
+ width="1em"
13
+ height="1em"
14
+ preserveAspectRatio="xMidYMid meet"
15
+ viewBox="0 0 32 32"
16
+ >
17
+ <path
18
+ d="M30 28.59L22.45 21A11 11 0 1 0 21 22.45L28.59 30zM5 14a9 9 0 1 1 9 9a9 9 0 0 1-9-9z"
19
+ fill="currentColor"
20
+ />
21
+ </svg>
src/lib/components/Icons/IconStar.svelte ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ xmlns:xlink="http://www.w3.org/1999/xlink"
9
+ aria-hidden="true"
10
+ fill="none"
11
+ focusable="false"
12
+ role="img"
13
+ width="1em"
14
+ height="1em"
15
+ preserveAspectRatio="xMidYMid meet"
16
+ viewBox="0 0 32 32"
17
+ >
18
+ <path
19
+ d="M16 6.52l2.76 5.58l.46 1l1 .15l6.16.89l-4.38 4.3l-.75.73l.18 1l1.05 6.13l-5.51-2.89L16 23l-.93.49l-5.51 2.85l1-6.13l.18-1l-.74-.77l-4.42-4.35l6.16-.89l1-.15l.46-1L16 6.52M16 2l-4.55 9.22l-10.17 1.47l7.36 7.18L6.9 30l9.1-4.78L25.1 30l-1.74-10.13l7.36-7.17l-10.17-1.48z"
20
+ fill="currentColor"
21
+ />
22
+ </svg>
src/lib/components/InferencePlayground/InferencePlayground.svelte CHANGED
@@ -8,6 +8,7 @@
8
  import PlaygroundOptions from './InferencePlaygroundGenerationConfig.svelte';
9
  import PlaygroundTokenModal from './InferencePlaygroundHFTokenModal.svelte';
10
  import PlaygroundModelSelector from './InferencePlaygroundModelSelector.svelte';
 
11
  import Conversation from './InferencePlaygroundConversation.svelte';
12
  import { onDestroy } from 'svelte';
13
  import { type ChatCompletionInputMessage } from '@huggingface/tasks';
@@ -16,7 +17,6 @@
16
  import IconShare from '../Icons/IconShare.svelte';
17
  import IconDelete from '../Icons/IconDelete.svelte';
18
  import IconCode from '../Icons/IconCode.svelte';
19
- import IconCaret from '../Icons/IconCaret.svelte';
20
 
21
  export let models: ModelEntryWithTokenizer[];
22
 
@@ -40,6 +40,7 @@
40
  let hfToken: string | null = import.meta.env.VITE_HF_TOKEN;
41
  let viewCode = false;
42
  let showTokenModal = false;
 
43
  let loading = false;
44
  let tokens = 0;
45
  let latency = 0;
@@ -196,6 +197,14 @@
196
  function changeSelectedModel(modelIdx: number) {
197
  conversations[0] = { ...conversations[0], model: models[modelIdx] };
198
  }
 
 
 
 
 
 
 
 
199
  </script>
200
 
201
  {#if showTokenModal}
@@ -210,6 +219,14 @@
210
  />
211
  {/if}
212
 
 
 
 
 
 
 
 
 
213
  <!-- svelte-ignore a11y-no-static-element-interactions -->
214
  <div
215
  class="w-dvh grid divide-gray-200 overflow-hidden bg-gray-100/50 max-md:divide-y md:h-dvh dark:[color-scheme:dark]
@@ -337,10 +354,11 @@
337
  class="flex flex-1 flex-col gap-6 overflow-y-hidden rounded-xl border border-gray-200/80 bg-gradient-to-b from-white via-white p-3 shadow-sm dark:border-white/5 dark:from-gray-800/40 dark:via-gray-800/40"
338
  >
339
  <PlaygroundModelSelector
340
- compatibleModels={models}
341
- on:modelIdxChange={(e) => changeSelectedModel(e.detail)}
 
342
  />
343
- <div
344
  class="group relative -mt-4 flex h-[26px] w-full items-center justify-center gap-2 rounded-lg bg-black px-5 text-sm text-white hover:bg-gray-900 focus:outline-none focus:ring-4 focus:ring-gray-300 dark:border-gray-700 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-gray-700"
345
  >
346
  Compare with...
@@ -363,7 +381,7 @@
363
  <option value={model.id}>{model.id}</option>
364
  {/each}
365
  </select>
366
- </div>
367
 
368
  <PlaygroundOptions bind:conversation={conversations[0]} />
369
  <div class="mt-auto">
 
8
  import PlaygroundOptions from './InferencePlaygroundGenerationConfig.svelte';
9
  import PlaygroundTokenModal from './InferencePlaygroundHFTokenModal.svelte';
10
  import PlaygroundModelSelector from './InferencePlaygroundModelSelector.svelte';
11
+ import ModelPickerModal from './InferencePlaygroundModelPickerModal.svelte';
12
  import Conversation from './InferencePlaygroundConversation.svelte';
13
  import { onDestroy } from 'svelte';
14
  import { type ChatCompletionInputMessage } from '@huggingface/tasks';
 
17
  import IconShare from '../Icons/IconShare.svelte';
18
  import IconDelete from '../Icons/IconDelete.svelte';
19
  import IconCode from '../Icons/IconCode.svelte';
 
20
 
21
  export let models: ModelEntryWithTokenizer[];
22
 
 
40
  let hfToken: string | null = import.meta.env.VITE_HF_TOKEN;
41
  let viewCode = false;
42
  let showTokenModal = false;
43
+ let showModelPickerModal = false;
44
  let loading = false;
45
  let tokens = 0;
46
  let latency = 0;
 
197
  function changeSelectedModel(modelIdx: number) {
198
  conversations[0] = { ...conversations[0], model: models[modelIdx] };
199
  }
200
+
201
+ function changeModel(modelId: string) {
202
+ const model = models.find((m) => m.id === modelId);
203
+ if (!model) {
204
+ return;
205
+ }
206
+ conversations[0].model = model;
207
+ }
208
  </script>
209
 
210
  {#if showTokenModal}
 
219
  />
220
  {/if}
221
 
222
+ {#if showModelPickerModal}
223
+ <ModelPickerModal
224
+ {models}
225
+ on:modelSelected={(e) => changeModel(e.detail)}
226
+ on:close={(e) => (showModelPickerModal = false)}
227
+ />
228
+ {/if}
229
+
230
  <!-- svelte-ignore a11y-no-static-element-interactions -->
231
  <div
232
  class="w-dvh grid divide-gray-200 overflow-hidden bg-gray-100/50 max-md:divide-y md:h-dvh dark:[color-scheme:dark]
 
354
  class="flex flex-1 flex-col gap-6 overflow-y-hidden rounded-xl border border-gray-200/80 bg-gradient-to-b from-white via-white p-3 shadow-sm dark:border-white/5 dark:from-gray-800/40 dark:via-gray-800/40"
355
  >
356
  <PlaygroundModelSelector
357
+ {models}
358
+ conversation={conversations[0]}
359
+ on:click={() => (showModelPickerModal = open)}
360
  />
361
+ <!-- <div
362
  class="group relative -mt-4 flex h-[26px] w-full items-center justify-center gap-2 rounded-lg bg-black px-5 text-sm text-white hover:bg-gray-900 focus:outline-none focus:ring-4 focus:ring-gray-300 dark:border-gray-700 dark:bg-blue-600 dark:hover:bg-blue-700 dark:focus:ring-gray-700"
363
  >
364
  Compare with...
 
381
  <option value={model.id}>{model.id}</option>
382
  {/each}
383
  </select>
384
+ </div> -->
385
 
386
  <PlaygroundOptions bind:conversation={conversations[0]} />
387
  <div class="mt-auto">
src/lib/components/InferencePlayground/InferencePlaygroundCodeSnippets.svelte CHANGED
@@ -253,7 +253,7 @@ print(output.choices[0].message)`
253
  <div class="flex items-center justify-between px-4 pb-4 pt-6">
254
  <h2 class="font-semibold">{label}</h2>
255
  <button
256
- class="flex items-center gap-x-1.5 rounded-md bg-gray-200 px-1.5 py-0.5 transition dark:bg-gray-950 text-sm"
257
  on:click={(e) => {
258
  const el = e.currentTarget;
259
  el.classList.add('text-green-500');
 
253
  <div class="flex items-center justify-between px-4 pb-4 pt-6">
254
  <h2 class="font-semibold">{label}</h2>
255
  <button
256
+ class="flex items-center gap-x-1.5 rounded-md bg-gray-200 px-1.5 py-0.5 text-sm transition dark:bg-gray-950"
257
  on:click={(e) => {
258
  const el = e.currentTarget;
259
  el.classList.add('text-green-500');
src/lib/components/InferencePlayground/InferencePlaygroundGenerationConfig.svelte CHANGED
@@ -13,7 +13,7 @@
13
  $: maxTokens = Math.min(modelMaxLength ?? GENERATION_CONFIG_SETTINGS['max_tokens'].max, 64_000);
14
  </script>
15
 
16
- <div class="flex flex-col gap-y-5 {classNames}">
17
  {#each GENERATION_CONFIG_KEYS as key}
18
  {@const { label, min, step } = GENERATION_CONFIG_SETTINGS[key]}
19
  {@const max = key === 'max_tokens' ? maxTokens : GENERATION_CONFIG_SETTINGS[key].max}
 
13
  $: maxTokens = Math.min(modelMaxLength ?? GENERATION_CONFIG_SETTINGS['max_tokens'].max, 64_000);
14
  </script>
15
 
16
+ <div class="flex flex-col gap-y-7 {classNames}">
17
  {#each GENERATION_CONFIG_KEYS as key}
18
  {@const { label, min, step } = GENERATION_CONFIG_SETTINGS[key]}
19
  {@const max = key === 'max_tokens' ? maxTokens : GENERATION_CONFIG_SETTINGS[key].max}
src/lib/components/InferencePlayground/InferencePlaygroundModelPickerModal.svelte ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <script lang="ts">
2
+ import type { ModelEntryWithTokenizer } from '$lib/types';
3
+ import { createEventDispatcher } from 'svelte';
4
+ import IconSearch from '../Icons/IconSearch.svelte';
5
+ import IconStar from '../Icons/IconStar.svelte';
6
+
7
+ export let models: ModelEntryWithTokenizer[];
8
+
9
+ let backdropEl: HTMLDivElement;
10
+
11
+ const dispatch = createEventDispatcher<{ modelSelected: string; close: void }>();
12
+
13
+ function handleKeydown(event: KeyboardEvent) {
14
+ // close on ESC
15
+ if (event.key === 'Escape') {
16
+ event.preventDefault();
17
+ dispatch('close');
18
+ }
19
+ }
20
+
21
+ function handleBackdropClick(event: MouseEvent) {
22
+ if (window?.getSelection()?.toString()) {
23
+ return;
24
+ }
25
+ if (event.target === backdropEl) {
26
+ dispatch('close');
27
+ }
28
+ }
29
+ </script>
30
+
31
+ <svelte:window on:keydown={handleKeydown} />
32
+
33
+ <div
34
+ class="fixed inset-0 z-10 flex h-screen items-start justify-center bg-black/85 pt-32"
35
+ bind:this={backdropEl}
36
+ on:click|stopPropagation={handleBackdropClick}
37
+ >
38
+ <div class="flex w-full max-w-[600px] items-start justify-center p-10">
39
+ <div
40
+ class="flex h-full w-full flex-col overflow-hidden rounded-lg border bg-white text-gray-900 shadow-md"
41
+ >
42
+ <div class="flex items-center border-b px-3">
43
+ <IconSearch classNames="mr-2 text-sm" />
44
+ <input
45
+ autofocus
46
+ class="flex h-10 w-full rounded-md bg-transparent py-3 text-sm placeholder-gray-400 outline-none"
47
+ placeholder="Search models ..."
48
+ value=""
49
+ />
50
+ </div>
51
+ <div class="max-h-[300px] overflow-y-auto overflow-x-hidden">
52
+ <div class="p-1">
53
+ <div class="px-2 py-1.5 text-xs font-medium text-gray-500">Trending</div>
54
+ <div>
55
+ <div class="flex cursor-pointer items-center px-2 py-1.5 text-sm hover:bg-gray-100">
56
+ <IconStar classNames="lucide lucide-star mr-2 h-4 w-4 text-yellow-400" />
57
+ <span class="inline-flex items-center"
58
+ ><span class="text-gray-500">meta-llama</span><span class="mx-1 text-black">/</span
59
+ ><span class="text-black">Meta-Llama-3-70B-Instruct</span></span
60
+ >
61
+ </div>
62
+ <div class="flex cursor-pointer items-center px-2 py-1.5 text-sm hover:bg-gray-100">
63
+ <IconStar classNames="lucide lucide-star mr-2 h-4 w-4 text-yellow-400" />
64
+ <span class="inline-flex items-center"
65
+ ><span class="text-gray-500">mistralai</span><span class="mx-1 text-black">/</span
66
+ ><span class="text-black">Mixtral-8x7B-Instruct-v0.1</span></span
67
+ >
68
+ </div>
69
+ </div>
70
+ </div>
71
+ <div class="mx-1 h-px bg-gray-200"></div>
72
+ <div class="p-1">
73
+ <div class="px-2 py-1.5 text-xs font-medium text-gray-500">Other Models</div>
74
+ <div>
75
+ {#each models as model}
76
+ {@const [nameSpace, modelName] = model.id.split('/')}
77
+ <button
78
+ class="flex cursor-pointer items-center px-2 py-1.5 text-sm hover:bg-gray-100"
79
+ on:click={() => {
80
+ dispatch('modelSelected', model.id);
81
+ dispatch('close');
82
+ }}
83
+ >
84
+ <span class="inline-flex items-center"
85
+ ><span class="text-gray-500">{nameSpace}</span><span class="mx-1 text-black"
86
+ >/</span
87
+ ><span class="text-black">{modelName}</span></span
88
+ >
89
+ </button>
90
+ {/each}
91
+ </div>
92
+ </div>
93
+ </div>
94
+ </div>
95
+ </div>
96
+ </div>
src/lib/components/InferencePlayground/InferencePlaygroundModelSelector.svelte CHANGED
@@ -1,26 +1,50 @@
1
  <script lang="ts">
2
- import { type ModelEntry } from '@huggingface/hub';
3
- import { createEventDispatcher } from 'svelte';
4
 
5
- export let compatibleModels: ModelEntry[] = [];
 
6
  export let disabled = false;
7
 
8
- const dispatch = createEventDispatcher<{ modelIdxChange: number }>();
 
 
 
 
 
 
 
 
 
 
 
 
9
  </script>
10
 
11
- <div>
12
  <label
13
  for="countries"
14
- class="mb-2 flex items-baseline text-sm font-medium text-gray-900 dark:text-white"
15
- >Models<span class="ml-4 font-normal text-gray-400">{compatibleModels.length}</span>
16
  </label>
17
- <select
18
- {disabled}
19
- class="block w-full rounded-lg border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-blue-500 dark:focus:ring-blue-500"
20
- on:change={(e) => dispatch('modelIdxChange', e.currentTarget.selectedIndex)}
21
  >
22
- {#each compatibleModels as model}
23
- <option value={model.id}>{model.id}</option>
24
- {/each}
25
- </select>
 
 
 
 
 
 
 
 
 
 
 
26
  </div>
 
1
  <script lang="ts">
2
+ import type { Conversation, ModelEntryWithTokenizer } from '$lib/types';
3
+ import IconCaret from '../Icons/IconCaret.svelte';
4
 
5
+ export let models: ModelEntryWithTokenizer[] = [];
6
+ export let conversation: Conversation;
7
  export let disabled = false;
8
 
9
+ async function getAvatarUrl(orgName: string) {
10
+ const url = `https://huggingface.co/api/organizations/${orgName}/avatar`;
11
+ const res = await fetch(url);
12
+ if (!res.ok) {
13
+ console.error(`Error getting avatar url for org: ${orgName}`, res.status, res.statusText);
14
+ return;
15
+ }
16
+ const json = await res.json();
17
+ const { avatarUrl } = json;
18
+ return avatarUrl;
19
+ }
20
+
21
+ $: [nameSpace, modelName] = conversation.model.id.split('/');
22
  </script>
23
 
24
+ <div class="flex flex-col gap-2">
25
  <label
26
  for="countries"
27
+ class="flex items-baseline text-sm font-medium text-gray-900 dark:text-white"
28
+ >Models<span class="ml-4 font-normal text-gray-400">{models.length}</span>
29
  </label>
30
+
31
+ <button
32
+ class="flex items-center justify-between gap-6 overflow-hidden whitespace-nowrap rounded-lg border bg-gray-100/80 px-3 py-1.5 leading-tight shadow dark:bg-gray-700"
33
+ on:click
34
  >
35
+ <div class="flex flex-col items-start">
36
+ <div class="flex items-center gap-1 text-sm text-gray-500 dark:text-gray-300">
37
+ {#await getAvatarUrl(nameSpace) then avatarUrl}
38
+ <img
39
+ class="size-3 flex-none rounded bg-gray-200 object-cover"
40
+ src={avatarUrl}
41
+ alt="{nameSpace} avatar"
42
+ />
43
+ {/await}
44
+ {nameSpace}
45
+ </div>
46
+ <div>{modelName}</div>
47
+ </div>
48
+ <IconCaret classNames="text-xl bg-gray-100 dark:bg-gray-500 rounded" />
49
+ </button>
50
  </div>