返回提交历史
Modified
g4f/Provider/Copilot.py
+55
-55
Modified
g4f/Provider/qwen/QwenCode.py
+6
-4
Modified
g4f/Provider/qwen/sharedTokenManager.py
+4
-3
Modified
g4f/gui/server/backend_api.py
+8
-2
Added
scripts/start-browser.sh
+6
-0
XFEstudio/gpt4free
Refactor Copilot provider to improve authentication handling and streamline media uploads; add error handling for user retrieval. Enhance QwenCode provider to manage token validation and refresh logic. Update backend API to include new error handling for model and provider not found scenarios. Add start-browser script for automated Chrome launch.
d0ae94de
代码差异
5 个文件
+79
-64
@@ -119,7 +119,7 @@ class Copilot(AsyncAuthedProvider, ProviderModelMixin):
119
119
model = cls.get_model(model)
120
120
websocket_url = cls.websocket_url
121
121
headers = None
122
if cls._access_token:
122
if cls._access_token or cls.needs_auth:
123
123
if api_key is not None:
124
124
cls._access_token = api_key
125
125
if cls._access_token is None:
@@ -144,17 +144,17 @@ class Copilot(AsyncAuthedProvider, ProviderModelMixin):
144
144
) as session:
145
145
if cls._access_token is not None:
146
146
cls._cookies = session.cookies.jar if hasattr(session.cookies, "jar") else session.cookies
147
response = await session.get("https://copilot.microsoft.com/c/api/user?api-version=2", headers={"x-useridentitytype": useridentitytype})
148
if response.status_code == 401:
149
raise MissingAuthError("Status 401: Invalid access token")
150
response.raise_for_status()
151
user = response.json().get('firstName')
152
if user is None:
153
if cls.needs_auth:
154
raise MissingAuthError("No user found, please login first")
155
cls._access_token = None
156
else:
157
debug.log(f"Copilot: User: {user}")
147
response = await session.get("https://copilot.microsoft.com/c/api/user?api-version=2", headers={"x-useridentitytype": useridentitytype})
148
if response.status_code == 401:
149
raise MissingAuthError("Status 401: Invalid access token")
150
response.raise_for_status()
151
user = response.json().get('firstName')
152
if user is None:
153
if cls.needs_auth:
154
raise MissingAuthError("No user found, please login first")
155
cls._access_token = None
156
else:
157
debug.log(f"Copilot: User: {user}")
158
158
if conversation is None:
159
159
response = await session.post(cls.conversation_url, headers={"x-useridentitytype": useridentitytype})
160
160
response.raise_for_status()
@@ -168,50 +168,50 @@ class Copilot(AsyncAuthedProvider, ProviderModelMixin):
168
168
yield conversation
169
169
170
170
uploaded_attachments = []
171
172
# Upload regular media (images)
173
for media, _ in merge_media(media, messages):
174
if not isinstance(media, str):
175
data = to_bytes(media)
176
response = await session.post(
177
"https://copilot.microsoft.com/c/api/attachments",
178
headers={
179
"content-type": is_accepted_format(data),
180
"content-length": str(len(data)),
181
"x-useridentitytype": useridentitytype
182
},
183
data=data
184
)
185
response.raise_for_status()
186
media = response.json().get("url")
187
uploaded_attachments.append({"type":"image", "url": media})
171
if cls._access_token is not None:
172
# Upload regular media (images)
173
for media, _ in merge_media(media, messages):
174
if not isinstance(media, str):
175
data = to_bytes(media)
176
response = await session.post(
177
"https://copilot.microsoft.com/c/api/attachments",
178
headers={
179
"content-type": is_accepted_format(data),
180
"content-length": str(len(data)),
181
"x-useridentitytype": useridentitytype
182
},
183
data=data
184
)
185
response.raise_for_status()
186
media = response.json().get("url")
187
uploaded_attachments.append({"type":"image", "url": media})
188
188
189
# Upload bucket files
190
bucket_items = extract_bucket_items(messages)
191
for item in bucket_items:
192
try:
193
# Handle plain text content from bucket
194
bucket_path = Path(get_bucket_dir(item["bucket_id"]))
195
for text_chunk in read_bucket(bucket_path):
196
if text_chunk.strip():
197
# Upload plain text as a text file
198
text_data = text_chunk.encode('utf-8')
199
data = CurlMime()
200
data.addpart("file", filename=f"bucket_{item['bucket_id']}.txt", content_type="text/plain", data=text_data)
201
response = await session.post(
202
"https://copilot.microsoft.com/c/api/attachments",
203
multipart=data,
204
headers={"x-useridentitytype": useridentitytype}
205
)
206
response.raise_for_status()
207
data = response.json()
208
uploaded_attachments.append({"type": "document", "attachmentId": data.get("id")})
209
debug.log(f"Copilot: Uploaded bucket text content: {item['bucket_id']}")
210
else:
211
debug.log(f"Copilot: No text content found in bucket: {item['bucket_id']}")
212
except Exception as e:
213
debug.log(f"Copilot: Failed to upload bucket item: {item}")
214
debug.error(e)
189
# Upload bucket files
190
bucket_items = extract_bucket_items(messages)
191
for item in bucket_items:
192
try:
193
# Handle plain text content from bucket
194
bucket_path = Path(get_bucket_dir(item["bucket_id"]))
195
for text_chunk in read_bucket(bucket_path):
196
if text_chunk.strip():
197
# Upload plain text as a text file
198
text_data = text_chunk.encode('utf-8')
199
data = CurlMime()
200
data.addpart("file", filename=f"bucket_{item['bucket_id']}.txt", content_type="text/plain", data=text_data)
201
response = await session.post(
202
"https://copilot.microsoft.com/c/api/attachments",
203
multipart=data,
204
headers={"x-useridentitytype": useridentitytype}
205
)
206
response.raise_for_status()
207
data = response.json()
208
uploaded_attachments.append({"type": "document", "attachmentId": data.get("id")})
209
debug.log(f"Copilot: Uploaded bucket text content: {item['bucket_id']}")
210
else:
211
debug.log(f"Copilot: No text content found in bucket: {item['bucket_id']}")
212
except Exception as e:
213
debug.log(f"Copilot: Failed to upload bucket item: {item}")
214
debug.error(e)
215
215
216
216
if prompt is None:
217
217
prompt = get_last_user_message(messages, False)
@@ -20,7 +20,11 @@ class QwenCode(OpenaiTemplate):
20
20
21
21
@classmethod
22
22
def get_models(cls, **kwargs):
23
if cls.live == 0 and cls.client.shared_manager.isTokenValid(cls.client.shared_manager.getCurrentCredentials()):
23
if cls.live == 0:
24
cls.client.shared_manager.checkAndReloadIfNeeded()
25
creds = cls.client.shared_manager.getCurrentCredentials()
26
if creds:
27
cls.client.shared_manager.isTokenValid(creds)
24
28
cls.live += 1
25
29
return cls.models
26
30
@@ -46,9 +50,7 @@ class QwenCode(OpenaiTemplate):
46
50
if chunk != last_chunk:
47
51
yield chunk
48
52
last_chunk = chunk
49
except TokenManagerError as e:
50
raise MissingAuthError(f"QwenCode: No valid authentication available: {e}")
51
except MissingAuthError:
53
except TokenManagerError:
52
54
await cls.client.shared_manager.getValidCredentials(cls.client.qwen_client, True)
53
55
creds = await cls.client.get_valid_token()
54
56
last_chunk = None
@@ -86,7 +86,7 @@ class SharedTokenManager(AuthFileMixin):
86
86
87
87
async def getValidCredentials(self, qwen_client: IQwenOAuth2Client, force_refresh: bool = False):
88
88
try:
89
await self.checkAndReloadIfNeeded()
89
self.checkAndReloadIfNeeded()
90
90
91
91
if (
92
92
self.memory_cache["credentials"]
@@ -107,7 +107,7 @@ class SharedTokenManager(AuthFileMixin):
107
107
raise
108
108
raise TokenManagerError(TokenError.REFRESH_FAILED, str(e), e)
109
109
110
async def checkAndReloadIfNeeded(self):
110
def checkAndReloadIfNeeded(self):
111
111
now = int(time.time() * 1000)
112
112
if now - self.memory_cache["last_check"] < CACHE_CHECK_INTERVAL_MS:
113
113
return
@@ -149,12 +149,13 @@ class SharedTokenManager(AuthFileMixin):
149
149
async def performTokenRefresh(self, qwen_client: IQwenOAuth2Client, force_refresh: bool):
150
150
lock_path = self.getLockFilePath()
151
151
try:
152
qwen_client.setCredentials(self.memory_cache["credentials"])
152
153
current_credentials = qwen_client.getCredentials()
153
154
if not current_credentials.get("refresh_token"):
154
155
raise TokenManagerError(TokenError.NO_REFRESH_TOKEN, "No refresh token")
155
156
await self.acquireLock(lock_path)
156
157
157
await self.checkAndReloadIfNeeded()
158
self.checkAndReloadIfNeeded()
158
159
159
160
if (
160
161
not force_refresh
@@ -31,7 +31,7 @@ try:
31
31
except ImportError as e:
32
32
has_markitdown = False
33
33
try:
34
from .crypto import rsa, serialization, create_or_read_keys, decrypt_data, encrypt_data, get_session_key
34
from .crypto import serialization, create_or_read_keys, decrypt_data, encrypt_data, get_session_key
35
35
has_crypto = True
36
36
except ImportError:
37
37
has_crypto = False
@@ -42,7 +42,7 @@ from ...providers.response import FinishReason, AudioResponse, MediaResponse, Re
42
42
from ...client.helper import filter_markdown
43
43
from ...tools.files import supports_filename, get_streaming, get_bucket_dir, get_tempfile
44
44
from ...tools.run_tools import iter_run_tools
45
from ...errors import ProviderNotFoundError, MissingAuthError
45
from ...errors import ModelNotFoundError, ProviderNotFoundError, MissingAuthError, RateLimitError
46
46
from ...image import is_allowed_extension, process_image, MEDIA_TYPE_MAP
47
47
from ...cookies import get_cookies_dir
48
48
from ...image.copy_images import secure_filename, get_source_url, get_media_dir, copy_media
@@ -368,6 +368,12 @@ class Backend_Api(Api):
368
368
response = response if isinstance(response, str) else "".join(response)
369
369
return Response(filter_markdown(response, None if is_true_filter else do_filter, response if is_true_filter else ""), mimetype='text/plain')
370
370
return Response(response, mimetype='text/plain')
371
except (ModelNotFoundError, ProviderNotFoundError) as e:
372
return jsonify({"error": {"message": f"{type(e).__name__}: {e}"}}), 404
373
except MissingAuthError as e:
374
return jsonify({"error": {"message": f"{type(e).__name__}: {e}"}}), 401
375
except RateLimitError as e:
376
return jsonify({"error": {"message": f"{type(e).__name__}: {e}"}}), 429
371
377
except Exception as e:
372
378
logger.exception(e)
373
379
return jsonify({"error": {"message": f"{type(e).__name__}: {e}"}}), 500
@@ -0,0 +1,6 @@
1
#!/bin/bash
2
while true; do
3
sleep 5
4
rm ~/.config/g4f/cookies/.nodriver_is_open
5
google-chrome --remote-allow-origins=* --no-first-run --no-service-autorun --no-default-browser-check --homepage=about:blank --no-pings --password-store=basic --disable-infobars --disable-breakpad --disable-dev-shm-usage --disable-session-crashed-bubble --disable-search-engine-choice-screen --user-data-dir="~/.config/g4f-nodriver" --disable-session-crashed-bubble --disable-features=IsolateOrigins,site-per-process --no-sandbox --remote-debugging-host=127.0.0.1 --remote-debugging-port=57011
6
done