8421bit commited on
Commit
91e52d9
·
verified ·
1 Parent(s): 23c4cde

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +207 -163
index.html CHANGED
@@ -3,246 +3,290 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>DeepSeek Chat (JS Demo)</title>
7
  <style>
8
  body {
9
- font-family: sans-serif;
10
  line-height: 1.6;
11
- margin: 20px;
12
- background-color: #f4f4f4;
 
 
 
 
13
  }
14
  .container {
15
- max-width: 700px;
16
- margin: auto;
17
  background: #fff;
18
- padding: 20px;
19
  border-radius: 8px;
20
- box-shadow: 0 2px 4px rgba(0,0,0,0.1);
21
  }
22
  h1 {
23
  text-align: center;
24
  color: #333;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  }
26
  label {
27
  display: block;
28
- margin-bottom: 5px;
29
- font-weight: bold;
 
30
  }
31
- input[type="password"],
32
  textarea {
33
- width: 95%; /* Slightly less than 100% to prevent overflow */
34
  padding: 10px;
35
  margin-bottom: 15px;
36
- border: 1px solid #ccc;
37
- border-radius: 4px;
38
- }
39
- textarea {
40
- height: 100px;
41
  resize: vertical;
42
  }
43
- button {
44
  display: block;
45
  width: 100%;
46
  padding: 12px;
47
- background-color: #5c67f2; /* Hugging Face purple */
48
  color: white;
49
  border: none;
50
- border-radius: 4px;
51
  cursor: pointer;
52
- font-size: 16px;
53
- transition: background-color 0.3s ease;
 
54
  }
55
- button:hover:not(:disabled) {
56
- background-color: #4a54e0;
57
  }
58
- button:disabled {
59
- background-color: #cccccc;
60
  cursor: not-allowed;
61
  }
62
- #chatbox {
63
- margin-top: 20px;
64
- border: 1px solid #eee;
65
  border-radius: 4px;
66
- padding: 15px;
67
- height: 300px;
68
- overflow-y: auto;
69
- background-color: #f9f9f9;
70
  }
71
- .message {
72
- margin-bottom: 10px;
73
- padding: 8px 12px;
74
- border-radius: 15px;
75
- max-width: 80%;
76
  }
77
- .user-message {
78
- background-color: #e1f5fe; /* Light blue */
79
- margin-left: auto;
80
- border-bottom-right-radius: 5px;
81
- text-align: right;
82
  }
83
- .assistant-message {
84
- background-color: #e8eaf6; /* Light purple/grey */
85
- margin-right: auto;
86
- border-bottom-left-radius: 5px;
87
- text-align: left;
 
 
 
 
88
  white-space: pre-wrap; /* Preserve whitespace and line breaks */
89
- }
90
- .error {
91
- color: #d9534f; /* Red */
92
- font-weight: bold;
93
- margin-top: 10px;
94
- }
95
- .info {
96
  font-size: 0.9em;
97
- color: #666;
98
- margin-bottom: 15px;
99
- padding: 10px;
100
- background-color: #fffde7; /* Light yellow */
101
- border: 1px solid #ffecb3; /* Pale yellow border */
102
- border-radius: 4px;
103
  }
104
  </style>
105
  </head>
106
  <body>
107
  <div class="container">
108
- <h1>DeepSeek Chat Demo (JS)</h1>
109
 
110
- <div class="info">
111
- <strong>Security Warning:</strong> This demo requires your Hugging Face API Token.
112
- Inputting your token here runs it entirely in *your* browser, but be cautious about sharing tokens.
113
- Get a token from <a href="https://huggingface.co/settings/tokens" target="_blank">HF Settings</a> (needs 'read' access).
114
- The token is *not* stored after you close the page.
115
  </div>
116
 
117
- <label for="hf-token">Hugging Face API Token:</label>
118
- <input type="password" id="hf-token" placeholder="Enter your HF Token (hf_...)">
119
 
120
- <label for="user-message">Your Message:</label>
121
- <textarea id="user-message" placeholder="Ask DeepSeek something..."></textarea>
 
 
 
122
 
123
- <button id="send-button">Send Message</button>
124
 
125
- <div id="status" style="margin-top: 15px;"></div>
126
- <div id="chatbox">
127
- <!-- Chat messages will appear here -->
128
- </div>
129
- </div>
130
 
131
- <!-- Import the Hugging Face Inference Client library -->
132
- <script type="module">
133
- // Use ES module import syntax
134
- import { HfInference } from "https://cdn.jsdelivr.net/npm/@huggingface/inference@2.6.4/+esm"; // Use specific version or latest
135
 
136
- // --- Configuration ---
137
- const MODEL_ID = "deepseek-ai/deepseek-llm-7b-chat"; // Or other DeepSeek chat model
138
- const MAX_NEW_TOKENS = 512;
139
- const TEMPERATURE = 0.7;
140
- const TOP_P = 0.95;
141
 
142
- // --- DOM Elements ---
143
- const tokenInput = document.getElementById('hf-token');
144
- const messageInput = document.getElementById('user-message');
 
145
  const sendButton = document.getElementById('send-button');
146
- const chatbox = document.getElementById('chatbox');
147
  const statusDiv = document.getElementById('status');
 
 
 
148
 
149
- // --- Chat History ---
150
- let chatHistory = []; // Stores messages like { role: 'user'/'assistant', content: '...' }
151
 
152
- // --- Add Message to UI ---
153
- function addMessageToChatbox(role, text) {
154
- const messageDiv = document.createElement('div');
155
- messageDiv.classList.add('message', role === 'user' ? 'user-message' : 'assistant-message');
156
- messageDiv.textContent = text; // Use textContent for security
157
- chatbox.appendChild(messageDiv);
158
- chatbox.scrollTop = chatbox.scrollHeight; // Auto-scroll to bottom
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
159
  }
160
 
161
- // --- Handle Send Button Click ---
 
 
 
 
 
 
162
  sendButton.addEventListener('click', async () => {
163
- const userToken = tokenInput.value.trim();
164
- const userMessage = messageInput.value.trim();
 
165
 
166
- if (!userToken) {
167
- statusDiv.textContent = 'Error: Please enter your Hugging Face API Token.';
168
- statusDiv.className = 'error';
169
- return;
170
- }
171
- if (!userMessage) {
172
- statusDiv.textContent = 'Error: Please enter a message.';
173
- statusDiv.className = 'error';
174
  return;
175
  }
176
 
177
- // Disable button and show loading
 
 
 
178
  sendButton.disabled = true;
179
- statusDiv.textContent = 'Sending request...';
180
- statusDiv.className = 'info'; // Use info class for status messages
181
- addMessageToChatbox('user', userMessage);
182
 
183
- // Add user message to history
184
- chatHistory.push({ role: 'user', content: userMessage });
 
 
 
 
185
 
186
  try {
187
- // Initialize HF Inference client *with the user's token*
188
- const hf = new HfInference(userToken);
189
-
190
- // Prepare messages in the format required by chatCompletion
191
- const messagesForApi = [...chatHistory]; // Send a copy
192
-
193
- console.log("Sending messages to API:", messagesForApi);
194
-
195
- // Call the chat completion endpoint
196
- const response = await hf.chatCompletion({
197
- model: MODEL_ID,
198
- messages: messagesForApi,
199
- max_tokens: MAX_NEW_TOKENS,
200
- temperature: TEMPERATURE,
201
- top_p: TOP_P,
202
- // stream: false // Default is false, streaming is more complex in pure JS
203
  });
204
 
205
- console.log("API Response:", response);
206
-
207
- // Assuming the response format contains the assistant's reply directly
208
- // Adjust based on the actual structure returned by the library/API if needed
209
- let assistantReply = "Sorry, couldn't parse the response."; // Default
210
- if (response && response.choices && response.choices[0] && response.choices[0].message && response.choices[0].message.content) {
211
- assistantReply = response.choices[0].message.content; // Standard OpenAI-like format
212
- } else if (response && response.generated_text) {
213
- assistantReply = response.generated_text; // Fallback for some older formats
214
- } else {
215
- console.warn("Unexpected response structure:", response);
 
 
 
 
 
 
 
 
 
216
  }
217
 
 
 
 
218
 
219
- // Add assistant response to history and UI
220
- chatHistory.push({ role: 'assistant', content: assistantReply });
221
- addMessageToChatbox('assistant', assistantReply);
222
 
223
- statusDiv.textContent = 'Response received.';
224
- statusDiv.className = 'info'; // Or clear it: statusDiv.textContent = '';
 
 
 
 
 
 
 
 
 
 
225
 
226
  } catch (error) {
227
- console.error("Error calling Hugging Face API:", error);
228
- statusDiv.textContent = `Error: ${error.message || 'Failed to get response from model.'}`;
229
- statusDiv.className = 'error';
230
- // Optionally remove the last user message from history if the API call failed severely
231
- // chatHistory.pop();
232
  } finally {
233
- // Re-enable button and clear input
234
- sendButton.disabled = false;
235
- messageInput.value = ''; // Clear the input field
236
  }
237
  });
238
 
239
- // Optional: Allow sending with Enter key in textarea (Shift+Enter for new line)
240
- messageInput.addEventListener('keydown', (event) => {
241
- if (event.key === 'Enter' && !event.shiftKey) {
242
- event.preventDefault(); // Prevent default newline insertion
243
- sendButton.click(); // Trigger the send button click
244
- }
245
- });
246
 
247
  </script>
248
  </body>
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>DeepSeek Chat Interface</title>
7
  <style>
8
  body {
9
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
10
  line-height: 1.6;
11
+ margin: 0;
12
+ padding: 20px;
13
+ background-color: #f0f2f5;
14
+ color: #1c1e21;
15
+ display: flex;
16
+ justify-content: center;
17
  }
18
  .container {
19
+ max-width: 800px;
20
+ width: 100%;
21
  background: #fff;
22
+ padding: 25px;
23
  border-radius: 8px;
24
+ box-shadow: 0 2px 10px rgba(0,0,0,0.1);
25
  }
26
  h1 {
27
  text-align: center;
28
  color: #333;
29
+ margin-bottom: 25px;
30
+ }
31
+ .auth-section {
32
+ text-align: right;
33
+ margin-bottom: 15px;
34
+ font-size: 0.9em;
35
+ }
36
+ .auth-section button {
37
+ padding: 5px 10px;
38
+ font-size: 0.9em;
39
+ cursor: pointer;
40
+ border: 1px solid #ddd;
41
+ border-radius: 4px;
42
+ background-color: #f5f5f5;
43
+ margin-left: 5px;
44
+ }
45
+ .auth-section button:hover {
46
+ background-color: #e9e9e9;
47
+ }
48
+ .auth-section span {
49
+ color: #606770;
50
  }
51
  label {
52
  display: block;
53
+ margin-bottom: 8px;
54
+ font-weight: 600;
55
+ color: #4b4f56;
56
  }
 
57
  textarea {
58
+ width: calc(100% - 22px); /* Full width minus padding */
59
  padding: 10px;
60
  margin-bottom: 15px;
61
+ border: 1px solid #ccd0d5;
62
+ border-radius: 6px;
63
+ font-size: 1rem;
64
+ min-height: 80px;
 
65
  resize: vertical;
66
  }
67
+ button#send-button {
68
  display: block;
69
  width: 100%;
70
  padding: 12px;
71
+ background-color: #1877f2; /* Facebook Blue */
72
  color: white;
73
  border: none;
74
+ border-radius: 6px;
75
  cursor: pointer;
76
+ font-size: 1rem;
77
+ font-weight: 600;
78
+ transition: background-color 0.2s ease;
79
  }
80
+ button#send-button:hover:not(:disabled) {
81
+ background-color: #166fe5;
82
  }
83
+ button#send-button:disabled {
84
+ background-color: #a0c3f0;
85
  cursor: not-allowed;
86
  }
87
+ #status {
88
+ margin-top: 15px;
89
+ padding: 10px;
90
  border-radius: 4px;
91
+ font-size: 0.95em;
92
+ display: none; /* Hidden by default */
93
+ text-align: center;
 
94
  }
95
+ #status.info {
96
+ background-color: #e7f3ff;
97
+ border: 1px solid #b8d7f7;
98
+ color: #1877f2;
99
+ display: block;
100
  }
101
+ #status.error {
102
+ background-color: #fdecea;
103
+ border: 1px solid #f8bbc1;
104
+ color: #a94442;
105
+ display: block;
106
  }
107
+ #response-area {
108
+ margin-top: 20px;
109
+ border: 1px solid #e0e0e0;
110
+ border-radius: 6px;
111
+ padding: 15px;
112
+ min-height: 150px;
113
+ background-color: #f9f9f9;
114
+ overflow-y: auto;
115
+ max-height: 400px;
116
  white-space: pre-wrap; /* Preserve whitespace and line breaks */
117
+ word-wrap: break-word; /* Wrap long words */
118
+ font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; /* Monospace font for code */
 
 
 
 
 
119
  font-size: 0.9em;
120
+ line-height: 1.5;
121
+ }
122
+ #response-area:empty::before {
123
+ content: "Model's response will appear here...";
124
+ color: #999;
125
+ font-style: italic;
126
  }
127
  </style>
128
  </head>
129
  <body>
130
  <div class="container">
131
+ <h1>DeepSeek Chat</h1>
132
 
133
+ <div class="auth-section">
134
+ <span id="user-info">Checking login status...</span>
135
+ <button id="auth-button">Login</button>
 
 
136
  </div>
137
 
138
+ <label for="user-prompt">Your Prompt:</label>
139
+ <textarea id="user-prompt" placeholder="Enter your message or instructions for the AI..."></textarea>
140
 
141
+ <!-- Optional: Add inputs/areas for 'html' and 'previousPrompt' if needed -->
142
+ <!--
143
+ <label for="context-html">Current HTML (Optional):</label>
144
+ <textarea id="context-html" placeholder="Paste existing HTML if you want the AI to modify it..."></textarea>
145
+ -->
146
 
147
+ <button id="send-button">Send Prompt</button>
148
 
149
+ <div id="status"></div>
 
 
 
 
150
 
151
+ <label for="response-area" style="margin-top: 20px;">AI Response:</label>
152
+ <div id="response-area"></div>
153
+ <!-- Use a div with pre-wrap instead of <pre> for better control -->
 
154
 
155
+ </div>
 
 
 
 
156
 
157
+ <script>
158
+ const userInfoSpan = document.getElementById('user-info');
159
+ const authButton = document.getElementById('auth-button');
160
+ const promptInput = document.getElementById('user-prompt');
161
  const sendButton = document.getElementById('send-button');
 
162
  const statusDiv = document.getElementById('status');
163
+ const responseArea = document.getElementById('response-area');
164
+ // Optional: Get context elements if you uncomment them
165
+ // const contextHtmlInput = document.getElementById('context-html');
166
 
167
+ let isLoggedIn = false;
168
+ let currentUsername = 'Guest';
169
 
170
+ // --- Authentication Handling ---
171
+ async function checkLoginStatus() {
172
+ try {
173
+ const response = await fetch('/api/@me');
174
+ if (response.ok) {
175
+ const user = await response.json();
176
+ currentUsername = user.preferred_username || user.name || 'Logged In User';
177
+ userInfoSpan.textContent = `Logged in as: ${currentUsername}`;
178
+ authButton.textContent = 'Logout';
179
+ authButton.onclick = () => window.location.href = '/auth/logout';
180
+ isLoggedIn = true;
181
+ } else {
182
+ throw new Error('Not logged in');
183
+ }
184
+ } catch (error) {
185
+ userInfoSpan.textContent = 'Not logged in.';
186
+ authButton.textContent = 'Login with Hugging Face';
187
+ authButton.onclick = () => window.location.href = '/api/login';
188
+ isLoggedIn = false;
189
+ }
190
+ }
191
+
192
+ // --- Display Status ---
193
+ function showStatus(message, type = 'info') {
194
+ statusDiv.textContent = message;
195
+ statusDiv.className = type; // 'info' or 'error'
196
  }
197
 
198
+ function clearStatus() {
199
+ statusDiv.textContent = '';
200
+ statusDiv.className = '';
201
+ statusDiv.style.display = 'none'; // Hide it properly
202
+ }
203
+
204
+ // --- Handle Sending Prompt ---
205
  sendButton.addEventListener('click', async () => {
206
+ const prompt = promptInput.value.trim();
207
+ // Optional: Get context
208
+ // const contextHtml = contextHtmlInput.value.trim();
209
 
210
+ if (!prompt) {
211
+ showStatus('Please enter a prompt.', 'error');
 
 
 
 
 
 
212
  return;
213
  }
214
 
215
+ // Clear previous results and disable button
216
+ responseArea.textContent = ''; // Clear previous response
217
+ clearStatus();
218
+ showStatus('Sending request and waiting for response...', 'info');
219
  sendButton.disabled = true;
 
 
 
220
 
221
+ const requestBody = {
222
+ prompt: prompt,
223
+ // Optional: Include context if available
224
+ // html: contextHtml || null, // Send null if empty
225
+ // previousPrompt: null // Add logic if you track previous prompts
226
+ };
227
 
228
  try {
229
+ const response = await fetch('/api/ask-ai', {
230
+ method: 'POST',
231
+ headers: {
232
+ 'Content-Type': 'application/json',
233
+ 'Accept': 'text/plain', // Expecting a stream
234
+ },
235
+ body: JSON.stringify(requestBody),
 
 
 
 
 
 
 
 
 
236
  });
237
 
238
+ // --- Handle Non-OK HTTP Responses (e.g., 429, 500, 402) ---
239
+ if (!response.ok) {
240
+ let errorMsg = `Error: ${response.status} ${response.statusText}`;
241
+ try {
242
+ // Try to parse JSON error message from backend
243
+ const errorData = await response.json();
244
+ errorMsg = errorData.message || errorMsg;
245
+ // Check for specific flags from backend
246
+ if (errorData.openLogin) {
247
+ errorMsg += " Please log in.";
248
+ // Maybe redirect or show login button prominently
249
+ }
250
+ if (errorData.openProModal) {
251
+ errorMsg += " (Credit limit likely reached)";
252
+ }
253
+ } catch (e) {
254
+ // If response is not JSON, use the status text
255
+ console.warn("Could not parse error response as JSON");
256
+ }
257
+ throw new Error(errorMsg); // Throw to be caught below
258
  }
259
 
260
+ // --- Process the Stream ---
261
+ const reader = response.body.getReader();
262
+ const decoder = new TextDecoder(); // Defaults to utf-8
263
 
264
+ responseArea.textContent = ''; // Ensure it's clear before streaming starts
265
+ showStatus('Receiving response...', 'info'); // Update status
 
266
 
267
+ while (true) {
268
+ const { value, done } = await reader.read();
269
+ if (done) {
270
+ console.log('Stream finished.');
271
+ break;
272
+ }
273
+ const chunk = decoder.decode(value, { stream: true });
274
+ responseArea.textContent += chunk; // Append chunk to display
275
+ // Auto-scroll to bottom (optional)
276
+ responseArea.scrollTop = responseArea.scrollHeight;
277
+ }
278
+ clearStatus(); // Clear status on successful completion
279
 
280
  } catch (error) {
281
+ console.error('Error fetching or streaming response:', error);
282
+ showStatus(`Failed: ${error.message}`, 'error');
 
 
 
283
  } finally {
284
+ sendButton.disabled = false; // Re-enable button
 
 
285
  }
286
  });
287
 
288
+ // --- Initial Setup ---
289
+ checkLoginStatus(); // Check login status when the page loads
 
 
 
 
 
290
 
291
  </script>
292
  </body>