New model selector (design only) (#39)
Browse files- src/lib/components/Icons/IconSearch.svelte +21 -0
- src/lib/components/Icons/IconStar.svelte +22 -0
- src/lib/components/InferencePlayground/InferencePlayground.svelte +23 -5
- src/lib/components/InferencePlayground/InferencePlaygroundCodeSnippets.svelte +1 -1
- src/lib/components/InferencePlayground/InferencePlaygroundGenerationConfig.svelte +1 -1
- src/lib/components/InferencePlayground/InferencePlaygroundModelPickerModal.svelte +96 -0
- src/lib/components/InferencePlayground/InferencePlaygroundModelSelector.svelte +39 -15
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 |
-
|
341 |
-
|
|
|
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
|
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-
|
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 {
|
3 |
-
import
|
4 |
|
5 |
-
export let
|
|
|
6 |
export let disabled = false;
|
7 |
|
8 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
9 |
</script>
|
10 |
|
11 |
-
<div>
|
12 |
<label
|
13 |
for="countries"
|
14 |
-
class="
|
15 |
-
>Models<span class="ml-4 font-normal text-gray-400">{
|
16 |
</label>
|
17 |
-
|
18 |
-
|
19 |
-
class="
|
20 |
-
on:
|
21 |
>
|
22 |
-
|
23 |
-
<
|
24 |
-
|
25 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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>
|