XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet
公开
关注 0 Fork 0 Star 1
返回提交历史

XFEstudio/gpt4free

Fix Bing Provider, Add Account Support for Meta AI Use default headers in OpenaiChat

db2d6cff
Heiner Lohaus <hlohaus@users.noreply.github.com>
提交于

代码差异

14 个文件 +238 -163
Modified etc/unittest/integration.py +1 -1
@@ -10,7 +10,7 @@ except ImportError:
10 10 from g4f.client import Client, ChatCompletion
11 11 from g4f.Provider import Bing, OpenaiChat, DuckDuckGo
12 12
13 DEFAULT_MESSAGES = [{"role": "system", "content": 'Response in json, Example: {"success: true"}'},
13 DEFAULT_MESSAGES = [{"role": "system", "content": 'Response in json, Example: {"success": false}'},
14 14 {"role": "user", "content": "Say success true in json"}]
15 15
16 16 class TestProviderIntegration(unittest.TestCase):
Modified g4f/Provider/Bing.py +68 -55
@@ -7,13 +7,13 @@ import time
7 7 import asyncio
8 8 from urllib import parse
9 9 from datetime import datetime, date
10 from aiohttp import ClientSession, ClientTimeout, BaseConnector, WSMsgType
11 10
12 11 from ..typing import AsyncResult, Messages, ImageType, Cookies
13 12 from ..image import ImageRequest
14 from ..errors import ResponseStatusError, RateLimitError
13 from ..errors import ResponseError, ResponseStatusError, RateLimitError
14 from ..requests import StreamSession, DEFAULT_HEADERS
15 15 from .base_provider import AsyncGeneratorProvider, ProviderModelMixin
16 from .helper import get_connector, get_random_hex
16 from .helper import get_random_hex
17 17 from .bing.upload_image import upload_image
18 18 from .bing.conversation import Conversation, create_conversation, delete_conversation
19 19 from .BingCreateImages import BingCreateImages
@@ -49,7 +49,6 @@ class Bing(AsyncGeneratorProvider, ProviderModelMixin):
49 49 timeout: int = 900,
50 50 api_key: str = None,
51 51 cookies: Cookies = None,
52 connector: BaseConnector = None,
53 52 tone: str = None,
54 53 image: ImageType = None,
55 54 web_search: bool = False,
@@ -79,7 +78,6 @@ class Bing(AsyncGeneratorProvider, ProviderModelMixin):
79 78
80 79 return stream_generate(
81 80 prompt, tone, image, context, cookies, api_key,
82 get_connector(connector, proxy, True),
83 81 proxy, web_search, gpt4_turbo, timeout,
84 82 **kwargs
85 83 )
@@ -102,25 +100,53 @@ def get_ip_address() -> str:
102 100 return f"13.{random.randint(104, 107)}.{random.randint(0, 255)}.{random.randint(0, 255)}"
103 101
104 102 def get_default_cookies():
103 #muid = get_random_hex().upper()
104 sid = get_random_hex().upper()
105 guid = get_random_hex().upper()
106 isodate = date.today().isoformat()
107 timestamp = int(time.time())
108 zdate = "0001-01-01T00:00:00.0000000"
105 109 return {
106 'SRCHD' : 'AF=NOFORM',
107 'PPLState' : '1',
108 'KievRPSSecAuth': '',
109 'SUID' : '',
110 'SRCHUSR' : f'DOB={date.today().strftime("%Y%m%d")}&T={int(time.time())}',
111 'SRCHHPGUSR' : f'HV={int(time.time())}',
112 'BCP' : 'AD=1&AL=1&SM=1',
113 '_Rwho' : f'u=d&ts={date.today().isoformat()}',
110 "_C_Auth": "",
111 #"MUID": muid,
112 #"MUIDB": muid,
113 "_EDGE_S": f"F=1&SID={sid}",
114 "_EDGE_V": "1",
115 "SRCHD": "AF=hpcodx",
116 "SRCHUID": f"V=2&GUID={guid}&dmnchg=1",
117 "_RwBf": (
118 f"r=0&ilt=1&ihpd=0&ispd=0&rc=3&rb=0&gb=0&rg=200&pc=0&mtu=0&rbb=0&g=0&cid="
119 f"&clo=0&v=1&l={isodate}&lft={zdate}&aof=0&ard={zdate}"
120 f"&rwdbt={zdate}&rwflt={zdate}&o=2&p=&c=&t=0&s={zdate}"
121 f"&ts={isodate}&rwred=0&wls=&wlb="
122 "&wle=&ccp=&cpt=&lka=0&lkt=0&aad=0&TH="
123 ),
124 '_Rwho': f'u=d&ts={isodate}',
125 "_SS": f"SID={sid}&R=3&RB=0&GB=0&RG=200&RP=0",
126 "SRCHUSR": f"DOB={date.today().strftime('%Y%m%d')}&T={timestamp}",
127 "SRCHHPGUSR": f"HV={int(time.time())}",
128 "BCP": "AD=1&AL=1&SM=1",
129 "ipv6": f"hit={timestamp}",
130 '_C_ETH' : '1',
114 131 }
115 132
116 def create_headers(cookies: Cookies = None, api_key: str = None) -> dict:
133 async def create_headers(cookies: Cookies = None, api_key: str = None) -> dict:
117 134 if cookies is None:
135 # import nodriver as uc
136 # browser = await uc.start(headless=False)
137 # page = await browser.get(Defaults.home)
138 # await asyncio.sleep(10)
139 # cookies = {}
140 # for c in await page.browser.cookies.get_all():
141 # if c.domain.endswith(".bing.com"):
142 # cookies[c.name] = c.value
143 # user_agent = await page.evaluate("window.navigator.userAgent")
144 # await page.close()
118 145 cookies = get_default_cookies()
119 146 if api_key is not None:
120 147 cookies["_U"] = api_key
121 148 headers = Defaults.headers.copy()
122 149 headers["cookie"] = "; ".join(f"{k}={v}" for k, v in cookies.items())
123 headers["x-forwarded-for"] = get_ip_address()
124 150 return headers
125 151
126 152 class Defaults:
@@ -246,25 +272,13 @@ class Defaults:
246 272 }
247 273
248 274 # Default headers for requests
249 home = 'https://www.bing.com/chat?q=Bing+AI&FORM=hpcodx'
275 home = "https://www.bing.com/chat?q=Microsoft+Copilot&FORM=hpcodx"
250 276 headers = {
251 'sec-ch-ua': '"Chromium";v="122", "Not(A:Brand";v="24", "Google Chrome";v="122"',
252 'sec-ch-ua-mobile': '?0',
253 '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',
254 'sec-ch-ua-arch': '"x86"',
255 'sec-ch-ua-full-version': '"122.0.6261.69"',
256 'accept': 'application/json',
257 'sec-ch-ua-platform-version': '"15.0.0"',
277 **DEFAULT_HEADERS,
278 "accept": "application/json",
279 "referer": home,
258 280 "x-ms-client-request-id": str(uuid.uuid4()),
259 'sec-ch-ua-full-version-list': '"Chromium";v="122.0.6261.69", "Not(A:Brand";v="24.0.0.0", "Google Chrome";v="122.0.6261.69"',
260 'x-ms-useragent': 'azsdk-js-api-client-factory/1.0.0-beta.1 core-rest-pipeline/1.12.3 OS/Windows',
261 'sec-ch-ua-model': '""',
262 'sec-ch-ua-platform': '"Windows"',
263 'sec-fetch-site': 'same-origin',
264 'sec-fetch-mode': 'cors',
265 'sec-fetch-dest': 'empty',
266 'referer': home,
267 'accept-language': 'en-US,en;q=0.9',
281 "x-ms-useragent": "azsdk-js-api-client-factory/1.0.0-beta.1 core-rest-pipeline/1.15.1 OS/Windows",
268 282 }
269 283
270 284 def format_message(msg: dict) -> str:
@@ -368,7 +382,6 @@ async def stream_generate(
368 382 context: str = None,
369 383 cookies: dict = None,
370 384 api_key: str = None,
371 connector: BaseConnector = None,
372 385 proxy: str = None,
373 386 web_search: bool = False,
374 387 gpt4_turbo: bool = False,
@@ -393,14 +406,12 @@ async def stream_generate(
393 406 :param timeout: Timeout for the request.
394 407 :return: An asynchronous generator yielding responses.
395 408 """
396 headers = create_headers(cookies, api_key)
409 headers = await create_headers(cookies, api_key)
397 410 new_conversation = conversation is None
398 411 max_retries = (5 if new_conversation else 0) if max_retries is None else max_retries
399 async with ClientSession(
400 timeout=ClientTimeout(total=timeout), connector=connector
401 ) as session:
402 first = True
403 while first or conversation is None:
412 first = True
413 while first or conversation is None:
414 async with StreamSession(timeout=timeout, proxy=proxy) as session:
404 415 first = False
405 416 do_read = True
406 417 try:
@@ -408,13 +419,13 @@ async def stream_generate(
408 419 conversation = await create_conversation(session, headers, tone)
409 420 if return_conversation:
410 421 yield conversation
411 except ResponseStatusError as e:
422 except (ResponseStatusError, RateLimitError) as e:
412 423 max_retries -= 1
413 424 if max_retries < 1:
414 425 raise e
415 426 if debug.logging:
416 427 print(f"Bing: Retry: {e}")
417 headers = create_headers()
428 headers = await create_headers()
418 429 await asyncio.sleep(sleep_retry)
419 430 continue
420 431
@@ -434,7 +445,7 @@ async def stream_generate(
434 445 ) as wss:
435 446 await wss.send_str(format_message({'protocol': 'json', 'version': 1}))
436 447 await wss.send_str(format_message({"type": 6}))
437 await wss.receive(timeout=timeout)
448 await wss.receive_str()
438 449 await wss.send_str(create_message(
439 450 conversation, prompt, tone,
440 451 context if new_conversation else None,
@@ -445,16 +456,15 @@ async def stream_generate(
445 456 returned_text = ''
446 457 message_id = None
447 458 while do_read:
448 msg = await wss.receive(timeout=timeout)
449 if msg.type == WSMsgType.CLOSED:
450 break
451 if msg.type != WSMsgType.TEXT or not msg.data:
452 continue
453 objects = msg.data.split(Defaults.delimiter)
459 msg = await wss.receive_str()
460 objects = msg.split(Defaults.delimiter)
454 461 for obj in objects:
455 462 if obj is None or not obj:
456 463 continue
457 response = json.loads(obj)
464 try:
465 response = json.loads(obj)
466 except json.JSONDecodeError:
467 continue
458 468 if response and response.get('type') == 1 and response['arguments'][0].get('messages'):
459 469 message = response['arguments'][0]['messages'][0]
460 470 if message_id is not None and message_id != message["messageId"]:
@@ -462,7 +472,7 @@ async def stream_generate(
462 472 message_id = message["messageId"]
463 473 image_response = None
464 474 if (raise_apology and message['contentOrigin'] == 'Apology'):
465 raise RuntimeError("Apology Response Error")
475 raise ResponseError("Apology Response Error")
466 476 if 'adaptiveCards' in message:
467 477 card = message['adaptiveCards'][0]['body'][0]
468 478 if "text" in card:
@@ -488,6 +498,7 @@ async def stream_generate(
488 498 yield image_response
489 499 elif response.get('type') == 2:
490 500 result = response['item']['result']
501 do_read = False
491 502 if result.get('error'):
492 503 max_retries -= 1
493 504 if max_retries < 1:
@@ -497,10 +508,12 @@ async def stream_generate(
497 508 raise RuntimeError(f"{result['value']}: {result['message']}")
498 509 if debug.logging:
499 510 print(f"Bing: Retry: {result['value']}: {result['message']}")
500 headers = create_headers()
501 do_read = False
511 headers = await create_headers()
502 512 conversation = None
503 513 await asyncio.sleep(sleep_retry)
504 break
505 return
506 await delete_conversation(session, conversation, headers)
514 break
515 elif response.get('type') == 3:
516 do_read = False
517 break
518 if conversation is not None:
519 await delete_conversation(session, conversation, headers)
Modified g4f/Provider/MetaAI.py +63 -31
@@ -1,3 +1,5 @@
1 from __future__ import annotations
2
1 3 import json
2 4 import uuid
3 5 import random
@@ -8,6 +10,8 @@ from aiohttp import ClientSession, BaseConnector
8 10
9 11 from ..typing import AsyncResult, Messages, Cookies
10 12 from ..requests import raise_for_status, DEFAULT_HEADERS
13 from ..image import ImageResponse, ImagePreview
14 from ..errors import ResponseError
11 15 from .base_provider import AsyncGeneratorProvider
12 16 from .helper import format_prompt, get_connector
13 17
@@ -22,6 +26,7 @@ class AbraGeoBlockedError(Exception):
22 26 pass
23 27
24 28 class MetaAI(AsyncGeneratorProvider):
29 label = "Meta AI"
25 30 url = "https://www.meta.ai"
26 31 working = True
27 32
@@ -38,11 +43,10 @@ class MetaAI(AsyncGeneratorProvider):
38 43 proxy: str = None,
39 44 **kwargs
40 45 ) -> AsyncResult:
41 #cookies = get_cookies(".meta.ai", False, True)
42 46 async for chunk in cls(proxy).prompt(format_prompt(messages)):
43 47 yield chunk
44 48
45 async def get_access_token(self, birthday: str = "1999-01-01") -> str:
49 async def update_access_token(self, birthday: str = "1999-01-01"):
46 50 url = "https://www.meta.ai/api/graphql/"
47 51
48 52 payload = {
@@ -66,25 +70,37 @@ class MetaAI(AsyncGeneratorProvider):
66 70 async with self.session.post(url, headers=headers, cookies=self.cookies, data=payload) as response:
67 71 await raise_for_status(response, "Fetch access_token failed")
68 72 auth_json = await response.json(content_type=None)
69 access_token = auth_json["data"]["xab_abra_accept_terms_of_service"]["new_temp_user_auth"]["access_token"]
70 return access_token
73 self.access_token = auth_json["data"]["xab_abra_accept_terms_of_service"]["new_temp_user_auth"]["access_token"]
71 74
72 75 async def prompt(self, message: str, cookies: Cookies = None) -> AsyncResult:
76 if self.cookies is None:
77 await self.update_cookies(cookies)
73 78 if cookies is not None:
74 self.cookies = cookies
75 79 self.access_token = None
76 if self.cookies is None:
77 self.cookies = await self.get_cookies()
78 if self.access_token is None:
79 self.access_token = await self.get_access_token()
80 if self.access_token is None and cookies is None:
81 await self.update_access_token()
80 82
81 url = "https://graph.meta.ai/graphql?locale=user"
82 #url = "https://www.meta.ai/api/graphql/"
83 if self.access_token is None:
84 url = "https://www.meta.ai/api/graphql/"
85 payload = {"lsd": self.lsd, 'fb_dtsg': self.dtsg}
86 headers = {'x-fb-lsd': self.lsd}
87 else:
88 url = "https://graph.meta.ai/graphql?locale=user"
89 payload = {"access_token": self.access_token}
90 headers = {}
91 headers = {
92 'content-type': 'application/x-www-form-urlencoded',
93 'cookie': "; ".join([f"{k}={v}" for k, v in cookies.items()]),
94 'origin': 'https://www.meta.ai',
95 'referer': 'https://www.meta.ai/',
96 'x-asbd-id': '129477',
97 'x-fb-friendly-name': 'useAbraSendMessageMutation',
98 **headers
99 }
83 100 payload = {
84 "access_token": self.access_token,
85 #"lsd": cookies["lsd"],
86 "fb_api_caller_class": "RelayModern",
87 "fb_api_req_friendly_name": "useAbraSendMessageMutation",
101 **payload,
102 'fb_api_caller_class': 'RelayModern',
103 'fb_api_req_friendly_name': 'useAbraSendMessageMutation',
88 104 "variables": json.dumps({
89 105 "message": {"sensitive_string_value": message},
90 106 "externalConversationId": str(uuid.uuid4()),
@@ -98,19 +114,16 @@ class MetaAI(AsyncGeneratorProvider):
98 114 "__relay_internal__pv__AbraDebugDevOnlyrelayprovider": False,
99 115 "__relay_internal__pv__WebPixelRatiorelayprovider": 1,
100 116 }),
101 "server_timestamps": "true",
102 "doc_id": "7783822248314888",
103 }
104 headers = {
105 "x-asbd-id": "129477",
106 "x-fb-friendly-name": "useAbraSendMessageMutation",
107 #"x-fb-lsd": cookies["lsd"],
117 'server_timestamps': 'true',
118 'doc_id': '7783822248314888'
108 119 }
109 async with self.session.post(url, headers=headers, cookies=self.cookies, data=payload) as response:
120 async with self.session.post(url, headers=headers, data=payload) as response:
110 121 await raise_for_status(response, "Fetch response failed")
111 122 last_snippet_len = 0
112 123 fetch_id = None
113 124 async for line in response.content:
125 if b"<h1>Something Went Wrong</h1>" in line:
126 raise ResponseError("Response: Something Went Wrong")
114 127 try:
115 128 json_line = json.loads(line)
116 129 except json.JSONDecodeError:
@@ -119,7 +132,14 @@ class MetaAI(AsyncGeneratorProvider):
119 132 streaming_state = bot_response_message.get("streaming_state")
120 133 fetch_id = bot_response_message.get("fetch_id") or fetch_id
121 134 if streaming_state in ("STREAMING", "OVERALL_DONE"):
122 #imagine_card = bot_response_message["imagine_card"]
135 imagine_card = bot_response_message.get("imagine_card")
136 if imagine_card is not None:
137 imagine_session = imagine_card.get("session")
138 if imagine_session is not None:
139 imagine_medias = imagine_session.get("media_sets", {}).pop().get("imagine_media")
140 if imagine_medias is not None:
141 image_class = ImageResponse if streaming_state == "OVERALL_DONE" else ImagePreview
142 yield image_class([media["uri"] for media in imagine_medias], imagine_medias[0]["prompt"])
123 143 snippet = bot_response_message["snippet"]
124 144 new_snippet_len = len(snippet)
125 145 if new_snippet_len > last_snippet_len:
@@ -135,7 +155,7 @@ class MetaAI(AsyncGeneratorProvider):
135 155 if sources is not None:
136 156 yield sources
137 157
138 async def get_cookies(self, cookies: Cookies = None) -> Cookies:
158 async def update_cookies(self, cookies: Cookies = None):
139 159 async with self.session.get("https://www.meta.ai/", cookies=cookies) as response:
140 160 await raise_for_status(response, "Fetch home failed")
141 161 text = await response.text()
@@ -148,12 +168,20 @@ class MetaAI(AsyncGeneratorProvider):
148 168 "datr": self.extract_value(text, "datr"),
149 169 }
150 170 self.lsd = self.extract_value(text, start_str='"LSD",[],{"token":"', end_str='"}')
151 return cookies
171 self.dtsg = self.extract_value(text, start_str='"DTSGInitialData",[],{"token":"', end_str='"}')
172 self.cookies = cookies
152 173
153 174 async def fetch_sources(self, fetch_id: str) -> Sources:
154 url = "https://graph.meta.ai/graphql?locale=user"
175 if self.access_token is None:
176 url = "https://www.meta.ai/api/graphql/"
177 payload = {"lsd": self.lsd, 'fb_dtsg': self.dtsg}
178 headers = {'x-fb-lsd': self.lsd}
179 else:
180 url = "https://graph.meta.ai/graphql?locale=user"
181 payload = {"access_token": self.access_token}
182 headers = {}
155 183 payload = {
156 "access_token": self.access_token,
184 **payload,
157 185 "fb_api_caller_class": "RelayModern",
158 186 "fb_api_req_friendly_name": "AbraSearchPluginDialogQuery",
159 187 "variables": json.dumps({"abraMessageFetchID": fetch_id}),
@@ -163,18 +191,22 @@ class MetaAI(AsyncGeneratorProvider):
163 191 headers = {
164 192 "authority": "graph.meta.ai",
165 193 "x-fb-friendly-name": "AbraSearchPluginDialogQuery",
194 **headers
166 195 }
167 196 async with self.session.post(url, headers=headers, cookies=self.cookies, data=payload) as response:
168 197 await raise_for_status(response)
169 response_json = await response.json()
198 text = await response.text()
199 if "<h1>Something Went Wrong</h1>" in text:
200 raise ResponseError("Response: Something Went Wrong")
170 201 try:
202 response_json = json.loads(text)
171 203 message = response_json["data"]["message"]
172 204 if message is not None:
173 205 searchResults = message["searchResults"]
174 206 if searchResults is not None:
175 207 return Sources(searchResults["references"])
176 except (KeyError, TypeError):
177 raise RuntimeError(f"Response: {response_json}")
208 except (KeyError, TypeError, json.JSONDecodeError):
209 raise RuntimeError(f"Response: {text}")
178 210
179 211 @staticmethod
180 212 def extract_value(text: str, key: str = None, start_str = None, end_str = '",') -> str:
Added g4f/Provider/MetaAIAccount.py +21 -0
@@ -0,0 +1,21 @@
1 from __future__ import annotations
2
3 from ..typing import AsyncResult, Messages, Cookies
4 from .helper import format_prompt, get_cookies
5 from .MetaAI import MetaAI
6
7 class MetaAIAccount(MetaAI):
8 needs_auth = True
9
10 @classmethod
11 async def create_async_generator(
12 cls,
13 model: str,
14 messages: Messages,
15 proxy: str = None,
16 cookies: Cookies = None,
17 **kwargs
18 ) -> AsyncResult:
19 cookies = get_cookies(".meta.ai", True, True) if cookies is None else cookies
20 async for chunk in cls(proxy).prompt(format_prompt(messages), cookies):
21 yield chunk
Modified g4f/Provider/You.py +5 -2
@@ -10,7 +10,7 @@ from .base_provider import AsyncGeneratorProvider, ProviderModelMixin
10 10 from .helper import format_prompt
11 11 from ..image import ImageResponse, to_bytes, is_accepted_format
12 12 from ..requests import StreamSession, FormData, raise_for_status
13 from .you.har_file import get_dfp_telemetry_id
13 from .you.har_file import get_telemetry_ids
14 14
15 15 class You(AsyncGeneratorProvider, ProviderModelMixin):
16 16 url = "https://you.com"
@@ -35,6 +35,7 @@ class You(AsyncGeneratorProvider, ProviderModelMixin):
35 35 }
36 36 _cookies = None
37 37 _cookies_used = 0
38 _telemetry_ids = []
38 39
39 40 @classmethod
40 41 async def create_async_generator(
@@ -159,6 +160,8 @@ class You(AsyncGeneratorProvider, ProviderModelMixin):
159 160
160 161 @classmethod
161 162 async def create_cookies(cls, client: StreamSession) -> Cookies:
163 if not cls._telemetry_ids:
164 cls._telemetry_ids = await get_telemetry_ids()
162 165 user_uuid = str(uuid.uuid4())
163 166 async with client.post(
164 167 "https://web.stytch.com/sdk/v1/passwords",
@@ -170,7 +173,7 @@ class You(AsyncGeneratorProvider, ProviderModelMixin):
170 173 "Referer": "https://you.com/"
171 174 },
172 175 json={
173 "dfp_telemetry_id": await get_dfp_telemetry_id(),
176 "dfp_telemetry_id": cls._telemetry_ids.pop(),
174 177 "email": f"{user_uuid}@gmail.com",
175 178 "password": f"{user_uuid}#{user_uuid}",
176 179 "session_duration_minutes": 129600
Modified g4f/Provider/__init__.py +1 -0
@@ -43,6 +43,7 @@ from .Liaobots import Liaobots
43 43 from .Llama import Llama
44 44 from .Local import Local
45 45 from .MetaAI import MetaAI
46 from .MetaAIAccount import MetaAIAccount
46 47 from .PerplexityLabs import PerplexityLabs
47 48 from .Pi import Pi
48 49 from .ReplicateImage import ReplicateImage
Modified g4f/Provider/bing/conversation.py +4 -5
@@ -1,7 +1,6 @@
1 1 from __future__ import annotations
2 2
3 from aiohttp import ClientSession
4 from ...requests import raise_for_status
3 from ...requests import StreamSession, raise_for_status
5 4 from ...errors import RateLimitError
6 5 from ...providers.conversation import BaseConversation
7 6
@@ -22,7 +21,7 @@ class Conversation(BaseConversation):
22 21 self.clientId = clientId
23 22 self.conversationSignature = conversationSignature
24 23
25 async def create_conversation(session: ClientSession, headers: dict, tone: str) -> Conversation:
24 async def create_conversation(session: StreamSession, headers: dict, tone: str) -> Conversation:
26 25 """
27 26 Create a new conversation asynchronously.
28 27
@@ -49,7 +48,7 @@ async def create_conversation(session: ClientSession, headers: dict, tone: str)
49 48 raise RuntimeError('Empty fields: Failed to create conversation')
50 49 return Conversation(conversationId, clientId, conversationSignature)
51 50
52 async def list_conversations(session: ClientSession) -> list:
51 async def list_conversations(session: StreamSession) -> list:
53 52 """
54 53 List all conversations asynchronously.
55 54
@@ -64,7 +63,7 @@ async def list_conversations(session: ClientSession) -> list:
64 63 response = await response.json()
65 64 return response["chats"]
66 65
67 async def delete_conversation(session: ClientSession, conversation: Conversation, headers: dict) -> bool:
66 async def delete_conversation(session: StreamSession, conversation: Conversation, headers: dict) -> bool:
68 67 """
69 68 Delete a conversation asynchronously.
70 69
Modified g4f/Provider/needs_auth/OpenaiChat.py +8 -15
@@ -24,7 +24,7 @@ except ImportError:
24 24 from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin
25 25 from ...webdriver import get_browser
26 26 from ...typing import AsyncResult, Messages, Cookies, ImageType, AsyncIterator
27 from ...requests import get_args_from_browser, raise_for_status
27 from ...requests import DEFAULT_HEADERS, get_args_from_browser, raise_for_status
28 28 from ...requests.aiohttp import StreamSession
29 29 from ...image import to_image, to_bytes, ImageResponse, ImageRequest
30 30 from ...errors import MissingAuthError, ResponseError
@@ -360,7 +360,6 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
360 360 if debug.logging:
361 361 print("OpenaiChat: Load default_model failed")
362 362 print(f"{e.__class__.__name__}: {e}")
363
364 363
365 364 arkose_token = None
366 365 if cls.default_model is None:
@@ -377,7 +376,8 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
377 376 cls.default_model = cls.get_model(await cls.get_default_model(session, cls._headers))
378 377
379 378 async with session.post(
380 f"{cls.url}/backend-anon/sentinel/chat-requirements" if not cls._api_key else
379 f"{cls.url}/backend-anon/sentinel/chat-requirements"
380 if not cls._api_key else
381 381 f"{cls.url}/backend-api/sentinel/chat-requirements",
382 382 json={"conversation_mode_kind": "primary_assistant"},
383 383 headers=cls._headers
@@ -388,7 +388,7 @@ class OpenaiChat(AsyncGeneratorProvider, ProviderModelMixin):
388 388 blob = data["arkose"]["dx"]
389 389 need_arkose = data["arkose"]["required"]
390 390 chat_token = data["token"]
391
391
392 392 if debug.logging:
393 393 print(f'Arkose: {need_arkose} Turnstile: {data["turnstile"]["required"]}')
394 394
@@ -595,8 +595,7 @@ this.fetch = async (url, options) => {
595 595 print(f"Open nodriver with user_dir: {user_data_dir}")
596 596 browser = await uc.start(user_data_dir=user_data_dir)
597 597 page = await browser.get("https://chat.openai.com/")
598 while await page.find("[id^=headlessui-menu-button-]") is None:
599 await asyncio.sleep(1)
598 await page.select("[id^=headlessui-menu-button-]", 240)
600 599 api_key = await page.evaluate(
601 600 "(async () => {"
602 601 "let session = await fetch('/api/auth/session');"
@@ -662,16 +661,10 @@ this.fetch = async (url, options) => {
662 661 @staticmethod
663 662 def get_default_headers() -> dict:
664 663 return {
665 "accept-language": "en-US",
664 **DEFAULT_HEADERS,
666 665 "content-type": "application/json",
667 666 "oai-device-id": str(uuid.uuid4()),
668 667 "oai-language": "en-US",
669 "sec-ch-ua": "\"Google Chrome\";v=\"123\", \"Not:A-Brand\";v=\"8\", \"Chromium\";v=\"123\"",
670 "sec-ch-ua-mobile": "?0",
671 "sec-ch-ua-platform": "\"Linux\"",
672 "sec-fetch-dest": "empty",
673 "sec-fetch-mode": "cors",
674 "sec-fetch-site": "same-origin"
675 668 }
676 669
677 670 @staticmethod
@@ -696,11 +689,11 @@ this.fetch = async (url, options) => {
696 689 def _set_api_key(cls, api_key: str):
697 690 cls._api_key = api_key
698 691 cls._expires = int(time.time()) + 60 * 60 * 4
699 cls._headers["Authorization"] = f"Bearer {api_key}"
692 cls._headers["authorization"] = f"Bearer {api_key}"
700 693
701 694 @classmethod
702 695 def _update_cookie_header(cls):
703 cls._headers["Cookie"] = cls._format_cookies(cls._cookies)
696 cls._headers["cookie"] = cls._format_cookies(cls._cookies)
704 697
705 698 class Conversation(BaseConversation):
706 699 """
Modified g4f/Provider/you/har_file.py +23 -35
@@ -3,11 +3,10 @@ from __future__ import annotations
3 3 import json
4 4 import os
5 5 import random
6 import uuid
7 import asyncio
8 6 import requests
9 7
10 8 from ...requests import StreamSession, raise_for_status
9 from ... import debug
11 10
12 11 class NoValidHarFileError(Exception):
13 12 ...
@@ -67,60 +66,49 @@ async def sendRequest(tmpArk: arkReq, proxy: str = None):
67 66 return await response.text()
68 67
69 68 async def get_dfp_telemetry_id(proxy: str = None):
70 return await telemetry_id_with_driver(proxy)
71 69 global chatArks
72 70 if chatArks is None:
73 71 chatArks = readHAR()
74 72 return await sendRequest(random.choice(chatArks), proxy)
75 73
76 async def telemetry_id_with_driver(proxy: str = None):
77 from ...debug import logging
78 if logging:
79 print('getting telemetry_id for you.com with nodriver')
74 def read_telemetry_file() -> list:
75 with open("hardir/you.com_telemetry_ids.txt", "r") as f:
76 ids = f.readlines()
77 random.shuffle(ids)
78 return ids
79
80 async def get_telemetry_ids(proxy: str = None) -> list:
81 if debug.logging:
82 print('Getting telemetry_id for you.com with nodriver')
80 83 try:
81 import nodriver as uc
82 from nodriver import start, cdp, loop
84 from nodriver import start
83 85 except ImportError:
84 if logging:
85 print('nodriver not found, random uuid (may fail)')
86 return str(uuid.uuid4())
87
88 CAN_EVAL = False
89 payload_received = False
90 payload = None
91
86 if debug.logging:
87 print('Install "nodriver" package | pip install -U nodriver')
88 return read_telemetry_file()
92 89 try:
93 90 browser = await start()
94 91 tab = browser.main_tab
95
96 async def send_handler(event: cdp.network.RequestWillBeSent):
97 nonlocal CAN_EVAL, payload_received, payload
98 if 'telemetry.js' in event.request.url:
99 CAN_EVAL = True
100 if "/submit" in event.request.url:
101 payload = event.request.post_data
102 payload_received = True
103
104 tab.add_handler(cdp.network.RequestWillBeSent, send_handler)
105 92 await browser.get("https://you.com")
106 93
107 while not CAN_EVAL:
94 while not await tab.evaluate('"GetTelemetryID" in this'):
108 95 await tab.sleep(1)
109 96
110 await tab.evaluate('window.GetTelemetryID("public-token-live-507a52ad-7e69-496b-aee0-1c9863c7c819", "https://telemetry.stytch.com/submit");')
111
112 while not payload_received:
113 await tab.sleep(.1)
97 async def get_telemetry_id():
98 public_token = "public-token-live-507a52ad-7e69-496b-aee0-1c9863c7c819"
99 telemetry_url = "https://telemetry.stytch.com/submit"
100 return await tab.evaluate(f'this.GetTelemetryID("{public_token}", "{telemetry_url}");', await_promise=True)
114 101
115 except Exception as e:
116 print(f"Error occurred: {str(e)}")
102 # for _ in range(500):
103 # with open("hardir/you.com_telemetry_ids.txt", "a") as f:
104 # f.write((await get_telemetry_id()) + "\n")
117 105
106 return [await get_telemetry_id() for _ in range(10)]
118 107 finally:
119 108 try:
120 109 await tab.close()
121 110 except Exception as e:
122 111 print(f"Error occurred while closing tab: {str(e)}")
123
124 112 try:
125 113 await browser.stop()
126 114 except Exception as e:
Modified g4f/gui/client/static/js/chat.v1.js +6 -2
@@ -41,7 +41,9 @@ appStorage = window.localStorage || {
41 41 length: 0
42 42 }
43 43
44 const markdown = window.markdownit();
44 const markdown = window.markdownit({
45 html: true,
46 });
45 47 const markdown_render = (content) => {
46 48 return markdown.render(content
47 49 .replaceAll(/<!-- generated images start -->|<!-- generated images end -->/gm, "")
@@ -312,6 +314,8 @@ async function add_message_chunk(message) {
312 314 window.error = message.error
313 315 console.error(message.error);
314 316 content_inner.innerHTML += `<p><strong>An error occured:</strong> ${message.error}</p>`;
317 } else if (message.type == "preview") {
318 content_inner.innerHTML = markdown_render(message.preview);
315 319 } else if (message.type == "content") {
316 320 window.text += message.content;
317 321 html = markdown_render(window.text);
@@ -545,7 +549,7 @@ const load_conversation = async (conversation_id, scroll=true) => {
545 549 last_model = item.provider?.model;
546 550 let next_i = parseInt(i) + 1;
547 551 let next_provider = item.provider ? item.provider : (messages.length > next_i ? messages[next_i].provider : null);
548 let provider_label = item.provider?.label ? item.provider?.label : item.provider?.name;
552 let provider_label = item.provider?.label ? item.provider.label : item.provider?.name;
549 553 let provider_link = item.provider?.name ? `<a href="${item.provider.url}" target="_blank">${provider_label}</a>` : "";
550 554 let provider = provider_link ? `
551 555 <div class="provider">
Modified g4f/gui/server/api.py +3 -0
@@ -7,6 +7,7 @@ from typing import Iterator
7 7 from g4f import version, models
8 8 from g4f import get_last_provider, ChatCompletion
9 9 from g4f.errors import VersionNotFoundError
10 from g4f.image import ImagePreview
10 11 from g4f.Provider import ProviderType, __providers__, __map__
11 12 from g4f.providers.base_provider import ProviderModelMixin, FinishReason
12 13 from g4f.providers.conversation import BaseConversation
@@ -146,6 +147,8 @@ class Api():
146 147 elif isinstance(chunk, Exception):
147 148 logging.exception(chunk)
148 149 yield self._format_json("message", get_error_message(chunk))
150 elif isinstance(chunk, ImagePreview):
151 yield self._format_json("preview", chunk.to_string())
149 152 elif not isinstance(chunk, FinishReason):
150 153 yield self._format_json("content", str(chunk))
151 154 except Exception as e:
Modified g4f/image.py +10 -1
@@ -210,7 +210,9 @@ 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"[![#{idx+1} {alt}]({preview[idx]})]({image})" for idx, image in enumerate(images)
213 #f"[![#{idx+1} {alt}]({preview[idx]})]({image})"
214 f'[<img src="{preview[idx]}" width="200" alt="#{idx+1} {alt}">]({image})'
215 for idx, image in enumerate(images)
214 216 )
215 217 start_flag = "<!-- generated images start -->\n"
216 218 end_flag = "<!-- generated images end -->\n"
@@ -259,6 +261,13 @@ class ImageResponse:
259 261 def get_list(self) -> list[str]:
260 262 return [self.images] if isinstance(self.images, str) else self.images
261 263
264 class ImagePreview(ImageResponse):
265 def __str__(self):
266 return ""
267
268 def to_string(self):
269 return super().__str__()
270
262 271 class ImageRequest:
263 272 def __init__(
264 273 self,
Modified g4f/requests/aiohttp.py +4 -1
Modified g4f/requests/defaults.py +21 -15