Spaces:
Running
Running
Replace manual token input with Hugging Face OAuth login
Browse filesmodified: 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 +9 -1
- package-lock.json +32 -0
- package.json +1 -0
- src/components/ChatInterface.jsx +2 -2
- src/components/OAuthLogin.jsx +81 -0
- src/hooks/useChatCompletion.js +1 -1
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
|
| 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
|
| 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 |
-
<
|
| 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
|
| 20 |
});
|
| 21 |
return;
|
| 22 |
}
|
|
|
|
| 16 |
if (!client) {
|
| 17 |
setError({
|
| 18 |
type: "MISSING_CLIENT",
|
| 19 |
+
message: "Please login Hugging Face first.",
|
| 20 |
});
|
| 21 |
return;
|
| 22 |
}
|