cfahlgren1 HF staff commited on
Commit
b14d567
1 Parent(s): 6bce3a4

setup webllm with smol 360m demo

Browse files
bun.lockb CHANGED
Binary files a/bun.lockb and b/bun.lockb differ
 
components.json ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "$schema": "https://shadcn-svelte.com/schema.json",
3
+ "style": "default",
4
+ "tailwind": {
5
+ "config": "tailwind.config.ts",
6
+ "css": "src/app.css",
7
+ "baseColor": "slate"
8
+ },
9
+ "aliases": {
10
+ "components": "$lib/components",
11
+ "utils": "$lib/utils"
12
+ },
13
+ "typescript": true
14
+ }
package.json CHANGED
@@ -13,10 +13,19 @@
13
  "@sveltejs/adapter-auto": "^3.0.0",
14
  "@sveltejs/kit": "^2.0.0",
15
  "@sveltejs/vite-plugin-svelte": "^3.0.0",
 
 
16
  "svelte": "^4.2.7",
17
  "svelte-check": "^3.6.0",
 
18
  "typescript": "^5.0.0",
19
  "vite": "^5.0.3"
20
  },
21
- "type": "module"
 
 
 
 
 
 
22
  }
 
13
  "@sveltejs/adapter-auto": "^3.0.0",
14
  "@sveltejs/kit": "^2.0.0",
15
  "@sveltejs/vite-plugin-svelte": "^3.0.0",
16
+ "@tailwindcss/typography": "^0.5.14",
17
+ "autoprefixer": "^10.4.20",
18
  "svelte": "^4.2.7",
19
  "svelte-check": "^3.6.0",
20
+ "tailwindcss": "^3.4.9",
21
  "typescript": "^5.0.0",
22
  "vite": "^5.0.3"
23
  },
24
+ "type": "module",
25
+ "dependencies": {
26
+ "@mlc-ai/web-llm": "^0.2.60",
27
+ "clsx": "^2.1.1",
28
+ "tailwind-merge": "^2.5.2",
29
+ "tailwind-variants": "^0.2.1"
30
+ }
31
  }
postcss.config.js ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ export default {
2
+ plugins: {
3
+ tailwindcss: {},
4
+ autoprefixer: {}
5
+ }
6
+ };
src/app.css ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @tailwind base;
2
+ @tailwind components;
3
+ @tailwind utilities;
4
+
5
+ @layer base {
6
+ :root {
7
+ --background: 0 0% 100%;
8
+ --foreground: 222.2 84% 4.9%;
9
+
10
+ --muted: 210 40% 96.1%;
11
+ --muted-foreground: 215.4 16.3% 46.9%;
12
+
13
+ --popover: 0 0% 100%;
14
+ --popover-foreground: 222.2 84% 4.9%;
15
+
16
+ --card: 0 0% 100%;
17
+ --card-foreground: 222.2 84% 4.9%;
18
+
19
+ --border: 214.3 31.8% 91.4%;
20
+ --input: 214.3 31.8% 91.4%;
21
+
22
+ --primary: 222.2 47.4% 11.2%;
23
+ --primary-foreground: 210 40% 98%;
24
+
25
+ --secondary: 210 40% 96.1%;
26
+ --secondary-foreground: 222.2 47.4% 11.2%;
27
+
28
+ --accent: 210 40% 96.1%;
29
+ --accent-foreground: 222.2 47.4% 11.2%;
30
+
31
+ --destructive: 0 72.2% 50.6%;
32
+ --destructive-foreground: 210 40% 98%;
33
+
34
+ --ring: 222.2 84% 4.9%;
35
+
36
+ --radius: 0.5rem;
37
+ }
38
+
39
+ .dark {
40
+ --background: 222.2 84% 4.9%;
41
+ --foreground: 210 40% 98%;
42
+
43
+ --muted: 217.2 32.6% 17.5%;
44
+ --muted-foreground: 215 20.2% 65.1%;
45
+
46
+ --popover: 222.2 84% 4.9%;
47
+ --popover-foreground: 210 40% 98%;
48
+
49
+ --card: 222.2 84% 4.9%;
50
+ --card-foreground: 210 40% 98%;
51
+
52
+ --border: 217.2 32.6% 17.5%;
53
+ --input: 217.2 32.6% 17.5%;
54
+
55
+ --primary: 210 40% 98%;
56
+ --primary-foreground: 222.2 47.4% 11.2%;
57
+
58
+ --secondary: 217.2 32.6% 17.5%;
59
+ --secondary-foreground: 210 40% 98%;
60
+
61
+ --accent: 217.2 32.6% 17.5%;
62
+ --accent-foreground: 210 40% 98%;
63
+
64
+ --destructive: 0 62.8% 30.6%;
65
+ --destructive-foreground: 210 40% 98%;
66
+
67
+ --ring: hsl(212.7,26.8%,83.9);
68
+ }
69
+ }
70
+
71
+ @layer base {
72
+ * {
73
+ @apply border-border;
74
+ }
75
+ body {
76
+ @apply bg-background text-foreground;
77
+ }
78
+ }
src/lib/components/ui/badge/badge.svelte ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <script lang="ts">
2
+ import { type Variant, badgeVariants } from "./index.js";
3
+ import { cn } from "$lib/utils.js";
4
+
5
+ let className: string | undefined | null = undefined;
6
+ export let href: string | undefined = undefined;
7
+ export let variant: Variant = "default";
8
+ export { className as class };
9
+ </script>
10
+
11
+ <svelte:element
12
+ this={href ? "a" : "span"}
13
+ {href}
14
+ class={cn(badgeVariants({ variant, className }))}
15
+ {...$$restProps}
16
+ >
17
+ <slot />
18
+ </svelte:element>
src/lib/components/ui/badge/index.ts ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { type VariantProps, tv } from "tailwind-variants";
2
+ export { default as Badge } from "./badge.svelte";
3
+
4
+ export const badgeVariants = tv({
5
+ base: "focus:ring-ring inline-flex select-none items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-offset-2",
6
+ variants: {
7
+ variant: {
8
+ default: "bg-primary text-primary-foreground hover:bg-primary/80 border-transparent",
9
+ secondary:
10
+ "bg-secondary text-secondary-foreground hover:bg-secondary/80 border-transparent",
11
+ destructive:
12
+ "bg-destructive text-destructive-foreground hover:bg-destructive/80 border-transparent",
13
+ outline: "text-foreground",
14
+ },
15
+ },
16
+ defaultVariants: {
17
+ variant: "default",
18
+ },
19
+ });
20
+
21
+ export type Variant = VariantProps<typeof badgeVariants>["variant"];
src/lib/components/ui/textarea/index.ts ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import Root from "./textarea.svelte";
2
+
3
+ type FormTextareaEvent<T extends Event = Event> = T & {
4
+ currentTarget: EventTarget & HTMLTextAreaElement;
5
+ };
6
+
7
+ type TextareaEvents = {
8
+ blur: FormTextareaEvent<FocusEvent>;
9
+ change: FormTextareaEvent<Event>;
10
+ click: FormTextareaEvent<MouseEvent>;
11
+ focus: FormTextareaEvent<FocusEvent>;
12
+ keydown: FormTextareaEvent<KeyboardEvent>;
13
+ keypress: FormTextareaEvent<KeyboardEvent>;
14
+ keyup: FormTextareaEvent<KeyboardEvent>;
15
+ mouseover: FormTextareaEvent<MouseEvent>;
16
+ mouseenter: FormTextareaEvent<MouseEvent>;
17
+ mouseleave: FormTextareaEvent<MouseEvent>;
18
+ paste: FormTextareaEvent<ClipboardEvent>;
19
+ input: FormTextareaEvent<InputEvent>;
20
+ };
21
+
22
+ export {
23
+ Root,
24
+ //
25
+ Root as Textarea,
26
+ type TextareaEvents,
27
+ type FormTextareaEvent,
28
+ };
src/lib/components/ui/textarea/textarea.svelte ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <script lang="ts">
2
+ import type { HTMLTextareaAttributes } from "svelte/elements";
3
+ import type { TextareaEvents } from "./index.js";
4
+ import { cn } from "$lib/utils.js";
5
+
6
+ type $$Props = HTMLTextareaAttributes;
7
+ type $$Events = TextareaEvents;
8
+
9
+ let className: $$Props["class"] = undefined;
10
+ export let value: $$Props["value"] = undefined;
11
+ export { className as class };
12
+
13
+ // Workaround for https://github.com/sveltejs/svelte/issues/9305
14
+ // Fixed in Svelte 5, but not backported to 4.x.
15
+ export let readonly: $$Props["readonly"] = undefined;
16
+ </script>
17
+
18
+ <textarea
19
+ class={cn(
20
+ "border-input bg-background ring-offset-background placeholder:text-muted-foreground focus-visible:ring-ring flex min-h-[80px] w-full rounded-md border px-3 py-2 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
21
+ className
22
+ )}
23
+ bind:value
24
+ {readonly}
25
+ on:blur
26
+ on:change
27
+ on:click
28
+ on:focus
29
+ on:keydown
30
+ on:keypress
31
+ on:keyup
32
+ on:mouseover
33
+ on:mouseenter
34
+ on:mouseleave
35
+ on:paste
36
+ on:input
37
+ {...$$restProps}
38
+ ></textarea>
src/lib/utils.ts ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { type ClassValue, clsx } from "clsx";
2
+ import { twMerge } from "tailwind-merge";
3
+ import { cubicOut } from "svelte/easing";
4
+ import type { TransitionConfig } from "svelte/transition";
5
+
6
+ export function cn(...inputs: ClassValue[]) {
7
+ return twMerge(clsx(inputs));
8
+ }
9
+
10
+ type FlyAndScaleParams = {
11
+ y?: number;
12
+ x?: number;
13
+ start?: number;
14
+ duration?: number;
15
+ };
16
+
17
+ export const flyAndScale = (
18
+ node: Element,
19
+ params: FlyAndScaleParams = { y: -8, x: 0, start: 0.95, duration: 150 }
20
+ ): TransitionConfig => {
21
+ const style = getComputedStyle(node);
22
+ const transform = style.transform === "none" ? "" : style.transform;
23
+
24
+ const scaleConversion = (
25
+ valueA: number,
26
+ scaleA: [number, number],
27
+ scaleB: [number, number]
28
+ ) => {
29
+ const [minA, maxA] = scaleA;
30
+ const [minB, maxB] = scaleB;
31
+
32
+ const percentage = (valueA - minA) / (maxA - minA);
33
+ const valueB = percentage * (maxB - minB) + minB;
34
+
35
+ return valueB;
36
+ };
37
+
38
+ const styleToString = (
39
+ style: Record<string, number | string | undefined>
40
+ ): string => {
41
+ return Object.keys(style).reduce((str, key) => {
42
+ if (style[key] === undefined) return str;
43
+ return str + `${key}:${style[key]};`;
44
+ }, "");
45
+ };
46
+
47
+ return {
48
+ duration: params.duration ?? 200,
49
+ delay: 0,
50
+ css: (t) => {
51
+ const y = scaleConversion(t, [0, 1], [params.y ?? 5, 0]);
52
+ const x = scaleConversion(t, [0, 1], [params.x ?? 0, 0]);
53
+ const scale = scaleConversion(t, [0, 1], [params.start ?? 0.95, 1]);
54
+
55
+ return styleToString({
56
+ transform: `${transform} translate3d(${x}px, ${y}px, 0) scale(${scale})`,
57
+ opacity: t
58
+ });
59
+ },
60
+ easing: cubicOut
61
+ };
62
+ };
src/routes/+layout.svelte ADDED
@@ -0,0 +1 @@
 
 
1
+ <script>import "../app.css";</script><slot></slot>
src/routes/+page.svelte CHANGED
@@ -1,2 +1,98 @@
1
- <h1>Welcome to SvelteKit</h1>
2
- <p>Visit <a href="https://kit.svelte.dev">kit.svelte.dev</a> to read the documentation</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <script lang="ts">
2
+ import Textarea from "@/lib/components/ui/textarea/textarea.svelte";
3
+ import Badge from "@/lib/components/ui/badge/badge.svelte";
4
+ import * as webllm from "@mlc-ai/web-llm";
5
+ import { onMount } from 'svelte';
6
+
7
+ let engine: webllm.MLCEngineInterface;
8
+ let isLoading = false;
9
+ let loadingStatus = '';
10
+ let inputText = '';
11
+ let outputText = '';
12
+ let error = '';
13
+ let completionSpeed: number | null = null;
14
+ let selectedModel = "SmolLM-360M-Instruct-q4f16_1-MLC";
15
+ let isGenerating = false;
16
+
17
+ async function loadWebLLM() {
18
+ isLoading = true;
19
+ error = '';
20
+ const initProgressCallback = (report: webllm.InitProgressReport) => {
21
+ loadingStatus = report.text;
22
+ };
23
+
24
+ const appConfig: webllm.AppConfig = {
25
+ model_list: [{
26
+ model: `https://huggingface.co/mlc-ai/${selectedModel}`,
27
+ model_id: selectedModel,
28
+ model_lib: `${webllm.modelLibURLPrefix}${webllm.modelVersion}/SmolLM-360M-Instruct-q4f16_1-ctx2k_cs1k-webgpu.wasm`,
29
+ overrides: { context_window_size: 2048 },
30
+ }],
31
+ };
32
+
33
+ try {
34
+ engine = await webllm.CreateMLCEngine(selectedModel, {
35
+ appConfig,
36
+ initProgressCallback,
37
+ logLevel: "INFO",
38
+ });
39
+ } catch (err) {
40
+ error = `Failed to load the model: ${(err as Error).message}`;
41
+ } finally {
42
+ isLoading = false;
43
+ }
44
+ }
45
+
46
+ async function generateCompletion() {
47
+ if (!engine || !inputText.trim() || isGenerating) return;
48
+
49
+ isGenerating = true;
50
+ const startTime = performance.now();
51
+ try {
52
+ const reply = await engine.completions.create({
53
+ prompt: inputText,
54
+ echo: false,
55
+ max_tokens: 10,
56
+ });
57
+
58
+ outputText = reply.choices[0].text;
59
+ completionSpeed = Math.round(performance.now() - startTime);
60
+ error = '';
61
+ } catch (err) {
62
+ error = `Error: ${(err as Error).message}`;
63
+ } finally {
64
+ isGenerating = false;
65
+ }
66
+ }
67
+
68
+ onMount(loadWebLLM);
69
+ </script>
70
+
71
+ <div class="flex my-20 flex-col items-center gap-4 max-w-lg mx-auto">
72
+ <h1 class="text-center font-mono font-bold text-4xl">Mini Playground</h1>
73
+ <p class="text-center font-mono text-sm mb-4">Powered by `{selectedModel}`</p>
74
+ <Textarea
75
+ bind:value={inputText}
76
+ on:input={() => {
77
+ if (!isGenerating) {
78
+ generateCompletion();
79
+ }
80
+ }}
81
+ disabled={isLoading}
82
+ class="w-full"
83
+ placeholder="Enter your prompt here"
84
+ />
85
+ <pre class="text-lg whitespace-pre-wrap">{outputText}</pre>
86
+ {#if isLoading}
87
+ <p class="text-sm text-slate-600 text-center">{loadingStatus}</p>
88
+ {:else if error}
89
+ <p class="text-sm text-red-600">{error}</p>
90
+ {:else}
91
+ <div class="flex gap-2">
92
+ {#if completionSpeed !== null}
93
+ <Badge>{completionSpeed}ms</Badge>
94
+ {/if}
95
+ <Badge class="bg-green-700">{selectedModel}</Badge>
96
+ </div>
97
+ {/if}
98
+ </div>
svelte.config.js CHANGED
@@ -11,7 +11,10 @@ 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
 
 
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
+ alias: {
16
+ "@/*": "./src/*",
17
+ },
18
  }
19
  };
20
 
tailwind.config.ts ADDED
@@ -0,0 +1,64 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { fontFamily } from "tailwindcss/defaultTheme";
2
+ import type { Config } from "tailwindcss";
3
+
4
+ const config: Config = {
5
+ darkMode: ["class"],
6
+ content: ["./src/**/*.{html,js,svelte,ts}"],
7
+ safelist: ["dark"],
8
+ theme: {
9
+ container: {
10
+ center: true,
11
+ padding: "2rem",
12
+ screens: {
13
+ "2xl": "1400px"
14
+ }
15
+ },
16
+ extend: {
17
+ colors: {
18
+ border: "hsl(var(--border) / <alpha-value>)",
19
+ input: "hsl(var(--input) / <alpha-value>)",
20
+ ring: "hsl(var(--ring) / <alpha-value>)",
21
+ background: "hsl(var(--background) / <alpha-value>)",
22
+ foreground: "hsl(var(--foreground) / <alpha-value>)",
23
+ primary: {
24
+ DEFAULT: "hsl(var(--primary) / <alpha-value>)",
25
+ foreground: "hsl(var(--primary-foreground) / <alpha-value>)"
26
+ },
27
+ secondary: {
28
+ DEFAULT: "hsl(var(--secondary) / <alpha-value>)",
29
+ foreground: "hsl(var(--secondary-foreground) / <alpha-value>)"
30
+ },
31
+ destructive: {
32
+ DEFAULT: "hsl(var(--destructive) / <alpha-value>)",
33
+ foreground: "hsl(var(--destructive-foreground) / <alpha-value>)"
34
+ },
35
+ muted: {
36
+ DEFAULT: "hsl(var(--muted) / <alpha-value>)",
37
+ foreground: "hsl(var(--muted-foreground) / <alpha-value>)"
38
+ },
39
+ accent: {
40
+ DEFAULT: "hsl(var(--accent) / <alpha-value>)",
41
+ foreground: "hsl(var(--accent-foreground) / <alpha-value>)"
42
+ },
43
+ popover: {
44
+ DEFAULT: "hsl(var(--popover) / <alpha-value>)",
45
+ foreground: "hsl(var(--popover-foreground) / <alpha-value>)"
46
+ },
47
+ card: {
48
+ DEFAULT: "hsl(var(--card) / <alpha-value>)",
49
+ foreground: "hsl(var(--card-foreground) / <alpha-value>)"
50
+ }
51
+ },
52
+ borderRadius: {
53
+ lg: "var(--radius)",
54
+ md: "calc(var(--radius) - 2px)",
55
+ sm: "calc(var(--radius) - 4px)"
56
+ },
57
+ fontFamily: {
58
+ sans: [...fontFamily.sans]
59
+ }
60
+ }
61
+ },
62
+ };
63
+
64
+ export default config;