Refactor (#40)
Browse files- .prettierrc +0 -8
- .prettierrc.mjs +11 -0
- postcss.config.js +2 -2
- src/app.css +1 -1
- src/lib/components/Icons/IconCaret.svelte +2 -5
- src/lib/components/Icons/IconCode.svelte +1 -1
- src/lib/components/Icons/IconCopyCode.svelte +1 -1
- src/lib/components/Icons/IconCross.svelte +1 -1
- src/lib/components/Icons/IconDelete.svelte +2 -5
- src/lib/components/Icons/IconPlus.svelte +2 -7
- src/lib/components/Icons/IconSearch.svelte +2 -5
- src/lib/components/Icons/IconShare.svelte +2 -9
- src/lib/components/Icons/IconStar.svelte +1 -1
- src/lib/components/InferencePlayground/InferencePlayground.svelte +110 -246
- src/lib/components/InferencePlayground/InferencePlaygroundCodeSnippets.svelte +54 -54
- src/lib/components/InferencePlayground/InferencePlaygroundConversation.svelte +8 -48
- src/lib/components/InferencePlayground/InferencePlaygroundGenerationConfig.svelte +11 -14
- src/lib/components/InferencePlayground/InferencePlaygroundHFTokenModal.svelte +11 -15
- src/lib/components/InferencePlayground/InferencePlaygroundMessage.svelte +5 -12
- src/lib/components/InferencePlayground/InferencePlaygroundModelSelector.svelte +25 -13
- src/lib/components/InferencePlayground/{InferencePlaygroundModelPickerModal.svelte → InferencePlaygroundModelSelectorModal.svelte} +20 -20
- src/lib/components/InferencePlayground/generationConfigSettings.ts +8 -12
- src/lib/components/InferencePlayground/inferencePlaygroundUtils.ts +12 -16
- src/lib/types/index.d.ts +3 -4
- src/routes/+layout.svelte +1 -1
- src/routes/+page.server.ts +9 -12
- src/routes/+page.svelte +1 -1
- svelte.config.js +4 -4
- tailwind.config.ts +5 -5
- vite.config.ts +3 -3
.prettierrc
DELETED
@@ -1,8 +0,0 @@
|
|
1 |
-
{
|
2 |
-
"useTabs": true,
|
3 |
-
"singleQuote": true,
|
4 |
-
"trailingComma": "none",
|
5 |
-
"printWidth": 100,
|
6 |
-
"plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"],
|
7 |
-
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
|
8 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.prettierrc.mjs
ADDED
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
export default {
|
2 |
+
arrowParens: "avoid",
|
3 |
+
quoteProps: "consistent",
|
4 |
+
trailingComma: "es5",
|
5 |
+
useTabs: true,
|
6 |
+
tabWidth: 2,
|
7 |
+
printWidth: 120,
|
8 |
+
overrides: [{ files: "*.svelte", options: { parser: "svelte" } }],
|
9 |
+
tailwindConfig: "./tailwind.config.ts",
|
10 |
+
plugins: [import("prettier-plugin-svelte"), import("prettier-plugin-tailwindcss")],
|
11 |
+
};
|
postcss.config.js
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
export default {
|
2 |
plugins: {
|
3 |
tailwindcss: {},
|
4 |
-
autoprefixer: {}
|
5 |
-
}
|
6 |
};
|
|
|
1 |
export default {
|
2 |
plugins: {
|
3 |
tailwindcss: {},
|
4 |
+
autoprefixer: {},
|
5 |
+
},
|
6 |
};
|
src/app.css
CHANGED
@@ -1,4 +1,4 @@
|
|
1 |
-
@import
|
2 |
@tailwind base;
|
3 |
@tailwind components;
|
4 |
@tailwind utilities;
|
|
|
1 |
+
@import "highlight.js/styles/atom-one-light";
|
2 |
@tailwind base;
|
3 |
@tailwind components;
|
4 |
@tailwind utilities;
|
src/lib/components/Icons/IconCaret.svelte
CHANGED
@@ -1,5 +1,5 @@
|
|
1 |
<script lang="ts">
|
2 |
-
export let classNames =
|
3 |
</script>
|
4 |
|
5 |
<svg
|
@@ -12,8 +12,5 @@
|
|
12 |
height="1em"
|
13 |
preserveAspectRatio="xMidYMid meet"
|
14 |
viewBox="0 0 24 24"
|
15 |
-
><path
|
16 |
-
d="M16.293 9.293L12 13.586L7.707 9.293l-1.414 1.414L12 16.414l5.707-5.707z"
|
17 |
-
fill="currentColor"
|
18 |
-
/>
|
19 |
</svg>
|
|
|
1 |
<script lang="ts">
|
2 |
+
export let classNames = "";
|
3 |
</script>
|
4 |
|
5 |
<svg
|
|
|
12 |
height="1em"
|
13 |
preserveAspectRatio="xMidYMid meet"
|
14 |
viewBox="0 0 24 24"
|
15 |
+
><path d="M16.293 9.293L12 13.586L7.707 9.293l-1.414 1.414L12 16.414l5.707-5.707z" fill="currentColor" />
|
|
|
|
|
|
|
16 |
</svg>
|
src/lib/components/Icons/IconCode.svelte
CHANGED
@@ -1,5 +1,5 @@
|
|
1 |
<script lang="ts">
|
2 |
-
export let classNames =
|
3 |
</script>
|
4 |
|
5 |
<svg
|
|
|
1 |
<script lang="ts">
|
2 |
+
export let classNames = "";
|
3 |
</script>
|
4 |
|
5 |
<svg
|
src/lib/components/Icons/IconCopyCode.svelte
CHANGED
@@ -1,5 +1,5 @@
|
|
1 |
<script lang="ts">
|
2 |
-
export let classNames =
|
3 |
</script>
|
4 |
|
5 |
<svg
|
|
|
1 |
<script lang="ts">
|
2 |
+
export let classNames = "";
|
3 |
</script>
|
4 |
|
5 |
<svg
|
src/lib/components/Icons/IconCross.svelte
CHANGED
@@ -1,5 +1,5 @@
|
|
1 |
<script lang="ts">
|
2 |
-
export let classNames =
|
3 |
</script>
|
4 |
|
5 |
<svg
|
|
|
1 |
<script lang="ts">
|
2 |
+
export let classNames = "";
|
3 |
</script>
|
4 |
|
5 |
<svg
|
src/lib/components/Icons/IconDelete.svelte
CHANGED
@@ -1,5 +1,5 @@
|
|
1 |
<script lang="ts">
|
2 |
-
export let classNames =
|
3 |
</script>
|
4 |
|
5 |
<svg
|
@@ -14,10 +14,7 @@
|
|
14 |
preserveAspectRatio="xMidYMid meet"
|
15 |
viewBox="0 0 32 32"
|
16 |
>
|
17 |
-
<path d="M12 12h2v12h-2z" fill="currentColor" /><path
|
18 |
-
d="M18 12h2v12h-2z"
|
19 |
-
fill="currentColor"
|
20 |
-
/><path
|
21 |
d="M4 6v2h2v20a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V8h2V6zm4 22V8h16v20z"
|
22 |
fill="currentColor"
|
23 |
/><path d="M12 2h8v2h-8z" fill="currentColor" />
|
|
|
1 |
<script lang="ts">
|
2 |
+
export let classNames = "";
|
3 |
</script>
|
4 |
|
5 |
<svg
|
|
|
14 |
preserveAspectRatio="xMidYMid meet"
|
15 |
viewBox="0 0 32 32"
|
16 |
>
|
17 |
+
<path d="M12 12h2v12h-2z" fill="currentColor" /><path d="M18 12h2v12h-2z" fill="currentColor" /><path
|
|
|
|
|
|
|
18 |
d="M4 6v2h2v20a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V8h2V6zm4 22V8h16v20z"
|
19 |
fill="currentColor"
|
20 |
/><path d="M12 2h8v2h-8z" fill="currentColor" />
|
src/lib/components/Icons/IconPlus.svelte
CHANGED
@@ -1,13 +1,8 @@
|
|
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 |
-
width="1em"
|
9 |
-
height="1em"
|
10 |
-
viewBox="0 0 32 32"
|
11 |
><path
|
12 |
fill="currentColor"
|
13 |
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"
|
|
|
1 |
<script lang="ts">
|
2 |
+
export let classNames = "";
|
3 |
</script>
|
4 |
|
5 |
+
<svg class={classNames} xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 32 32"
|
|
|
|
|
|
|
|
|
|
|
6 |
><path
|
7 |
fill="currentColor"
|
8 |
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"
|
src/lib/components/Icons/IconSearch.svelte
CHANGED
@@ -1,5 +1,5 @@
|
|
1 |
<script lang="ts">
|
2 |
-
export let classNames =
|
3 |
</script>
|
4 |
|
5 |
<svg
|
@@ -14,8 +14,5 @@
|
|
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>
|
|
|
1 |
<script lang="ts">
|
2 |
+
export let classNames = "";
|
3 |
</script>
|
4 |
|
5 |
<svg
|
|
|
14 |
preserveAspectRatio="xMidYMid meet"
|
15 |
viewBox="0 0 32 32"
|
16 |
>
|
17 |
+
<path 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" fill="currentColor" />
|
|
|
|
|
|
|
18 |
</svg>
|
src/lib/components/Icons/IconShare.svelte
CHANGED
@@ -1,15 +1,8 @@
|
|
1 |
<script lang="ts">
|
2 |
-
export let classNames =
|
3 |
</script>
|
4 |
|
5 |
-
<svg
|
6 |
-
class={classNames}
|
7 |
-
width="1em"
|
8 |
-
height="1em"
|
9 |
-
viewBox="0 0 24 25"
|
10 |
-
fill="none"
|
11 |
-
xmlns="http://www.w3.org/2000/svg"
|
12 |
-
>
|
13 |
<path
|
14 |
fill-rule="evenodd"
|
15 |
clip-rule="evenodd"
|
|
|
1 |
<script lang="ts">
|
2 |
+
export let classNames = "";
|
3 |
</script>
|
4 |
|
5 |
+
<svg class={classNames} width="1em" height="1em" viewBox="0 0 24 25" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6 |
<path
|
7 |
fill-rule="evenodd"
|
8 |
clip-rule="evenodd"
|
src/lib/components/Icons/IconStar.svelte
CHANGED
@@ -1,5 +1,5 @@
|
|
1 |
<script lang="ts">
|
2 |
-
export let classNames =
|
3 |
</script>
|
4 |
|
5 |
<svg
|
|
|
1 |
<script lang="ts">
|
2 |
+
export let classNames = "";
|
3 |
</script>
|
4 |
|
5 |
<svg
|
src/lib/components/InferencePlayground/InferencePlayground.svelte
CHANGED
@@ -3,164 +3,73 @@
|
|
3 |
createHfInference,
|
4 |
handleStreamingResponse,
|
5 |
handleNonStreamingResponse,
|
6 |
-
isSystemPromptSupported
|
7 |
-
} from
|
8 |
-
import
|
9 |
-
import
|
10 |
-
import
|
11 |
-
import
|
12 |
-
import
|
13 |
-
import {
|
14 |
-
import {
|
15 |
-
import
|
16 |
-
import
|
17 |
-
import
|
18 |
-
import
|
19 |
-
import IconCode from '../Icons/IconCode.svelte';
|
20 |
|
21 |
export let models: ModelEntryWithTokenizer[];
|
22 |
|
23 |
-
const startMessages: ChatCompletionInputMessage[] = [{ role:
|
24 |
|
25 |
-
let
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
streaming: true
|
32 |
-
}
|
33 |
-
];
|
34 |
-
|
35 |
-
$: if (conversations.length > 1) {
|
36 |
-
viewCode = false;
|
37 |
-
}
|
38 |
|
39 |
-
let systemMessage: ChatCompletionInputMessage = { role:
|
40 |
-
let hfToken: string |
|
41 |
let viewCode = false;
|
42 |
let showTokenModal = false;
|
43 |
-
let showModelPickerModal = false;
|
44 |
let loading = false;
|
45 |
-
let tokens = 0;
|
46 |
let latency = 0;
|
47 |
-
let
|
48 |
let waitForNonStreaming = true;
|
49 |
|
50 |
-
$: systemPromptSupported = isSystemPromptSupported(
|
51 |
|
52 |
onDestroy(() => {
|
53 |
-
|
54 |
-
abortController.abort();
|
55 |
-
}
|
56 |
});
|
57 |
|
58 |
function addMessage() {
|
59 |
-
|
60 |
-
conversation.messages
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
];
|
67 |
-
return conversation;
|
68 |
-
});
|
69 |
-
}
|
70 |
-
|
71 |
-
function updateMessage(value: string, conversationIdx: number, messageIdx: number) {
|
72 |
-
const lastMsgIdx = conversations[0].messages.length - 1;
|
73 |
-
const msg = conversations[conversationIdx].messages[messageIdx];
|
74 |
-
msg.content = value;
|
75 |
-
const { role } = msg;
|
76 |
-
if (messageIdx === lastMsgIdx && role === 'user') {
|
77 |
-
conversations = conversations.map((conversation) => {
|
78 |
-
conversation.messages[messageIdx].content = value;
|
79 |
-
return conversation;
|
80 |
-
});
|
81 |
-
}
|
82 |
-
conversations = conversations;
|
83 |
-
}
|
84 |
-
|
85 |
-
function deleteAndGetItem<T>(array: T[], index: number) {
|
86 |
-
if (index >= 0 && index < array.length) {
|
87 |
-
return array.splice(index, 1)[0];
|
88 |
-
}
|
89 |
-
return undefined;
|
90 |
}
|
91 |
|
92 |
function deleteMessage(idx: number) {
|
93 |
-
|
94 |
-
|
95 |
-
return conversation;
|
96 |
-
});
|
97 |
-
}
|
98 |
-
|
99 |
-
function deleteConversation(idx: number) {
|
100 |
-
deleteAndGetItem(conversations, idx);
|
101 |
-
conversations = conversations;
|
102 |
}
|
103 |
|
104 |
function reset() {
|
105 |
-
systemMessage.content =
|
106 |
-
|
107 |
-
conversation.messages = [...startMessages];
|
108 |
-
return conversation;
|
109 |
-
});
|
110 |
}
|
111 |
|
112 |
function abort() {
|
113 |
-
|
114 |
-
|
115 |
-
abortController.abort();
|
116 |
-
}
|
117 |
-
abortControllers = [];
|
118 |
-
}
|
119 |
loading = false;
|
120 |
waitForNonStreaming = false;
|
121 |
}
|
122 |
|
123 |
-
async function runInference(conversation: Conversation) {
|
124 |
-
const startTime = performance.now();
|
125 |
-
const hf = createHfInference(hfToken);
|
126 |
-
const requestMessages = [
|
127 |
-
...(systemPromptSupported && systemMessage?.content?.length ? [systemMessage] : []),
|
128 |
-
...conversation.messages
|
129 |
-
];
|
130 |
-
|
131 |
-
if (conversation.streaming) {
|
132 |
-
const streamingMessage = { role: 'assistant', content: '' };
|
133 |
-
conversation.messages = [...conversation.messages, streamingMessage];
|
134 |
-
const abortController = new AbortController();
|
135 |
-
abortControllers.push(abortController);
|
136 |
-
|
137 |
-
await handleStreamingResponse(
|
138 |
-
hf,
|
139 |
-
conversation,
|
140 |
-
(content) => {
|
141 |
-
if (streamingMessage) {
|
142 |
-
streamingMessage.content = content;
|
143 |
-
conversation.messages = [...conversation.messages];
|
144 |
-
conversations = conversations;
|
145 |
-
}
|
146 |
-
},
|
147 |
-
abortController,
|
148 |
-
systemMessage
|
149 |
-
);
|
150 |
-
} else {
|
151 |
-
waitForNonStreaming = true;
|
152 |
-
const newMessage = await handleNonStreamingResponse(hf, conversation, systemMessage);
|
153 |
-
// check if the user did not abort the request
|
154 |
-
if (waitForNonStreaming) {
|
155 |
-
conversation.messages = [...conversation.messages, newMessage];
|
156 |
-
conversations = conversations;
|
157 |
-
}
|
158 |
-
}
|
159 |
-
|
160 |
-
const endTime = performance.now();
|
161 |
-
latency = Math.round(endTime - startTime);
|
162 |
-
}
|
163 |
-
|
164 |
async function submit() {
|
165 |
// // last message has to be from user
|
166 |
// if (currentConversation.messages?.at(-1)?.role !== 'user') {
|
@@ -175,66 +84,71 @@
|
|
175 |
loading = true;
|
176 |
|
177 |
try {
|
178 |
-
const
|
179 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
180 |
addMessage();
|
181 |
} catch (error) {
|
182 |
-
if (error.name !==
|
183 |
-
alert(
|
184 |
}
|
185 |
} finally {
|
186 |
loading = false;
|
187 |
-
|
188 |
}
|
189 |
}
|
190 |
|
191 |
function onKeydown(event: KeyboardEvent) {
|
192 |
-
if (!event.shiftKey && event.key ===
|
193 |
submit();
|
194 |
}
|
195 |
}
|
196 |
-
|
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}
|
211 |
-
<
|
212 |
on:close={() => (showTokenModal = false)}
|
213 |
-
on:submit={
|
214 |
const formData = new FormData(e.target);
|
215 |
-
hfToken = formData.get(
|
216 |
submit();
|
217 |
showTokenModal = false;
|
218 |
}}
|
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]
|
233 |
-
{conversations.length === 1
|
234 |
-
? 'md:grid-cols-[clamp(220px,20%,350px),minmax(0,1fr),clamp(270px,25%,300px)]'
|
235 |
-
: 'md:grid-cols-[clamp(220px,20%,350px),minmax(0,1fr),0]'}
|
236 |
-
|
237 |
-
dark:divide-gray-800 dark:bg-gray-900 dark:text-gray-300"
|
238 |
>
|
239 |
<div class=" flex flex-col overflow-y-auto py-3 pr-3">
|
240 |
<div
|
@@ -247,37 +161,23 @@
|
|
247 |
name=""
|
248 |
id=""
|
249 |
placeholder={systemPromptSupported
|
250 |
-
?
|
251 |
-
:
|
252 |
bind:value={systemMessage.content}
|
253 |
class="absolute inset-x-0 bottom-0 h-full resize-none bg-transparent px-3 pt-10 text-sm outline-none"
|
254 |
></textarea>
|
255 |
</div>
|
256 |
</div>
|
257 |
<div class="relative divide-y divide-gray-200 pt-3 dark:divide-gray-800" on:keydown={onKeydown}>
|
258 |
-
<div
|
259 |
-
|
260 |
-
|
261 |
-
|
262 |
-
|
263 |
-
|
264 |
-
|
265 |
-
|
266 |
-
|
267 |
-
{loading}
|
268 |
-
{conversation}
|
269 |
-
{index}
|
270 |
-
{viewCode}
|
271 |
-
sideBySide={conversations.length > 1}
|
272 |
-
on:addMessage={addMessage}
|
273 |
-
on:messageValueChanged={(e) => {
|
274 |
-
const { conversationIdx, messageIdx, value } = e.detail;
|
275 |
-
updateMessage(value, conversationIdx, messageIdx);
|
276 |
-
}}
|
277 |
-
on:deleteMessage={(e) => deleteMessage(e.detail)}
|
278 |
-
on:deleteConversation={(e) => deleteConversation(e.detail)}
|
279 |
-
/>
|
280 |
-
{/each}
|
281 |
</div>
|
282 |
<div
|
283 |
class="fixed inset-x-0 bottom-0 flex h-20 items-center gap-2 overflow-hidden whitespace-nowrap px-3 md:absolute"
|
@@ -286,9 +186,7 @@
|
|
286 |
type="button"
|
287 |
class="flex h-[39px] flex-none gap-2 rounded-lg border border-gray-200 bg-white px-3 py-2.5 text-sm font-medium text-gray-900 hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:outline-none focus:ring-4 focus:ring-gray-100 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white dark:focus:ring-gray-700"
|
288 |
>
|
289 |
-
<div
|
290 |
-
class="flex size-5 items-center justify-center rounded border border-black/5 bg-black/5 text-xs"
|
291 |
-
>
|
292 |
<IconShare />
|
293 |
</div>
|
294 |
|
@@ -311,7 +209,7 @@
|
|
311 |
class="flex h-[39px] items-center gap-2 rounded-lg border border-gray-200 bg-white px-3 py-2.5 text-sm font-medium text-gray-900 hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:outline-none focus:ring-4 focus:ring-gray-100 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white dark:focus:ring-gray-700"
|
312 |
>
|
313 |
<IconCode />
|
314 |
-
{!viewCode ?
|
315 |
>
|
316 |
<button
|
317 |
on:click={() => {
|
@@ -340,68 +238,34 @@
|
|
340 |
/>
|
341 |
</div>
|
342 |
{:else}
|
343 |
-
Run <span
|
344 |
-
class="inline-flex gap-0.5 rounded border border-white/20 bg-white/10 px-0.5 text-xs text-white/70"
|
345 |
>↵</span
|
346 |
>
|
347 |
{/if}
|
348 |
</button>
|
349 |
</div>
|
350 |
</div>
|
351 |
-
|
352 |
-
<div
|
353 |
-
|
354 |
-
|
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...
|
365 |
-
<IconCaret classNames="opacity-70" />
|
366 |
-
<select
|
367 |
-
class="absolute inset-0 border-none bg-white text-base opacity-0 outline-none"
|
368 |
-
on:change|preventDefault={(e) => {
|
369 |
-
conversations = [
|
370 |
-
...conversations,
|
371 |
-
{
|
372 |
-
id: String(Math.random()),
|
373 |
-
model: e.target.value,
|
374 |
-
config: { temperature: 0.5, maxTokens: 2048, streaming: true },
|
375 |
-
messages: [...conversations[0].messages]
|
376 |
-
}
|
377 |
-
];
|
378 |
-
}}
|
379 |
-
>
|
380 |
-
{#each models as model}
|
381 |
-
<option value={model.id}>{model.id}</option>
|
382 |
-
{/each}
|
383 |
-
</select>
|
384 |
-
</div> -->
|
385 |
|
386 |
-
|
387 |
-
|
388 |
-
|
389 |
-
|
390 |
-
|
391 |
-
|
392 |
-
>
|
393 |
-
|
394 |
-
class="rounded bg-gray-100 px-1.5 py-0.5 text-xs font-medium text-gray-800 dark:bg-gray-700 dark:text-gray-300"
|
395 |
-
>Free</span
|
396 |
-
>
|
397 |
|
398 |
-
|
399 |
-
|
400 |
-
|
401 |
-
|
402 |
-
</div>
|
403 |
</div>
|
404 |
</div>
|
405 |
</div>
|
406 |
-
|
407 |
</div>
|
|
|
3 |
createHfInference,
|
4 |
handleStreamingResponse,
|
5 |
handleNonStreamingResponse,
|
6 |
+
isSystemPromptSupported,
|
7 |
+
} from "./inferencePlaygroundUtils";
|
8 |
+
import GenerationConfig from "./InferencePlaygroundGenerationConfig.svelte";
|
9 |
+
import HFTokenModal from "./InferencePlaygroundHFTokenModal.svelte";
|
10 |
+
import ModelSelector from "./InferencePlaygroundModelSelector.svelte";
|
11 |
+
import Conversation from "./InferencePlaygroundConversation.svelte";
|
12 |
+
import { onDestroy } from "svelte";
|
13 |
+
import { type ChatCompletionInputMessage } from "@huggingface/tasks";
|
14 |
+
import type { ModelEntryWithTokenizer } from "$lib/types";
|
15 |
+
import { defaultGenerationConfig } from "./generationConfigSettings";
|
16 |
+
import IconShare from "../Icons/IconShare.svelte";
|
17 |
+
import IconDelete from "../Icons/IconDelete.svelte";
|
18 |
+
import IconCode from "../Icons/IconCode.svelte";
|
|
|
19 |
|
20 |
export let models: ModelEntryWithTokenizer[];
|
21 |
|
22 |
+
const startMessages: ChatCompletionInputMessage[] = [{ role: "user", content: "" }];
|
23 |
|
24 |
+
let conversation: Conversation = {
|
25 |
+
model: models[0],
|
26 |
+
config: defaultGenerationConfig,
|
27 |
+
messages: startMessages,
|
28 |
+
streaming: true,
|
29 |
+
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
30 |
|
31 |
+
let systemMessage: ChatCompletionInputMessage = { role: "system", content: "" };
|
32 |
+
let hfToken: string | undefined = import.meta.env.VITE_HF_TOKEN;
|
33 |
let viewCode = false;
|
34 |
let showTokenModal = false;
|
|
|
35 |
let loading = false;
|
|
|
36 |
let latency = 0;
|
37 |
+
let abortController: AbortController | undefined = undefined;
|
38 |
let waitForNonStreaming = true;
|
39 |
|
40 |
+
$: systemPromptSupported = isSystemPromptSupported(conversation.model);
|
41 |
|
42 |
onDestroy(() => {
|
43 |
+
abortController?.abort();
|
|
|
|
|
44 |
});
|
45 |
|
46 |
function addMessage() {
|
47 |
+
conversation.messages = [
|
48 |
+
...conversation.messages,
|
49 |
+
{
|
50 |
+
role: conversation.messages.at(-1)?.role === "user" ? "assistant" : "user",
|
51 |
+
content: "",
|
52 |
+
},
|
53 |
+
];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
54 |
}
|
55 |
|
56 |
function deleteMessage(idx: number) {
|
57 |
+
conversation.messages.splice(idx, 1)[0];
|
58 |
+
conversation = conversation;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
59 |
}
|
60 |
|
61 |
function reset() {
|
62 |
+
systemMessage.content = "";
|
63 |
+
conversation.messages = [...startMessages];
|
|
|
|
|
|
|
64 |
}
|
65 |
|
66 |
function abort() {
|
67 |
+
abortController?.abort();
|
68 |
+
abortController = undefined;
|
|
|
|
|
|
|
|
|
69 |
loading = false;
|
70 |
waitForNonStreaming = false;
|
71 |
}
|
72 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
73 |
async function submit() {
|
74 |
// // last message has to be from user
|
75 |
// if (currentConversation.messages?.at(-1)?.role !== 'user') {
|
|
|
84 |
loading = true;
|
85 |
|
86 |
try {
|
87 |
+
const startTime = performance.now();
|
88 |
+
const hf = createHfInference(hfToken);
|
89 |
+
|
90 |
+
if (conversation.streaming) {
|
91 |
+
const streamingMessage = { role: "assistant", content: "" };
|
92 |
+
conversation.messages = [...conversation.messages, streamingMessage];
|
93 |
+
abortController = new AbortController();
|
94 |
+
|
95 |
+
await handleStreamingResponse(
|
96 |
+
hf,
|
97 |
+
conversation,
|
98 |
+
content => {
|
99 |
+
if (streamingMessage) {
|
100 |
+
streamingMessage.content = content;
|
101 |
+
conversation.messages = [...conversation.messages];
|
102 |
+
}
|
103 |
+
},
|
104 |
+
abortController,
|
105 |
+
systemMessage
|
106 |
+
);
|
107 |
+
} else {
|
108 |
+
waitForNonStreaming = true;
|
109 |
+
const newMessage = await handleNonStreamingResponse(hf, conversation, systemMessage);
|
110 |
+
// check if the user did not abort the request
|
111 |
+
if (waitForNonStreaming) {
|
112 |
+
conversation.messages = [...conversation.messages, newMessage];
|
113 |
+
}
|
114 |
+
}
|
115 |
+
|
116 |
+
const endTime = performance.now();
|
117 |
+
latency = Math.round(endTime - startTime);
|
118 |
+
|
119 |
addMessage();
|
120 |
} catch (error) {
|
121 |
+
if (error.name !== "AbortError") {
|
122 |
+
alert("error: " + (error as Error).message);
|
123 |
}
|
124 |
} finally {
|
125 |
loading = false;
|
126 |
+
abortController = undefined;
|
127 |
}
|
128 |
}
|
129 |
|
130 |
function onKeydown(event: KeyboardEvent) {
|
131 |
+
if (!event.shiftKey && event.key === "Enter") {
|
132 |
submit();
|
133 |
}
|
134 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
135 |
</script>
|
136 |
|
137 |
{#if showTokenModal}
|
138 |
+
<HFTokenModal
|
139 |
on:close={() => (showTokenModal = false)}
|
140 |
+
on:submit={e => {
|
141 |
const formData = new FormData(e.target);
|
142 |
+
hfToken = formData.get("hf-token");
|
143 |
submit();
|
144 |
showTokenModal = false;
|
145 |
}}
|
146 |
/>
|
147 |
{/if}
|
148 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
149 |
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
150 |
<div
|
151 |
+
class="w-dvh grid divide-gray-200 overflow-hidden bg-gray-100/50 max-md:divide-y md:h-dvh md:grid-cols-[clamp(220px,20%,350px),minmax(0,1fr),clamp(270px,25%,300px)] dark:divide-gray-800 dark:bg-gray-900 dark:text-gray-300 dark:[color-scheme:dark]"
|
|
|
|
|
|
|
|
|
|
|
152 |
>
|
153 |
<div class=" flex flex-col overflow-y-auto py-3 pr-3">
|
154 |
<div
|
|
|
161 |
name=""
|
162 |
id=""
|
163 |
placeholder={systemPromptSupported
|
164 |
+
? "Enter a custom prompt"
|
165 |
+
: "System prompt is not supported with the chosen model."}
|
166 |
bind:value={systemMessage.content}
|
167 |
class="absolute inset-x-0 bottom-0 h-full resize-none bg-transparent px-3 pt-10 text-sm outline-none"
|
168 |
></textarea>
|
169 |
</div>
|
170 |
</div>
|
171 |
<div class="relative divide-y divide-gray-200 pt-3 dark:divide-gray-800" on:keydown={onKeydown}>
|
172 |
+
<div class="flex h-[calc(100dvh-5rem)] divide-x divide-gray-200 *:w-full dark:divide-gray-800">
|
173 |
+
<Conversation
|
174 |
+
{loading}
|
175 |
+
{conversation}
|
176 |
+
index={0}
|
177 |
+
{viewCode}
|
178 |
+
on:addMessage={addMessage}
|
179 |
+
on:deleteMessage={e => deleteMessage(e.detail)}
|
180 |
+
/>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
181 |
</div>
|
182 |
<div
|
183 |
class="fixed inset-x-0 bottom-0 flex h-20 items-center gap-2 overflow-hidden whitespace-nowrap px-3 md:absolute"
|
|
|
186 |
type="button"
|
187 |
class="flex h-[39px] flex-none gap-2 rounded-lg border border-gray-200 bg-white px-3 py-2.5 text-sm font-medium text-gray-900 hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:outline-none focus:ring-4 focus:ring-gray-100 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white dark:focus:ring-gray-700"
|
188 |
>
|
189 |
+
<div class="flex size-5 items-center justify-center rounded border border-black/5 bg-black/5 text-xs">
|
|
|
|
|
190 |
<IconShare />
|
191 |
</div>
|
192 |
|
|
|
209 |
class="flex h-[39px] items-center gap-2 rounded-lg border border-gray-200 bg-white px-3 py-2.5 text-sm font-medium text-gray-900 hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:outline-none focus:ring-4 focus:ring-gray-100 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white dark:focus:ring-gray-700"
|
210 |
>
|
211 |
<IconCode />
|
212 |
+
{!viewCode ? "View Code" : "Hide Code"}</button
|
213 |
>
|
214 |
<button
|
215 |
on:click={() => {
|
|
|
238 |
/>
|
239 |
</div>
|
240 |
{:else}
|
241 |
+
Run <span class="inline-flex gap-0.5 rounded border border-white/20 bg-white/10 px-0.5 text-xs text-white/70"
|
|
|
242 |
>↵</span
|
243 |
>
|
244 |
{/if}
|
245 |
</button>
|
246 |
</div>
|
247 |
</div>
|
248 |
+
<div class="flex flex-col p-3">
|
249 |
+
<div
|
250 |
+
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"
|
251 |
+
>
|
252 |
+
<ModelSelector {models} bind:conversation />
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
253 |
|
254 |
+
<GenerationConfig bind:conversation />
|
255 |
+
<div class="mt-auto">
|
256 |
+
<div class="mb-3 flex items-center justify-between gap-2">
|
257 |
+
<label for="default-range" class="block text-sm font-medium text-gray-900 dark:text-white">API Quota</label>
|
258 |
+
<span
|
259 |
+
class="rounded bg-gray-100 px-1.5 py-0.5 text-xs font-medium text-gray-800 dark:bg-gray-700 dark:text-gray-300"
|
260 |
+
>Free</span
|
261 |
+
>
|
|
|
|
|
|
|
262 |
|
263 |
+
<div class="ml-auto w-12 text-right text-sm">76%</div>
|
264 |
+
</div>
|
265 |
+
<div class="h-2 w-full rounded-full bg-gray-200 dark:bg-gray-700">
|
266 |
+
<div class="h-2 rounded-full bg-black dark:bg-gray-400" style="width: 75%"></div>
|
|
|
267 |
</div>
|
268 |
</div>
|
269 |
</div>
|
270 |
+
</div>
|
271 |
</div>
|
src/lib/components/InferencePlayground/InferencePlaygroundCodeSnippets.svelte
CHANGED
@@ -1,24 +1,24 @@
|
|
1 |
<script lang="ts">
|
2 |
-
import hljs from
|
3 |
-
import javascript from
|
4 |
-
import python from
|
5 |
-
import http from
|
6 |
-
import type { Conversation } from
|
7 |
-
import IconCopyCode from
|
8 |
-
import { onDestroy } from
|
9 |
-
|
10 |
-
hljs.registerLanguage(
|
11 |
-
hljs.registerLanguage(
|
12 |
-
hljs.registerLanguage(
|
13 |
|
14 |
export let conversation: Conversation;
|
15 |
|
16 |
-
const lanuages = [
|
17 |
type Language = (typeof lanuages)[number];
|
18 |
const labelsByLanguage: Record<Language, string> = {
|
19 |
-
javascript:
|
20 |
-
python:
|
21 |
-
http:
|
22 |
};
|
23 |
|
24 |
interface Snippet {
|
@@ -30,16 +30,16 @@
|
|
30 |
$: snippetsByLanguage = {
|
31 |
javascript: getJavascriptSnippets(conversation),
|
32 |
python: getPythonSnippets(conversation),
|
33 |
-
http: getHttpSnippets(conversation)
|
34 |
};
|
35 |
|
36 |
-
let selectedLanguage: Language =
|
37 |
let timeout: ReturnType<typeof setTimeout>;
|
38 |
|
39 |
function getMessages() {
|
40 |
-
const placeholder = [{ role:
|
41 |
let messages = conversation.messages;
|
42 |
-
if (messages.length === 1 && messages[0].role ===
|
43 |
messages = placeholder;
|
44 |
}
|
45 |
return messages;
|
@@ -66,13 +66,13 @@
|
|
66 |
|
67 |
const snippets: Snippet[] = [];
|
68 |
snippets.push({
|
69 |
-
label:
|
70 |
-
language:
|
71 |
-
code: `npm install --save @huggingface/inference
|
72 |
});
|
73 |
if (conversation.streaming) {
|
74 |
snippets.push({
|
75 |
-
label:
|
76 |
code: `import { HfInference } from "@huggingface/inference"
|
77 |
|
78 |
const inference = new HfInference("your HF token")
|
@@ -81,8 +81,8 @@ let out = "";
|
|
81 |
|
82 |
for await (const chunk of inference.chatCompletionStream({
|
83 |
model: "${conversation.model.id}",
|
84 |
-
messages: ${formattedMessages({ sep:
|
85 |
-
${formattedConfig({ sep:
|
86 |
seed: 0,
|
87 |
})) {
|
88 |
if (chunk.choices && chunk.choices.length > 0) {
|
@@ -91,24 +91,24 @@ for await (const chunk of inference.chatCompletionStream({
|
|
91 |
console.clear();
|
92 |
console.log(out);
|
93 |
}
|
94 |
-
}
|
95 |
});
|
96 |
} else {
|
97 |
// non-streaming
|
98 |
snippets.push({
|
99 |
-
label:
|
100 |
code: `import { HfInference } from '@huggingface/inference'
|
101 |
|
102 |
const inference = new HfInference("your access token")
|
103 |
|
104 |
const out = await inference.chatCompletion({
|
105 |
model: "${conversation.model.id}",
|
106 |
-
messages: ${formattedMessages({ sep:
|
107 |
-
${formattedConfig({ sep:
|
108 |
seed: 0,
|
109 |
});
|
110 |
|
111 |
-
console.log(out.choices[0].message)
|
112 |
});
|
113 |
}
|
114 |
|
@@ -132,13 +132,13 @@ console.log(out.choices[0].message);`
|
|
132 |
|
133 |
const snippets: Snippet[] = [];
|
134 |
snippets.push({
|
135 |
-
label:
|
136 |
-
language:
|
137 |
-
code: `pip install huggingface_hub
|
138 |
});
|
139 |
if (conversation.streaming) {
|
140 |
snippets.push({
|
141 |
-
label:
|
142 |
code: `from huggingface_hub import InferenceClient
|
143 |
|
144 |
model_id="${conversation.model.id}"
|
@@ -147,28 +147,28 @@ inference_client = InferenceClient(model_id, token=hf_token)
|
|
147 |
|
148 |
output = ""
|
149 |
|
150 |
-
messages = ${formattedMessages({ sep:
|
151 |
|
152 |
-
for token in client.chat_completion(messages, stream=True, ${formattedConfig({ sep:
|
153 |
new_content = token.choices[0].delta.content
|
154 |
print(new_content, end="")
|
155 |
-
output += new_content
|
156 |
});
|
157 |
} else {
|
158 |
// non-streaming
|
159 |
snippets.push({
|
160 |
-
label:
|
161 |
code: `from huggingface_hub import InferenceClient
|
162 |
|
163 |
model_id="${conversation.model.id}"
|
164 |
hf_token = "your HF token"
|
165 |
inference_client = InferenceClient(model_id, token=hf_token)
|
166 |
|
167 |
-
messages = ${formattedMessages({ sep:
|
168 |
|
169 |
-
output = inference_client.chat_completion(messages, ${formattedConfig({ sep:
|
170 |
|
171 |
-
print(output.choices[0].message)
|
172 |
});
|
173 |
}
|
174 |
|
@@ -194,29 +194,29 @@ print(output.choices[0].message)`
|
|
194 |
|
195 |
if (conversation.streaming) {
|
196 |
snippets.push({
|
197 |
-
label:
|
198 |
code: `curl 'https://api-inference.huggingface.co/models/${conversation.model.id}/v1/chat/completions' \\
|
199 |
--header "Authorization: Bearer {YOUR_HF_TOKEN}" \\
|
200 |
--header 'Content-Type: application/json' \\
|
201 |
--data '{
|
202 |
-
"model": "
|
203 |
-
"messages": ${formattedMessages({ sep:
|
204 |
-
${formattedConfig({ sep:
|
205 |
"stream": true
|
206 |
-
}'
|
207 |
});
|
208 |
} else {
|
209 |
// non-streaming
|
210 |
snippets.push({
|
211 |
-
label:
|
212 |
code: `curl 'https://api-inference.huggingface.co/models/${conversation.model.id}/v1/chat/completions' \\
|
213 |
--header "Authorization: Bearer {YOUR_HF_TOKEN}" \\
|
214 |
--header 'Content-Type: application/json' \\
|
215 |
--data '{
|
216 |
-
"model": "
|
217 |
-
"messages": ${formattedMessages({ sep:
|
218 |
-
${formattedConfig({ sep:
|
219 |
-
}'
|
220 |
});
|
221 |
}
|
222 |
|
@@ -254,15 +254,15 @@ print(output.choices[0].message)`
|
|
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={
|
258 |
const el = e.currentTarget;
|
259 |
-
el.classList.add(
|
260 |
navigator.clipboard.writeText(code);
|
261 |
if (timeout) {
|
262 |
clearTimeout(timeout);
|
263 |
}
|
264 |
timeout = setTimeout(() => {
|
265 |
-
el.classList.remove(
|
266 |
}, 1000);
|
267 |
}}
|
268 |
>
|
|
|
1 |
<script lang="ts">
|
2 |
+
import hljs from "highlight.js/lib/core";
|
3 |
+
import javascript from "highlight.js/lib/languages/javascript";
|
4 |
+
import python from "highlight.js/lib/languages/python";
|
5 |
+
import http from "highlight.js/lib/languages/http";
|
6 |
+
import type { Conversation } from "$lib/types";
|
7 |
+
import IconCopyCode from "../Icons/IconCopyCode.svelte";
|
8 |
+
import { onDestroy } from "svelte";
|
9 |
+
|
10 |
+
hljs.registerLanguage("javascript", javascript);
|
11 |
+
hljs.registerLanguage("python", python);
|
12 |
+
hljs.registerLanguage("http", http);
|
13 |
|
14 |
export let conversation: Conversation;
|
15 |
|
16 |
+
const lanuages = ["javascript", "python", "http"];
|
17 |
type Language = (typeof lanuages)[number];
|
18 |
const labelsByLanguage: Record<Language, string> = {
|
19 |
+
javascript: "JavaScript",
|
20 |
+
python: "Python",
|
21 |
+
http: "Curl",
|
22 |
};
|
23 |
|
24 |
interface Snippet {
|
|
|
30 |
$: snippetsByLanguage = {
|
31 |
javascript: getJavascriptSnippets(conversation),
|
32 |
python: getPythonSnippets(conversation),
|
33 |
+
http: getHttpSnippets(conversation),
|
34 |
};
|
35 |
|
36 |
+
let selectedLanguage: Language = "javascript";
|
37 |
let timeout: ReturnType<typeof setTimeout>;
|
38 |
|
39 |
function getMessages() {
|
40 |
+
const placeholder = [{ role: "user", content: "Tell me a story" }];
|
41 |
let messages = conversation.messages;
|
42 |
+
if (messages.length === 1 && messages[0].role === "user" && !messages[0].content) {
|
43 |
messages = placeholder;
|
44 |
}
|
45 |
return messages;
|
|
|
66 |
|
67 |
const snippets: Snippet[] = [];
|
68 |
snippets.push({
|
69 |
+
label: "Install @huggingface/inference",
|
70 |
+
language: "http",
|
71 |
+
code: `npm install --save @huggingface/inference`,
|
72 |
});
|
73 |
if (conversation.streaming) {
|
74 |
snippets.push({
|
75 |
+
label: "Streaming API",
|
76 |
code: `import { HfInference } from "@huggingface/inference"
|
77 |
|
78 |
const inference = new HfInference("your HF token")
|
|
|
81 |
|
82 |
for await (const chunk of inference.chatCompletionStream({
|
83 |
model: "${conversation.model.id}",
|
84 |
+
messages: ${formattedMessages({ sep: ",\n ", start: "[\n ", end: "\n ]" })},
|
85 |
+
${formattedConfig({ sep: ",\n ", start: "", end: "" })},
|
86 |
seed: 0,
|
87 |
})) {
|
88 |
if (chunk.choices && chunk.choices.length > 0) {
|
|
|
91 |
console.clear();
|
92 |
console.log(out);
|
93 |
}
|
94 |
+
}`,
|
95 |
});
|
96 |
} else {
|
97 |
// non-streaming
|
98 |
snippets.push({
|
99 |
+
label: "Non-Streaming API",
|
100 |
code: `import { HfInference } from '@huggingface/inference'
|
101 |
|
102 |
const inference = new HfInference("your access token")
|
103 |
|
104 |
const out = await inference.chatCompletion({
|
105 |
model: "${conversation.model.id}",
|
106 |
+
messages: ${formattedMessages({ sep: ",\n ", start: "[\n ", end: "\n ]" })},
|
107 |
+
${formattedConfig({ sep: ",\n ", start: "", end: "" })},
|
108 |
seed: 0,
|
109 |
});
|
110 |
|
111 |
+
console.log(out.choices[0].message);`,
|
112 |
});
|
113 |
}
|
114 |
|
|
|
132 |
|
133 |
const snippets: Snippet[] = [];
|
134 |
snippets.push({
|
135 |
+
label: "Install huggingface_hub",
|
136 |
+
language: "http",
|
137 |
+
code: `pip install huggingface_hub`,
|
138 |
});
|
139 |
if (conversation.streaming) {
|
140 |
snippets.push({
|
141 |
+
label: "Streaming API",
|
142 |
code: `from huggingface_hub import InferenceClient
|
143 |
|
144 |
model_id="${conversation.model.id}"
|
|
|
147 |
|
148 |
output = ""
|
149 |
|
150 |
+
messages = ${formattedMessages({ sep: ",\n ", start: `[\n `, end: `\n]` })}
|
151 |
|
152 |
+
for token in client.chat_completion(messages, stream=True, ${formattedConfig({ sep: ", ", start: "", end: "" })}):
|
153 |
new_content = token.choices[0].delta.content
|
154 |
print(new_content, end="")
|
155 |
+
output += new_content`,
|
156 |
});
|
157 |
} else {
|
158 |
// non-streaming
|
159 |
snippets.push({
|
160 |
+
label: "Non-Streaming API",
|
161 |
code: `from huggingface_hub import InferenceClient
|
162 |
|
163 |
model_id="${conversation.model.id}"
|
164 |
hf_token = "your HF token"
|
165 |
inference_client = InferenceClient(model_id, token=hf_token)
|
166 |
|
167 |
+
messages = ${formattedMessages({ sep: ",\n ", start: `[\n `, end: `\n]` })}
|
168 |
|
169 |
+
output = inference_client.chat_completion(messages, ${formattedConfig({ sep: ", ", start: "", end: "" })})
|
170 |
|
171 |
+
print(output.choices[0].message)`,
|
172 |
});
|
173 |
}
|
174 |
|
|
|
194 |
|
195 |
if (conversation.streaming) {
|
196 |
snippets.push({
|
197 |
+
label: "Streaming API",
|
198 |
code: `curl 'https://api-inference.huggingface.co/models/${conversation.model.id}/v1/chat/completions' \\
|
199 |
--header "Authorization: Bearer {YOUR_HF_TOKEN}" \\
|
200 |
--header 'Content-Type: application/json' \\
|
201 |
--data '{
|
202 |
+
"model": "${conversation.model.id}",
|
203 |
+
"messages": ${formattedMessages({ sep: ",\n ", start: `[\n `, end: `\n]` })},
|
204 |
+
${formattedConfig({ sep: ",\n ", start: "", end: "" })},
|
205 |
"stream": true
|
206 |
+
}'`,
|
207 |
});
|
208 |
} else {
|
209 |
// non-streaming
|
210 |
snippets.push({
|
211 |
+
label: "Non-Streaming API",
|
212 |
code: `curl 'https://api-inference.huggingface.co/models/${conversation.model.id}/v1/chat/completions' \\
|
213 |
--header "Authorization: Bearer {YOUR_HF_TOKEN}" \\
|
214 |
--header 'Content-Type: application/json' \\
|
215 |
--data '{
|
216 |
+
"model": "${conversation.model.id}",
|
217 |
+
"messages": ${formattedMessages({ sep: ",\n ", start: `[\n `, end: `\n]` })},
|
218 |
+
${formattedConfig({ sep: ",\n ", start: "", end: "" })}
|
219 |
+
}'`,
|
220 |
});
|
221 |
}
|
222 |
|
|
|
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");
|
260 |
navigator.clipboard.writeText(code);
|
261 |
if (timeout) {
|
262 |
clearTimeout(timeout);
|
263 |
}
|
264 |
timeout = setTimeout(() => {
|
265 |
+
el.classList.remove("text-green-500");
|
266 |
}, 1000);
|
267 |
}}
|
268 |
>
|
src/lib/components/InferencePlayground/InferencePlaygroundConversation.svelte
CHANGED
@@ -1,21 +1,17 @@
|
|
1 |
<script lang="ts">
|
2 |
-
import { createEventDispatcher } from
|
3 |
-
import CodeSnippets from
|
4 |
-
import Message from
|
5 |
-
import
|
6 |
-
import
|
7 |
-
import type { Conversation } from '$lib/types';
|
8 |
|
9 |
export let loading;
|
10 |
export let conversation: Conversation;
|
11 |
-
export let index;
|
12 |
export let viewCode;
|
13 |
-
export let sideBySide = false;
|
14 |
|
15 |
const dispatch = createEventDispatcher<{
|
16 |
addMessage: void;
|
17 |
deleteMessage: number;
|
18 |
-
deleteConversation: number;
|
19 |
}>();
|
20 |
|
21 |
let messageContainer: HTMLDivElement | null = null;
|
@@ -39,55 +35,19 @@
|
|
39 |
class:animate-pulse={loading && !conversation.streaming}
|
40 |
bind:this={messageContainer}
|
41 |
>
|
42 |
-
{#if sideBySide}
|
43 |
-
<div
|
44 |
-
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"
|
45 |
-
class:mr-3={index === 0}
|
46 |
-
class:mx-3={index === 1}
|
47 |
-
>
|
48 |
-
<div class="size-3.5 rounded bg-black dark:bg-gray-400"></div>
|
49 |
-
<div>{conversation.model}</div>
|
50 |
-
<button
|
51 |
-
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"
|
52 |
-
on:click={() => dispatch('deleteConversation', index)}
|
53 |
-
>
|
54 |
-
✕
|
55 |
-
</button>
|
56 |
-
<button
|
57 |
-
class="group relative flex size-6 items-center justify-center rounded bg-gray-50 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700"
|
58 |
-
>
|
59 |
-
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 32 32"
|
60 |
-
><path
|
61 |
-
fill="currentColor"
|
62 |
-
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"
|
63 |
-
/><path
|
64 |
-
fill="currentColor"
|
65 |
-
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"
|
66 |
-
/></svg
|
67 |
-
>
|
68 |
-
<PlaygroundOptions
|
69 |
-
bind:conversation
|
70 |
-
classNames="absolute top-8 right-0 w-56 invisible group-focus:visible hover:visible border border-gray-200/80 bg-white z-10 px-4 py-6 text-sm shadow-sm dark:border-gray-800 dark:bg-gray-800 rounded-xl"
|
71 |
-
/>
|
72 |
-
</button>
|
73 |
-
</div>
|
74 |
-
{/if}
|
75 |
{#if !viewCode}
|
76 |
{#each conversation.messages as message, messageIdx}
|
77 |
<Message
|
78 |
class="border-b"
|
79 |
{message}
|
80 |
-
|
81 |
-
{messageIdx}
|
82 |
-
on:messageValueChanged
|
83 |
-
on:delete={() => dispatch('deleteMessage', messageIdx)}
|
84 |
-
autofocus={!sideBySide && !loading && messageIdx === conversation.messages.length - 1}
|
85 |
/>
|
86 |
{/each}
|
87 |
|
88 |
<button
|
89 |
class="flex px-6 py-6 hover:bg-gray-50 dark:hover:bg-gray-800/50"
|
90 |
-
on:click={() => dispatch(
|
91 |
disabled={loading}
|
92 |
>
|
93 |
<div class="flex items-center gap-2 !p-0 text-sm font-semibold">
|
|
|
1 |
<script lang="ts">
|
2 |
+
import { createEventDispatcher } from "svelte";
|
3 |
+
import CodeSnippets from "./InferencePlaygroundCodeSnippets.svelte";
|
4 |
+
import Message from "./InferencePlaygroundMessage.svelte";
|
5 |
+
import IconPlus from "../Icons/IconPlus.svelte";
|
6 |
+
import type { Conversation } from "$lib/types";
|
|
|
7 |
|
8 |
export let loading;
|
9 |
export let conversation: Conversation;
|
|
|
10 |
export let viewCode;
|
|
|
11 |
|
12 |
const dispatch = createEventDispatcher<{
|
13 |
addMessage: void;
|
14 |
deleteMessage: number;
|
|
|
15 |
}>();
|
16 |
|
17 |
let messageContainer: HTMLDivElement | null = null;
|
|
|
35 |
class:animate-pulse={loading && !conversation.streaming}
|
36 |
bind:this={messageContainer}
|
37 |
>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
38 |
{#if !viewCode}
|
39 |
{#each conversation.messages as message, messageIdx}
|
40 |
<Message
|
41 |
class="border-b"
|
42 |
{message}
|
43 |
+
on:delete={() => dispatch("deleteMessage", messageIdx)}
|
44 |
+
autofocus={!loading && messageIdx === conversation.messages.length - 1}
|
|
|
|
|
|
|
45 |
/>
|
46 |
{/each}
|
47 |
|
48 |
<button
|
49 |
class="flex px-6 py-6 hover:bg-gray-50 dark:hover:bg-gray-800/50"
|
50 |
+
on:click={() => dispatch("addMessage")}
|
51 |
disabled={loading}
|
52 |
>
|
53 |
<div class="flex items-center gap-2 !p-0 text-sm font-semibold">
|
src/lib/components/InferencePlayground/InferencePlaygroundGenerationConfig.svelte
CHANGED
@@ -1,27 +1,26 @@
|
|
1 |
<script lang="ts">
|
2 |
-
import type { Conversation } from
|
3 |
import {
|
4 |
GENERATION_CONFIG_KEYS,
|
5 |
GENERATION_CONFIG_KEYS_ADVANCED,
|
6 |
-
GENERATION_CONFIG_SETTINGS
|
7 |
-
} from
|
8 |
|
9 |
export let conversation: Conversation;
|
10 |
-
export let classNames =
|
11 |
|
12 |
$: modelMaxLength = conversation.model.tokenizerConfig.model_max_length;
|
13 |
-
$: maxTokens = Math.min(modelMaxLength ?? GENERATION_CONFIG_SETTINGS[
|
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 ===
|
20 |
<div>
|
21 |
<div class="flex items-center justify-between">
|
22 |
-
<label
|
23 |
-
|
24 |
-
class="mb-2 block text-sm font-medium text-gray-900 dark:text-white">{label}</label
|
25 |
>
|
26 |
<input
|
27 |
type="number"
|
@@ -51,9 +50,7 @@
|
|
51 |
{@const settings = GENERATION_CONFIG_SETTINGS[key]}
|
52 |
<div>
|
53 |
<div class="flex items-center justify-between">
|
54 |
-
<label
|
55 |
-
for="temperature-range"
|
56 |
-
class="mb-2 block text-sm font-medium text-gray-900 dark:text-white"
|
57 |
>{settings.label}</label
|
58 |
>
|
59 |
<input
|
@@ -63,7 +60,7 @@
|
|
63 |
max={settings.max}
|
64 |
step={settings.step}
|
65 |
value={conversation.config[key] ?? settings.default}
|
66 |
-
on:input={
|
67 |
/>
|
68 |
</div>
|
69 |
<input
|
@@ -73,7 +70,7 @@
|
|
73 |
max={settings.max}
|
74 |
step={settings.step}
|
75 |
value={conversation.config[key] ?? settings.default}
|
76 |
-
on:input={
|
77 |
class="h-2 w-full cursor-pointer appearance-none rounded-lg bg-gray-200 accent-black dark:bg-gray-700 dark:accent-blue-500"
|
78 |
/>
|
79 |
</div>
|
|
|
1 |
<script lang="ts">
|
2 |
+
import type { Conversation } from "$lib/types";
|
3 |
import {
|
4 |
GENERATION_CONFIG_KEYS,
|
5 |
GENERATION_CONFIG_KEYS_ADVANCED,
|
6 |
+
GENERATION_CONFIG_SETTINGS,
|
7 |
+
} from "./generationConfigSettings";
|
8 |
|
9 |
export let conversation: Conversation;
|
10 |
+
export let classNames = "";
|
11 |
|
12 |
$: modelMaxLength = conversation.model.tokenizerConfig.model_max_length;
|
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}
|
20 |
<div>
|
21 |
<div class="flex items-center justify-between">
|
22 |
+
<label for="temperature-range" class="mb-2 block text-sm font-medium text-gray-900 dark:text-white"
|
23 |
+
>{label}</label
|
|
|
24 |
>
|
25 |
<input
|
26 |
type="number"
|
|
|
50 |
{@const settings = GENERATION_CONFIG_SETTINGS[key]}
|
51 |
<div>
|
52 |
<div class="flex items-center justify-between">
|
53 |
+
<label for="temperature-range" class="mb-2 block text-sm font-medium text-gray-900 dark:text-white"
|
|
|
|
|
54 |
>{settings.label}</label
|
55 |
>
|
56 |
<input
|
|
|
60 |
max={settings.max}
|
61 |
step={settings.step}
|
62 |
value={conversation.config[key] ?? settings.default}
|
63 |
+
on:input={e => (conversation.config[key] = Number(e.currentTarget.value))}
|
64 |
/>
|
65 |
</div>
|
66 |
<input
|
|
|
70 |
max={settings.max}
|
71 |
step={settings.step}
|
72 |
value={conversation.config[key] ?? settings.default}
|
73 |
+
on:input={e => (conversation.config[key] = Number(e.currentTarget.value))}
|
74 |
class="h-2 w-full cursor-pointer appearance-none rounded-lg bg-gray-200 accent-black dark:bg-gray-700 dark:accent-blue-500"
|
75 |
/>
|
76 |
</div>
|
src/lib/components/InferencePlayground/InferencePlaygroundHFTokenModal.svelte
CHANGED
@@ -1,8 +1,8 @@
|
|
1 |
<!-- Main modal -->
|
2 |
<script lang="ts">
|
3 |
-
import { createEventDispatcher, onDestroy, onMount } from
|
4 |
-
import { browser } from
|
5 |
-
import IconCross from
|
6 |
|
7 |
let backdropEl: HTMLDivElement;
|
8 |
let modalEl: HTMLDivElement;
|
@@ -11,9 +11,9 @@
|
|
11 |
|
12 |
function handleKeydown(event: KeyboardEvent) {
|
13 |
// close on ESC
|
14 |
-
if (event.key ===
|
15 |
event.preventDefault();
|
16 |
-
dispatch(
|
17 |
}
|
18 |
}
|
19 |
|
@@ -22,12 +22,12 @@
|
|
22 |
return;
|
23 |
}
|
24 |
if (event.target === backdropEl) {
|
25 |
-
dispatch(
|
26 |
}
|
27 |
}
|
28 |
|
29 |
onMount(() => {
|
30 |
-
document.getElementById(
|
31 |
modalEl.focus();
|
32 |
});
|
33 |
|
@@ -35,7 +35,7 @@
|
|
35 |
if (!browser) return;
|
36 |
// remove inert attribute if this is the last modal
|
37 |
if (document.querySelectorAll('[role="dialog"]:not(#app *)').length === 1) {
|
38 |
-
document.getElementById(
|
39 |
}
|
40 |
});
|
41 |
</script>
|
@@ -56,9 +56,7 @@
|
|
56 |
on:keydown={handleKeydown}
|
57 |
>
|
58 |
<form on:submit|preventDefault class="relative rounded-lg bg-white shadow dark:bg-gray-900">
|
59 |
-
<div
|
60 |
-
class="flex items-center justify-between rounded-t border-b p-4 md:px-5 md:py-4 dark:border-gray-600"
|
61 |
-
>
|
62 |
<h3 class="flex items-center gap-2.5 text-lg font-semibold text-gray-900 dark:text-white">
|
63 |
<img
|
64 |
alt="Hugging Face's logo"
|
@@ -68,7 +66,7 @@
|
|
68 |
</h3>
|
69 |
<button
|
70 |
type="button"
|
71 |
-
on:click={() => dispatch(
|
72 |
class="ms-auto inline-flex h-8 w-8 items-center justify-center rounded-lg bg-transparent text-sm text-gray-400 hover:bg-gray-200 hover:text-gray-900 dark:hover:bg-gray-600 dark:hover:text-white"
|
73 |
>
|
74 |
<IconCross classNames="text-xl" />
|
@@ -98,9 +96,7 @@
|
|
98 |
</div>
|
99 |
|
100 |
<!-- Modal footer -->
|
101 |
-
<div
|
102 |
-
class="flex items-center justify-between rounded-b border-t border-gray-200 p-4 md:p-5 dark:border-gray-600"
|
103 |
-
>
|
104 |
<a
|
105 |
href="https://huggingface.co/settings/tokens?new_token=true"
|
106 |
tabindex="-1"
|
|
|
1 |
<!-- Main modal -->
|
2 |
<script lang="ts">
|
3 |
+
import { createEventDispatcher, onDestroy, onMount } from "svelte";
|
4 |
+
import { browser } from "$app/environment";
|
5 |
+
import IconCross from "../Icons/IconCross.svelte";
|
6 |
|
7 |
let backdropEl: HTMLDivElement;
|
8 |
let modalEl: HTMLDivElement;
|
|
|
11 |
|
12 |
function handleKeydown(event: KeyboardEvent) {
|
13 |
// close on ESC
|
14 |
+
if (event.key === "Escape") {
|
15 |
event.preventDefault();
|
16 |
+
dispatch("close");
|
17 |
}
|
18 |
}
|
19 |
|
|
|
22 |
return;
|
23 |
}
|
24 |
if (event.target === backdropEl) {
|
25 |
+
dispatch("close");
|
26 |
}
|
27 |
}
|
28 |
|
29 |
onMount(() => {
|
30 |
+
document.getElementById("app")?.setAttribute("inert", "true");
|
31 |
modalEl.focus();
|
32 |
});
|
33 |
|
|
|
35 |
if (!browser) return;
|
36 |
// remove inert attribute if this is the last modal
|
37 |
if (document.querySelectorAll('[role="dialog"]:not(#app *)').length === 1) {
|
38 |
+
document.getElementById("app")?.removeAttribute("inert");
|
39 |
}
|
40 |
});
|
41 |
</script>
|
|
|
56 |
on:keydown={handleKeydown}
|
57 |
>
|
58 |
<form on:submit|preventDefault class="relative rounded-lg bg-white shadow dark:bg-gray-900">
|
59 |
+
<div class="flex items-center justify-between rounded-t border-b p-4 md:px-5 md:py-4 dark:border-gray-600">
|
|
|
|
|
60 |
<h3 class="flex items-center gap-2.5 text-lg font-semibold text-gray-900 dark:text-white">
|
61 |
<img
|
62 |
alt="Hugging Face's logo"
|
|
|
66 |
</h3>
|
67 |
<button
|
68 |
type="button"
|
69 |
+
on:click={() => dispatch("close")}
|
70 |
class="ms-auto inline-flex h-8 w-8 items-center justify-center rounded-lg bg-transparent text-sm text-gray-400 hover:bg-gray-200 hover:text-gray-900 dark:hover:bg-gray-600 dark:hover:text-white"
|
71 |
>
|
72 |
<IconCross classNames="text-xl" />
|
|
|
96 |
</div>
|
97 |
|
98 |
<!-- Modal footer -->
|
99 |
+
<div class="flex items-center justify-between rounded-b border-t border-gray-200 p-4 md:p-5 dark:border-gray-600">
|
|
|
|
|
100 |
<a
|
101 |
href="https://huggingface.co/settings/tokens?new_token=true"
|
102 |
tabindex="-1"
|
src/lib/components/InferencePlayground/InferencePlaygroundMessage.svelte
CHANGED
@@ -1,16 +1,11 @@
|
|
1 |
<script lang="ts">
|
2 |
-
import { createEventDispatcher } from
|
3 |
-
import { type ChatCompletionInputMessage } from
|
4 |
|
5 |
export let message: ChatCompletionInputMessage;
|
6 |
-
export let conversationIdx: number;
|
7 |
-
export let messageIdx: number;
|
8 |
export let autofocus: boolean = false;
|
9 |
|
10 |
-
const dispatch = createEventDispatcher<{
|
11 |
-
delete: void;
|
12 |
-
messageValueChanged: { conversationIdx: number; messageIdx: number; value: string };
|
13 |
-
}>();
|
14 |
</script>
|
15 |
|
16 |
<div
|
@@ -21,9 +16,7 @@
|
|
21 |
</div>
|
22 |
<textarea
|
23 |
{autofocus}
|
24 |
-
value={message.content}
|
25 |
-
on:input={(e) =>
|
26 |
-
dispatch('messageValueChanged', { conversationIdx, messageIdx, value: e.target.value })}
|
27 |
placeholder="Enter {message.role} message"
|
28 |
class="resize-none rounded bg-transparent px-2 py-2.5 ring-gray-100 [field-sizing:content] hover:resize-y hover:bg-white focus:resize-y focus:bg-white focus:ring group-hover/message:ring @2xl:px-3 dark:ring-gray-600 dark:hover:bg-gray-900 dark:focus:bg-gray-900"
|
29 |
rows="1"
|
@@ -31,7 +24,7 @@
|
|
31 |
<button
|
32 |
tabindex="1"
|
33 |
on:click={() => {
|
34 |
-
dispatch(
|
35 |
}}
|
36 |
type="button"
|
37 |
class="mt-1.5 hidden size-8 rounded-lg border border-gray-200 bg-white text-xs font-medium text-gray-900 hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:outline-none focus:ring-4 focus:ring-gray-100 group-hover/message:block dark:border-gray-600 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white dark:focus:ring-gray-700"
|
|
|
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;
|
7 |
|
8 |
+
const dispatch = createEventDispatcher<{ delete: void }>();
|
|
|
|
|
|
|
9 |
</script>
|
10 |
|
11 |
<div
|
|
|
16 |
</div>
|
17 |
<textarea
|
18 |
{autofocus}
|
19 |
+
bind:value={message.content}
|
|
|
|
|
20 |
placeholder="Enter {message.role} message"
|
21 |
class="resize-none rounded bg-transparent px-2 py-2.5 ring-gray-100 [field-sizing:content] hover:resize-y hover:bg-white focus:resize-y focus:bg-white focus:ring group-hover/message:ring @2xl:px-3 dark:ring-gray-600 dark:hover:bg-gray-900 dark:focus:bg-gray-900"
|
22 |
rows="1"
|
|
|
24 |
<button
|
25 |
tabindex="1"
|
26 |
on:click={() => {
|
27 |
+
dispatch("delete");
|
28 |
}}
|
29 |
type="button"
|
30 |
class="mt-1.5 hidden size-8 rounded-lg border border-gray-200 bg-white text-xs font-medium text-gray-900 hover:bg-gray-100 hover:text-blue-700 focus:z-10 focus:outline-none focus:ring-4 focus:ring-gray-100 group-hover/message:block dark:border-gray-600 dark:bg-gray-800 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white dark:focus:ring-gray-700"
|
src/lib/components/InferencePlayground/InferencePlaygroundModelSelector.svelte
CHANGED
@@ -1,10 +1,12 @@
|
|
1 |
<script lang="ts">
|
2 |
-
import type { Conversation, ModelEntryWithTokenizer } from
|
3 |
-
import IconCaret from
|
|
|
4 |
|
5 |
export let models: ModelEntryWithTokenizer[] = [];
|
6 |
export let conversation: Conversation;
|
7 |
-
|
|
|
8 |
|
9 |
async function getAvatarUrl(orgName: string) {
|
10 |
const url = `https://huggingface.co/api/organizations/${orgName}/avatar`;
|
@@ -18,28 +20,38 @@
|
|
18 |
return avatarUrl;
|
19 |
}
|
20 |
|
21 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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>
|
|
|
1 |
<script lang="ts">
|
2 |
+
import type { Conversation, ModelEntryWithTokenizer } from "$lib/types";
|
3 |
+
import IconCaret from "../Icons/IconCaret.svelte";
|
4 |
+
import ModelSelectorModal from "./InferencePlaygroundModelSelectorModal.svelte";
|
5 |
|
6 |
export let models: ModelEntryWithTokenizer[] = [];
|
7 |
export let conversation: Conversation;
|
8 |
+
|
9 |
+
let showModelPickerModal = false;
|
10 |
|
11 |
async function getAvatarUrl(orgName: string) {
|
12 |
const url = `https://huggingface.co/api/organizations/${orgName}/avatar`;
|
|
|
20 |
return avatarUrl;
|
21 |
}
|
22 |
|
23 |
+
function changeModel(modelId: string) {
|
24 |
+
const model = models.find(m => m.id === modelId);
|
25 |
+
if (!model) {
|
26 |
+
return;
|
27 |
+
}
|
28 |
+
conversation.model = model;
|
29 |
+
}
|
30 |
+
|
31 |
+
$: [nameSpace, modelName] = conversation.model.id.split("/");
|
32 |
</script>
|
33 |
|
34 |
+
{#if showModelPickerModal}
|
35 |
+
<ModelSelectorModal
|
36 |
+
{models}
|
37 |
+
on:modelSelected={e => changeModel(e.detail)}
|
38 |
+
on:close={e => (showModelPickerModal = false)}
|
39 |
+
/>
|
40 |
+
{/if}
|
41 |
+
|
42 |
<div class="flex flex-col gap-2">
|
43 |
+
<label for="countries" class="flex items-baseline text-sm font-medium text-gray-900 dark:text-white"
|
|
|
|
|
44 |
>Models<span class="ml-4 font-normal text-gray-400">{models.length}</span>
|
45 |
</label>
|
46 |
|
47 |
<button
|
48 |
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"
|
49 |
+
on:click={() => (showModelPickerModal = true)}
|
50 |
>
|
51 |
<div class="flex flex-col items-start">
|
52 |
<div class="flex items-center gap-1 text-sm text-gray-500 dark:text-gray-300">
|
53 |
{#await getAvatarUrl(nameSpace) then avatarUrl}
|
54 |
+
<img class="size-3 flex-none rounded bg-gray-200 object-cover" src={avatarUrl} alt="{nameSpace} avatar" />
|
|
|
|
|
|
|
|
|
55 |
{/await}
|
56 |
{nameSpace}
|
57 |
</div>
|
src/lib/components/InferencePlayground/{InferencePlaygroundModelPickerModal.svelte → InferencePlaygroundModelSelectorModal.svelte}
RENAMED
@@ -1,8 +1,8 @@
|
|
1 |
<script lang="ts">
|
2 |
-
import type { ModelEntryWithTokenizer } from
|
3 |
-
import { createEventDispatcher } from
|
4 |
-
import IconSearch from
|
5 |
-
import IconStar from
|
6 |
|
7 |
export let models: ModelEntryWithTokenizer[];
|
8 |
|
@@ -12,9 +12,9 @@
|
|
12 |
|
13 |
function handleKeydown(event: KeyboardEvent) {
|
14 |
// close on ESC
|
15 |
-
if (event.key ===
|
16 |
event.preventDefault();
|
17 |
-
dispatch(
|
18 |
}
|
19 |
}
|
20 |
|
@@ -23,7 +23,7 @@
|
|
23 |
return;
|
24 |
}
|
25 |
if (event.target === backdropEl) {
|
26 |
-
dispatch(
|
27 |
}
|
28 |
}
|
29 |
</script>
|
@@ -36,9 +36,7 @@
|
|
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
|
@@ -55,15 +53,17 @@
|
|
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 |
-
|
|
|
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 |
-
|
|
|
67 |
>
|
68 |
</div>
|
69 |
</div>
|
@@ -73,18 +73,18 @@
|
|
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(
|
81 |
-
dispatch(
|
82 |
}}
|
83 |
>
|
84 |
<span class="inline-flex items-center"
|
85 |
-
><span class="text-gray-500">{nameSpace}</span><span class="mx-1 text-black"
|
86 |
-
|
87 |
-
|
88 |
>
|
89 |
</button>
|
90 |
{/each}
|
|
|
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 |
|
|
|
12 |
|
13 |
function handleKeydown(event: KeyboardEvent) {
|
14 |
// close on ESC
|
15 |
+
if (event.key === "Escape") {
|
16 |
event.preventDefault();
|
17 |
+
dispatch("close");
|
18 |
}
|
19 |
}
|
20 |
|
|
|
23 |
return;
|
24 |
}
|
25 |
if (event.target === backdropEl) {
|
26 |
+
dispatch("close");
|
27 |
}
|
28 |
}
|
29 |
</script>
|
|
|
36 |
on:click|stopPropagation={handleBackdropClick}
|
37 |
>
|
38 |
<div class="flex w-full max-w-[600px] items-start justify-center p-10">
|
39 |
+
<div class="flex h-full w-full flex-col overflow-hidden rounded-lg border bg-white text-gray-900 shadow-md">
|
|
|
|
|
40 |
<div class="flex items-center border-b px-3">
|
41 |
<IconSearch classNames="mr-2 text-sm" />
|
42 |
<input
|
|
|
53 |
<div class="flex cursor-pointer items-center px-2 py-1.5 text-sm hover:bg-gray-100">
|
54 |
<IconStar classNames="lucide lucide-star mr-2 h-4 w-4 text-yellow-400" />
|
55 |
<span class="inline-flex items-center"
|
56 |
+
><span class="text-gray-500">meta-llama</span><span class="mx-1 text-black">/</span><span
|
57 |
+
class="text-black">Meta-Llama-3-70B-Instruct</span
|
58 |
+
></span
|
59 |
>
|
60 |
</div>
|
61 |
<div class="flex cursor-pointer items-center px-2 py-1.5 text-sm hover:bg-gray-100">
|
62 |
<IconStar classNames="lucide lucide-star mr-2 h-4 w-4 text-yellow-400" />
|
63 |
<span class="inline-flex items-center"
|
64 |
+
><span class="text-gray-500">mistralai</span><span class="mx-1 text-black">/</span><span
|
65 |
+
class="text-black">Mixtral-8x7B-Instruct-v0.1</span
|
66 |
+
></span
|
67 |
>
|
68 |
</div>
|
69 |
</div>
|
|
|
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">/</span><span
|
86 |
+
class="text-black">{modelName}</span
|
87 |
+
></span
|
88 |
>
|
89 |
</button>
|
90 |
{/each}
|
src/lib/components/InferencePlayground/generationConfigSettings.ts
CHANGED
@@ -12,47 +12,43 @@ export const GENERATION_CONFIG_SETTINGS: Record<string, GenerationKeySettings> =
|
|
12 |
step: 0.01,
|
13 |
min: 0,
|
14 |
max: 2,
|
15 |
-
label:
|
16 |
},
|
17 |
max_tokens: {
|
18 |
default: 512,
|
19 |
step: 1,
|
20 |
min: 1,
|
21 |
max: 8192, // changed dynamically based on model
|
22 |
-
label:
|
23 |
},
|
24 |
top_p: {
|
25 |
default: 0.7,
|
26 |
step: 0.01,
|
27 |
min: 0,
|
28 |
max: 1,
|
29 |
-
label:
|
30 |
},
|
31 |
top_k: {
|
32 |
default: 50,
|
33 |
step: 1,
|
34 |
min: 1,
|
35 |
max: 100,
|
36 |
-
label:
|
37 |
},
|
38 |
repetition_penalty: {
|
39 |
default: 1,
|
40 |
step: 0.01,
|
41 |
min: 1,
|
42 |
max: 2,
|
43 |
-
label:
|
44 |
-
}
|
45 |
};
|
46 |
|
47 |
export type GenerationConfigKey = keyof typeof GENERATION_CONFIG_SETTINGS;
|
48 |
|
49 |
-
export const GENERATION_CONFIG_KEYS: GenerationConfigKey[] = [
|
50 |
|
51 |
-
export const GENERATION_CONFIG_KEYS_ADVANCED: GenerationConfigKey[] = [
|
52 |
-
'top_p',
|
53 |
-
'top_k',
|
54 |
-
'repetition_penalty'
|
55 |
-
];
|
56 |
|
57 |
export type GenerationConfig = Record<GenerationConfigKey, number>;
|
58 |
|
|
|
12 |
step: 0.01,
|
13 |
min: 0,
|
14 |
max: 2,
|
15 |
+
label: "Temperature",
|
16 |
},
|
17 |
max_tokens: {
|
18 |
default: 512,
|
19 |
step: 1,
|
20 |
min: 1,
|
21 |
max: 8192, // changed dynamically based on model
|
22 |
+
label: "Max Tokens",
|
23 |
},
|
24 |
top_p: {
|
25 |
default: 0.7,
|
26 |
step: 0.01,
|
27 |
min: 0,
|
28 |
max: 1,
|
29 |
+
label: "Top-P",
|
30 |
},
|
31 |
top_k: {
|
32 |
default: 50,
|
33 |
step: 1,
|
34 |
min: 1,
|
35 |
max: 100,
|
36 |
+
label: "Top-K",
|
37 |
},
|
38 |
repetition_penalty: {
|
39 |
default: 1,
|
40 |
step: 0.01,
|
41 |
min: 1,
|
42 |
max: 2,
|
43 |
+
label: "Repetition Penalty",
|
44 |
+
},
|
45 |
};
|
46 |
|
47 |
export type GenerationConfigKey = keyof typeof GENERATION_CONFIG_SETTINGS;
|
48 |
|
49 |
+
export const GENERATION_CONFIG_KEYS: GenerationConfigKey[] = ["temperature", "max_tokens"];
|
50 |
|
51 |
+
export const GENERATION_CONFIG_KEYS_ADVANCED: GenerationConfigKey[] = ["top_p", "top_k", "repetition_penalty"];
|
|
|
|
|
|
|
|
|
52 |
|
53 |
export type GenerationConfig = Record<GenerationConfigKey, number>;
|
54 |
|
src/lib/components/InferencePlayground/inferencePlaygroundUtils.ts
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
-
import { type ChatCompletionInputMessage } from
|
2 |
-
import { HfInference } from
|
3 |
-
import type { Conversation, ModelEntryWithTokenizer } from
|
4 |
|
5 |
export function createHfInference(token: string): HfInference {
|
6 |
return new HfInference(token);
|
@@ -14,18 +14,16 @@ export async function handleStreamingResponse(
|
|
14 |
systemMessage?: ChatCompletionInputMessage
|
15 |
): Promise<void> {
|
16 |
const messages = [
|
17 |
-
...(isSystemPromptSupported(conversation.model) && systemMessage?.content?.length
|
18 |
-
|
19 |
-
: []),
|
20 |
-
...conversation.messages
|
21 |
];
|
22 |
-
let out =
|
23 |
for await (const chunk of hf.chatCompletionStream(
|
24 |
{
|
25 |
model: conversation.model.id,
|
26 |
messages,
|
27 |
temperature: conversation.config.temperature,
|
28 |
-
max_tokens: conversation.config.maxTokens
|
29 |
},
|
30 |
{ signal: abortController.signal }
|
31 |
)) {
|
@@ -42,25 +40,23 @@ export async function handleNonStreamingResponse(
|
|
42 |
systemMessage?: ChatCompletionInputMessage
|
43 |
): Promise<ChatCompletionInputMessage> {
|
44 |
const messages = [
|
45 |
-
...(isSystemPromptSupported(conversation.model) && systemMessage?.content?.length
|
46 |
-
|
47 |
-
: []),
|
48 |
-
...conversation.messages
|
49 |
];
|
50 |
|
51 |
const response = await hf.chatCompletion({
|
52 |
model: conversation.model,
|
53 |
messages,
|
54 |
temperature: conversation.config.temperature,
|
55 |
-
max_tokens: conversation.config.maxTokens
|
56 |
});
|
57 |
|
58 |
if (response.choices && response.choices.length > 0) {
|
59 |
return response.choices[0].message;
|
60 |
}
|
61 |
-
throw new Error(
|
62 |
}
|
63 |
|
64 |
export function isSystemPromptSupported(model: ModelEntryWithTokenizer) {
|
65 |
-
return model.tokenizerConfig?.chat_template?.includes(
|
66 |
}
|
|
|
1 |
+
import { type ChatCompletionInputMessage } from "@huggingface/tasks";
|
2 |
+
import { HfInference } from "@huggingface/inference";
|
3 |
+
import type { Conversation, ModelEntryWithTokenizer } from "$lib/types";
|
4 |
|
5 |
export function createHfInference(token: string): HfInference {
|
6 |
return new HfInference(token);
|
|
|
14 |
systemMessage?: ChatCompletionInputMessage
|
15 |
): Promise<void> {
|
16 |
const messages = [
|
17 |
+
...(isSystemPromptSupported(conversation.model) && systemMessage?.content?.length ? [systemMessage] : []),
|
18 |
+
...conversation.messages,
|
|
|
|
|
19 |
];
|
20 |
+
let out = "";
|
21 |
for await (const chunk of hf.chatCompletionStream(
|
22 |
{
|
23 |
model: conversation.model.id,
|
24 |
messages,
|
25 |
temperature: conversation.config.temperature,
|
26 |
+
max_tokens: conversation.config.maxTokens,
|
27 |
},
|
28 |
{ signal: abortController.signal }
|
29 |
)) {
|
|
|
40 |
systemMessage?: ChatCompletionInputMessage
|
41 |
): Promise<ChatCompletionInputMessage> {
|
42 |
const messages = [
|
43 |
+
...(isSystemPromptSupported(conversation.model) && systemMessage?.content?.length ? [systemMessage] : []),
|
44 |
+
...conversation.messages,
|
|
|
|
|
45 |
];
|
46 |
|
47 |
const response = await hf.chatCompletion({
|
48 |
model: conversation.model,
|
49 |
messages,
|
50 |
temperature: conversation.config.temperature,
|
51 |
+
max_tokens: conversation.config.maxTokens,
|
52 |
});
|
53 |
|
54 |
if (response.choices && response.choices.length > 0) {
|
55 |
return response.choices[0].message;
|
56 |
}
|
57 |
+
throw new Error("No response from the model");
|
58 |
}
|
59 |
|
60 |
export function isSystemPromptSupported(model: ModelEntryWithTokenizer) {
|
61 |
+
return model.tokenizerConfig?.chat_template?.includes("system");
|
62 |
}
|
src/lib/types/index.d.ts
CHANGED
@@ -1,9 +1,8 @@
|
|
1 |
-
import type { GenerationConfig } from
|
2 |
-
import type { ModelEntry } from
|
3 |
-
import type { ChatCompletionInputMessage } from
|
4 |
|
5 |
type Conversation = {
|
6 |
-
id: string;
|
7 |
model: ModelEntryWithTokenizer;
|
8 |
config: GenerationConfig;
|
9 |
messages: ChatCompletionInputMessage[];
|
|
|
1 |
+
import type { GenerationConfig } from "$lib/components/InferencePlayground/generationConfigSettings";
|
2 |
+
import type { ModelEntry } from "@huggingface/hub";
|
3 |
+
import type { ChatCompletionInputMessage } from "@huggingface/tasks";
|
4 |
|
5 |
type Conversation = {
|
|
|
6 |
model: ModelEntryWithTokenizer;
|
7 |
config: GenerationConfig;
|
8 |
messages: ChatCompletionInputMessage[];
|
src/routes/+layout.svelte
CHANGED
@@ -1,5 +1,5 @@
|
|
1 |
<script>
|
2 |
-
import
|
3 |
</script>
|
4 |
|
5 |
<slot></slot>
|
|
|
1 |
<script>
|
2 |
+
import "../app.css";
|
3 |
</script>
|
4 |
|
5 |
<slot></slot>
|
src/routes/+page.server.ts
CHANGED
@@ -1,22 +1,21 @@
|
|
1 |
-
import type { ModelEntryWithTokenizer } from
|
2 |
-
import type { ModelEntry } from
|
3 |
-
import type { PageServerLoad } from
|
4 |
-
import { env } from
|
5 |
|
6 |
export const load: PageServerLoad = async ({ fetch }) => {
|
7 |
-
const apiUrl =
|
8 |
-
'https://huggingface.co/api/models?pipeline_tag=text-generation&inference=Warm&filter=conversational';
|
9 |
const HF_TOKEN = env.HF_TOKEN;
|
10 |
|
11 |
const res = await fetch(apiUrl, {
|
12 |
headers: {
|
13 |
-
Authorization: `Bearer ${HF_TOKEN}
|
14 |
-
}
|
15 |
});
|
16 |
const compatibleModels: ModelEntry[] = await res.json();
|
17 |
compatibleModels.sort((a, b) => a.id.toLowerCase().localeCompare(b.id.toLowerCase()));
|
18 |
|
19 |
-
const promises = compatibleModels.map(async
|
20 |
const configUrl = `https://huggingface.co/${model.modelId}/raw/main/tokenizer_config.json`;
|
21 |
const res = await fetch(configUrl);
|
22 |
if (!res.ok) {
|
@@ -26,9 +25,7 @@ export const load: PageServerLoad = async ({ fetch }) => {
|
|
26 |
return { ...model, tokenizerConfig } satisfies ModelEntryWithTokenizer;
|
27 |
});
|
28 |
|
29 |
-
const models: ModelEntryWithTokenizer[] = (await Promise.all(promises)).filter(
|
30 |
-
(model) => model !== null
|
31 |
-
);
|
32 |
|
33 |
return { models };
|
34 |
};
|
|
|
1 |
+
import type { ModelEntryWithTokenizer } from "$lib/types";
|
2 |
+
import type { ModelEntry } from "@huggingface/hub";
|
3 |
+
import type { PageServerLoad } from "./$types";
|
4 |
+
import { env } from "$env/dynamic/private";
|
5 |
|
6 |
export const load: PageServerLoad = async ({ fetch }) => {
|
7 |
+
const apiUrl = "https://huggingface.co/api/models?pipeline_tag=text-generation&inference=Warm&filter=conversational";
|
|
|
8 |
const HF_TOKEN = env.HF_TOKEN;
|
9 |
|
10 |
const res = await fetch(apiUrl, {
|
11 |
headers: {
|
12 |
+
Authorization: `Bearer ${HF_TOKEN}`,
|
13 |
+
},
|
14 |
});
|
15 |
const compatibleModels: ModelEntry[] = await res.json();
|
16 |
compatibleModels.sort((a, b) => a.id.toLowerCase().localeCompare(b.id.toLowerCase()));
|
17 |
|
18 |
+
const promises = compatibleModels.map(async model => {
|
19 |
const configUrl = `https://huggingface.co/${model.modelId}/raw/main/tokenizer_config.json`;
|
20 |
const res = await fetch(configUrl);
|
21 |
if (!res.ok) {
|
|
|
25 |
return { ...model, tokenizerConfig } satisfies ModelEntryWithTokenizer;
|
26 |
});
|
27 |
|
28 |
+
const models: ModelEntryWithTokenizer[] = (await Promise.all(promises)).filter(model => model !== null);
|
|
|
|
|
29 |
|
30 |
return { models };
|
31 |
};
|
src/routes/+page.svelte
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
<script lang="ts">
|
2 |
export let data;
|
3 |
-
import InferencePlayground from
|
4 |
</script>
|
5 |
|
6 |
<InferencePlayground models={data.models} />
|
|
|
1 |
<script lang="ts">
|
2 |
export let data;
|
3 |
+
import InferencePlayground from "$lib/components/InferencePlayground/InferencePlayground.svelte";
|
4 |
</script>
|
5 |
|
6 |
<InferencePlayground models={data.models} />
|
svelte.config.js
CHANGED
@@ -1,5 +1,5 @@
|
|
1 |
-
import adapter from
|
2 |
-
import { vitePreprocess } from
|
3 |
|
4 |
/** @type {import('@sveltejs/kit').Config} */
|
5 |
const config = {
|
@@ -11,8 +11,8 @@ const config = {
|
|
11 |
// adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
|
12 |
// If your environment is not supported, or you settled on a specific environment, switch out the adapter.
|
13 |
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
|
14 |
-
adapter: adapter()
|
15 |
-
}
|
16 |
};
|
17 |
|
18 |
export default config;
|
|
|
1 |
+
import adapter from "@sveltejs/adapter-node";
|
2 |
+
import { vitePreprocess } from "@sveltejs/vite-plugin-svelte";
|
3 |
|
4 |
/** @type {import('@sveltejs/kit').Config} */
|
5 |
const config = {
|
|
|
11 |
// adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
|
12 |
// If your environment is not supported, or you settled on a specific environment, switch out the adapter.
|
13 |
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
|
14 |
+
adapter: adapter(),
|
15 |
+
},
|
16 |
};
|
17 |
|
18 |
export default config;
|
tailwind.config.ts
CHANGED
@@ -1,12 +1,12 @@
|
|
1 |
-
import type { Config } from
|
2 |
-
import containerQueries from
|
3 |
|
4 |
export default {
|
5 |
-
content: [
|
6 |
|
7 |
theme: {
|
8 |
-
extend: {}
|
9 |
},
|
10 |
|
11 |
-
plugins: [containerQueries]
|
12 |
} as Config;
|
|
|
1 |
+
import type { Config } from "tailwindcss";
|
2 |
+
import containerQueries from "@tailwindcss/container-queries";
|
3 |
|
4 |
export default {
|
5 |
+
content: ["./src/**/*.{html,js,svelte,ts}"],
|
6 |
|
7 |
theme: {
|
8 |
+
extend: {},
|
9 |
},
|
10 |
|
11 |
+
plugins: [containerQueries],
|
12 |
} as Config;
|
vite.config.ts
CHANGED
@@ -1,6 +1,6 @@
|
|
1 |
-
import { sveltekit } from
|
2 |
-
import { defineConfig } from
|
3 |
|
4 |
export default defineConfig({
|
5 |
-
plugins: [sveltekit()]
|
6 |
});
|
|
|
1 |
+
import { sveltekit } from "@sveltejs/kit/vite";
|
2 |
+
import { defineConfig } from "vite";
|
3 |
|
4 |
export default defineConfig({
|
5 |
+
plugins: [sveltekit()],
|
6 |
});
|