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

XFEstudio/gpt4free

feat: integrate AppConfig for API key management and environment loading across providers

54eb2bd4
hlohaus <983577+hlohaus@users.noreply.github.com>
提交于

代码差异

14 个文件 +243 -72
Modified g4f/Provider/PollinationsAI.py +2 -1
@@ -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.")
Modified g4f/Provider/github/GithubCopilot.py +36 -14
@@ -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:
Modified g4f/Provider/local/Ollama.py +2 -1
@@ -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:
Modified g4f/Provider/needs_auth/Antigravity.py +29 -5
@@ -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(
Modified g4f/Provider/needs_auth/GeminiCLI.py +104 -5
@@ -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(
Modified g4f/Provider/needs_auth/OpenaiChat.py +5 -1
@@ -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]))
Modified g4f/Provider/template/OpenaiTemplate.py +2 -1
@@ -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
Modified g4f/api/__init__.py +6 -23
@@ -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:
Modified g4f/config.py +34 -1
@@ -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")
Modified g4f/cookies.py +5 -1
@@ -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):
Modified g4f/providers/retry_provider.py +13 -15
@@ -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:
Modified g4f/requests/__init__.py +1 -1
@@ -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.
Modified g4f/tools/run_tools.py +3 -2
Modified scripts/start-browser.sh +1 -1