machineuser commited on
Commit
388ac76
1 Parent(s): 767bfd8

Sync widgets demo

Browse files
packages/widgets/README.md CHANGED
@@ -6,13 +6,26 @@ Open-source version of the inference widgets from huggingface.co
6
 
7
  **Demo page:** https://huggingface.co/spaces/huggingfacejs/inference-widgets
8
 
9
- You can also run the demo locally:
 
 
 
 
 
 
10
 
11
  ```console
12
  pnpm install
13
  pnpm dev
14
  ```
15
 
16
- ## Publishing
 
 
 
 
 
 
 
17
 
18
- Because `@huggingface/widgets` depends on `@huggingface/tasks`, you need to publish `@huggingface/tasks` first, and then `@huggingface/widgets`.
 
6
 
7
  **Demo page:** https://huggingface.co/spaces/huggingfacejs/inference-widgets
8
 
9
+ ## Publishing
10
+
11
+ Because `@huggingface/widgets` depends on `@huggingface/tasks`, you need to publish `@huggingface/tasks` first, and then `@huggingface/widgets`. There should be a CI check to prevent publishing `@huggingface/widgets` if `@huggingface/tasks` hasn't been published yet.
12
+
13
+ ## Demo
14
+
15
+ You can run the demo locally:
16
 
17
  ```console
18
  pnpm install
19
  pnpm dev
20
  ```
21
 
22
+ If you want to try the "Sign-in with HF" feature locally, you will need to https://huggingface.co/settings/applications/new an OAuth application with `"openid"` and `"inference-api"` scopes and `http://localhost:5173/auth/callback/huggingface` as the redirect URL.
23
+
24
+ Then you can create a `.env.local` file with the following content:
25
+
26
+ ```env
27
+ OAUTH_CLIENT_ID=...
28
+ OAUTH_CLIENT_SECRET=...
29
+ ```
30
 
31
+ If you want to try the "Sign-in with HF" feature in a Space, you can just duplicate https://huggingface.co/spaces/huggingfacejs/inference-widgets, it should work out of the box thanks to the metadata in the `README.md` file.
packages/widgets/package.json CHANGED
@@ -38,6 +38,8 @@
38
  "svelte": "^3.59.2"
39
  },
40
  "devDependencies": {
 
 
41
  "@fontsource/ibm-plex-mono": "^5.0.8",
42
  "@fontsource/source-sans-pro": "^5.0.8",
43
  "@sveltejs/adapter-node": "^1.3.1",
 
38
  "svelte": "^3.59.2"
39
  },
40
  "devDependencies": {
41
+ "@auth/core": "^0.18.3",
42
+ "@auth/sveltekit": "^0.3.14",
43
  "@fontsource/ibm-plex-mono": "^5.0.8",
44
  "@fontsource/source-sans-pro": "^5.0.8",
45
  "@sveltejs/adapter-node": "^1.3.1",
packages/widgets/pnpm-lock.yaml CHANGED
@@ -10,6 +10,12 @@ dependencies:
10
  version: link:../tasks
11
 
12
  devDependencies:
 
 
 
 
 
 
13
  '@fontsource/ibm-plex-mono':
14
  specifier: ^5.0.8
15
  version: 5.0.8
@@ -68,6 +74,35 @@ packages:
68
  engines: {node: '>=10'}
69
  dev: true
70
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
  /@esbuild/android-arm64@0.18.20:
72
  resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==}
73
  engines: {node: '>=12'}
@@ -367,6 +402,10 @@ packages:
367
  fastq: 1.15.0
368
  dev: true
369
 
 
 
 
 
370
  /@polka/url@1.0.0-next.23:
371
  resolution: {integrity: sha512-C16M+IYz0rgRhWZdCmK+h58JMv8vijAA61gmz2rspCSwKwzBebpdcsiUmwrtJRdphuY30i6BSLEOP8ppbNLyLg==}
372
  dev: true
@@ -1230,6 +1269,10 @@ packages:
1230
  hasBin: true
1231
  dev: true
1232
 
 
 
 
 
1233
  /js-sdsl@4.4.2:
1234
  resolution: {integrity: sha512-dwXFwByc/ajSV6m5bcKAPwe4yDDF6D614pxmIi5odytzxRlwqF6nwoiCek80Ixc7Cvma5awClxrzFtxCQvcM8w==}
1235
  dev: true
@@ -1440,6 +1483,10 @@ packages:
1440
  npm-normalize-package-bin: 2.0.0
1441
  dev: true
1442
 
 
 
 
 
1443
  /object-assign@4.1.1:
1444
  resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
1445
  engines: {node: '>=0.10.0'}
@@ -1604,11 +1651,28 @@ packages:
1604
  source-map-js: 1.0.2
1605
  dev: true
1606
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1607
  /prelude-ls@1.2.1:
1608
  resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
1609
  engines: {node: '>= 0.8.0'}
1610
  dev: true
1611
 
 
 
 
 
1612
  /publint@0.1.9:
1613
  resolution: {integrity: sha512-O53y7vbePxuGFmEjgcrafMSlDpOJwOkj8YdexOt7yWlv7SB3rXoT3mHknyMJ3lf2UFH5Bmt6tnIkHcOTR6dEoA==}
1614
  engines: {node: '>=16'}
 
10
  version: link:../tasks
11
 
12
  devDependencies:
13
+ '@auth/core':
14
+ specifier: ^0.18.3
15
+ version: 0.18.3
16
+ '@auth/sveltekit':
17
+ specifier: ^0.3.14
18
+ version: 0.3.14(@sveltejs/kit@1.27.4)(svelte@3.59.2)
19
  '@fontsource/ibm-plex-mono':
20
  specifier: ^5.0.8
21
  version: 5.0.8
 
74
  engines: {node: '>=10'}
75
  dev: true
76
 
77
+ /@auth/core@0.18.3:
78
+ resolution: {integrity: sha512-YXQWxi3pKxngt+2vo3dq8+wDANlUH8nhQgX6EVdd3Enfe3vweBtHqzaWrtWzQnVb8wdGxdhxaoOlYroEBE+/yw==}
79
+ peerDependencies:
80
+ nodemailer: ^6.8.0
81
+ peerDependenciesMeta:
82
+ nodemailer:
83
+ optional: true
84
+ dependencies:
85
+ '@panva/hkdf': 1.1.1
86
+ cookie: 0.5.0
87
+ jose: 5.1.2
88
+ oauth4webapi: 2.4.0
89
+ preact: 10.11.3
90
+ preact-render-to-string: 5.2.3(preact@10.11.3)
91
+ dev: true
92
+
93
+ /@auth/sveltekit@0.3.14(@sveltejs/kit@1.27.4)(svelte@3.59.2):
94
+ resolution: {integrity: sha512-Ealyi4uM4V42tk4UhhRQ+iZKah0OOp12siXwgDsCj1uBOCeZwL97RkvdMDX7pg18zBP/h2qFAuMWjW5q7bSYxA==}
95
+ peerDependencies:
96
+ '@sveltejs/kit': ^1.0.0
97
+ svelte: ^3.54.0 || ^4.0.0
98
+ dependencies:
99
+ '@auth/core': 0.18.3
100
+ '@sveltejs/kit': 1.27.4(svelte@3.59.2)(vite@4.5.0)
101
+ svelte: 3.59.2
102
+ transitivePeerDependencies:
103
+ - nodemailer
104
+ dev: true
105
+
106
  /@esbuild/android-arm64@0.18.20:
107
  resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==}
108
  engines: {node: '>=12'}
 
402
  fastq: 1.15.0
403
  dev: true
404
 
405
+ /@panva/hkdf@1.1.1:
406
+ resolution: {integrity: sha512-dhPeilub1NuIG0X5Kvhh9lH4iW3ZsHlnzwgwbOlgwQ2wG1IqFzsgHqmKPk3WzsdWAeaxKJxgM0+W433RmN45GA==}
407
+ dev: true
408
+
409
  /@polka/url@1.0.0-next.23:
410
  resolution: {integrity: sha512-C16M+IYz0rgRhWZdCmK+h58JMv8vijAA61gmz2rspCSwKwzBebpdcsiUmwrtJRdphuY30i6BSLEOP8ppbNLyLg==}
411
  dev: true
 
1269
  hasBin: true
1270
  dev: true
1271
 
1272
+ /jose@5.1.2:
1273
+ resolution: {integrity: sha512-X7TOC/d8KPvx4wPUuLHVgTSdoWw0UW5TQOUwhvCvj+ZPfsf9vUPhhksYPjNBWVGPQ/6yd/JrL1gQxBnIDwYdFg==}
1274
+ dev: true
1275
+
1276
  /js-sdsl@4.4.2:
1277
  resolution: {integrity: sha512-dwXFwByc/ajSV6m5bcKAPwe4yDDF6D614pxmIi5odytzxRlwqF6nwoiCek80Ixc7Cvma5awClxrzFtxCQvcM8w==}
1278
  dev: true
 
1483
  npm-normalize-package-bin: 2.0.0
1484
  dev: true
1485
 
1486
+ /oauth4webapi@2.4.0:
1487
+ resolution: {integrity: sha512-ZWl8ov8HeGVyc9Icl1cag76HvIcDAp23eIIT+UVGir+dEu8BMgMlvZeZwqLVd0P8DqaumH4N+QLQXN69G1QjSA==}
1488
+ dev: true
1489
+
1490
  /object-assign@4.1.1:
1491
  resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
1492
  engines: {node: '>=0.10.0'}
 
1651
  source-map-js: 1.0.2
1652
  dev: true
1653
 
1654
+ /preact-render-to-string@5.2.3(preact@10.11.3):
1655
+ resolution: {integrity: sha512-aPDxUn5o3GhWdtJtW0svRC2SS/l8D9MAgo2+AWml+BhDImb27ALf04Q2d+AHqUUOc6RdSXFIBVa2gxzgMKgtZA==}
1656
+ peerDependencies:
1657
+ preact: '>=10'
1658
+ dependencies:
1659
+ preact: 10.11.3
1660
+ pretty-format: 3.8.0
1661
+ dev: true
1662
+
1663
+ /preact@10.11.3:
1664
+ resolution: {integrity: sha512-eY93IVpod/zG3uMF22Unl8h9KkrcKIRs2EGar8hwLZZDU1lkjph303V9HZBwufh2s736U6VXuhD109LYqPoffg==}
1665
+ dev: true
1666
+
1667
  /prelude-ls@1.2.1:
1668
  resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
1669
  engines: {node: '>= 0.8.0'}
1670
  dev: true
1671
 
1672
+ /pretty-format@3.8.0:
1673
+ resolution: {integrity: sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==}
1674
+ dev: true
1675
+
1676
  /publint@0.1.9:
1677
  resolution: {integrity: sha512-O53y7vbePxuGFmEjgcrafMSlDpOJwOkj8YdexOt7yWlv7SB3rXoT3mHknyMJ3lf2UFH5Bmt6tnIkHcOTR6dEoA==}
1678
  engines: {node: '>=16'}
packages/widgets/src/app.d.ts CHANGED
@@ -7,6 +7,16 @@ declare global {
7
  // interface PageData {}
8
  // interface Platform {}
9
  }
 
 
 
 
 
 
 
 
 
 
10
  }
11
 
12
  export {};
 
7
  // interface PageData {}
8
  // interface Platform {}
9
  }
10
+
11
+ export interface Session {
12
+ access_token?: string;
13
+ }
14
+ }
15
+
16
+ declare module "@auth/core/types" {
17
+ export interface Session {
18
+ access_token?: string;
19
+ }
20
  }
21
 
22
  export {};
packages/widgets/src/hooks.server.ts ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { env } from "$env/dynamic/private";
2
+ import { skipCSRFCheck } from "@auth/core";
3
+ import { SvelteKitAuth } from "@auth/sveltekit";
4
+ import type { Handle } from "@sveltejs/kit";
5
+ import { sequence } from "@sveltejs/kit/hooks";
6
+
7
+ const handleSSO =
8
+ env.OAUTH_CLIENT_ID && env.OAUTH_CLIENT_SECRET
9
+ ? SvelteKitAuth({
10
+ // Should be fine as long as your reverse proxy is configured to only accept traffic with the correct host header
11
+ trustHost: true,
12
+ /**
13
+ * SvelteKit has built-in CSRF protection, so we can skip the check
14
+ */
15
+ skipCSRFCheck: skipCSRFCheck,
16
+ providers: [
17
+ {
18
+ name: "Hugging Face",
19
+ id: "huggingface",
20
+ type: "oidc",
21
+ clientId: env.OAUTH_CLIENT_ID,
22
+ clientSecret: env.OAUTH_CLIENT_SECRET,
23
+ issuer: "https://huggingface.co",
24
+ wellKnown: "https://huggingface.co/.well-known/openid-configuration",
25
+ /** Add "inference-api" scope and remove "email" scope */
26
+ authorization: { params: { scope: "openid profile inference-api" } },
27
+ checks: ["state" as never, "pkce" as never],
28
+ },
29
+ ],
30
+ secret: env.OAUTH_CLIENT_SECRET,
31
+ /**
32
+ * Get the access_token without an account in DB, to make calls to the inference API
33
+ */
34
+ callbacks: {
35
+ async jwt({ token, account }) {
36
+ if (account) {
37
+ return {
38
+ ...token,
39
+ access_token: account.access_token,
40
+ };
41
+ }
42
+ return token;
43
+ },
44
+ async session({ session, token }) {
45
+ return {
46
+ ...session,
47
+ access_token: token.access_token,
48
+ };
49
+ },
50
+ },
51
+ })
52
+ : null;
53
+
54
+ const handleGlobal: Handle = async ({ event, resolve }) => {
55
+ const response = await resolve(event);
56
+ return response;
57
+ };
58
+
59
+ export const handle = handleSSO ? sequence(handleSSO, handleGlobal) : handleGlobal;
packages/widgets/src/routes/+layout.server.ts ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { env } from "$env/dynamic/private";
2
+ import type { LayoutServerLoad } from "./$types.js";
3
+
4
+ export const load: LayoutServerLoad = async ({ locals }) => {
5
+ const session = await locals.getSession();
6
+
7
+ return {
8
+ access_token: session?.access_token,
9
+ supportsOAuth: !!env.OAUTH_CLIENT_ID && !!env.OAUTH_CLIENT_SECRET,
10
+ };
11
+ };
packages/widgets/src/routes/+page.svelte CHANGED
@@ -5,17 +5,27 @@
5
  import InferenceWidget from "$lib/components/InferenceWidget/InferenceWidget.svelte";
6
  import ModeSwitcher from "$lib/components/DemoThemeSwitcher/DemoThemeSwitcher.svelte";
7
  import { onMount } from "svelte";
 
8
 
9
- let apiToken = "";
 
10
 
11
  function storeHFToken() {
12
  window.localStorage.setItem("hf_token", apiToken);
13
  }
14
 
 
 
 
 
 
 
15
  onMount(() => {
16
- const token = window.localStorage.getItem("hf_token");
17
- if (token) {
18
- apiToken = token;
 
 
19
  }
20
  });
21
 
@@ -526,10 +536,24 @@
526
  <div class="flex flex-col gap-6 py-12 px-4">
527
  <ModeSwitcher />
528
 
529
- <label>
530
- <div class="text-xl font-semibold">First, Enter HF token</div>
531
- <input class="form-input" type="text" bind:value={apiToken} placeholder="hf_..." on:change={storeHFToken} />
532
- </label>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
533
 
534
  <div>
535
  <h1 class="mb-8 text-4xl font-semibold">Showcase of all types of inference widgets running</h1>
 
5
  import InferenceWidget from "$lib/components/InferenceWidget/InferenceWidget.svelte";
6
  import ModeSwitcher from "$lib/components/DemoThemeSwitcher/DemoThemeSwitcher.svelte";
7
  import { onMount } from "svelte";
8
+ import { browser } from "$app/environment";
9
 
10
+ export let data;
11
+ let apiToken = data.access_token || "";
12
 
13
  function storeHFToken() {
14
  window.localStorage.setItem("hf_token", apiToken);
15
  }
16
 
17
+ /**
18
+ * If we are in an iframe, we need to open the auth page in a new tab
19
+ * to avoid issues with third-party cookies in a space
20
+ */
21
+ const isIframe = browser && window.self !== window.parent;
22
+
23
  onMount(() => {
24
+ if (!data.supportsOAuth) {
25
+ const token = window.localStorage.getItem("hf_token");
26
+ if (token) {
27
+ apiToken = token;
28
+ }
29
  }
30
  });
31
 
 
536
  <div class="flex flex-col gap-6 py-12 px-4">
537
  <ModeSwitcher />
538
 
539
+ {#if data.supportsOAuth}
540
+ {#if !data.access_token}
541
+ <form class="contents" method="post" action="/auth/signin/huggingface" target={isIframe ? "_blank" : ""}>
542
+ <button type="submit" title="Sign in with Hugging Face">
543
+ <img
544
+ src="https://huggingface.co/datasets/huggingface/badges/resolve/main/sign-in-with-huggingface-xl-dark.svg"
545
+ alt="Sign in with Hugging Face"
546
+ class="h-12 w-auto"
547
+ />
548
+ </button>
549
+ </form>
550
+ {/if}
551
+ {:else}
552
+ <label>
553
+ <div class="text-xl font-semibold">First, Enter HF token</div>
554
+ <input class="form-input" type="text" bind:value={apiToken} placeholder="hf_..." on:change={storeHFToken} />
555
+ </label>
556
+ {/if}
557
 
558
  <div>
559
  <h1 class="mb-8 text-4xl font-semibold">Showcase of all types of inference widgets running</h1>