ferrywuai commited on
Commit
a9b2457
·
1 Parent(s): cc1ccff

Replace manual token input with Hugging Face OAuth login

Browse files

modified: README.md
modified: package-lock.json
modified: package.json
modified: src/components/ChatInterface.jsx
new file: src/components/OAuthLogin.jsx
modified: src/hooks/useChatCompletion.js

README.md CHANGED
@@ -9,6 +9,9 @@ app_build_command: npm run build
9
  app_file: build/index.html
10
  license: mit
11
  short_description: Test static sdk react template
 
 
 
12
  ---
13
 
14
  # A Chatbot built with React
@@ -17,7 +20,7 @@ This project was bootstrapped with [Create React App](https://github.com/faceboo
17
 
18
  ## Features
19
 
20
- - Support user input of Hugging Face Access Token.
21
  - Support tuning system prompt and parameters: temperature, top_p, max_tokens.
22
  - Support typing animation, loading animation, and error handling.
23
  - Support UI to send user input and get response of the chatbot.
@@ -25,6 +28,11 @@ This project was bootstrapped with [Create React App](https://github.com/faceboo
25
 
26
  ## Usage
27
 
 
 
 
 
 
28
  ### Development mode
29
 
30
  - `npm start`
 
9
  app_file: build/index.html
10
  license: mit
11
  short_description: Test static sdk react template
12
+ hf_oauth: true
13
+ hf_oauth_scopes:
14
+ - inference-api
15
  ---
16
 
17
  # A Chatbot built with React
 
20
 
21
  ## Features
22
 
23
+ - Support OAuth login to Hugging Face.
24
  - Support tuning system prompt and parameters: temperature, top_p, max_tokens.
25
  - Support typing animation, loading animation, and error handling.
26
  - Support UI to send user input and get response of the chatbot.
 
28
 
29
  ## Usage
30
 
31
+ ### Create OAuth App
32
+
33
+ Create an OAuth app at [Connected Applications](https://huggingface.co/settings/connected-applications) to support OAuth login.\
34
+ Refer to the description of [Sign in with Hugging Face](https://huggingface.co/docs/hub/oauth)
35
+
36
  ### Development mode
37
 
38
  - `npm start`
package-lock.json CHANGED
@@ -8,6 +8,7 @@
8
  "name": "react-template",
9
  "version": "0.1.0",
10
  "dependencies": {
 
11
  "@huggingface/inference": "^4.8.0",
12
  "@testing-library/dom": "^10.4.0",
13
  "@testing-library/jest-dom": "^6.6.3",
@@ -2444,6 +2445,24 @@
2444
  "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
2445
  }
2446
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2447
  "node_modules/@huggingface/inference": {
2448
  "version": "4.8.0",
2449
  "resolved": "https://registry.npmjs.org/@huggingface/inference/-/inference-4.8.0.tgz",
@@ -5650,6 +5669,19 @@
5650
  "node": ">=0.10.0"
5651
  }
5652
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
5653
  "node_modules/cliui": {
5654
  "version": "7.0.4",
5655
  "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
 
8
  "name": "react-template",
9
  "version": "0.1.0",
10
  "dependencies": {
11
+ "@huggingface/hub": "^2.6.5",
12
  "@huggingface/inference": "^4.8.0",
13
  "@testing-library/dom": "^10.4.0",
14
  "@testing-library/jest-dom": "^6.6.3",
 
2445
  "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
2446
  }
2447
  },
2448
+ "node_modules/@huggingface/hub": {
2449
+ "version": "2.6.5",
2450
+ "resolved": "https://registry.npmjs.org/@huggingface/hub/-/hub-2.6.5.tgz",
2451
+ "integrity": "sha512-3O20Pe9bSXQ/4LxUNnf75jZAZaYvF/3lVdIU0GrJOL3oTlneuxdRHZ9AXuu25hYY05fyng0G83EIS5Xn9uJtGg==",
2452
+ "license": "MIT",
2453
+ "dependencies": {
2454
+ "@huggingface/tasks": "^0.19.46"
2455
+ },
2456
+ "bin": {
2457
+ "hfjs": "dist/cli.js"
2458
+ },
2459
+ "engines": {
2460
+ "node": ">=18"
2461
+ },
2462
+ "optionalDependencies": {
2463
+ "cli-progress": "^3.12.0"
2464
+ }
2465
+ },
2466
  "node_modules/@huggingface/inference": {
2467
  "version": "4.8.0",
2468
  "resolved": "https://registry.npmjs.org/@huggingface/inference/-/inference-4.8.0.tgz",
 
5669
  "node": ">=0.10.0"
5670
  }
5671
  },
5672
+ "node_modules/cli-progress": {
5673
+ "version": "3.12.0",
5674
+ "resolved": "https://registry.npmjs.org/cli-progress/-/cli-progress-3.12.0.tgz",
5675
+ "integrity": "sha512-tRkV3HJ1ASwm19THiiLIXLO7Im7wlTuKnvkYaTkyoAPefqjNg7W7DHKUlGRxy9vxDvbyCYQkQozvptuMkGCg8A==",
5676
+ "license": "MIT",
5677
+ "optional": true,
5678
+ "dependencies": {
5679
+ "string-width": "^4.2.3"
5680
+ },
5681
+ "engines": {
5682
+ "node": ">=4"
5683
+ }
5684
+ },
5685
  "node_modules/cliui": {
5686
  "version": "7.0.4",
5687
  "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
package.json CHANGED
@@ -3,6 +3,7 @@
3
  "version": "0.1.0",
4
  "private": true,
5
  "dependencies": {
 
6
  "@huggingface/inference": "^4.8.0",
7
  "@testing-library/dom": "^10.4.0",
8
  "@testing-library/jest-dom": "^6.6.3",
 
3
  "version": "0.1.0",
4
  "private": true,
5
  "dependencies": {
6
+ "@huggingface/hub": "^2.6.5",
7
  "@huggingface/inference": "^4.8.0",
8
  "@testing-library/dom": "^10.4.0",
9
  "@testing-library/jest-dom": "^6.6.3",
src/components/ChatInterface.jsx CHANGED
@@ -6,7 +6,7 @@ import ChatSettings from "./ChatSettings";
6
  import LoadingIndicator from "./LoadingIndicator";
7
  import MessageInput from "./MessageInput";
8
  import MessagePair from "./MessagePair";
9
- import TokenInput from "./TokenInput";
10
  import TypingMessage from "./TypingMessage";
11
  import UISettings from "./UISettings";
12
 
@@ -44,7 +44,7 @@ export default function ChatInterface() {
44
  >
45
  {/* Token input */}
46
  <div>
47
- <TokenInput onHFClientReady={handleHFClientReady} />
48
  </div>
49
 
50
  {/* Chat settings */}
 
6
  import LoadingIndicator from "./LoadingIndicator";
7
  import MessageInput from "./MessageInput";
8
  import MessagePair from "./MessagePair";
9
+ import OAuthLogin from "./OAuthLogin";
10
  import TypingMessage from "./TypingMessage";
11
  import UISettings from "./UISettings";
12
 
 
44
  >
45
  {/* Token input */}
46
  <div>
47
+ <OAuthLogin onHFClientReady={handleHFClientReady} />
48
  </div>
49
 
50
  {/* Chat settings */}
src/components/OAuthLogin.jsx ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Login with Hugging Face OAuth
2
+ import { oauthHandleRedirectIfPresent, oauthLoginUrl } from "@huggingface/hub";
3
+ import { InferenceClient } from "@huggingface/inference";
4
+ import { useEffect, useRef, useState } from "react";
5
+
6
+ export default function OAuthLogin({ onHFClientReady }) {
7
+ const [loading, setLoading] = useState(false);
8
+ const [error, setError] = useState("");
9
+ const [oauthResult, setOAuthResult] = useState(null);
10
+ const [userInfo, setUserInfo] = useState(null);
11
+ const oauthHandled = useRef(false);
12
+ const stableOnHFClientReady = useRef(onHFClientReady);
13
+
14
+ useEffect(() => {
15
+ // Prevent handling OAuth twice in Strict Mode
16
+ if (oauthHandled.current) return;
17
+ oauthHandled.current = true;
18
+
19
+ (async () => {
20
+ setLoading(true);
21
+ try {
22
+ const result = await oauthHandleRedirectIfPresent();
23
+ if (!result) return;
24
+ setOAuthResult(result);
25
+ setError("");
26
+ } catch (err) {
27
+ setError(err.message);
28
+ } finally {
29
+ setLoading(false);
30
+ }
31
+ })();
32
+ }, []);
33
+
34
+ useEffect(() => {
35
+ stableOnHFClientReady.current = onHFClientReady;
36
+ }, [onHFClientReady]);
37
+
38
+ useEffect(() => {
39
+ if (oauthResult) {
40
+ try {
41
+ const client = new InferenceClient(oauthResult.accessToken);
42
+ stableOnHFClientReady.current(client);
43
+ setUserInfo(oauthResult.userInfo);
44
+ setError("");
45
+ } catch (err) {
46
+ setError(err.message);
47
+ }
48
+ }
49
+ }, [oauthResult]);
50
+
51
+ const handleLogin = async () => {
52
+ if (window.location.hostname.endsWith(".hf.space")) {
53
+ window.location.href = await oauthLoginUrl();
54
+ } else {
55
+ window.location.href = await oauthLoginUrl({
56
+ clientId: "d9a24fb2-1e7c-4a57-ace3-c75fa6a99305",
57
+ redirectUrl: window.location.origin,
58
+ scopes: ["inference-api"],
59
+ });
60
+ }
61
+ };
62
+
63
+ return (
64
+ <div style={{ padding: "20px" }}>
65
+ {userInfo ? (
66
+ <div>
67
+ <p>
68
+ ✅ {userInfo.name || userInfo.email} has successfully logged in!
69
+ </p>
70
+ </div>
71
+ ) : loading ? (
72
+ <p>🔄 Authenticating with Hugging Face…</p>
73
+ ) : (
74
+ <button onClick={handleLogin} style={{ padding: "10px 20px" }}>
75
+ Login with Hugging Face
76
+ </button>
77
+ )}
78
+ {error && <p style={{ color: "red" }}>{error}</p>}
79
+ </div>
80
+ );
81
+ }
src/hooks/useChatCompletion.js CHANGED
@@ -16,7 +16,7 @@ export function useChatCompletion(
16
  if (!client) {
17
  setError({
18
  type: "MISSING_CLIENT",
19
- message: "Please enter a valid Hugging Face token first.",
20
  });
21
  return;
22
  }
 
16
  if (!client) {
17
  setError({
18
  type: "MISSING_CLIENT",
19
+ message: "Please login Hugging Face first.",
20
  });
21
  return;
22
  }