mishig HF staff commited on
Commit
5213b80
1 Parent(s): 502cb81
src/app.css CHANGED
@@ -2,7 +2,6 @@
2
  @tailwind components;
3
  @tailwind utilities;
4
 
5
-
6
  html {
7
- font-size: 15px;
8
  }
 
2
  @tailwind components;
3
  @tailwind utilities;
4
 
 
5
  html {
6
+ font-size: 15px;
7
  }
src/lib/components/CodeSnippets.svelte CHANGED
@@ -1,5 +1,5 @@
1
  <script lang="ts">
2
- import { type ChatCompletionInputMessage } from "@huggingface/tasks";
3
 
4
  export let model: string;
5
  export let streaming: Boolean;
 
1
  <script lang="ts">
2
+ import { type ChatCompletionInputMessage } from '@huggingface/tasks';
3
 
4
  export let model: string;
5
  export let streaming: Boolean;
src/lib/components/Conversation.svelte CHANGED
@@ -1,5 +1,5 @@
1
  <script lang="ts">
2
- import { createEventDispatcher } from "svelte";
3
  import PlaygroundCode from '$lib/components/CodeSnippets.svelte';
4
  import PlaygroundMessage from '$lib/components/Message.svelte';
5
 
@@ -22,77 +22,78 @@
22
  }
23
  }
24
 
25
- $:{
26
- if(currentConversation.messages.at(-1)){
27
  scrollToBottom();
28
  }
29
  }
30
  </script>
 
31
  <div
32
- class="flex max-h-[calc(100dvh-5.8rem)] flex-col overflow-y-auto overflow-x-hidden @container"
33
- class:pointer-events-none={loading}
34
- class:animate-pulse={loading && !streamingMessage}
35
- bind:this={messageContainer}
36
  >
37
- {#if conversations.length > 1}
38
- <div
39
- class="sticky top-0 flex h-11 flex-none items-center gap-2 whitespace-nowrap rounded-lg border border-gray-200/80 bg-white pl-3 pr-2 text-sm leading-none shadow-sm *:flex-none dark:border-gray-800 dark:bg-gray-800/70 dark:hover:bg-gray-800"
40
- class:mr-3={index === 0}
41
- class:mx-3={index === 1}
42
- >
43
- <div class="size-3.5 rounded bg-black dark:bg-gray-400"></div>
44
- <div>{conversation.model}</div>
45
- <button
46
- class="ml-auto flex size-6 items-center justify-center rounded bg-gray-50 text-xs hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700"
47
- on:click={() => (conversations = conversations.filter((_, i) => i !== index))}
48
  >
49
-
50
- </button>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
  <button
52
- class=" flex size-6 items-center justify-center rounded bg-gray-50 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700"
 
 
53
  >
54
- <svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 32 32"
55
- ><path
56
- fill="currentColor"
57
- d="M27 16.76v-1.53l1.92-1.68A2 2 0 0 0 29.3 11l-2.36-4a2 2 0 0 0-1.73-1a2 2 0 0 0-.64.1l-2.43.82a11.35 11.35 0 0 0-1.31-.75l-.51-2.52a2 2 0 0 0-2-1.61h-4.68a2 2 0 0 0-2 1.61l-.51 2.52a11.48 11.48 0 0 0-1.32.75l-2.38-.86A2 2 0 0 0 6.79 6a2 2 0 0 0-1.73 1L2.7 11a2 2 0 0 0 .41 2.51L5 15.24v1.53l-1.89 1.68A2 2 0 0 0 2.7 21l2.36 4a2 2 0 0 0 1.73 1a2 2 0 0 0 .64-.1l2.43-.82a11.35 11.35 0 0 0 1.31.75l.51 2.52a2 2 0 0 0 2 1.61h4.72a2 2 0 0 0 2-1.61l.51-2.52a11.48 11.48 0 0 0 1.32-.75l2.42.82a2 2 0 0 0 .64.1a2 2 0 0 0 1.73-1l2.28-4a2 2 0 0 0-.41-2.51ZM25.21 24l-3.43-1.16a8.86 8.86 0 0 1-2.71 1.57L18.36 28h-4.72l-.71-3.55a9.36 9.36 0 0 1-2.7-1.57L6.79 24l-2.36-4l2.72-2.4a8.9 8.9 0 0 1 0-3.13L4.43 12l2.36-4l3.43 1.16a8.86 8.86 0 0 1 2.71-1.57L13.64 4h4.72l.71 3.55a9.36 9.36 0 0 1 2.7 1.57L25.21 8l2.36 4l-2.72 2.4a8.9 8.9 0 0 1 0 3.13L27.57 20Z"
58
- /><path
59
- fill="currentColor"
60
- d="M16 22a6 6 0 1 1 6-6a5.94 5.94 0 0 1-6 6Zm0-10a3.91 3.91 0 0 0-4 4a3.91 3.91 0 0 0 4 4a3.91 3.91 0 0 0 4-4a3.91 3.91 0 0 0-4-4Z"
61
- /></svg
62
- >
 
 
 
 
63
  </button>
64
- </div>
65
- {/if}
66
- {#if !viewCode}
67
- {#each messages as message, i}
68
- <PlaygroundMessage
69
- class="border-b"
70
- {message}
71
- on:delete={() => dispatch("deleteMessage", i)}
72
- autofocus={conversations.length === 1 && !loading && i === messages.length - 1}
73
- />
74
- {/each}
75
-
76
- <button
77
- class="flex px-6 py-6 hover:bg-gray-50 dark:hover:bg-gray-800/50"
78
- on:click={() => dispatch("addMessage")}
79
- disabled={loading}
80
- >
81
- <div class="flex items-center gap-2 !p-0 text-sm font-semibold">
82
- <svg
83
- xmlns="http://www.w3.org/2000/svg"
84
- width="1em"
85
- height="1em"
86
- viewBox="0 0 32 32"
87
- class="text-lg"
88
- ><path
89
- fill="currentColor"
90
- d="M16 2A14.172 14.172 0 0 0 2 16a14.172 14.172 0 0 0 14 14a14.172 14.172 0 0 0 14-14A14.172 14.172 0 0 0 16 2Zm8 15h-7v7h-2v-7H8v-2h7V8h2v7h7Z"
91
- /><path fill="none" d="M24 17h-7v7h-2v-7H8v-2h7V8h2v7h7v2z" /></svg
92
- >Add message
93
- </div>
94
- </button>
95
- {:else}
96
- <PlaygroundCode {...currentConversation} {...currentConversation.config} />
97
- {/if}
98
- </div>
 
1
  <script lang="ts">
2
+ import { createEventDispatcher } from 'svelte';
3
  import PlaygroundCode from '$lib/components/CodeSnippets.svelte';
4
  import PlaygroundMessage from '$lib/components/Message.svelte';
5
 
 
22
  }
23
  }
24
 
25
+ $: {
26
+ if (currentConversation.messages.at(-1)) {
27
  scrollToBottom();
28
  }
29
  }
30
  </script>
31
+
32
  <div
33
+ class="flex max-h-[calc(100dvh-5.8rem)] flex-col overflow-y-auto overflow-x-hidden @container"
34
+ class:pointer-events-none={loading}
35
+ class:animate-pulse={loading && !streamingMessage}
36
+ bind:this={messageContainer}
37
  >
38
+ {#if conversations.length > 1}
39
+ <div
40
+ class="sticky top-0 flex h-11 flex-none items-center gap-2 whitespace-nowrap rounded-lg border border-gray-200/80 bg-white pl-3 pr-2 text-sm leading-none shadow-sm *:flex-none dark:border-gray-800 dark:bg-gray-800/70 dark:hover:bg-gray-800"
41
+ class:mr-3={index === 0}
42
+ class:mx-3={index === 1}
 
 
 
 
 
 
43
  >
44
+ <div class="size-3.5 rounded bg-black dark:bg-gray-400"></div>
45
+ <div>{conversation.model}</div>
46
+ <button
47
+ class="ml-auto flex size-6 items-center justify-center rounded bg-gray-50 text-xs hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700"
48
+ on:click={() => (conversations = conversations.filter((_, i) => i !== index))}
49
+ >
50
+
51
+ </button>
52
+ <button
53
+ class=" flex size-6 items-center justify-center rounded bg-gray-50 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700"
54
+ >
55
+ <svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 32 32"
56
+ ><path
57
+ fill="currentColor"
58
+ d="M27 16.76v-1.53l1.92-1.68A2 2 0 0 0 29.3 11l-2.36-4a2 2 0 0 0-1.73-1a2 2 0 0 0-.64.1l-2.43.82a11.35 11.35 0 0 0-1.31-.75l-.51-2.52a2 2 0 0 0-2-1.61h-4.68a2 2 0 0 0-2 1.61l-.51 2.52a11.48 11.48 0 0 0-1.32.75l-2.38-.86A2 2 0 0 0 6.79 6a2 2 0 0 0-1.73 1L2.7 11a2 2 0 0 0 .41 2.51L5 15.24v1.53l-1.89 1.68A2 2 0 0 0 2.7 21l2.36 4a2 2 0 0 0 1.73 1a2 2 0 0 0 .64-.1l2.43-.82a11.35 11.35 0 0 0 1.31.75l.51 2.52a2 2 0 0 0 2 1.61h4.72a2 2 0 0 0 2-1.61l.51-2.52a11.48 11.48 0 0 0 1.32-.75l2.42.82a2 2 0 0 0 .64.1a2 2 0 0 0 1.73-1l2.28-4a2 2 0 0 0-.41-2.51ZM25.21 24l-3.43-1.16a8.86 8.86 0 0 1-2.71 1.57L18.36 28h-4.72l-.71-3.55a9.36 9.36 0 0 1-2.7-1.57L6.79 24l-2.36-4l2.72-2.4a8.9 8.9 0 0 1 0-3.13L4.43 12l2.36-4l3.43 1.16a8.86 8.86 0 0 1 2.71-1.57L13.64 4h4.72l.71 3.55a9.36 9.36 0 0 1 2.7 1.57L25.21 8l2.36 4l-2.72 2.4a8.9 8.9 0 0 1 0 3.13L27.57 20Z"
59
+ /><path
60
+ fill="currentColor"
61
+ d="M16 22a6 6 0 1 1 6-6a5.94 5.94 0 0 1-6 6Zm0-10a3.91 3.91 0 0 0-4 4a3.91 3.91 0 0 0 4 4a3.91 3.91 0 0 0 4-4a3.91 3.91 0 0 0-4-4Z"
62
+ /></svg
63
+ >
64
+ </button>
65
+ </div>
66
+ {/if}
67
+ {#if !viewCode}
68
+ {#each messages as message, i}
69
+ <PlaygroundMessage
70
+ class="border-b"
71
+ {message}
72
+ on:delete={() => dispatch('deleteMessage', i)}
73
+ autofocus={conversations.length === 1 && !loading && i === messages.length - 1}
74
+ />
75
+ {/each}
76
+
77
  <button
78
+ class="flex px-6 py-6 hover:bg-gray-50 dark:hover:bg-gray-800/50"
79
+ on:click={() => dispatch('addMessage')}
80
+ disabled={loading}
81
  >
82
+ <div class="flex items-center gap-2 !p-0 text-sm font-semibold">
83
+ <svg
84
+ xmlns="http://www.w3.org/2000/svg"
85
+ width="1em"
86
+ height="1em"
87
+ viewBox="0 0 32 32"
88
+ class="text-lg"
89
+ ><path
90
+ fill="currentColor"
91
+ d="M16 2A14.172 14.172 0 0 0 2 16a14.172 14.172 0 0 0 14 14a14.172 14.172 0 0 0 14-14A14.172 14.172 0 0 0 16 2Zm8 15h-7v7h-2v-7H8v-2h7V8h2v7h7Z"
92
+ /><path fill="none" d="M24 17h-7v7h-2v-7H8v-2h7V8h2v7h7v2z" /></svg
93
+ >Add message
94
+ </div>
95
  </button>
96
+ {:else}
97
+ <PlaygroundCode {...currentConversation} {...currentConversation.config} />
98
+ {/if}
99
+ </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/components/Message.svelte CHANGED
@@ -1,6 +1,6 @@
1
  <script lang="ts">
2
  import { createEventDispatcher } from 'svelte';
3
- import { type ChatCompletionInputMessage } from "@huggingface/tasks";
4
 
5
  export let message: ChatCompletionInputMessage;
6
  export let autofocus: boolean = false;
 
1
  <script lang="ts">
2
  import { createEventDispatcher } from 'svelte';
3
+ import { type ChatCompletionInputMessage } from '@huggingface/tasks';
4
 
5
  export let message: ChatCompletionInputMessage;
6
  export let autofocus: boolean = false;
src/lib/components/ModelSelector.svelte CHANGED
@@ -1,5 +1,5 @@
1
  <script lang="ts">
2
- import { type ModelEntry } from "@huggingface/hub";
3
 
4
  export let compatibleModels: ModelEntry[] = [];
5
  export let currentModel = compatibleModels[0];
 
1
  <script lang="ts">
2
+ import { type ModelEntry } from '@huggingface/hub';
3
 
4
  export let compatibleModels: ModelEntry[] = [];
5
  export let currentModel = compatibleModels[0];
src/lib/components/playgroundUtils.ts CHANGED
@@ -1,11 +1,14 @@
1
- import { type ChatCompletionInputMessage } from "@huggingface/tasks";
2
  import { HfInference } from '@huggingface/inference';
3
 
4
  export function createHfInference(token: string): HfInference {
5
  return new HfInference(token);
6
  }
7
 
8
- export function prepareRequestMessages(systemMessage: ChatCompletionInputMessage, messages: ChatCompletionInputMessage[]): ChatCompletionInputMessage[] {
 
 
 
9
  return [...(systemMessage.content.length ? [systemMessage] : []), ...messages];
10
  }
11
 
@@ -21,13 +24,16 @@ export async function handleStreamingResponse(
21
  ): Promise<void> {
22
  let out = '';
23
  try {
24
- for await (const chunk of hf.chatCompletionStream({
25
- model: model,
26
- messages: messages,
27
- temperature: temperature,
28
- max_tokens: maxTokens,
29
- json_mode: jsonMode
30
- }, { signal: abortController.signal })) {
 
 
 
31
  if (chunk.choices && chunk.choices.length > 0 && chunk.choices[0]?.delta?.content) {
32
  out += chunk.choices[0].delta.content;
33
  onChunk(out);
 
1
+ import { type ChatCompletionInputMessage } from '@huggingface/tasks';
2
  import { HfInference } from '@huggingface/inference';
3
 
4
  export function createHfInference(token: string): HfInference {
5
  return new HfInference(token);
6
  }
7
 
8
+ export function prepareRequestMessages(
9
+ systemMessage: ChatCompletionInputMessage,
10
+ messages: ChatCompletionInputMessage[]
11
+ ): ChatCompletionInputMessage[] {
12
  return [...(systemMessage.content.length ? [systemMessage] : []), ...messages];
13
  }
14
 
 
24
  ): Promise<void> {
25
  let out = '';
26
  try {
27
+ for await (const chunk of hf.chatCompletionStream(
28
+ {
29
+ model: model,
30
+ messages: messages,
31
+ temperature: temperature,
32
+ max_tokens: maxTokens,
33
+ json_mode: jsonMode
34
+ },
35
+ { signal: abortController.signal }
36
+ )) {
37
  if (chunk.choices && chunk.choices.length > 0 && chunk.choices[0]?.delta?.content) {
38
  out += chunk.choices[0].delta.content;
39
  onChunk(out);
src/lib/types/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import type { ModelEntry } from "@huggingface/hub";
2
- import type { ChatCompletionInputMessage } from "@huggingface/tasks";
3
 
4
  type Model = string;
5
 
 
1
+ import type { ModelEntry } from '@huggingface/hub';
2
+ import type { ChatCompletionInputMessage } from '@huggingface/tasks';
3
 
4
  type Model = string;
5
 
src/routes/+page.svelte CHANGED
@@ -11,9 +11,8 @@
11
  import PlaygroundModelSelector from '$lib/components/ModelSelector.svelte';
12
  import Conversation from '$lib/components/Conversation.svelte';
13
  import { onDestroy, onMount } from 'svelte';
14
- import { type ModelEntry } from "@huggingface/hub";
15
- import { type ChatCompletionInputMessage } from "@huggingface/tasks";
16
-
17
 
18
  let compatibleModels: ModelEntry[] = [];
19
 
@@ -49,11 +48,13 @@
49
  onMount(() => {
50
  (async () => {
51
  // TODO: use hfjs.hub listModels after https://github.com/huggingface/huggingface.js/pull/795
52
- const res = await fetch("https://huggingface.co/api/models?pipeline_tag=text-generation&inferenceStatus=loaded&filter=conversational");
53
- compatibleModels = await res.json() as ModelEntry[];
 
 
54
  compatibleModels.sort((a, b) => a.id.toLowerCase().localeCompare(b.id.toLowerCase()));
55
  })();
56
- })
57
 
58
  onDestroy(() => {
59
  if (abortController) {
@@ -80,11 +81,14 @@
80
  }
81
 
82
  function deleteMessage(idx: number) {
83
- const deletedMsg = deleteAndGetItem<ChatCompletionInputMessage>(currentConversation.messages, idx);
 
 
 
84
  // delete messages in user/assistant pairs. otherwise, the chat template will be broken
85
- if(deletedMsg){
86
  const { role } = deletedMsg;
87
- const pairIdx = role === "user" ? idx : idx - 1;
88
  deleteAndGetItem<ChatCompletionInputMessage>(currentConversation.messages, pairIdx);
89
  }
90
  conversations = conversations;
@@ -96,7 +100,7 @@
96
  conversations = conversations;
97
  }
98
 
99
- function abort(){
100
  if (streamingMessage && abortController) {
101
  abortController.abort();
102
  abortController = null;
@@ -108,7 +112,7 @@
108
 
109
  async function submit() {
110
  // last message has to be from user
111
- if(currentConversation.messages?.at(-1)?.role !== "user"){
112
  addMessage();
113
  return;
114
  }
@@ -157,7 +161,7 @@
157
  currentConversation.config.jsonMode
158
  );
159
  // check if the user did not abort the request
160
- if(waitForNonStreaming){
161
  currentConversation.messages = [...currentConversation.messages, newMessage];
162
  conversations = conversations;
163
  }
@@ -241,7 +245,6 @@
241
  on:addMessage={addMessage}
242
  on:deleteMessage={(e) => deleteMessage(e.detail)}
243
  />
244
-
245
  {/each}
246
  </div>
247
  <div
@@ -311,7 +314,9 @@
311
  loading ? abort() : submit();
312
  }}
313
  type="button"
314
- class="flex h-[39px] w-24 items-center justify-center gap-2 rounded-lg px-5 py-2.5 text-sm font-medium text-white focus:outline-none focus:ring-4 focus:ring-gray-300 dark:border-gray-700 dark:focus:ring-gray-700 {loading ? 'bg-red-900 hover:bg-red-800 dark:bg-red-600 dark:hover:bg-red-700' : 'bg-black hover:bg-gray-900 dark:bg-blue-600 dark:hover:bg-blue-700'}"
 
 
315
  >
316
  {#if loading}
317
  <div class="flex flex-none items-center gap-[3px]">
 
11
  import PlaygroundModelSelector from '$lib/components/ModelSelector.svelte';
12
  import Conversation from '$lib/components/Conversation.svelte';
13
  import { onDestroy, onMount } from 'svelte';
14
+ import { type ModelEntry } from '@huggingface/hub';
15
+ import { type ChatCompletionInputMessage } from '@huggingface/tasks';
 
16
 
17
  let compatibleModels: ModelEntry[] = [];
18
 
 
48
  onMount(() => {
49
  (async () => {
50
  // TODO: use hfjs.hub listModels after https://github.com/huggingface/huggingface.js/pull/795
51
+ const res = await fetch(
52
+ 'https://huggingface.co/api/models?pipeline_tag=text-generation&inferenceStatus=loaded&filter=conversational'
53
+ );
54
+ compatibleModels = (await res.json()) as ModelEntry[];
55
  compatibleModels.sort((a, b) => a.id.toLowerCase().localeCompare(b.id.toLowerCase()));
56
  })();
57
+ });
58
 
59
  onDestroy(() => {
60
  if (abortController) {
 
81
  }
82
 
83
  function deleteMessage(idx: number) {
84
+ const deletedMsg = deleteAndGetItem<ChatCompletionInputMessage>(
85
+ currentConversation.messages,
86
+ idx
87
+ );
88
  // delete messages in user/assistant pairs. otherwise, the chat template will be broken
89
+ if (deletedMsg) {
90
  const { role } = deletedMsg;
91
+ const pairIdx = role === 'user' ? idx : idx - 1;
92
  deleteAndGetItem<ChatCompletionInputMessage>(currentConversation.messages, pairIdx);
93
  }
94
  conversations = conversations;
 
100
  conversations = conversations;
101
  }
102
 
103
+ function abort() {
104
  if (streamingMessage && abortController) {
105
  abortController.abort();
106
  abortController = null;
 
112
 
113
  async function submit() {
114
  // last message has to be from user
115
+ if (currentConversation.messages?.at(-1)?.role !== 'user') {
116
  addMessage();
117
  return;
118
  }
 
161
  currentConversation.config.jsonMode
162
  );
163
  // check if the user did not abort the request
164
+ if (waitForNonStreaming) {
165
  currentConversation.messages = [...currentConversation.messages, newMessage];
166
  conversations = conversations;
167
  }
 
245
  on:addMessage={addMessage}
246
  on:deleteMessage={(e) => deleteMessage(e.detail)}
247
  />
 
248
  {/each}
249
  </div>
250
  <div
 
314
  loading ? abort() : submit();
315
  }}
316
  type="button"
317
+ class="flex h-[39px] w-24 items-center justify-center gap-2 rounded-lg px-5 py-2.5 text-sm font-medium text-white focus:outline-none focus:ring-4 focus:ring-gray-300 dark:border-gray-700 dark:focus:ring-gray-700 {loading
318
+ ? 'bg-red-900 hover:bg-red-800 dark:bg-red-600 dark:hover:bg-red-700'
319
+ : 'bg-black hover:bg-gray-900 dark:bg-blue-600 dark:hover:bg-blue-700'}"
320
  >
321
  {#if loading}
322
  <div class="flex flex-none items-center gap-[3px]">