返回提交历史
Modified
README.md
+25
-5
Modified
g4f/Provider/BingCreateImages.py
+7
-33
Modified
g4f/Provider/Blackbox.py
+9
-1
Modified
g4f/Provider/bing/create_images.py
+3
-27
Modified
g4f/Provider/needs_auth/OpenaiChat.py
+21
-56
Modified
g4f/Provider/openai/har_file.py
+2
-4
Modified
g4f/Provider/you/har_file.py
+6
-12
Modified
g4f/cookies.py
+21
-16
Modified
g4f/gui/client/index.html
+14
-11
Modified
g4f/gui/client/static/css/style.css
+12
-1
Modified
g4f/gui/client/static/js/chat.v1.js
+17
-6
Modified
g4f/image.py
+2
-2
XFEstudio/gpt4free
Add get/set cookies dir, hide prompt option in gui
9ddac171
代码差异
12 个文件
+139
-174
@@ -236,19 +236,39 @@ set_cookies(".google.com", {
236
236
})
237
237
```
238
238
239
Alternatively, you can place your .har and cookie files in the `/har_and_cookies` directory. To export a cookie file, use the EditThisCookie extension available on the Chrome Web Store: [EditThisCookie Extension](https://chromewebstore.google.com/detail/editthiscookie/fngmhnnpilhplaeedifhccceomclgfbg).
239
#### Using .har and Cookie Files
240
240
241
You can also create .har files to capture cookies. If you need further assistance, refer to the next section.
241
You can place `.har` and cookie files in the default `./har_and_cookies` directory. To export a cookie file, use the [EditThisCookie Extension](https://chromewebstore.google.com/detail/editthiscookie/fngmhnnpilhplaeedifhccceomclgfbg) available on the Chrome Web Store.
242
242
243
```bash
244
python -m g4f.cli api --debug
243
#### Creating .har Files to Capture Cookies
244
245
To capture cookies, you can also create `.har` files. For more details, refer to the next section.
246
247
#### Changing the Cookies Directory and Loading Cookie Files in Python
248
249
You can change the cookies directory and load cookie files in your Python environment. To set the cookies directory relative to your Python file, use the following code:
250
251
```python
252
import os.path
253
from g4f.cookies import set_cookies_dir, read_cookie_files
254
255
import g4f.debug
256
g4f.debug.logging = True
257
258
cookies_dir = os.path.join(os.path.dirname(__file__), "har_and_cookies")
259
set_cookies_dir(cookies_dir)
260
read_cookie_files(cookies_dir)
245
261
```
262
263
### Debug Mode
264
265
If you enable debug mode, you will see logs similar to the following:
266
246
267
```
247
268
Read .har file: ./har_and_cookies/you.com.har
248
269
Cookies added: 10 from .you.com
249
270
Read cookie file: ./har_and_cookies/google.json
250
271
Cookies added: 16 from .google.com
251
Starting server... [g4f v-0.0.0] (debug)
252
272
```
253
273
254
274
#### .HAR File for OpenaiChat Provider
@@ -1,18 +1,14 @@
1
1
from __future__ import annotations
2
2
3
import asyncio
4
import os
5
from typing import Iterator, Union
6
7
3
from ..cookies import get_cookies
8
4
from ..image import ImageResponse
9
from ..errors import MissingRequirementsError, MissingAuthError
5
from ..errors import MissingAuthError
10
6
from ..typing import AsyncResult, Messages, Cookies
11
7
from .base_provider import AsyncGeneratorProvider, ProviderModelMixin
12
from .bing.create_images import create_images, create_session, get_cookies_from_browser
8
from .bing.create_images import create_images, create_session
13
9
14
10
class BingCreateImages(AsyncGeneratorProvider, ProviderModelMixin):
15
label = "Microsoft Designer"
11
label = "Microsoft Designer in Bing"
16
12
parent = "Bing"
17
13
url = "https://www.bing.com/images/create"
18
14
working = True
@@ -38,30 +34,9 @@ class BingCreateImages(AsyncGeneratorProvider, ProviderModelMixin):
38
34
**kwargs
39
35
) -> AsyncResult:
40
36
session = BingCreateImages(cookies, proxy, api_key)
41
yield await session.create_async(messages[-1]["content"])
42
43
def create(self, prompt: str) -> Iterator[Union[ImageResponse, str]]:
44
"""
45
Generator for creating imagecompletion based on a prompt.
46
47
Args:
48
prompt (str): Prompt to generate images.
49
50
Yields:
51
Generator[str, None, None]: The final output as markdown formatted string with images.
52
"""
53
cookies = self.cookies or get_cookies(".bing.com", False)
54
if cookies is None or "_U" not in cookies:
55
login_url = os.environ.get("G4F_LOGIN_URL")
56
if login_url:
57
yield f"Please login: [Bing]({login_url})\n\n"
58
try:
59
self.cookies = get_cookies_from_browser(self.proxy)
60
except MissingRequirementsError as e:
61
raise MissingAuthError(f'Missing "_U" cookie. {e}')
62
yield asyncio.run(self.create_async(prompt))
37
yield await session.generate(messages[-1]["content"])
63
38
64
async def create_async(self, prompt: str) -> ImageResponse:
39
async def generate(self, prompt: str) -> ImageResponse:
65
40
"""
66
41
Asynchronously creates a markdown formatted string with images based on the prompt.
67
42
@@ -74,7 +49,6 @@ class BingCreateImages(AsyncGeneratorProvider, ProviderModelMixin):
74
49
cookies = self.cookies or get_cookies(".bing.com", False)
75
50
if cookies is None or "_U" not in cookies:
76
51
raise MissingAuthError('Missing "_U" cookie')
77
proxy = self.proxy or os.environ.get("G4F_PROXY")
78
async with create_session(cookies, proxy) as session:
79
images = await create_images(session, prompt, proxy)
52
async with create_session(cookies, self.proxy) as session:
53
images = await create_images(session, prompt)
80
54
return ImageResponse(images, prompt, {"preview": "{image}?w=200&h=200"} if len(images) > 1 else {})
@@ -4,7 +4,8 @@ import uuid
4
4
import secrets
5
5
from aiohttp import ClientSession
6
6
7
from ..typing import AsyncResult, Messages
7
from ..typing import AsyncResult, Messages, ImageType
8
from ..image import to_data_uri
8
9
from .base_provider import AsyncGeneratorProvider
9
10
10
11
class Blackbox(AsyncGeneratorProvider):
@@ -17,8 +18,15 @@ class Blackbox(AsyncGeneratorProvider):
17
18
model: str,
18
19
messages: Messages,
19
20
proxy: str = None,
21
image: ImageType = None,
22
image_name: str = None,
20
23
**kwargs
21
24
) -> AsyncResult:
25
if image is not None:
26
messages[-1]["data"] = {
27
"fileText": image_name,
28
"imageBase64": to_data_uri(image)
29
}
22
30
headers = {
23
31
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36",
24
32
"Accept": "*/*",
@@ -1,7 +1,3 @@
1
"""
2
This module provides functionalities for creating and managing images using Bing's service.
3
It includes functions for user login, session creation, image creation, and processing.
4
"""
5
1
from __future__ import annotations
6
2
7
3
import asyncio
@@ -17,9 +13,7 @@ try:
17
13
except ImportError:
18
14
has_requirements = False
19
15
20
from ...providers.create_images import CreateImagesProvider
21
16
from ..helper import get_connector
22
from ...providers.types import ProviderType
23
17
from ...errors import MissingRequirementsError, RateLimitError
24
18
from ...webdriver import WebDriver, get_driver_cookies, get_browser
25
19
@@ -101,7 +95,7 @@ def create_session(cookies: Dict[str, str], proxy: str = None, connector: BaseCo
101
95
headers["Cookie"] = "; ".join(f"{k}={v}" for k, v in cookies.items())
102
96
return ClientSession(headers=headers, connector=get_connector(connector, proxy))
103
97
104
async def create_images(session: ClientSession, prompt: str, proxy: str = None, timeout: int = TIMEOUT_IMAGE_CREATION) -> List[str]:
98
async def create_images(session: ClientSession, prompt: str, timeout: int = TIMEOUT_IMAGE_CREATION) -> List[str]:
105
99
"""
106
100
Creates images based on a given prompt using Bing's service.
107
101
@@ -132,7 +126,7 @@ async def create_images(session: ClientSession, prompt: str, proxy: str = None,
132
126
raise RuntimeError(f"Create images failed: {error}")
133
127
if response.status != 302:
134
128
url = f"{BING_URL}/images/create?q={url_encoded_prompt}&rt=3&FORM=GENCRE"
135
async with session.post(url, allow_redirects=False, proxy=proxy, timeout=timeout) as response:
129
async with session.post(url, allow_redirects=False, timeout=timeout) as response:
136
130
if response.status != 302:
137
131
raise RuntimeError(f"Create images failed. Code: {response.status}")
138
132
@@ -185,22 +179,4 @@ def read_images(html_content: str) -> List[str]:
185
179
raise RuntimeError("Bad images found")
186
180
if not images:
187
181
raise RuntimeError("No images found")
188
return images
189
190
def patch_provider(provider: ProviderType) -> CreateImagesProvider:
191
"""
192
Patches a provider to include image creation capabilities.
193
194
Args:
195
provider (ProviderType): The provider to be patched.
196
197
Returns:
198
CreateImagesProvider: The patched provider with image creation capabilities.
199
"""
200
from ..BingCreateImages import BingCreateImages
201
service = BingCreateImages()
202
return CreateImagesProvider(
203
provider,
204
service.create,
205
service.create_async
206
)
182
return images
@@ -26,7 +26,7 @@ from ...webdriver import get_browser
26
26
from ...typing import AsyncResult, Messages, Cookies, ImageType, AsyncIterator
27
27
from ...requests import get_args_from_browser, raise_for_status
28
28
from ...requests.aiohttp import StreamSession
29
from ...image import to_image, to_bytes, ImageResponse, ImageRequest
29
from ...image import ImageResponse, ImageRequest, to_image, to_bytes, is_accepted_format
30
30
from ...errors import MissingAuthError, ResponseError
31
31
from ...providers.conversation import BaseConversation
32
32
from ..helper import format_cookies
@@ -138,23 +138,22 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
138
138
An ImageRequest object that contains the download URL, file name, and other data
139
139
"""
140
140
# Convert the image to a PIL Image object and get the extension
141
image = to_image(image)
142
extension = image.format.lower()
143
# Convert the image to a bytes object and get the size
144
141
data_bytes = to_bytes(image)
142
image = to_image(data_bytes)
143
extension = image.format.lower()
145
144
data = {
146
"file_name": image_name if image_name else f"{image.width}x{image.height}.{extension}",
145
"file_name": "" if image_name is None else image_name,
147
146
"file_size": len(data_bytes),
148
147
"use_case": "multimodal"
149
148
}
150
149
# Post the image data to the service and get the image data
151
150
async with session.post(f"{cls.url}/backend-api/files", json=data, headers=headers) as response:
152
cls._update_request_args()
151
cls._update_request_args(session)
153
152
await raise_for_status(response)
154
153
image_data = {
155
154
**data,
156
155
**await response.json(),
157
"mime_type": f"image/{extension}",
156
"mime_type": is_accepted_format(data_bytes),
158
157
"extension": extension,
159
158
"height": image.height,
160
159
"width": image.width
@@ -275,7 +274,7 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
275
274
first_part = line["message"]["content"]["parts"][0]
276
275
if "asset_pointer" not in first_part or "metadata" not in first_part:
277
276
return
278
if first_part["metadata"] is None:
277
if first_part["metadata"] is None or first_part["metadata"]["dalle"] is None:
279
278
return
280
279
prompt = first_part["metadata"]["dalle"]["prompt"]
281
280
file_id = first_part["asset_pointer"].split("file-service://", 1)[1]
@@ -365,49 +364,17 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
365
364
) as session:
366
365
if cls._expires is not None and cls._expires < time.time():
367
366
cls._headers = cls._api_key = None
368
if cls._headers is None or cookies is not None:
369
cls._create_request_args(cookies)
370
api_key = kwargs["access_token"] if "access_token" in kwargs else api_key
371
if api_key is not None:
372
cls._set_api_key(api_key)
373
374
if cls.default_model is None and (not cls.needs_auth or cls._api_key is not None):
375
if cls._api_key is None:
376
cls._create_request_args(cookies)
377
async with session.get(
378
f"{cls.url}/",
379
headers=DEFAULT_HEADERS
380
) as response:
381
cls._update_request_args(session)
382
await raise_for_status(response)
383
try:
384
if not model:
385
cls.default_model = cls.get_model(await cls.get_default_model(session, cls._headers))
386
else:
387
cls.default_model = cls.get_model(model)
388
except MissingAuthError:
389
pass
390
except Exception as e:
391
api_key = cls._api_key = None
392
cls._create_request_args()
393
if debug.logging:
394
print("OpenaiChat: Load default model failed")
395
print(f"{e.__class__.__name__}: {e}")
396
397
367
arkose_token = None
398
368
proofTokens = None
399
if cls.default_model is None:
400
error = None
401
try:
402
arkose_token, api_key, cookies, headers, proofTokens = await getArkoseAndAccessToken(proxy)
403
cls._create_request_args(cookies, headers)
404
cls._set_api_key(api_key)
405
except NoValidHarFileError as e:
406
error = e
407
if cls._api_key is None:
408
await cls.nodriver_access_token(proxy)
369
try:
370
arkose_token, api_key, cookies, headers, proofTokens = await getArkoseAndAccessToken(proxy)
371
cls._create_request_args(cookies, headers)
372
cls._set_api_key(api_key)
373
except NoValidHarFileError as e:
409
374
if cls._api_key is None and cls.needs_auth:
410
raise error
375
raise e
376
377
if cls.default_model is None:
411
378
cls.default_model = cls.get_model(await cls.get_default_model(session, cls._headers))
412
379
413
380
try:
@@ -461,7 +428,7 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
461
428
)
462
429
ws = None
463
430
if need_arkose:
464
async with session.post("https://chatgpt.com/backend-api/register-websocket", headers=cls._headers) as response:
431
async with session.post(f"{cls.url}/backend-api/register-websocket", headers=cls._headers) as response:
465
432
wss_url = (await response.json()).get("wss_url")
466
433
if wss_url:
467
434
ws = await session.ws_connect(wss_url)
@@ -490,7 +457,8 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
490
457
if proofofwork is not None:
491
458
headers["Openai-Sentinel-Proof-Token"] = proofofwork
492
459
async with session.post(
493
f"{cls.url}/backend-anon/conversation" if cls._api_key is None else
460
f"{cls.url}/backend-anon/conversation"
461
if cls._api_key is None else
494
462
f"{cls.url}/backend-api/conversation",
495
463
json=data,
496
464
headers=headers
@@ -580,12 +548,9 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
580
548
raise RuntimeError(line["error"])
581
549
if "message_type" not in line["message"]["metadata"]:
582
550
return
583
try:
584
image_response = await cls.get_generated_image(session, cls._headers, line)
585
if image_response is not None:
586
yield image_response
587
except Exception as e:
588
yield e
551
image_response = await cls.get_generated_image(session, cls._headers, line)
552
if image_response is not None:
553
yield image_response
589
554
if line["message"]["author"]["role"] != "assistant":
590
555
return
591
556
if line["message"]["content"]["content_type"] != "text":
@@ -12,6 +12,7 @@ from copy import deepcopy
12
12
13
13
from .crypt import decrypt, encrypt
14
14
from ...requests import StreamSession
15
from ...cookies import get_cookies_dir
15
16
from ... import debug
16
17
17
18
class NoValidHarFileError(Exception):
@@ -36,17 +37,14 @@ proofTokens: list = []
36
37
37
38
def readHAR():
38
39
global proofTokens
39
dirPath = "./"
40
40
harPath = []
41
41
chatArks = []
42
42
accessToken = None
43
43
cookies = {}
44
for root, dirs, files in os.walk(dirPath):
44
for root, dirs, files in os.walk(get_cookies_dir()):
45
45
for file in files:
46
46
if file.endswith(".har"):
47
47
harPath.append(os.path.join(root, file))
48
if harPath:
49
break
50
48
if not harPath:
51
49
raise NoValidHarFileError("No .har file found")
52
50
for path in harPath:
@@ -7,6 +7,7 @@ import random
7
7
import logging
8
8
9
9
from ...requests import StreamSession, raise_for_status
10
from ...cookies import get_cookies_dir
10
11
from ...errors import MissingRequirementsError
11
12
from ... import debug
12
13
@@ -28,10 +29,9 @@ public_token = "public-token-live-507a52ad-7e69-496b-aee0-1c9863c7c819"
28
29
chatArks: list = None
29
30
30
31
def readHAR():
31
dirPath = "./"
32
32
harPath = []
33
33
chatArks = []
34
for root, dirs, files in os.walk(dirPath):
34
for root, dirs, files in os.walk(get_cookies_dir()):
35
35
for file in files:
36
36
if file.endswith(".har"):
37
37
harPath.append(os.path.join(root, file))
@@ -65,16 +65,10 @@ def parseHAREntry(entry) -> arkReq:
65
65
return tmpArk
66
66
67
67
async def sendRequest(tmpArk: arkReq, proxy: str = None):
68
try:
69
async with StreamSession(headers=tmpArk.arkHeaders, cookies=tmpArk.arkCookies, proxy=proxy) as session:
70
async with session.post(tmpArk.arkURL, data=tmpArk.arkBody) as response:
71
await raise_for_status(response)
72
return await response.text()
73
except RuntimeError as e:
74
if str(e) == "Event loop is closed":
75
print("Event loop is closed error occurred in sendRequest.")
76
else:
77
raise
68
async with StreamSession(headers=tmpArk.arkHeaders, cookies=tmpArk.arkCookies, proxy=proxy) as session:
69
async with session.post(tmpArk.arkURL, data=tmpArk.arkBody) as response:
70
await raise_for_status(response)
71
return await response.text()
78
72
79
73
async def create_telemetry_id(proxy: str = None):
80
74
global chatArks
@@ -23,8 +23,9 @@ from .typing import Dict, Cookies
23
23
from .errors import MissingRequirementsError
24
24
from . import debug
25
25
26
# Global variable to store cookies
27
_cookies: Dict[str, Cookies] = {}
26
class CookiesConfig():
27
cookies: Dict[str, Cookies] = {}
28
cookies_dir: str = "./har_and_cookies"
28
29
29
30
DOMAINS = [
30
31
".bing.com",
@@ -48,20 +49,18 @@ def get_cookies(domain_name: str = '', raise_requirements_error: bool = True, si
48
49
Returns:
49
50
Dict[str, str]: A dictionary of cookie names and values.
50
51
"""
51
global _cookies
52
if domain_name in _cookies:
53
return _cookies[domain_name]
52
if domain_name in CookiesConfig.cookies:
53
return CookiesConfig.cookies[domain_name]
54
54
55
55
cookies = load_cookies_from_browsers(domain_name, raise_requirements_error, single_browser)
56
_cookies[domain_name] = cookies
56
CookiesConfig.cookies[domain_name] = cookies
57
57
return cookies
58
58
59
59
def set_cookies(domain_name: str, cookies: Cookies = None) -> None:
60
global _cookies
61
60
if cookies:
62
_cookies[domain_name] = cookies
63
elif domain_name in _cookies:
64
_cookies.pop(domain_name)
61
CookiesConfig.cookies[domain_name] = cookies
62
elif domain_name in CookiesConfig.cookies:
63
CookiesConfig.cookies.pop(domain_name)
65
64
66
65
def load_cookies_from_browsers(domain_name: str, raise_requirements_error: bool = True, single_browser: bool = False) -> Cookies:
67
66
"""
@@ -96,7 +95,13 @@ def load_cookies_from_browsers(domain_name: str, raise_requirements_error: bool
96
95
print(f"Error reading cookies from {cookie_fn.__name__} for {domain_name}: {e}")
97
96
return cookies
98
97
99
def read_cookie_files(dirPath: str = "./har_and_cookies"):
98
def set_cookies_dir(dir: str) -> None:
99
CookiesConfig.cookies_dir = dir
100
101
def get_cookies_dir() -> str:
102
return CookiesConfig.cookies_dir
103
104
def read_cookie_files(dirPath: str = None):
100
105
def get_domain(v: dict) -> str:
101
106
host = [h["value"] for h in v['request']['headers'] if h["name"].lower() in ("host", ":authority")]
102
107
if not host:
@@ -106,16 +111,16 @@ def read_cookie_files(dirPath: str = "./har_and_cookies"):
106
111
if d in host:
107
112
return d
108
113
109
global _cookies
110
114
harFiles = []
111
115
cookieFiles = []
112
for root, dirs, files in os.walk(dirPath):
116
for root, dirs, files in os.walk(CookiesConfig.cookies_dir if dirPath is None else dirPath):
113
117
for file in files:
114
118
if file.endswith(".har"):
115
119
harFiles.append(os.path.join(root, file))
116
120
elif file.endswith(".json"):
117
121
cookieFiles.append(os.path.join(root, file))
118
_cookies = {}
122
123
CookiesConfig.cookies = {}
119
124
for path in harFiles:
120
125
with open(path, 'rb') as file:
121
126
try:
@@ -134,7 +139,7 @@ def read_cookie_files(dirPath: str = "./har_and_cookies"):
134
139
for c in v['request']['cookies']:
135
140
v_cookies[c['name']] = c['value']
136
141
if len(v_cookies) > 0:
137
_cookies[domain] = v_cookies
142
CookiesConfig.cookies[domain] = v_cookies
138
143
new_cookies[domain] = len(v_cookies)
139
144
if debug.logging:
140
145
for domain, new_values in new_cookies.items():
@@ -159,7 +164,7 @@ def read_cookie_files(dirPath: str = "./har_and_cookies"):
159
164
for domain, new_values in new_cookies.items():
160
165
if debug.logging:
161
166
print(f"Cookies added: {len(new_values)} from {domain}")
162
_cookies[domain] = new_values
167
CookiesConfig.cookies[domain] = new_values
163
168
164
169
def _g4f(domain_name: str) -> list:
165
170
"""
@@ -93,22 +93,22 @@
93
93
<div class="paper">
94
94
<h3>Settings</h3>
95
95
<div class="field">
96
<span class="label">Web Access</span>
96
<span class="label">Web Access with DuckDuckGo</span>
97
97
<input type="checkbox" id="switch" />
98
98
<label for="switch" class="toogle" title="Add the pages of the first 5 search results to the query."></label>
99
99
</div>
100
100
<div class="field">
101
<span class="label">Disable History</span>
101
<span class="label">Disable Conversation History</span>
102
102
<input type="checkbox" id="history" />
103
103
<label for="history" class="toogle" title="To improve the reaction time or if you have trouble with large conversations."></label>
104
104
</div>
105
105
<div class="field">
106
<span class="label">Hide System prompt</span>
106
<span class="label">Hide System-prompt</span>
107
107
<input type="checkbox" id="hide-systemPrompt" />
108
108
<label for="hide-systemPrompt" class="toogle" title="For more space on phones"></label>
109
109
</div>
110
110
<div class="field">
111
<span class="label">Auto continue</span>
111
<span class="label">Auto continue in ChatGPT</span>
112
112
<input id="auto_continue" type="checkbox" name="auto_continue" checked/>
113
113
<label for="auto_continue" class="toogle" title="Continue large responses in OpenaiChat"></label>
114
114
</div>
@@ -121,8 +121,8 @@
121
121
<input type="text" id="recognition-language" value="" placeholder="navigator.language"/>
122
122
</div>
123
123
<div class="field box">
124
<label for="Bing-api_key" class="label" title="">Bing:</label>
125
<textarea id="Bing-api_key" name="Bing[api_key]" class="BingCreateImages-api_key" placeholder=""_U" cookie"></textarea>
124
<label for="BingCreateImages-api_key" class="label" title="">Microsoft Designer in Bing:</label>
125
<textarea id="BingCreateImages-api_key" name="BingCreateImages[api_key]" placeholder=""_U" cookie"></textarea>
126
126
</div>
127
127
<div class="field box">
128
128
<label for="DeepInfra-api_key" class="label" title="">DeepInfra:</label>
@@ -144,14 +144,14 @@
144
144
<label for="Openai-api_key" class="label" title="">OpenAI API:</label>
145
145
<textarea id="Openai-api_key" name="Openai[api_key]" placeholder="api_key"></textarea>
146
146
</div>
147
<div class="field box">
148
<label for="OpenaiAccount-api_key" class="label" title="">OpenAI ChatGPT:</label>
149
<textarea id="OpenaiAccount-api_key" name="OpenaiAccount[api_key]" class="OpenaiChat-api_key" placeholder="access_key"></textarea>
150
</div>
151
147
<div class="field box">
152
148
<label for="OpenRouter-api_key" class="label" title="">OpenRouter:</label>
153
149
<textarea id="OpenRouter-api_key" name="OpenRouter[api_key]" placeholder="api_key"></textarea>
154
150
</div>
151
<div class="field box">
152
<label for="PerplexityApi-api_key" class="label" title="">Perplexity API:</label>
153
<textarea id="PerplexityApi-api_key" name="PerplexityApi[api_key]" placeholder="api_key"></textarea>
154
</div>
155
155
<div class="field box">
156
156
<label for="Replicate-api_key" class="label" title="">Replicate:</label>
157
157
<textarea id="Replicate-api_key" name="Replicate[api_key]" class="ReplicateImage-api_key" placeholder="api_key"></textarea>
@@ -173,7 +173,10 @@
173
173
<div id="messages" class="box"></div>
174
174
<div class="toolbar">
175
175
<div id="input-count" class="">
176
176
<button class="hide-input">
177
<i class="fa-solid fa-angles-down"></i>
178
</button>
179
<span class="text"></span>
177
180
</div>
178
181
<div class="stop_generating stop_generating-hidden">
179
182
<button id="cancelButton">
@@ -457,7 +457,11 @@ body {
457
457
#input-count {
458
458
width: fit-content;
459
459
font-size: 12px;
460
padding: 6px var(--inner-gap);
460
padding: 6px 6px;
461
}
462
463
#input-count .text {
464
padding: 0 4px;
461
465
}
462
466
463
467
.stop_generating, .toolbar .regenerate {
@@ -491,6 +495,13 @@ body {
491
495
animation: show_popup 0.4s;
492
496
}
493
497
498
.toolbar .hide-input {
499
background: transparent;
500
border: none;
501
color: var(--colour-3);
502
cursor: pointer;
503
}
504
494
505
@keyframes show_popup {
495
506
from {
496
507
opacity: 0;
@@ -11,7 +11,7 @@ const imageInput = document.getElementById("image");
11
11
const cameraInput = document.getElementById("camera");
12
12
const fileInput = document.getElementById("file");
13
13
const microLabel = document.querySelector(".micro-label");
14
const inputCount = document.getElementById("input-count");
14
const inputCount = document.getElementById("input-count").querySelector(".text");
15
15
const providerSelect = document.getElementById("provider");
16
16
const modelSelect = document.getElementById("model");
17
17
const modelProvider = document.getElementById("model2");
@@ -41,9 +41,7 @@ appStorage = window.localStorage || {
41
41
length: 0
42
42
}
43
43
44
const markdown = window.markdownit({
45
html: true,
46
});
44
const markdown = window.markdownit();
47
45
const markdown_render = (content) => {
48
46
return markdown.render(content
49
47
.replaceAll(/<!-- generated images start -->|<!-- generated images end -->/gm, "")
@@ -813,6 +811,17 @@ document.getElementById("regenerateButton").addEventListener("click", async () =
813
811
await ask_gpt();
814
812
});
815
813
814
const hide_input = document.querySelector(".toolbar .hide-input");
815
hide_input.addEventListener("click", async (e) => {
816
const icon = hide_input.querySelector("i");
817
const func = icon.classList.contains("fa-angles-down") ? "add" : "remove";
818
const remv = icon.classList.contains("fa-angles-down") ? "remove" : "add";
819
icon.classList[func]("fa-angles-up");
820
icon.classList[remv]("fa-angles-down");
821
document.querySelector(".conversation .user-input").classList[func]("hidden");
822
document.querySelector(".conversation .buttons").classList[func]("hidden");
823
});
824
816
825
const uuid = () => {
817
826
return `xxxxxxxx-xxxx-4xxx-yxxx-${Date.now().toString(16)}`.replace(
818
827
/[xy]/g,
@@ -1016,7 +1025,7 @@ const count_input = async () => {
1016
1025
if (countFocus.value) {
1017
1026
inputCount.innerText = count_words_and_tokens(countFocus.value, get_selected_model());
1018
1027
} else {
1019
inputCount.innerHTML = " "
1028
inputCount.innerText = "";
1020
1029
}
1021
1030
}, 100);
1022
1031
};
@@ -1060,6 +1069,8 @@ async function on_api() {
1060
1069
messageInput.addEventListener("keydown", async (evt) => {
1061
1070
if (prompt_lock) return;
1062
1071
1072
// If not mobile
1073
if (!window.matchMedia("(pointer:coarse)").matches)
1063
1074
if (evt.keyCode === 13 && !evt.shiftKey) {
1064
1075
evt.preventDefault();
1065
1076
console.log("pressed enter");
@@ -1262,6 +1273,7 @@ async function load_provider_models(providerIndex=null) {
1262
1273
if (!providerIndex) {
1263
1274
providerIndex = providerSelect.selectedIndex;
1264
1275
}
1276
modelProvider.innerHTML = '';
1265
1277
const provider = providerSelect.options[providerIndex].value;
1266
1278
if (!provider) {
1267
1279
modelProvider.classList.add("hidden");
@@ -1269,7 +1281,6 @@ async function load_provider_models(providerIndex=null) {
1269
1281
return;
1270
1282
}
1271
1283
const models = await api('models', provider);
1272
modelProvider.innerHTML = '';
1273
1284
if (models.length > 0) {
1274
1285
modelSelect.classList.add("hidden");
1275
1286
modelProvider.classList.remove("hidden");
@@ -210,8 +210,8 @@ def format_images_markdown(images: Union[str, list], alt: str, preview: Union[st
210
210
if not isinstance(preview, list):
211
211
preview = [preview.replace('{image}', image) if preview else image for image in images]
212
212
result = "\n".join(
213
#f"[]({image})"
214
f'[<img src="{preview[idx]}" width="200" alt="#{idx+1} {alt}">]({image})'
213
f"[]({image})"
214
#f'[<img src="{preview[idx]}" width="200" alt="#{idx+1} {alt}">]({image})'
215
215
for idx, image in enumerate(images)
216
216
)
217
217
start_flag = "<!-- generated images start -->\n"