返回提交历史
Modified
g4f/Provider/hf/HuggingFaceMedia.py
+27
-5
Modified
g4f/Provider/needs_auth/OpenaiChat.py
+2
-1
Modified
g4f/gui/client/index.html
+109
-100
Modified
g4f/gui/client/static/css/style.css
+114
-160
Modified
g4f/gui/client/static/js/chat.v1.js
+93
-83
Modified
g4f/image/__init__.py
+10
-1
Modified
g4f/image/copy_images.py
+2
-0
XFEstudio/gpt4free
Update mobile template in UI
ff365ff5
代码差异
7 个文件
+357
-350
@@ -33,15 +33,24 @@ class HuggingFaceMedia(AsyncGeneratorProvider, ProviderModelMixin):
33
33
response = requests.get(url)
34
34
if response.ok:
35
35
models = response.json()
36
cls.models = [
37
model["id"]
36
providers = {
37
model["id"]: [
38
provider
39
for provider in model.get("inferenceProviderMapping")
40
if provider.get("status") == "live" and provider.get("task") in cls.tasks
41
]
38
42
for model in models
39
43
if [
40
44
provider
41
45
for provider in model.get("inferenceProviderMapping")
42
46
if provider.get("status") == "live" and provider.get("task") in cls.tasks
43
47
]
44
]
48
}
49
new_models = []
50
for model, provider_keys in providers.items():
51
new_models.append(model)
52
for provider_data in provider_keys:
53
new_models.append(f"{model}:{provider_data.get('provider')}")
45
54
cls.task_mapping = {
46
55
model["id"]: [
47
56
provider.get("task")
@@ -49,6 +58,14 @@ class HuggingFaceMedia(AsyncGeneratorProvider, ProviderModelMixin):
49
58
].pop()
50
59
for model in models
51
60
}
61
prepend_models = []
62
for model, provider_keys in providers.items():
63
task = cls.task_mapping.get(model)
64
if task == "text-to-video":
65
prepend_models.append(model)
66
for provider_data in provider_keys:
67
prepend_models.append(f"{model}:{provider_data.get('provider')}")
68
cls.models = prepend_models + [model for model in new_models if model not in prepend_models]
52
69
else:
53
70
cls.models = []
54
71
return cls.models
@@ -85,6 +102,9 @@ class HuggingFaceMedia(AsyncGeneratorProvider, ProviderModelMixin):
85
102
aspect_ratio: str = "1:1",
86
103
**kwargs
87
104
):
105
selected_provider = None
106
if ":" in model:
107
model, selected_provider = model.split(":", 1)
88
108
provider_mapping = await cls.get_mapping(model, api_key)
89
109
headers = {
90
110
'Accept-Encoding': 'gzip, deflate',
@@ -98,6 +118,8 @@ class HuggingFaceMedia(AsyncGeneratorProvider, ProviderModelMixin):
98
118
async def generate(extra_data: dict, prompt: str):
99
119
last_response = None
100
120
for provider_key, provider in provider_mapping.items():
121
if selected_provider is not None and selected_provider != provider_key:
122
continue
101
123
provider_info = ProviderInfo(**{**cls.get_dict(), "label": f"HuggingFace ({provider_key})", "url": f"{cls.url}/{model}"})
102
124
103
125
api_base = f"https://router.huggingface.co/{provider_key}"
@@ -124,7 +146,7 @@ class HuggingFaceMedia(AsyncGeneratorProvider, ProviderModelMixin):
124
146
**extra_data
125
147
}
126
148
elif provider_key == "replicate":
127
url = f"{api_base}/v1/models/{provider_id}/prediction"
149
url = f"{api_base}/v1/models/{provider_id}/predictions"
128
150
data = {
129
151
"input": {
130
152
"prompt": prompt,
@@ -151,7 +173,7 @@ class HuggingFaceMedia(AsyncGeneratorProvider, ProviderModelMixin):
151
173
}
152
174
153
175
async with StreamSession(
154
headers=headers if provider_key == "free" or api_key is None else {**headers, "Authorization": f"Bearer {api_key}"},
176
headers=headers if provider_key == "hf-free" or api_key is None else {**headers, "Authorization": f"Bearer {api_key}"},
155
177
proxy=proxy,
156
178
timeout=timeout
157
179
) as session:
@@ -384,7 +384,7 @@ class OpenaiChat(AsyncAuthedProvider, ProviderModelMixin):
384
384
#f"Proofofwork: {'False' if proofofwork is None else proofofwork[:12]+'...'}",
385
385
#f"AccessToken: {'False' if cls._api_key is None else cls._api_key[:12]+'...'}",
386
386
)]
387
if action == "continue" and conversation.message_id is None:
387
if action is None or action == "variant" or action == "continue" and conversation.message_id is None:
388
388
action = "next"
389
389
data = {
390
390
"action": action,
@@ -400,6 +400,7 @@ class OpenaiChat(AsyncAuthedProvider, ProviderModelMixin):
400
400
"client_contextual_info":{"is_dark_mode":False,"time_since_loaded":random.randint(20, 500),"page_height":578,"page_width":1850,"pixel_ratio":1,"screen_height":1080,"screen_width":1920},
401
401
"paragen_cot_summary_display_override":"allow"
402
402
}
403
print(data)
403
404
if conversation.conversation_id is not None:
404
405
data["conversation_id"] = conversation.conversation_id
405
406
debug.log(f"OpenaiChat: Use conversation: {conversation.conversation_id}")
@@ -60,14 +60,13 @@
60
60
const user_image = '<img src="/static/img/user.png" alt="your avatar">';
61
61
const gpt_image = '<img src="/static/img/gpt.png" alt="your avatar">';
62
62
</script>
63
<script src="/static/js/highlight.min.js"></script>
63
<script src="/static/js/highlight.min.js" async></script>
64
64
<script>window.conversation_id = "{{chat_id}}"</script>
65
<title>g4f - gui</title>
65
<title>G4F Chat</title>
66
66
</head>
67
67
<body>
68
68
<div class="gradient"></div>
69
<div class="row">
70
<div class="box conversations">
69
<div class="sidebar shown">
71
70
<div class="top">
72
71
<button class="new_convo" onclick="new_conversation()">
73
72
<i class="fa-regular fa-plus"></i>
@@ -120,8 +119,8 @@
120
119
</div>
121
120
<div class="field">
122
121
<span class="label">Download generated images</span>
123
<input type="checkbox" id="download_images" checked/>
124
<label for="download_images" class="toogle" title="Download and save generated images to /generated_images"></label>
122
<input type="checkbox" id="download_media" checked/>
123
<label for="download_media" class="toogle" title="Download and save generated images, audios and videos"></label>
125
124
</div>
126
125
<div class="field">
127
126
<span class="label">Refine files with spaCy</span>
@@ -148,8 +147,8 @@
148
147
<textarea id="systemPrompt" placeholder="You are a helpful assistant." data-example="If you need to generate images, you can use the following format: . This will enable the use of an image generation tool."></textarea>
149
148
</div>
150
149
<div class="field box">
151
<label for="message-input-height" class="label" title="">Input max. height</label>
152
<input type="number" id="message-input-height" value="200"/>
150
<label for="userInput-height" class="label" title="">Input max. height</label>
151
<input type="number" id="userInput-height" value="200"/>
153
152
</div>
154
153
<div class="field box">
155
154
<label for="recognition-language" class="label" title="">Speech recognition language</label>
@@ -208,114 +207,124 @@
208
207
</button>
209
208
</div>
210
209
</div>
211
<div class="conversation">
210
<div class="chat-container">
211
<div class="chat-header box">
212
G4F Chat
213
</div>
212
214
<textarea id="chatPrompt" class="box" placeholder="System prompt"></textarea>
213
<div id="messages" class="box"></div>
214
215
<button class="slide-systemPrompt">
215
216
<i class="fa-solid fa-angles-up"></i>
216
217
</button>
217
<div class="media_player">
218
<i class="fa-regular fa-x"></i>
219
</div>
220
<div class="media-select hidden">
221
<label class="image-select" for="image" title="">
222
<input type="file" id="image" name="image" accept="image/*" required/>
223
<i class="fa-regular fa-image"></i>
224
</label>
225
<label class="capture-camera" for="camera">
226
<input type="file" id="camera" name="camera" accept="image/*" capture="camera" required/>
227
<i class="fa-solid fa-camera"></i>
228
</label>
229
<button class="close">
230
<i class="fa-solid fa-xmark"></i>
231
</button>
232
</div>
233
<div class="toolbar">
234
<div id="input-count" class="">
235
<button class="hide-input">
236
<i class="fa-solid fa-angles-down"></i>
237
</button>
238
<input type="checkbox" id="agree" name="agree" value="yes" checked>
239
<label for="agree" class="text" onclick="this.innerText='';">Scroll to bottom</label>
240
</div>
241
<div class="stop_generating stop_generating-hidden">
242
<button id="cancelButton">
243
<span>Stop Generating</span>
244
<i class="fa-solid fa-stop"></i>
245
</button>
218
<div class="chat-body" id="chatBody"></div>
219
<div class="chat-footer">
220
<div class="chat-toolbar">
221
<div id="input-count" class="">
222
<button class="hide-input">
223
<i class="fa-solid fa-angles-down"></i>
224
</button>
225
<input type="checkbox" id="agree" name="agree" value="yes" checked>
226
<label for="agree" class="text" onclick="this.innerText='';">Scroll to bottom</label>
227
</div>
228
<div class="stop_generating stop_generating-hidden">
229
<button id="cancelButton">
230
<span>Stop Generating</span>
231
<i class="fa-solid fa-stop"></i>
232
</button>
233
</div>
234
<div class="regenerate">
235
<button id="regenerateButton">
236
<span>Regenerate</span>
237
<i class="fa-solid fa-rotate"></i>
238
</button>
239
</div>
246
240
</div>
247
<div class="regenerate">
248
<button id="regenerateButton">
249
<span>Regenerate</span>
250
<i class="fa-solid fa-rotate"></i>
251
</button>
241
<div class="media-player">
242
<i class="fa-regular fa-x"></i>
252
243
</div>
253
</div>
254
<div class="user-input">
255
<div class="box input-area">
256
<textarea id="message-input" placeholder="Ask a question" cols="30" rows="10"
257
style="white-space: pre-wrap;resize: none;"></textarea>
258
<label class="file-label image-label">
244
<div class="media-select hidden">
245
<label class="image-select" for="image" title="">
246
<input type="file" id="image" name="image" accept="image/*" required/>
259
247
<i class="fa-regular fa-image"></i>
260
248
</label>
261
<label class="file-label" for="file">
262
<input type="file" id="file" name="file" accept=".txt, .html, .xml, .json, .js, .har, .sh, .py, .php, .css, .yaml, .sql, .log, .csv, .twig, .md, .pdf, .docx, .odt, .epub, .xlsx, .zip" required multiple/>
263
<i class="fa-solid fa-paperclip"></i>
264
</label>
265
<label class="micro-label" for="micro">
266
<i class="fa-solid fa-microphone-slash"></i>
249
<label class="capture-camera" for="camera">
250
<input type="file" id="camera" name="camera" accept="image/*" capture="camera" required/>
251
<i class="fa-solid fa-camera"></i>
267
252
</label>
268
<div id="send-button">
269
<i class="fa-solid fa-square-plus"></i>
270
<i class="fa-regular fa-paper-plane"></i>
271
<a href="" id="download" class="hidden"></a>
272
</div>
273
</div>
274
</div>
275
<div class="buttons">
276
<div class="field">
277
<button id="search">
278
<i class="fa-solid fa-search"></i>
253
<button class="close">
254
<i class="fa-solid fa-xmark"></i>
279
255
</button>
280
256
</div>
281
<div class="field">
282
<select name="model" id="model">
283
<option value="" selected="selected">Model: Default</option>
284
<option value="gpt-4">gpt-4</option>
285
<option value="gpt-4o">gpt-4o</option>
286
<option value="gpt-4o-mini">gpt-4o-mini</option>
287
<option value="llama-3.1-70b">llama-3.1-70b</option>
288
<option value="mixtral-8x7b">mixtral-8x7b</option>
289
<option value="claude-3.5-sonnet">claude-3.5-sonnet</option>
290
<option value="flux">flux (Image Generation)</option>
291
<option value="dall-e-3">dall-e-3 (Image Generation)</option>
292
<option disabled="disabled">----</option>
293
</select>
294
<select name="model2" id="model2" class="hidden model"></select>
295
<input type="text" id="model3" value="" class="hidden model" placeholder="Model:"/>
296
</div>
297
<div class="field">
298
<select name="provider" id="provider">
299
<option value="">Provider: Auto</option>
300
<option value="OpenaiChat">OpenAI ChatGPT</option>
301
<option value="Copilot">Microsoft Copilot</option>
302
<option value="Gemini">Google Gemini</option>
303
<option value="DDG">DuckDuckGo AI Chat</option>
304
<option value="Blackbox">Blackbox AI</option>
305
<option value="Custom Model">Custom Model</option>
306
<option disabled="disabled">----</option>
307
</select>
257
<div class="user-input">
258
<div class="input-area">
259
<textarea id="userInput" class="box" placeholder="Type a message..." cols="30" rows="10"
260
style="white-space: pre-wrap;resize: none;"></textarea>
261
<label class="file-label image-label">
262
<i class="fa-regular fa-image"></i>
263
</label>
264
<label class="file-label" for="file">
265
<input type="file" id="file" name="file" accept=".txt, .html, .xml, .json, .js, .har, .sh, .py, .php, .css, .yaml, .sql, .log, .csv, .twig, .md, .pdf, .docx, .odt, .epub, .xlsx, .zip" required multiple/>
266
<i class="fa-solid fa-paperclip"></i>
267
</label>
268
<label class="micro-label" for="micro">
269
<i class="fa-solid fa-microphone-slash"></i>
270
</label>
271
<div class="send-buttons">
272
<button id="addButton">
273
<i class="fa-solid fa-square-plus"></i>
274
Add
275
</button>
276
<button id="sendButton">
277
<i class="fa-regular fa-paper-plane"></i>
278
<a href="" id="download" class="hidden"></a>
279
Send
280
</button>
281
</div>
282
</div>
308
283
</div>
309
<div class="field">
310
<button id="pin">
311
<i class="fa-solid fa-thumbtack"></i>
312
</button>
284
<div class="chat-buttons">
285
<div class="field">
286
<button id="search">
287
<i class="fa-solid fa-search"></i>
288
</button>
289
</div>
290
<div class="field">
291
<select name="model" id="model">
292
<option value="" selected="selected">Model: Default</option>
293
<option value="gpt-4">gpt-4</option>
294
<option value="gpt-4o">gpt-4o</option>
295
<option value="gpt-4o-mini">gpt-4o-mini</option>
296
<option value="llama-3.1-70b">llama-3.1-70b</option>
297
<option value="mixtral-8x7b">mixtral-8x7b</option>
298
<option value="claude-3.5-sonnet">claude-3.5-sonnet</option>
299
<option value="flux">flux (Image Generation)</option>
300
<option value="dall-e-3">dall-e-3 (Image Generation)</option>
301
<option disabled="disabled">----</option>
302
</select>
303
<select name="model2" id="model2" class="hidden model"></select>
304
<input type="text" id="model3" value="" class="hidden model" placeholder="Model:"/>
305
</div>
306
<div class="field">
307
<select name="provider" id="provider">
308
<option value="">Provider: Auto</option>
309
<option value="OpenaiChat">OpenAI ChatGPT</option>
310
<option value="Copilot">Microsoft Copilot</option>
311
<option value="Gemini">Google Gemini</option>
312
<option value="DDG">DuckDuckGo AI Chat</option>
313
<option value="Blackbox">Blackbox AI</option>
314
<option value="Custom Model">Custom Model</option>
315
<option disabled="disabled">----</option>
316
</select>
317
</div>
318
<div class="field">
319
<button id="pin">
320
<i class="fa-solid fa-thumbtack"></i>
321
</button>
322
</div>
323
<div id="pin_container" class="field"></div>
313
324
</div>
314
<div id="pin_container" class="field"></div>
315
325
</div>
316
326
</div>
317
327
<div class="log hidden"></div>
318
</div>
319
328
<div class="mobile-sidebar">
320
329
<i class="fa-solid fa-bars"></i>
321
330
</div>
@@ -63,6 +63,7 @@ body {
63
63
background: var(--background);
64
64
color: var(--colour-3);
65
65
height: 100vh;
66
display: flex;
66
67
}
67
68
68
69
body:not(.white) a:link,
@@ -104,8 +105,6 @@ body:not(.white) a:visited{
104
105
backdrop-filter: blur(20px);
105
106
-webkit-backdrop-filter: blur(20px);
106
107
background-color: var(--blur-bg);
107
height: 100%;
108
width: 100%;
109
108
border-radius: var(--border-radius-1);
110
109
border: 1px solid var(--blur-border);
111
110
}
@@ -131,67 +130,20 @@ body:not(.white) a:visited{
131
130
text-decoration: underline;
132
131
}
133
132
134
.conversations {
135
max-width: 300px;
136
padding: var(--section-gap);
137
overflow: auto;
138
flex-shrink: 0;
139
display: flex;
140
flex-direction: column;
141
justify-content: space-between;
142
}
143
144
.conversation {
145
width: 100%;
146
height: 100%;
147
display: flex;
148
flex-direction: column;
149
gap: 5px;
150
}
151
152
.conversation #messages {
153
width: 100%;
154
height: 100%;
155
display: flex;
156
flex-direction: column;
157
overflow: auto;
158
overflow-wrap: break-word;
159
padding-bottom: 10px;
160
background-color: transparent;
161
}
162
163
.conversation .user-input {
133
.chat-footer .user-input {
164
134
margin-bottom: 4px;
165
135
}
166
136
167
.conversation .user-input input {
168
font-size: 15px;
169
width: 100%;
170
height: 100%;
171
padding: 12px 15px;
172
background: none;
173
border: none;
174
outline: none;
175
color: var(--colour-3);
176
}
177
178
.conversation .user-input input::placeholder {
137
.chat-footer .user-input input::placeholder {
179
138
color: var(--user-input)
180
139
}
181
140
182
.conversations {
183
display: flex;
184
flex-direction: column;
185
gap: 10px;
186
padding: 10px;
187
}
188
189
.conversations .title {
141
.sidebar .title {
190
142
font-size: 14px;
191
143
font-weight: 500;
192
144
}
193
145
194
.conversations .convo {
146
.sidebar .convo {
195
147
padding: 8px 12px;
196
148
display: flex;
197
149
gap: 10px;
@@ -202,7 +154,7 @@ body:not(.white) a:visited{
202
154
border-radius: var(--border-radius-1);
203
155
}
204
156
205
.conversations .convo .left {
157
.sidebar .convo .left {
206
158
width: 100%;
207
159
cursor: pointer;
208
160
display: flex;
@@ -210,20 +162,20 @@ body:not(.white) a:visited{
210
162
gap: 4px;
211
163
}
212
164
213
.conversations .convo .fa-ellipsis-vertical {
165
.sidebar .convo .fa-ellipsis-vertical {
214
166
position: absolute;
215
167
right: 8px;
216
168
width: 14px;
217
169
text-align: center;
218
170
}
219
171
220
.conversations .convo .choise {
172
.sidebar .convo .choise {
221
173
position: absolute;
222
174
right: 8px;
223
175
background-color: var(--blur-bg);
224
176
}
225
177
226
.conversations i, .bottom_buttons i, .mem0 button i {
178
.sidebar i, .bottom_buttons i, .mem0 button i {
227
179
color: var(--conversations);
228
180
cursor: pointer;
229
181
}
@@ -486,22 +438,22 @@ body:not(.white) a:visited{
486
438
border-left: .25em solid var(--colour-4);
487
439
}
488
440
489
.media_player {
441
.media-player {
490
442
display: none;
491
443
}
492
444
493
.media_player audio {
445
.media-player audio {
494
446
right: 28px;
495
447
position: absolute;
496
448
top: -4px;
497
449
z-index: 900;
498
450
}
499
451
500
.media_player.show {
452
.media-player.show {
501
453
display: block;
502
454
}
503
455
504
.media_player .fa-x {
456
.media-player .fa-x {
505
457
position: absolute;
506
458
right: 8px;
507
459
top: 8px;
@@ -568,7 +520,7 @@ body:not(.white) a:visited{
568
520
font-size: 14px;
569
521
}
570
522
571
.toolbar {
523
.chat-toolbar {
572
524
position: relative;
573
525
}
574
526
@@ -591,14 +543,14 @@ input-count .text {
591
543
display: none;
592
544
}
593
545
594
.stop_generating, .toolbar .regenerate {
546
.stop_generating, .chat-toolbar .regenerate {
595
547
position: absolute;
596
548
top: 0;
597
549
right: 0;
598
550
animation: show_popup 0.4s;
599
551
}
600
552
601
.stop_generating button, .toolbar .regenerate button, button.regenerate_button, button.continue_button, button.options_button {
553
.stop_generating button, .chat-toolbar .regenerate button, button.regenerate_button, button.continue_button, button.options_button {
602
554
backdrop-filter: blur(20px);
603
555
-webkit-backdrop-filter: blur(20px);
604
556
background-color: var(--blur-bg);
@@ -614,13 +566,13 @@ input-count .text {
614
566
height: 28px;
615
567
}
616
568
617
.toolbar .regenerate {
569
.chat-toolbar .regenerate {
618
570
left: 50%;
619
571
transform: translateX(-50%);
620
572
right: auto;
621
573
}
622
574
623
.toolbar .regenerate span, .regenerate_button span, .continue_button span, .options_button div {
575
.chat-toolbar .regenerate span, .regenerate_button span, .continue_button span, .options_button div {
624
576
display: none;
625
577
}
626
578
@@ -665,12 +617,12 @@ input-count .text {
665
617
.stop_generating {
666
618
right: 4px;
667
619
}
668
.toolbar .regenerate span {
620
.chat-toolbar .regenerate span {
669
621
display: block;
670
622
}
671
623
}
672
624
673
.toolbar .hide-input {
625
.chat-toolbar .hide-input {
674
626
background: transparent;
675
627
border: none;
676
628
color: var(--colour-3);
@@ -744,14 +696,7 @@ label[for="micro"] {
744
696
}
745
697
}
746
698
747
#messages form {
748
position: absolute;
749
width: 100%;
750
background: var(--button-hover);
751
z-index: 2000;
752
}
753
754
.buttons input[type="checkbox"],
699
.chat-buttons input[type="checkbox"],
755
700
.settings input[type="checkbox"],
756
701
form input[type="checkbox"] {
757
702
height: 0;
@@ -759,7 +704,7 @@ form input[type="checkbox"] {
759
704
display: none;
760
705
}
761
706
762
.buttons label,
707
.chat-buttons label,
763
708
.settings label.toogle,
764
709
form label.toogle {
765
710
cursor: pointer;
@@ -780,7 +725,7 @@ form label.toogle {
780
725
margin-left: 0;
781
726
}
782
727
783
.buttons label:after,
728
.chat-buttons label:after,
784
729
.settings label.toogle:after,
785
730
form label.toogle:after {
786
731
content: "";
@@ -795,7 +740,7 @@ form label.toogle:after {
795
740
transition: 0.33s;
796
741
}
797
742
798
.buttons input:checked+label,
743
.chat-buttons input:checked+label,
799
744
.settings input:checked+label,
800
745
form input:checked+label {
801
746
background: var(--accent);
@@ -810,13 +755,13 @@ form input:checked+label {
810
755
width: 100%;
811
756
}
812
757
813
.buttons input:checked+label:after,
758
.chat-buttons input:checked+label:after,
814
759
.settings input:checked+label:after,
815
760
form input:checked+label:after {
816
761
left: calc(100% - 5px - 20px);
817
762
}
818
763
819
.buttons {
764
.chat-buttons {
820
765
display: flex;
821
766
align-items: center;
822
767
justify-content: left;
@@ -874,7 +819,7 @@ select, input.model {
874
819
border-radius: 25px;
875
820
}
876
821
877
.buttons button, button.regenerate_button, button.continue_button, button.options_button {
822
.chat-buttons button, button.regenerate_button, button.continue_button, button.options_button {
878
823
border-radius: 8px;
879
824
backdrop-filter: blur(20px);
880
825
cursor: pointer;
@@ -895,7 +840,7 @@ button.options_button {
895
840
margin-left: auto;
896
841
}
897
842
898
.buttons button.pinned span {
843
.chat-buttons button.pinned span {
899
844
max-width: 160px;
900
845
overflow: hidden;
901
846
text-wrap: nowrap;
@@ -904,7 +849,7 @@ button.options_button {
904
849
text-overflow: ellipsis;
905
850
}
906
851
907
.buttons button.pinned i {
852
.chat-buttons button.pinned i {
908
853
position: absolute;
909
854
top: 10px;
910
855
right: 6px;
@@ -912,10 +857,10 @@ button.options_button {
912
857
913
858
select:hover,
914
859
input.model:hover
915
.buttons button:hover,
860
.chat-buttons button:hover,
916
861
.stop_generating button:hover,
917
.toolbar .regenerate button:hover,
918
#send-button:hover {
862
.chat-toolbar .regenerate button:hover,
863
.send-buttons button:hover {
919
864
background-color: var(--button-hover);
920
865
}
921
866
@@ -932,7 +877,6 @@ input.model:hover
932
877
933
878
#systemPrompt, #chatPrompt, .settings textarea, form textarea {
934
879
font-size: 15px;
935
width: 100%;
936
880
color: var(--colour-3);
937
881
outline: none;
938
882
transition: max-height 0.15s ease-out;
@@ -993,7 +937,8 @@ input.model:hover
993
937
994
938
.slide-systemPrompt {
995
939
position: absolute;
996
top: 0;
940
top: 42px;
941
z-index: 1;
997
942
padding: var(--inner-gap) 10px;
998
943
border: none;
999
944
background: transparent;
@@ -1028,8 +973,6 @@ input.model:hover
1028
973
1029
974
.input-area {
1030
975
display: flex;
1031
align-items: center;
1032
padding: 10px;
1033
976
}
1034
977
1035
978
.info {
@@ -1080,7 +1023,7 @@ input.model:hover
1080
1023
text-decoration: none;
1081
1024
}
1082
1025
1083
.conversations .top {
1026
.sidebar .top {
1084
1027
display: flex;
1085
1028
flex-direction: column;
1086
1029
gap: var(--inner-gap);
@@ -1158,7 +1101,6 @@ ul {
1158
1101
}
1159
1102
1160
1103
.mobile-sidebar {
1161
display: none;
1162
1104
position: fixed;
1163
1105
z-index: 1000;
1164
1106
top: 10px;
@@ -1190,9 +1132,8 @@ ul {
1190
1132
padding-top: 10px;
1191
1133
}
1192
1134
@media screen and (max-width: 990px) {
1193
.conversations {
1194
display: none;
1195
width: 100%;
1135
.sidebar {
1136
width: 300px;
1196
1137
max-width: none;
1197
1138
}
1198
1139
@@ -1201,17 +1142,13 @@ ul {
1201
1142
padding-top: 18px;
1202
1143
}
1203
1144
1204
.buttons {
1145
.chat-buttons {
1205
1146
align-items: flex-start;
1206
1147
flex-wrap: wrap;
1207
1148
gap: 8px;
1208
1149
padding-left: 4px;
1209
1150
}
1210
1151
1211
.mobile-sidebar {
1212
display: flex;
1213
}
1214
1215
1152
#chatPrompt {
1216
1153
padding-left: 30px;
1217
1154
}
@@ -1221,23 +1158,10 @@ ul {
1221
1158
}
1222
1159
}
1223
1160
1224
.shown {
1225
display: flex;
1226
}
1227
1228
.conversation .user-input textarea {
1229
font-size: 15px;
1230
width: 100%;
1231
height: 100%;
1232
padding: 12px var(--inner-gap);
1233
background: none;
1234
border: none;
1235
outline: none;
1236
color: var(--colour-3);
1237
1238
resize: vertical;
1239
max-height: 200px;
1240
min-height: 100px;
1161
.sidebar.shown {
1162
width: 300px;
1163
padding: 15px;
1164
margin-right: 10px;
1241
1165
}
1242
1166
1243
1167
/* style for hljs copy */
@@ -1370,24 +1294,6 @@ ul {
1370
1294
color: var(--colour-3);
1371
1295
}
1372
1296
1373
#send-button {
1374
border: 1px dashed #e4d4ffa6;
1375
border-radius: 4px;
1376
cursor: pointer;
1377
position: absolute;
1378
bottom: 8px;
1379
right: 4px;
1380
padding: 4px;
1381
}
1382
1383
#send-button:hover {
1384
border: 1px solid #e4d4ffc9;
1385
}
1386
1387
#send-button i {
1388
padding: 2px;
1389
}
1390
1391
1297
form textarea {
1392
1298
height: 20px;
1393
1299
min-height: 20px;
@@ -1538,16 +1444,15 @@ form .field.saved .fa-xmark {
1538
1444
font-size: 15px;
1539
1445
}
1540
1446
1541
#message-input {
1542
height: 90px;
1447
#userInput {
1448
min-height: 94px;
1449
height: 94px;
1543
1450
flex: 1;
1544
padding: 10px;
1545
padding-left: 24px;
1546
border-radius: 20px;
1547
font-size: 14px;
1548
margin-right: 10px;
1549
outline: none;
1451
padding: 10px 30px;
1452
color: var(--colour-3);
1550
1453
max-height: 200px;
1454
outline: none;
1455
font-size: 14px;
1551
1456
}
1552
1457
1553
1458
.hidden, input.hidden {
@@ -1566,10 +1471,10 @@ form .field.saved .fa-xmark {
1566
1471
1567
1472
@media print {
1568
1473
#chatPrompt:placeholder-shown,
1569
.conversations,
1474
.sidebar,
1570
1475
.conversation .user-input,
1571
.conversation .buttons,
1572
.conversation .toolbar,
1476
.conversation .chat-buttons,
1477
.conversation .chat-toolbar,
1573
1478
.conversation .slide-systemPrompt,
1574
1479
.message .count i,
1575
1480
.message .assistant,
@@ -1584,21 +1489,70 @@ form .field.saved .fa-xmark {
1584
1489
}
1585
1490
}
1586
1491
1587
/* Media queries for mobile devices */
1588
@media (max-width: 768px) {
1589
.row {
1590
flex-direction: column;
1591
}
1592
1593
.conversations, .settings {
1594
width: 100%;
1595
max-width: 100%;
1596
margin: 0;
1597
}
1598
}
1599
1600
1492
@media (max-width: 480px) {
1601
1493
.info .convo-title {
1602
1494
font-size: 12px;
1603
1495
}
1604
1496
}
1497
1498
.sidebar {
1499
display: flex;
1500
flex-direction: column;
1501
transition: width 0.3s ease-in-out;
1502
overflow: hidden;
1503
width: 0;
1504
}
1505
.chat-container {
1506
flex: 1;
1507
display: flex;
1508
flex-direction: column;
1509
overflow: hidden;
1510
}
1511
.chat-header {
1512
padding: 10px;
1513
font-weight: 500;
1514
white-space: nowrap;
1515
text-overflow: ellipsis;
1516
overflow: hidden;
1517
}
1518
@media only screen and (min-width: 40em) {
1519
.sidebar {
1520
width: 300px;
1521
padding: 15px;
1522
margin-right: 10px;
1523
}
1524
}
1525
.chat-body {
1526
flex: 1;
1527
padding: 10px;
1528
overflow-y: auto;
1529
display: flex;
1530
flex-direction: column;
1531
}
1532
.chat-footer {
1533
display: flex;
1534
padding: 4px 0;
1535
flex-direction: column;
1536
}
1537
.chat-footer input {
1538
flex: 1;
1539
padding: 10px;
1540
border: none;
1541
border-radius: 5px;
1542
outline: none;
1543
}
1544
.send-buttons {
1545
display: flex;
1546
gap: 10px;
1547
flex-direction: column;
1548
}
1549
.chat-footer .send-buttons button {
1550
background: var(--blur-bg);
1551
color: white;
1552
border: none;
1553
padding: 12px 15px;
1554
margin: 0 10px;
1555
border-radius: 5px;
1556
cursor: pointer;
1557
border: 1px dashed #e4d4ffa6;
1558
}
@@ -1,12 +1,13 @@
1
1
const colorThemes = document.querySelectorAll('[name="theme"]');
2
const message_box = document.getElementById(`messages`);
3
const messageInput = document.getElementById(`message-input`);
2
const chatBody = document.getElementById(`chatBody`);
3
const userInput = document.getElementById("userInput");
4
4
const box_conversations = document.querySelector(`.top`);
5
5
const stop_generating = document.querySelector(`.stop_generating`);
6
6
const regenerate_button = document.querySelector(`.regenerate`);
7
const sidebar = document.querySelector(".conversations");
7
const sidebar = document.querySelector(".sidebar");
8
8
const sidebar_button = document.querySelector(".mobile-sidebar");
9
const sendButton = document.getElementById("send-button");
9
const sendButton = document.getElementById("sendButton");
10
const addButton = document.getElementById("addButton");
10
11
const imageInput = document.querySelector(".image-label");
11
12
const mediaSelect = document.querySelector(".media-select");
12
13
const imageSelect = document.getElementById("image");
@@ -20,7 +21,7 @@ const modelProvider = document.getElementById("model2");
20
21
const custom_model = document.getElementById("model3");
21
22
const chatPrompt = document.getElementById("chatPrompt");
22
23
const settings = document.querySelector(".settings");
23
const chat = document.querySelector(".conversation");
24
const chat = document.querySelector(".chat-container");
24
25
const album = document.querySelector(".images");
25
26
const log_storage = document.querySelector(".log");
26
27
const switchInput = document.getElementById("switch");
@@ -47,11 +48,11 @@ let wakeLock = null;
47
48
let countTokensEnabled = true;
48
49
let reloadConversation = true;
49
50
50
messageInput.addEventListener("blur", () => {
51
userInput.addEventListener("blur", () => {
51
52
document.documentElement.scrollTop = 0;
52
53
});
53
54
54
messageInput.addEventListener("focus", () => {
55
userInput.addEventListener("focus", () => {
55
56
document.documentElement.scrollTop = document.documentElement.scrollHeight;
56
57
});
57
58
@@ -216,8 +217,8 @@ const get_message_el = (el) => {
216
217
}
217
218
218
219
function register_message_images() {
219
message_box.querySelectorAll(`.loading-indicator`).forEach((el) => el.remove());
220
message_box.querySelectorAll(`.message img:not([alt="your avatar"])`).forEach(async (el) => {
220
chatBody.querySelectorAll(`.loading-indicator`).forEach((el) => el.remove());
221
chatBody.querySelectorAll(`.message img:not([alt="your avatar"])`).forEach(async (el) => {
221
222
if (!el.complete) {
222
223
const indicator = document.createElement("span");
223
224
indicator.classList.add("loading-indicator");
@@ -264,7 +265,7 @@ function register_message_images() {
264
265
}
265
266
266
267
const register_message_buttons = async () => {
267
message_box.querySelectorAll(".message .content .provider").forEach(async (el) => {
268
chatBody.querySelectorAll(".message .content .provider").forEach(async (el) => {
268
269
if (el.dataset.click) {
269
270
return
270
271
}
@@ -288,7 +289,7 @@ const register_message_buttons = async () => {
288
289
});
289
290
});
290
291
291
message_box.querySelectorAll(".message .fa-xmark").forEach(async (el) => {
292
chatBody.querySelectorAll(".message .fa-xmark").forEach(async (el) => {
292
293
if (el.dataset.click) {
293
294
return
294
295
}
@@ -306,7 +307,7 @@ const register_message_buttons = async () => {
306
307
});
307
308
});
308
309
309
message_box.querySelectorAll(".message .fa-clipboard").forEach(async (el) => {
310
chatBody.querySelectorAll(".message .fa-clipboard").forEach(async (el) => {
310
311
if (el.dataset.click) {
311
312
return
312
313
}
@@ -330,7 +331,7 @@ const register_message_buttons = async () => {
330
331
});
331
332
})
332
333
333
message_box.querySelectorAll(".message .fa-file-export").forEach(async (el) => {
334
chatBody.querySelectorAll(".message .fa-file-export").forEach(async (el) => {
334
335
if (el.dataset.click) {
335
336
return
336
337
}
@@ -355,7 +356,7 @@ const register_message_buttons = async () => {
355
356
});
356
357
})
357
358
358
message_box.querySelectorAll(".message .fa-volume-high").forEach(async (el) => {
359
chatBody.querySelectorAll(".message .fa-volume-high").forEach(async (el) => {
359
360
if (el.dataset.click) {
360
361
return
361
362
}
@@ -366,7 +367,7 @@ const register_message_buttons = async () => {
366
367
if (message_el.dataset.synthesize_url) {
367
368
el.classList.add("active");
368
369
setTimeout(()=>el.classList.remove("active"), 2000);
369
const media_player = document.querySelector(".media_player");
370
const media_player = document.querySelector(".media-player");
370
371
if (!media_player.classList.contains("show")) {
371
372
media_player.classList.add("show");
372
373
audio = new Audio(message_el.dataset.synthesize_url);
@@ -382,7 +383,7 @@ const register_message_buttons = async () => {
382
383
});
383
384
});
384
385
385
message_box.querySelectorAll(".message .regenerate_button").forEach(async (el) => {
386
chatBody.querySelectorAll(".message .regenerate_button").forEach(async (el) => {
386
387
if (el.dataset.click) {
387
388
return
388
389
}
@@ -395,7 +396,7 @@ const register_message_buttons = async () => {
395
396
});
396
397
});
397
398
398
message_box.querySelectorAll(".message .continue_button").forEach(async (el) => {
399
chatBody.querySelectorAll(".message .continue_button").forEach(async (el) => {
399
400
if (el.dataset.click) {
400
401
return
401
402
}
@@ -411,7 +412,7 @@ const register_message_buttons = async () => {
411
412
});
412
413
});
413
414
414
message_box.querySelectorAll(".message .fa-whatsapp").forEach(async (el) => {
415
chatBody.querySelectorAll(".message .fa-whatsapp").forEach(async (el) => {
415
416
if (el.dataset.click) {
416
417
return
417
418
}
@@ -422,7 +423,7 @@ const register_message_buttons = async () => {
422
423
});
423
424
});
424
425
425
message_box.querySelectorAll(".message .fa-print").forEach(async (el) => {
426
chatBody.querySelectorAll(".message .fa-print").forEach(async (el) => {
426
427
if (el.dataset.click) {
427
428
return
428
429
}
@@ -430,7 +431,7 @@ const register_message_buttons = async () => {
430
431
el.addEventListener("click", async () => {
431
432
const message_el = get_message_el(el);
432
433
el.classList.add("clicked");
433
message_box.scrollTop = 0;
434
chatBody.scrollTop = 0;
434
435
message_el.classList.add("print");
435
436
setTimeout(() => {
436
437
el.classList.remove("clicked");
@@ -440,7 +441,7 @@ const register_message_buttons = async () => {
440
441
});
441
442
});
442
443
443
message_box.querySelectorAll(".message .reasoning_title").forEach(async (el) => {
444
chatBody.querySelectorAll(".message .reasoning_title").forEach(async (el) => {
444
445
if (el.dataset.click) {
445
446
return
446
447
}
@@ -468,15 +469,15 @@ const delete_conversations = async () => {
468
469
};
469
470
470
471
const handle_ask = async (do_ask_gpt = true) => {
471
messageInput.style.height = "82px";
472
messageInput.focus();
472
userInput.style.height = "82px";
473
userInput.focus();
473
474
await scroll_to_bottom();
474
475
475
let message = messageInput.value.trim();
476
let message = userInput.value.trim();
476
477
if (message.length <= 0) {
477
478
return;
478
479
}
479
messageInput.value = "";
480
userInput.value = "";
480
481
await count_input()
481
482
await add_conversation(window.conversation_id);
482
483
@@ -518,7 +519,7 @@ const handle_ask = async (do_ask_gpt = true) => {
518
519
</div>
519
520
</div>
520
521
`;
521
message_box.appendChild(message_el);
522
chatBody.appendChild(message_el);
522
523
highlight(message_el);
523
524
if (do_ask_gpt) {
524
525
const all_pinned = document.querySelectorAll(".buttons button.pinned")
@@ -583,10 +584,10 @@ stop_generating.addEventListener("click", async () => {
583
584
await load_conversation(window.conversation_id, false);
584
585
});
585
586
586
document.querySelector(".media_player .fa-x").addEventListener("click", ()=>{
587
const media_player = document.querySelector(".media_player");
587
document.querySelector(".media-player .fa-x").addEventListener("click", ()=>{
588
const media_player = document.querySelector(".media-player");
588
589
media_player.classList.remove("show");
589
const audio = document.querySelector(".media_player audio");
590
const audio = document.querySelector(".media-player audio");
590
591
media_player.removeChild(audio);
591
592
});
592
593
@@ -948,7 +949,7 @@ const ask_gpt = async (message_id, message_index = -1, regenerate = false, provi
948
949
await lazy_scroll_to_bottom();
949
950
}
950
951
if (countTokensEnabled) {
951
let count_total = message_box.querySelector('.count_total');
952
let count_total = chatBody.querySelector('.count_total');
952
953
count_total ? count_total.parentElement.removeChild(count_total) : null;
953
954
}
954
955
@@ -970,9 +971,9 @@ const ask_gpt = async (message_id, message_index = -1, regenerate = false, provi
970
971
</div>
971
972
`;
972
973
if (message_index == -1) {
973
message_box.appendChild(message_el);
974
chatBody.appendChild(message_el);
974
975
} else {
975
parent_message = message_box.querySelector(`.message[data-index="${message_index}"]`);
976
parent_message = chatBody.querySelector(`.message[data-index="${message_index}"]`);
976
977
if (!parent_message) {
977
978
return;
978
979
}
@@ -1166,7 +1167,7 @@ const ask_gpt = async (message_id, message_index = -1, regenerate = false, provi
1166
1167
1167
1168
async function scroll_to_bottom() {
1168
1169
window.scrollTo(0, 0);
1169
message_box.scrollTop = message_box.scrollHeight;
1170
chatBody.scrollTop = chatBody.scrollHeight;
1170
1171
}
1171
1172
1172
1173
async function lazy_scroll_to_bottom() {
@@ -1193,10 +1194,10 @@ const clear_conversations = async () => {
1193
1194
};
1194
1195
1195
1196
const clear_conversation = async () => {
1196
let messages = message_box.getElementsByTagName(`div`);
1197
let messages = chatBody.getElementsByTagName(`div`);
1197
1198
1198
1199
while (messages.length > 0) {
1199
message_box.removeChild(messages[0]);
1200
chatBody.removeChild(messages[0]);
1200
1201
}
1201
1202
};
1202
1203
@@ -1301,7 +1302,7 @@ const set_conversation = async (conversation_id) => {
1301
1302
await clear_conversation();
1302
1303
await load_conversation(conversation_id);
1303
1304
load_conversations();
1304
hide_sidebar();
1305
hide_sidebar(true);
1305
1306
};
1306
1307
1307
1308
const new_conversation = async () => {
@@ -1372,10 +1373,11 @@ const load_conversation = async (conversation_id, scroll=true) => {
1372
1373
return;
1373
1374
}
1374
1375
let title = conversation.title || conversation.new_title;
1375
title = title ? `${title} - g4f` : window.title;
1376
title = title ? `${title} - G4F` : window.title;
1376
1377
if (title) {
1377
1378
document.title = title;
1378
1379
}
1380
document.querySelector(".chat-header").innerText = title;
1379
1381
1380
1382
if (chatPrompt) {
1381
1383
chatPrompt.value = conversation.system || "";
@@ -1521,19 +1523,19 @@ const load_conversation = async (conversation_id, scroll=true) => {
1521
1523
}
1522
1524
}
1523
1525
1524
message_box.innerHTML = elements.join("");
1526
chatBody.innerHTML = elements.join("");
1525
1527
[...new Set(providers)].forEach(async (provider) => {
1526
1528
await load_provider_parameters(provider);
1527
1529
});
1528
1530
await register_message_buttons();
1529
highlight(message_box);
1531
highlight(chatBody);
1530
1532
regenerate_button.classList.remove("regenerate-hidden");
1531
1533
1532
1534
if (scroll && document.querySelector("#input-count input").checked) {
1533
message_box.scrollTo({ top: message_box.scrollHeight, behavior: "smooth" });
1535
chatBody.scrollTo({ top: chatBody.scrollHeight, behavior: "smooth" });
1534
1536
1535
1537
setTimeout(() => {
1536
message_box.scrollTop = message_box.scrollHeight;
1538
chatBody.scrollTop = chatBody.scrollHeight;
1537
1539
}, 500);
1538
1540
return true;
1539
1541
}
@@ -1726,15 +1728,15 @@ const load_conversations = async () => {
1726
1728
box_conversations.innerHTML += html.join("");
1727
1729
};
1728
1730
1729
const hide_input = document.querySelector(".toolbar .hide-input");
1731
const hide_input = document.querySelector(".chat-toolbar .hide-input");
1730
1732
hide_input.addEventListener("click", async (e) => {
1731
1733
const icon = hide_input.querySelector("i");
1732
1734
const func = icon.classList.contains("fa-angles-down") ? "add" : "remove";
1733
1735
const remv = icon.classList.contains("fa-angles-down") ? "remove" : "add";
1734
1736
icon.classList[func]("fa-angles-up");
1735
1737
icon.classList[remv]("fa-angles-down");
1736
document.querySelector(".conversation .user-input").classList[func]("hidden");
1737
document.querySelector(".conversation .buttons").classList[func]("hidden");
1738
document.querySelector(".chat-footer .user-input").classList[func]("hidden");
1739
document.querySelector(".chat-footer .buttons").classList[func]("hidden");
1738
1740
});
1739
1741
1740
1742
const uuid = () => {
@@ -1757,8 +1759,10 @@ function get_message_id() {
1757
1759
return BigInt(`0b${unix}${random_bytes}`).toString();
1758
1760
};
1759
1761
1760
async function hide_sidebar() {
1761
sidebar.classList.remove("shown");
1762
async function hide_sidebar(remove_shown=false) {
1763
if (remove_shown) {
1764
sidebar.classList.remove("shown");
1765
}
1762
1766
sidebar_button.classList.remove("rotated");
1763
1767
settings.classList.add("hidden");
1764
1768
chat.classList.remove("hidden");
@@ -1778,9 +1782,11 @@ async function hide_settings() {
1778
1782
window.addEventListener('popstate', hide_sidebar, false);
1779
1783
1780
1784
sidebar_button.addEventListener("click", async () => {
1781
if (sidebar.classList.contains("shown")) {
1785
if (sidebar.classList.contains("shown") || sidebar_button.classList.contains("rotated")) {
1782
1786
await hide_sidebar();
1783
1787
chat.classList.remove("hidden");
1788
sidebar.classList.remove("shown");
1789
sidebar_button.classList.remove("rotated");
1784
1790
} else {
1785
1791
await show_menu();
1786
1792
chat.classList.add("hidden");
@@ -1893,7 +1899,7 @@ const load_settings_storage = async () => {
1893
1899
const say_hello = async () => {
1894
1900
tokens = [`Hello`, `!`, ` How`,` can`, ` I`,` assist`,` you`,` today`,`?`]
1895
1901
1896
message_box.innerHTML += `
1902
chatBody.innerHTML += `
1897
1903
<div class="message">
1898
1904
<div class="assistant">
1899
1905
${gpt_image}
@@ -1913,6 +1919,9 @@ const say_hello = async () => {
1913
1919
}
1914
1920
1915
1921
function count_tokens(model, text, prompt_tokens = 0) {
1922
if (!text) {
1923
return 0;
1924
}
1916
1925
if (model) {
1917
1926
if (window.llamaTokenizer)
1918
1927
if (model.startsWith("llama") || model.startsWith("codellama")) {
@@ -1988,7 +1997,7 @@ function update_message(content_map, message_id, content = null, scroll = true)
1988
1997
}, 100));
1989
1998
};
1990
1999
1991
let countFocus = messageInput;
2000
let countFocus = userInput;
1992
2001
const count_input = async () => {
1993
2002
if (countTokensEnabled && countFocus.value) {
1994
2003
if (window.matchMedia("(pointer:coarse)")) {
@@ -2000,14 +2009,14 @@ const count_input = async () => {
2000
2009
inputCount.innerText = "";
2001
2010
}
2002
2011
};
2003
messageInput.addEventListener("keyup", count_input);
2012
userInput.addEventListener("keyup", count_input);
2004
2013
chatPrompt.addEventListener("keyup", count_input);
2005
2014
chatPrompt.addEventListener("focus", function() {
2006
2015
countFocus = chatPrompt;
2007
2016
count_input();
2008
2017
});
2009
2018
chatPrompt.addEventListener("input", function() {
2010
countFocus = messageInput;
2019
countFocus = userInput;
2011
2020
count_input();
2012
2021
});
2013
2022
@@ -2037,9 +2046,9 @@ async function on_load() {
2037
2046
let chat_url = new URL(window.location.href)
2038
2047
let chat_params = new URLSearchParams(chat_url.search);
2039
2048
if (chat_params.get("prompt")) {
2040
messageInput.value = chat_params.get("prompt");
2041
messageInput.style.height = messageInput.scrollHeight + "px";
2042
messageInput.focus();
2049
userInput.value = chat_params.get("prompt");
2050
userInput.style.height = userInput.scrollHeight + "px";
2051
userInput.focus();
2043
2052
//await handle_ask();
2044
2053
}
2045
2054
} else if (/\/chat\/[?$]/.test(window.location.href)) {
@@ -2089,10 +2098,10 @@ const load_provider_option = (input, provider_name) => {
2089
2098
async function on_api() {
2090
2099
load_version();
2091
2100
let prompt_lock = false;
2092
messageInput.addEventListener("keydown", async (evt) => {
2101
userInput.addEventListener("keydown", async (evt) => {
2093
2102
if (prompt_lock) return;
2094
2103
// If not mobile and not shift enter
2095
let do_enter = messageInput.value.endsWith("\n\n\n\n");
2104
let do_enter = userInput.value.endsWith("\n\n\n\n");
2096
2105
if (do_enter || !window.matchMedia("(pointer:coarse)").matches && evt.keyCode === 13 && !evt.shiftKey) {
2097
2106
evt.preventDefault();
2098
2107
console.log("pressed enter");
@@ -2100,10 +2109,10 @@ async function on_api() {
2100
2109
setTimeout(()=>prompt_lock=false, 3000);
2101
2110
await handle_ask(!do_enter);
2102
2111
} else {
2103
messageInput.style.height = messageInput.scrollHeight + "px";
2112
userInput.style.height = userInput.scrollHeight + "px";
2104
2113
}
2105
2114
});
2106
sendButton.querySelector(".fa-paper-plane").addEventListener(`click`, async () => {
2115
sendButton.addEventListener(`click`, async () => {
2107
2116
console.log("clicked send");
2108
2117
if (prompt_lock) return;
2109
2118
prompt_lock = true;
@@ -2111,11 +2120,11 @@ async function on_api() {
2111
2120
stop_recognition();
2112
2121
await handle_ask();
2113
2122
});
2114
sendButton.querySelector(".fa-square-plus").addEventListener(`click`, async () => {
2123
addButton.addEventListener(`click`, async () => {
2115
2124
stop_recognition();
2116
2125
await handle_ask(false);
2117
2126
});
2118
messageInput.addEventListener(`click`, async () => {
2127
userInput.addEventListener(`click`, async () => {
2119
2128
stop_recognition();
2120
2129
});
2121
2130
@@ -2145,6 +2154,7 @@ async function on_api() {
2145
2154
<option value="G4F">G4F framework</option>
2146
2155
<option value="Gemini">Gemini Provider</option>
2147
2156
<option value="HuggingFace">HuggingFace</option>
2157
<option value="HuggingFaceMedia">HuggingFace (Image/Video Generation)</option>
2148
2158
<option value="HuggingSpace">HuggingSpace</option>
2149
2159
<option value="HuggingChat">HuggingChat</option>`;
2150
2160
document.getElementById("pin").disabled = true;
@@ -2158,8 +2168,8 @@ async function on_api() {
2158
2168
}
2159
2169
});
2160
2170
login_urls = {
2161
"HuggingFace": ["HuggingFace", "https://huggingface.co/settings/tokens", []],
2162
"HuggingSpace": ["HuggingSpace", "https://huggingface.co/spaces/roxky/g4f-new?get_gpu_token=true", []],
2171
"HuggingFace": ["HuggingFace", "https://huggingface.co/settings/tokens", ["HuggingFaceMedia"]],
2172
"HuggingSpace": ["HuggingSpace", "", []],
2163
2173
};
2164
2174
} else {
2165
2175
providers = await api("providers")
@@ -2299,13 +2309,13 @@ async function on_api() {
2299
2309
slide_systemPrompt_icon.classList[checked ? "remove": "add"]("fa-angles-up");
2300
2310
slide_systemPrompt_icon.classList[checked ? "add": "remove"]("fa-angles-down");
2301
2311
});
2302
const messageInputHeight = document.getElementById("message-input-height");
2303
if (messageInputHeight) {
2304
if (messageInputHeight.value) {
2305
messageInput.style.maxHeight = `${messageInputHeight.value}px`;
2312
const userInputHeight = document.getElementById("message-input-height");
2313
if (userInputHeight) {
2314
if (userInputHeight.value) {
2315
userInput.style.maxHeight = `${userInputHeight.value}px`;
2306
2316
}
2307
messageInputHeight.addEventListener('change', async () => {
2308
messageInput.style.maxHeight = `${messageInputHeight.value}px`;
2317
userInputHeight.addEventListener('change', async () => {
2318
userInput.style.maxHeight = `${userInputHeight.value}px`;
2309
2319
});
2310
2320
}
2311
2321
const darkMode = document.getElementById("darkMode");
@@ -2333,7 +2343,7 @@ async function load_version() {
2333
2343
document.title = window.title;
2334
2344
}
2335
2345
let text = "version ~ "
2336
if (versions["version"] != versions["latest_version"]) {
2346
if (versions["latest_version"] && versions["version"] != versions["latest_version"]) {
2337
2347
let release_url = 'https://github.com/xtekky/gpt4free/releases/latest';
2338
2348
let title = `New version: ${versions["latest_version"]}`;
2339
2349
text += `<a href="${release_url}" target="_blank" title="${title}">${versions["version"]}</a> 🆕`;
@@ -2468,11 +2478,11 @@ function connectToSSE(url, do_refine, bucket_id) {
2468
2478
}
2469
2479
appStorage.setItem(`bucket:${bucket_id}`, data.size);
2470
2480
inputCount.innerText = "Files are loaded successfully";
2471
if (!messageInput.value) {
2472
messageInput.value = JSON.stringify({bucket_id: bucket_id});
2481
if (!userInput.value) {
2482
userInput.value = JSON.stringify({bucket_id: bucket_id});
2473
2483
handle_ask(false);
2474
2484
} else {
2475
messageInput.value += (messageInput.value ? "\n" : "") + JSON.stringify({bucket_id: bucket_id}) + "\n";
2485
userInput.value += (userInput.value ? "\n" : "") + JSON.stringify({bucket_id: bucket_id}) + "\n";
2476
2486
paperclip.classList.remove("blink");
2477
2487
fileInput.value = "";
2478
2488
}
@@ -2773,15 +2783,15 @@ async function load_provider_models(provider=null) {
2773
2783
};
2774
2784
providerSelect.addEventListener("change", () => {
2775
2785
load_provider_models()
2776
messageInput.focus();
2786
userInput.focus();
2777
2787
});
2778
modelSelect.addEventListener("change", () => messageInput.focus());
2779
modelProvider.addEventListener("change", () => messageInput.focus());
2788
modelSelect.addEventListener("change", () => userInput.focus());
2789
modelProvider.addEventListener("change", () => userInput.focus());
2780
2790
custom_model.addEventListener("change", () => {
2781
2791
if (!custom_model.value) {
2782
2792
load_provider_models();
2783
2793
}
2784
messageInput.focus();
2794
userInput.focus();
2785
2795
});
2786
2796
2787
2797
document.getElementById("pin").addEventListener("click", async () => {
@@ -2817,7 +2827,7 @@ switchInput.addEventListener("change", () => {
2817
2827
});
2818
2828
searchButton.addEventListener("click", async () => {
2819
2829
switchInput.click();
2820
messageInput.focus();
2830
userInput.focus();
2821
2831
});
2822
2832
2823
2833
function save_storage(settings=false) {
@@ -2910,20 +2920,20 @@ if (SpeechRecognition) {
2910
2920
let buffer;
2911
2921
let lastDebounceTranscript;
2912
2922
recognition.onstart = function() {
2913
startValue = messageInput.value;
2923
startValue = userInput.value;
2914
2924
lastDebounceTranscript = "";
2915
messageInput.readOnly = true;
2925
userInput.readOnly = true;
2916
2926
buffer = "";
2917
2927
};
2918
2928
recognition.onend = function() {
2919
2929
if (buffer) {
2920
messageInput.value = `${startValue ? startValue + "\n" : ""}${buffer}`;
2930
userInput.value = `${startValue ? startValue + "\n" : ""}${buffer}`;
2921
2931
}
2922
2932
if (microLabel.classList.contains("recognition")) {
2923
2933
recognition.start();
2924
2934
} else {
2925
messageInput.readOnly = false;
2926
messageInput.focus();
2935
userInput.readOnly = false;
2936
userInput.focus();
2927
2937
}
2928
2938
};
2929
2939
recognition.onresult = function(event) {
@@ -2951,7 +2961,7 @@ if (SpeechRecognition) {
2951
2961
if (microLabel.classList.contains("recognition")) {
2952
2962
microLabel.classList.remove("recognition");
2953
2963
recognition.stop();
2954
messageInput.value = `${startValue ? startValue + "\n" : ""}${buffer}`;
2964
userInput.value = `${startValue ? startValue + "\n" : ""}${buffer}`;
2955
2965
count_input();
2956
2966
return true;
2957
2967
}
@@ -13,10 +13,18 @@ try:
13
13
except ImportError:
14
14
has_requirements = False
15
15
16
from ..providers.helper import filter_none
16
17
from ..typing import ImageType, Union, Image
17
18
from ..errors import MissingRequirementsError
18
19
19
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif', 'webp', 'webm', 'svg', 'mp3', 'wav', 'mp4', 'flac', 'opus', 'ogg', 'mkv'}
20
ALLOWED_EXTENSIONS = {
21
# Image
22
'png', 'jpg', 'jpeg', 'gif', 'webp',
23
# Audio
24
'wav', 'mp3', 'flac', 'opus', 'ogg',
25
# Video
26
'mkv', 'webm', 'mp4'
27
}
20
28
21
29
EXTENSIONS_MAP: dict[str, str] = {
22
30
"image/png": "png",
@@ -260,6 +268,7 @@ def to_input_audio(audio: ImageType, filename: str = None) -> str:
260
268
raise ValueError("Invalid input audio")
261
269
262
270
def use_aspect_ratio(extra_data: dict, aspect_ratio: str) -> Image:
271
extra_data = filter_none(**extra_data)
263
272
if aspect_ratio == "1:1":
264
273
extra_data = {
265
274
"width": 1024,
@@ -39,6 +39,8 @@ def get_source_url(image: str, default: str = None) -> str:
39
39
return default
40
40
41
41
def secure_filename(filename: str) -> str:
42
if filename is None:
43
return None
42
44
# Keep letters, numbers, basic punctuation and all Unicode chars
43
45
filename = re.sub(
44
46
r'[^\w.,_-]+',