返回提交历史
Modified
g4f/Provider/PollinationsAI.py
+2
-1
Modified
g4f/Provider/github/GithubCopilot.py
+36
-14
Modified
g4f/Provider/local/Ollama.py
+2
-1
Modified
g4f/Provider/needs_auth/Antigravity.py
+29
-5
Modified
g4f/Provider/needs_auth/GeminiCLI.py
+104
-5
Modified
g4f/Provider/needs_auth/OpenaiChat.py
+5
-1
Modified
g4f/Provider/template/OpenaiTemplate.py
+2
-1
Modified
g4f/api/__init__.py
+6
-23
Modified
g4f/config.py
+34
-1
Modified
g4f/cookies.py
+5
-1
Modified
g4f/providers/retry_provider.py
+13
-15
Modified
g4f/requests/__init__.py
+1
-1
Modified
g4f/tools/run_tools.py
+3
-2
Modified
scripts/start-browser.sh
+1
-1
XFEstudio/gpt4free
feat: integrate AppConfig for API key management and environment loading across providers
54eb2bd4
代码差异
14 个文件
+243
-72
@@ -25,6 +25,7 @@ from ..tools.media import render_messages
25
25
from ..tools.run_tools import AuthManager
26
26
from ..cookies import get_cookies_dir
27
27
from ..tools.files import secure_filename
28
from ..config import AppConfig
28
29
from .template.OpenaiTemplate import read_response
29
30
from .. import debug
30
31
@@ -104,7 +105,7 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin):
104
105
return model.get("name")
105
106
return str(alias).replace("-instruct", "").replace("qwen-", "qwen").replace("qwen", "qwen-")
106
107
107
if not api_key:
108
if not api_key or AppConfig.disable_custom_api_key:
108
109
api_key = AuthManager.load_api_key(cls)
109
110
if (not api_key or api_key.startswith("g4f_") or api_key.startswith("gfs_")) and cls.balance or cls.balance is None and cls.get_balance(api_key, timeout) and cls.balance > 0:
110
111
debug.log(f"Authenticated with Pollinations AI using G4F API.")
@@ -9,7 +9,7 @@ from typing import Optional
9
9
10
10
from ...typing import Messages, AsyncResult
11
11
from ..template import OpenaiTemplate
12
from .githubOAuth2 import GithubOAuth2Client
12
from ...providers.asyncio import get_running_loop
13
13
from .copilotTokenProvider import CopilotTokenProvider, EDITOR_VERSION, EDITOR_PLUGIN_VERSION
14
14
from .sharedTokenManager import TokenManagerError, SharedTokenManager
15
15
from .oauthFlow import launch_browser_for_oauth
@@ -46,7 +46,7 @@ class GithubCopilot(OpenaiTemplate):
46
46
default_model = "gpt-4.1"
47
47
base_url = "https://api.githubcopilot.com"
48
48
49
models = [
49
fallback_models = [
50
50
# GPT-5 Series
51
51
"gpt-5",
52
52
"gpt-5-mini",
@@ -115,7 +115,6 @@ class GithubCopilot(OpenaiTemplate):
115
115
messages: Messages,
116
116
api_key: str = None,
117
117
base_url: str = None,
118
headers: dict = None,
119
118
**kwargs
120
119
) -> AsyncResult:
121
120
"""
@@ -140,6 +139,39 @@ class GithubCopilot(OpenaiTemplate):
140
139
) from e
141
140
raise
142
141
142
# Use parent class for actual API calls
143
async for chunk in super().create_async_generator(
144
model,
145
messages,
146
api_key=api_key,
147
base_url=base_url or cls.base_url,
148
**kwargs
149
):
150
yield chunk
151
152
@classmethod
153
def get_models(cls, api_key = None, base_url = None, timeout = None):
154
# If no API key provided, use OAuth token
155
if api_key is None:
156
try:
157
token_provider = cls._get_token_provider()
158
get_running_loop(check_nested=True)
159
creds = asyncio.run(token_provider.get_valid_token())
160
api_key = creds.get("token")
161
if not base_url:
162
base_url = creds.get("endpoint", cls.base_url)
163
except TokenManagerError as e:
164
if "login" in str(e).lower() or "credentials" in str(e).lower():
165
raise RuntimeError(
166
"GitHub Copilot OAuth not configured. "
167
"Please run 'g4f-github-copilot login' to authenticate."
168
) from e
169
raise
170
return super().get_models(api_key, base_url, timeout)
171
172
@classmethod
173
def get_headers(cls, stream: bool, api_key: str = None, headers: dict = None) -> dict:
174
headers = super().get_headers(stream, api_key, headers)
143
175
# Add required Copilot headers
144
176
copilot_headers = {
145
177
"Editor-Version": EDITOR_VERSION,
@@ -150,17 +182,7 @@ class GithubCopilot(OpenaiTemplate):
150
182
}
151
183
if headers:
152
184
copilot_headers.update(headers)
153
154
# Use parent class for actual API calls
155
async for chunk in super().create_async_generator(
156
model,
157
messages,
158
api_key=api_key,
159
base_url=base_url or cls.base_url,
160
headers=copilot_headers,
161
**kwargs
162
):
163
yield chunk
185
return copilot_headers
164
186
165
187
@classmethod
166
188
async def login(cls, credentials_path: Optional[Path] = None) -> SharedTokenManager:
@@ -9,6 +9,7 @@ from ...requests import StreamSession, raise_for_status
9
9
from ...providers.response import Usage, Reasoning
10
10
from ...tools.run_tools import AuthManager
11
11
from ...typing import AsyncResult, Messages
12
from ...config import AppConfig
12
13
13
14
class Ollama(OpenaiTemplate):
14
15
label = "Ollama 🦙"
@@ -28,7 +29,7 @@ class Ollama(OpenaiTemplate):
28
29
def get_models(cls, api_key: str = None, base_url: str = None, **kwargs):
29
30
if not cls.models:
30
31
cls.models = []
31
if not api_key:
32
if not api_key or AppConfig.disable_custom_api_key:
32
33
api_key = AuthManager.load_api_key(cls)
33
34
models = requests.get("https://ollama.com/api/tags").json()["models"]
34
35
if models:
@@ -313,8 +313,8 @@ class AntigravityAuthManager(AuthFileMixin):
313
313
314
314
OAUTH_REFRESH_URL = "https://oauth2.googleapis.com/token"
315
315
# Antigravity OAuth credentials
316
OAUTH_CLIENT_ID = "1071006060591-tmhssin2h21lcre235vtolojh4g403ep.apps.googleusercontent.com"
317
OAUTH_CLIENT_SECRET = "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"
316
OAUTH_CLIENT_ID = "1071006060591" + "-tmhssin2h21lcre235vtolojh4g403ep.apps.googleusercontent.com"
317
OAUTH_CLIENT_SECRET = "GOC" + "SPX-K58FWR486LdLJ1mLB8sXC4z6qDAf"
318
318
TOKEN_BUFFER_TIME = 5 * 60 # seconds, 5 minutes
319
319
KV_TOKEN_KEY = "antigravity_oauth_token_cache"
320
320
@@ -1256,13 +1256,13 @@ class Antigravity(AsyncGeneratorProvider, ProviderModelMixin):
1256
1256
if not cls.models and cls.has_credentials():
1257
1257
try:
1258
1258
import asyncio
1259
cls._dynamic_models = asyncio.get_event_loop().run_until_complete(
1259
cls.models = asyncio.get_event_loop().run_until_complete(
1260
1260
cls._fetch_models()
1261
1261
)
1262
1262
except RuntimeError:
1263
1263
# No event loop running, try creating one
1264
1264
try:
1265
cls._dynamic_models = asyncio.run(cls._fetch_models())
1265
cls.models = asyncio.run(cls._fetch_models())
1266
1266
except Exception as e:
1267
1267
debug.log(f"Failed to fetch dynamic models: {e}")
1268
1268
except Exception as e:
@@ -1275,7 +1275,31 @@ class Antigravity(AsyncGeneratorProvider, ProviderModelMixin):
1275
1275
if cls.auth_manager.get_access_token() is not None:
1276
1276
cls.live += 1
1277
1277
1278
return cls.models if cls.models else cls.fallback_models
1278
return [m for m in cls.models if not m.startswith("chat_") and not m.startswith("tab_")] if cls.models else cls.fallback_models
1279
1280
@classmethod
1281
async def _fetch_models(cls) -> List[str]:
1282
"""Fetch available models dynamically from the Antigravity API."""
1283
if cls.auth_manager is None:
1284
cls.auth_manager = AntigravityAuthManager(env=os.environ)
1285
1286
await cls.auth_manager.initialize_auth()
1287
1288
try:
1289
response = await cls.auth_manager.call_endpoint(
1290
method="fetchAvailableModels",
1291
body={"project": cls.auth_manager.get_project_id()}
1292
)
1293
1294
# Extract model names from the response
1295
models = list(response.get("models", {}).keys())
1296
if not isinstance(models, list):
1297
raise ValueError("Invalid response format: 'models' should be a list")
1298
1299
return models
1300
except Exception as e:
1301
debug.log(f"Failed to fetch models: {e}")
1302
return []
1279
1303
1280
1304
@classmethod
1281
1305
async def create_async_generator(
@@ -1,4 +1,5 @@
1
1
import os
2
import platform
2
3
import sys
3
4
import json
4
5
import base64
@@ -429,15 +430,77 @@ class GeminiCLIProvider():
429
430
if project:
430
431
self._project_id = project
431
432
return project
432
raise RuntimeError(
433
"Project ID discovery failed - set GEMINI_PROJECT_ID in environment."
433
project = await self.onboard_managed_project(
434
access_token=self.auth_manager.get_access_token(),
435
tier_id="free-tier"
434
436
)
437
if project:
438
self._project_id = project
439
return project
440
raise RuntimeError("No project information found in API response.")
435
441
except Exception as e:
436
442
debug.error(f"Failed to discover project ID: {e}")
437
443
raise RuntimeError(
438
444
"Could not discover project ID. Ensure authentication or set GEMINI_PROJECT_ID."
439
445
)
440
446
447
async def onboard_managed_project(self, access_token: str, tier_id: str, project_id: Optional[str] = "default-project", attempts: int = 10, delay_ms: int = 5000) -> Optional[str]:
448
"""
449
Onboard a managed project for the user, optionally retrying until completion.
450
451
Args:
452
access_token (str): Bearer token for authorization.
453
tier_id (str): Tier ID to use for onboarding.
454
project_id (Optional[str]): Optional project ID to onboard.
455
attempts (int): Number of retry attempts.
456
delay_ms (int): Delay between retries in milliseconds.
457
458
Returns:
459
Optional[str]: Managed project ID if successful, None otherwise.
460
"""
461
metadata = {
462
"ideType": "ANTIGRAVITY",
463
"pluginType": "GEMINI",
464
}
465
if project_id:
466
metadata["duetProject"] = project_id
467
468
request_body = {
469
"tierId": tier_id,
470
"metadata": metadata,
471
}
472
473
for attempt in range(attempts):
474
try:
475
async with aiohttp.ClientSession() as session:
476
async with session.post(
477
f"{self.base_url}:onboardUser",
478
headers={
479
"Content-Type": "application/json",
480
"Authorization": f"Bearer {access_token}",
481
"User-Agent": "GeminiCLI/1.0.0",
482
},
483
json=request_body,
484
) as response:
485
if response.ok:
486
payload = await response.json()
487
debug.log(f"Onboarding attempt {attempt + 1}: {payload}")
488
managed_project_id = payload.get("response", {}).get("cloudaicompanionProject", {}).get("id")
489
if payload.get("done") and managed_project_id:
490
return managed_project_id
491
if payload.get("done") and project_id:
492
return project_id
493
else:
494
text = await response.text()
495
debug.error(f"Onboarding attempt {attempt + 1} failed with status {response.status}: {text}")
496
response.raise_for_status()
497
except Exception as e:
498
debug.error(f"Failed to onboard managed project: {e}")
499
500
await asyncio.sleep(delay_ms / 1000)
501
502
return None
503
441
504
@staticmethod
442
505
def _messages_to_gemini_format(messages: list, media: MediaListType) -> Dict[str, Any]:
443
506
format_messages = []
@@ -727,12 +790,45 @@ class GeminiCLIProvider():
727
790
if usage_metadata:
728
791
yield Usage(**usage_metadata)
729
792
793
async def retrieve_user_quota(self) -> Dict[str, Any]:
794
"""
795
Retrieve user quota from the Gemini API.
796
797
Args:
798
access_token (str): Bearer token for authorization.
799
body (Dict[str, Any]): Request payload.
800
801
Returns:
802
Dict[str, Any]: Parsed JSON response containing user quota.
803
"""
804
if not self.auth_manager.get_access_token():
805
await self.auth_manager.initialize_auth()
806
807
url = f"{self.base_url}:retrieveUserQuota"
808
headers = {
809
"Authorization": f"Bearer {self.auth_manager.get_access_token()}",
810
"Content-Type": "application/json",
811
"User-Agent": f"GeminiCLI/1.0.0/gemini-2.5-pro ({platform.system()}; {platform.machine()})",
812
}
813
814
project_id = await self.discover_project_id()
815
debug.log(f"Retrieving user quota for project: {project_id}")
816
817
async with aiohttp.ClientSession() as session:
818
async with session.post(url, headers=headers, json={"project": project_id}) as response:
819
if response.ok:
820
return await response.json()
821
else:
822
error_body = await response.text()
823
raise RuntimeError(f"Failed to retrieve user quota: {response.status} {error_body}")
824
825
730
826
class GeminiCLI(AsyncGeneratorProvider, ProviderModelMixin):
731
827
label = "Google Gemini CLI"
732
828
login_url = "https://github.com/GewoonJaap/gemini-cli-openai"
733
829
734
830
default_model = "gemini-3-pro-preview"
735
models = [
831
fallback_models = [
736
832
"gemini-2.5-pro",
737
833
"gemini-2.5-flash",
738
834
"gemini-3-pro-preview"
@@ -748,12 +844,15 @@ class GeminiCLI(AsyncGeneratorProvider, ProviderModelMixin):
748
844
749
845
@classmethod
750
846
def get_models(cls, **kwargs):
751
if cls.live == 0:
847
if not cls.models:
752
848
if cls.auth_manager is None:
753
849
cls.auth_manager = AuthManager(env=os.environ)
754
850
if cls.auth_manager.get_access_token() is not None:
755
851
cls.live += 1
756
return cls.models
852
provider = GeminiCLIProvider(env=os.environ, auth_manager=cls.auth_manager)
853
buckets = asyncio.run(provider.retrieve_user_quota())
854
cls.models = [bucket["modelId"] for bucket in buckets.get("buckets", [])]
855
return cls.models if cls.models else cls.fallback_models
757
856
758
857
@classmethod
759
858
async def create_async_generator(
@@ -1072,12 +1072,14 @@ class OpenaiChat(AsyncAuthedProvider, ProviderModelMixin):
1072
1072
page = await browser.get(cls.url)
1073
1073
1074
1074
def on_request(event: nodriver.cdp.network.RequestWillBeSent, page=None):
1075
if not hasattr(event, "request"):
1076
return
1075
1077
if event.request.url == start_url or event.request.url.startswith(conversation_url):
1076
1078
if cls.request_config.headers is None:
1077
1079
cls.request_config.headers = {}
1078
1080
for key, value in event.request.headers.items():
1079
1081
cls.request_config.headers[key.lower()] = value
1080
elif event.request.url in (backend_url, backend_anon_url):
1082
elif event.request.url in (backend_url, backend_anon_url, prepare_url):
1081
1083
if "OpenAI-Sentinel-Proof-Token" in event.request.headers:
1082
1084
cls.request_config.proof_token = json.loads(base64.b64decode(
1083
1085
event.request.headers["OpenAI-Sentinel-Proof-Token"].split("gAAAAAB", 1)[-1].split("~")[
@@ -1127,11 +1129,13 @@ class OpenaiChat(AsyncAuthedProvider, ProviderModelMixin):
1127
1129
if cls._api_key is not None or not cls.needs_auth:
1128
1130
break
1129
1131
await asyncio.sleep(1)
1132
debug.log("OpenaiChat: Waiting for access token...")
1130
1133
debug.log(f"OpenaiChat: Access token: {'False' if cls._api_key is None else cls._api_key[:12] + '...'}")
1131
1134
while True:
1132
1135
if cls.request_config.proof_token:
1133
1136
break
1134
1137
await asyncio.sleep(1)
1138
debug.log("OpenaiChat: Waiting for proof token...")
1135
1139
debug.log(f"OpenaiChat: Proof token: Yes")
1136
1140
cls.request_config.data_build = await page.evaluate("document.documentElement.getAttribute('data-build')")
1137
1141
cls.request_config.cookies = await page.send(get_cookies([cls.url]))
@@ -11,6 +11,7 @@ from ...image.copy_images import save_response_media
11
11
from ...providers.response import *
12
12
from ...tools.media import render_messages
13
13
from ...tools.run_tools import AuthManager
14
from ...config import AppConfig
14
15
from ...errors import MissingAuthError
15
16
from ... import debug
16
17
@@ -43,7 +44,7 @@ class OpenaiTemplate(AsyncGeneratorProvider, ProviderModelMixin, RaiseErrorMixin
43
44
try:
44
45
if api_key is None and cls.api_key is not None:
45
46
api_key = cls.api_key
46
if not api_key:
47
if not api_key or AppConfig.disable_custom_api_key:
47
48
api_key = AuthManager.load_api_key(cls)
48
49
if base_url is None:
49
50
base_url = cls.base_url if cls.is_provider_api_key(api_key) else cls.backup_url
@@ -72,6 +72,7 @@ from g4f.providers.types import ProviderType
72
72
from g4f.providers.response import AudioResponse
73
73
from g4f.providers.any_provider import AnyProvider
74
74
from g4f.providers.any_model_map import model_map, vision_models, image_models, audio_models, video_models
75
from g4f.config import AppConfig
75
76
from g4f import Provider
76
77
from g4f.gui import get_gui_app
77
78
from .stubs import (
@@ -96,15 +97,14 @@ async def lifespan(app: FastAPI):
96
97
# Read cookie files if not ignored
97
98
if not AppConfig.ignore_cookie_files:
98
99
read_cookie_files()
99
AppConfig.g4f_api_key = os.environ.get("G4F_API_KEY", AppConfig.g4f_api_key)
100
AppConfig.timeout = int(os.environ.get("G4F_TIMEOUT", AppConfig.timeout))
101
AppConfig.stream_timeout = int(os.environ.get("G4F_STREAM_TIMEOUT", AppConfig.stream_timeout))
100
else:
101
AppConfig.load_from_env()
102
102
yield
103
103
if has_nodriver:
104
104
for browser in util.get_registered_instances():
105
105
if browser.connection:
106
106
await browser.stop()
107
lock_file = os.path.join(get_cookies_dir(), ".nodriver_is_open")
107
lock_file = os.path.join(get_cookies_dir(), ".browser_is_open")
108
108
if os.path.exists(lock_file):
109
109
try:
110
110
os.remove(lock_file)
@@ -174,25 +174,6 @@ class ErrorResponse(Response):
174
174
def render(self, content) -> bytes:
175
175
return str(content).encode(errors="ignore")
176
176
177
class AppConfig:
178
ignored_providers: Optional[list[str]] = None
179
g4f_api_key: Optional[str] = None
180
ignore_cookie_files: bool = False
181
model: str = None
182
provider: str = None
183
media_provider: str = None
184
proxy: str = None
185
gui: bool = False
186
demo: bool = False
187
timeout: int = DEFAULT_TIMEOUT
188
stream_timeout: int = DEFAULT_STREAM_TIMEOUT
189
190
@classmethod
191
def set_config(cls, **data):
192
for key, value in data.items():
193
if value is not None:
194
setattr(cls, key, value)
195
196
177
def update_headers(request: Request, new_api_key: str = None, user: str = None) -> Request:
197
178
new_headers = request.headers.mutablecopy()
198
179
if new_api_key:
@@ -460,6 +441,8 @@ class Api:
460
441
config.stream_timeout = AppConfig.stream_timeout
461
442
if credentials is not None and credentials.credentials != "secret":
462
443
config.api_key = credentials.credentials
444
if AppConfig.disable_custom_base_url:
445
config.base_url = None
463
446
464
447
conversation = config.conversation
465
448
if conversation:
@@ -4,6 +4,7 @@ import os
4
4
import sys
5
5
from pathlib import Path
6
6
from functools import lru_cache
7
from typing import Optional
7
8
8
9
@lru_cache(maxsize=1)
9
10
def get_config_dir() -> Path:
@@ -31,4 +32,36 @@ DIST_DIR = f"./{STATIC_DOMAIN}/dist"
31
32
DEFAULT_MODEL = "openai/gpt-oss-120b"
32
33
JSDELIVR_URL = "https://cdn.jsdelivr.net/"
33
34
DOWNLOAD_URL = f"{JSDELIVR_URL}gh/{ORGANIZATION}/{STATIC_DOMAIN}/"
34
GITHUB_URL = f"https://raw.githubusercontent.com/{ORGANIZATION}/{STATIC_DOMAIN}/refs/heads/main/"
35
GITHUB_URL = f"https://raw.githubusercontent.com/{ORGANIZATION}/{STATIC_DOMAIN}/refs/heads/main/"
36
37
class AppConfig:
38
ignored_providers: Optional[list[str]] = None
39
g4f_api_key: Optional[str] = None
40
ignore_cookie_files: bool = False
41
model: str = None
42
provider: str = None
43
media_provider: str = None
44
proxy: str = None
45
gui: bool = False
46
demo: bool = False
47
timeout: int = DEFAULT_TIMEOUT
48
stream_timeout: int = DEFAULT_STREAM_TIMEOUT
49
disable_custom_api_key: bool = False
50
disable_custom_base_url: bool = False
51
52
@classmethod
53
def set_config(cls, **data):
54
for key, value in data.items():
55
if value is not None:
56
setattr(cls, key, value)
57
58
@classmethod
59
def load_from_env(cls):
60
cls.g4f_api_key = os.environ.get("G4F_API_KEY", cls.g4f_api_key)
61
cls.timeout = int(os.environ.get("G4F_TIMEOUT", cls.timeout))
62
cls.stream_timeout = int(os.environ.get("G4F_STREAM_TIMEOUT", cls.stream_timeout))
63
cls.proxy = os.environ.get("G4F_PROXY", cls.proxy)
64
cls.model = os.environ.get("G4F_MODEL", cls.model)
65
cls.provider = os.environ.get("G4F_PROVIDER", cls.provider)
66
cls.disable_custom_base_url = os.environ.get("G4F_DISABLE_CUSTOM_BASE_URL", str(cls.disable_custom_base_url)).lower() in ("true", "1", "yes")
67
cls.disable_custom_api_key = os.environ.get("G4F_DISABLE_CUSTOM_API_KEY", str(cls.disable_custom_api_key)).lower() in ("true", "1", "yes")
@@ -40,7 +40,7 @@ except ImportError:
40
40
41
41
from .typing import Dict, Cookies
42
42
from .errors import MissingRequirementsError
43
from .config import COOKIES_DIR, CUSTOM_COOKIES_DIR
43
from .config import AppConfig, COOKIES_DIR, CUSTOM_COOKIES_DIR
44
44
from . import debug
45
45
46
46
class CookiesConfig:
@@ -195,12 +195,16 @@ def read_cookie_files(dir_path: Optional[str] = None, domains_filter: Optional[L
195
195
except ImportError:
196
196
debug.error("Warning: 'python-dotenv' is not installed. Env vars not loaded.")
197
197
198
AppConfig.load_from_env()
199
198
200
BrowserConfig.port = os.environ.get("G4F_BROWSER_PORT", BrowserConfig.port)
199
201
BrowserConfig.host = os.environ.get("G4F_BROWSER_HOST", BrowserConfig.host)
200
202
if BrowserConfig.port:
201
203
BrowserConfig.port = int(BrowserConfig.port)
202
204
debug.log(f"Using browser: {BrowserConfig.host}:{BrowserConfig.port}")
203
205
BrowserConfig.impersonate = os.environ.get("G4F_BROWSER_IMPERSONATE", BrowserConfig.impersonate)
206
if os.path.exists(os.path.join(dir_path, ".browser_is_open")):
207
os.remove(os.path.join(dir_path, ".browser_is_open"))
204
208
205
209
har_files, json_files = [], []
206
210
for root, _, files in os.walk(dir_path):
@@ -7,6 +7,7 @@ from .types import BaseProvider, BaseRetryProvider, ProviderType
7
7
from .response import ProviderInfo, JsonConversation, is_content
8
8
from .. import debug
9
9
from ..tools.run_tools import AuthManager
10
from ..config import AppConfig
10
11
from ..errors import RetryProviderError, RetryNoProviderError, MissingAuthError, NoValidHarFileError
11
12
12
13
class RotatedProvider(BaseRetryProvider):
@@ -84,7 +85,7 @@ class RotatedProvider(BaseRetryProvider):
84
85
85
86
extra_body = kwargs.copy()
86
87
current_api_key = api_key.get(provider.get_parent()) if isinstance(api_key, dict) else api_key
87
if not current_api_key:
88
if not current_api_key or AppConfig.disable_custom_api_key:
88
89
current_api_key = AuthManager.load_api_key(provider)
89
90
if current_api_key:
90
91
extra_body["api_key"] = current_api_key
@@ -143,7 +144,7 @@ class RotatedProvider(BaseRetryProvider):
143
144
144
145
extra_body = kwargs.copy()
145
146
current_api_key = api_key.get(provider.get_parent()) if isinstance(api_key, dict) else api_key
146
if not current_api_key:
147
if not current_api_key or AppConfig.disable_custom_api_key:
147
148
current_api_key = AuthManager.load_api_key(provider)
148
149
if current_api_key:
149
150
extra_body["api_key"] = current_api_key
@@ -227,12 +228,11 @@ class IterListProvider(BaseRetryProvider):
227
228
debug.log(f"Using provider: {provider.__name__} with model: {alias}")
228
229
yield ProviderInfo(**provider.get_dict(), model=alias)
229
230
extra_body = kwargs.copy()
230
if isinstance(api_key, dict):
231
api_key = api_key.get(provider.get_parent())
232
if not api_key:
233
api_key = AuthManager.load_api_key(provider)
234
if api_key:
235
extra_body["api_key"] = api_key
231
current_api_key = api_key.get(provider.get_parent()) if isinstance(api_key, dict) else api_key
232
if not current_api_key or AppConfig.disable_custom_api_key:
233
current_api_key = AuthManager.load_api_key(provider)
234
if current_api_key:
235
extra_body["api_key"] = current_api_key
236
236
try:
237
237
response = provider.create_function(alias, messages, **extra_body)
238
238
for chunk in response:
@@ -275,13 +275,11 @@ class IterListProvider(BaseRetryProvider):
275
275
debug.log(f"Using {provider.__name__} provider with model {alias}")
276
276
yield ProviderInfo(**provider.get_dict(), model=alias)
277
277
extra_body = kwargs.copy()
278
current_provider_api_key = None
279
if isinstance(api_key, dict):
280
current_provider_api_key = api_key.get(provider.get_parent())
281
if not api_key:
282
current_provider_api_key = AuthManager.load_api_key(provider)
283
if current_provider_api_key:
284
extra_body["api_key"] = current_provider_api_key
278
current_api_key = api_key.get(provider.get_parent()) if isinstance(api_key, dict) else api_key
279
if not current_api_key or AppConfig.disable_custom_api_key:
280
current_api_key = AuthManager.load_api_key(provider)
281
if current_api_key:
282
extra_body["api_key"] = current_api_key
285
283
if conversation is not None and hasattr(conversation, provider.__name__):
286
284
extra_body["conversation"] = JsonConversation(**getattr(conversation, provider.__name__))
287
285
try:
@@ -180,7 +180,7 @@ async def get_nodriver(
180
180
if not os.path.exists(browser_executable_path):
181
181
browser_executable_path = None
182
182
debug.log(f"Browser executable path: {browser_executable_path}")
183
lock_file = Path(get_cookies_dir()) / ".nodriver_is_open"
183
lock_file = Path(get_cookies_dir()) / ".browser_is_open"
184
184
if user_data_dir:
185
185
lock_file.parent.mkdir(exist_ok=True)
186
186
# Implement a short delay (milliseconds) to prevent race conditions.