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

XFEstudio/gpt4free

Update provider urls

94ba86d2
hlohaus <983577+hlohaus@users.noreply.github.com>
提交于

代码差异

9 个文件 +61 -201
Modified g4f/Provider/__init__.py +6 -0
@@ -218,6 +218,7 @@ class ProviderLoader:
218 218 None, "default"
219 219 )
220 220 cls.loaded[name].__name__ = name
221 cls.loaded[name].url = "https://g4f.dev"
221 222 cls.loaded[name].active_by_default = True
222 223 return cls.loaded[name]
223 224 elif name == "GLM":
@@ -322,6 +323,7 @@ class ProviderLoader:
322 323 None, "custom:srv_mt1wbaxgf9c946af0c58",
323 324 )
324 325 cls.loaded[name].__name__ = name
326 cls.loaded[name].url = "https://relayrouter.org"
325 327 cls.loaded[name].active_by_default = True
326 328 return cls.loaded[name]
327 329 elif name == "KiloCode":
@@ -330,6 +332,7 @@ class ProviderLoader:
330 332 None, "https://api.kilo.ai/api/gateway"
331 333 )
332 334 cls.loaded[name].__name__ = name
335 cls.loaded[name].url = "https://kilo.ai"
333 336 cls.loaded[name].active_by_default = True
334 337 cls.loaded[name].default_model = "kilo-auto/free"
335 338 return cls.loaded[name]
@@ -339,6 +342,7 @@ class ProviderLoader:
339 342 None, "https://api.llm7.io/v1"
340 343 )
341 344 cls.loaded[name].__name__ = name
345 cls.loaded[name].url = "https://llm7.io"
342 346 cls.loaded[name].active_by_default = True
343 347 cls.loaded[name].default_model = "default"
344 348 cls.loaded[name].models = ["default"]
@@ -358,6 +362,7 @@ class ProviderLoader:
358 362 None, "https://opencode.ai/zen/v1"
359 363 )
360 364 cls.loaded[name].__name__ = name
365 cls.loaded[name].url = "https://opencode.ai"
361 366 cls.loaded[name].active_by_default = True
362 367 cls.loaded[name].default_model = "big-pickle"
363 368 return cls.loaded[name]
@@ -376,6 +381,7 @@ class ProviderLoader:
376 381 None, name.lower()
377 382 )
378 383 cls.loaded[name].__name__ = name
384 cls.loaded[name].url = "https://orcarouter.ai"
379 385 cls.loaded[name].active_by_default = True
380 386 cls.loaded[name].supports_native_tools = True
381 387 return cls.loaded[name]
Modified g4f/Provider/needs_auth/AIBadgr.py +1 -0
@@ -6,6 +6,7 @@ from ..template import OpenaiTemplate
6 6 class AIBadgr(OpenaiTemplate):
7 7 label = "AI Badgr"
8 8 url = "https://aibadgr.com"
9 screenshot_url = "https://aibadgr.com";
9 10 login_url = "https://aibadgr.com/api-keys"
10 11 base_url = "https://aibadgr.com/api/v1"
11 12 working = True
Modified g4f/Provider/needs_auth/Anthropic.py +1 -0
@@ -17,6 +17,7 @@ from .OpenaiAPI import OpenaiAPI
17 17 class Anthropic(OpenaiAPI):
18 18 label = "Anthropic API"
19 19 url = "https://console.anthropic.com"
20 screenshot_url = "https://anthropic.com"
20 21 login_url = "https://console.anthropic.com/settings/keys"
21 22 working = True
22 23 base_url = "https://api.anthropic.com/v1"
Modified g4f/Provider/needs_auth/Antigravity.py +2 -1
@@ -1445,8 +1445,9 @@ class Antigravity(AsyncGeneratorProvider, ProviderModelMixin):
1445 1445 """
1446 1446
1447 1447 label = "Google Antigravity"
1448 login_url = "https://cloud.google.com/code-assist"
1449 1448 url = "https://antigravity.google"
1449 screenshot_url = "https://antigravity.google"
1450 login_url = "https://cloud.google.com/code-assist"
1450 1451
1451 1452 default_model = "gemini-3-flash"
1452 1453 fallback_models = [
Modified g4f/Provider/needs_auth/Cerebras.py +1 -1
@@ -10,7 +10,7 @@ from ...cookies import get_cookies, get_cookies_async
10 10
11 11 class Cerebras(OpenaiAPI):
12 12 label = "Cerebras Inference"
13 url = "https://inference.cerebras.ai/"
13 url = "https://chat.cerebras.ai/"
14 14 login_url = "https://cloud.cerebras.ai"
15 15 base_url = "https://api.cerebras.ai/v1"
16 16 working = True
Modified g4f/Provider/needs_auth/Puter.py +1 -0
@@ -17,6 +17,7 @@ from ... import debug
17 17 class Puter(AsyncGeneratorProvider, ProviderModelMixin):
18 18 label = "Puter.js"
19 19 url = "https://docs.puter.com/playground"
20 screenshot_url = "https://puter.com"
20 21 login_url = "https://github.com/HeyPuter/puter-cli"
21 22 models_endpoint = "https://api.puter.com/puterai/chat/models/"
22 23 api_endpoint = "https://api.puter.com/drivers/call"
Modified g4f/Provider/needs_auth/You.py +4 -186
@@ -1,191 +1,9 @@
1 1 from __future__ import annotations
2 2
3 import re
4 import json
5 import uuid
3 from ..template import OpenaiTemplate
6 4
7 from ...requests.cdp_browser import cdp
8 from ...typing import AsyncResult, Messages, ImageType, Cookies
9 from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin
10 from ..helper import format_prompt
11 from ...image import MEDIA_TYPE_MAP, to_bytes, is_accepted_format
12 from ...requests import StreamSession, FormData, raise_for_status, get_nodriver
13 from ...providers.response import ImagePreview, ImageResponse
14 from ...cookies import get_cookies
15 from ...errors import MissingRequirementsError, ResponseError
16 from ... import debug
17
18
19 class You(AsyncGeneratorProvider, ProviderModelMixin):
5 class You(OpenaiTemplate):
20 6 label = "You.com"
21 7 url = "https://you.com"
22 working = False
23 needs_auth = True
24 default_model = "gpt-4o-mini"
25 default_vision_model = "agent"
26 image_models = ["dall-e"]
27 models = [
28 default_model,
29 "gpt-4o",
30 "gpt-4o-mini",
31 "gpt-4-turbo",
32 "grok-2",
33 "claude-3.5-sonnet",
34 "claude-3.5-haiku",
35 "claude-3-opus",
36 "claude-3-sonnet",
37 "claude-3-haiku",
38 "llama-3.3-70b",
39 "llama-3.1-70b",
40 "llama-3",
41 "gemini-1-5-flash",
42 "gemini-1-5-pro",
43 "databricks-dbrx-instruct",
44 "command-r",
45 "command-r-plus",
46 "dolphin-2.5",
47 default_vision_model,
48 *image_models,
49 ]
50 _cookies = None
51 _cookies_used = 0
52 _telemetry_ids = []
53
54 @classmethod
55 async def create_async_generator(
56 cls,
57 model: str,
58 messages: Messages,
59 stream: bool = True,
60 image: ImageType = None,
61 image_name: str = None,
62 proxy: str = None,
63 timeout: int = 240,
64 chat_mode: str = "default",
65 cookies: Cookies = None,
66 **kwargs,
67 ) -> AsyncResult:
68 if image is not None or model == cls.default_vision_model:
69 chat_mode = "agent"
70 elif not model or model == cls.default_model:
71 ...
72 elif model.startswith("dall-e"):
73 chat_mode = "create"
74 messages = [messages[-1]]
75 else:
76 chat_mode = "custom"
77 model = cls.get_model(model)
78 if cookies is None and chat_mode != "default":
79 try:
80 cookies = get_cookies(".you.com")
81 except MissingRequirementsError:
82 pass
83 if not cookies or "afUserId" not in cookies:
84 browser, stop_browser = await get_nodriver(proxy=proxy)
85 try:
86 page = await browser.get(cls.url)
87 await page.wait_for(
88 '[data-testid="user-profile-button"]', timeout=900
89 )
90 cookies = {}
91 result = await page.send(
92 cdp.network.get_cookies([cls.url])
93 )
94 for c in result.get("cookies", []):
95 cookies[c["name"]] = c["value"]
96 await page.close()
97 finally:
98 await stop_browser()
99 async with StreamSession(
100 proxy=proxy, impersonate="chrome", timeout=(30, timeout)
101 ) as session:
102 upload = ""
103 if image is not None:
104 upload_file = await cls.upload_file(
105 session, cookies, to_bytes(image), image_name
106 )
107 upload = json.dumps([upload_file])
108 headers = {
109 "Accept": "text/event-stream",
110 "Referer": f"{cls.url}/api/streamingSearch",
111 }
112 data = {
113 "userFiles": upload,
114 "q": format_prompt(messages),
115 "domain": "youchat",
116 "selectedChatMode": chat_mode,
117 "conversationTurnId": str(uuid.uuid4()),
118 "chatId": str(uuid.uuid4()),
119 }
120 if chat_mode == "custom":
121 if debug.logging:
122 print(f"You model: {model}")
123 data["selectedAiModel"] = model.replace("-", "_")
124
125 async with session.get(
126 f"{cls.url}/api/streamingSearch",
127 params=data,
128 headers=headers,
129 cookies=cookies,
130 ) as response:
131 await raise_for_status(response)
132 async for line in response.iter_lines():
133 if line.startswith(b"event: "):
134 event = line[7:].decode()
135 elif line.startswith(b"data: "):
136 if event == "error":
137 raise ResponseError(line[6:])
138 if event in ["youChatUpdate", "youChatToken"]:
139 data = json.loads(line[6:])
140 if event == "youChatToken" and event in data and data[event]:
141 if data[event].startswith(
142 "#### You've hit your free quota for the Model Agent. For more usage of the Model Agent, learn more at:"
143 ):
144 continue
145 yield data[event]
146 elif event == "youChatUpdate" and "t" in data and data["t"]:
147 if chat_mode == "create":
148 match = re.search(r"!\[(.+?)\]\((.+?)\)", data["t"])
149 if match:
150 if match.group(1) == "fig":
151 yield ImagePreview(
152 match.group(2), messages[-1]["content"]
153 )
154 else:
155 yield ImageResponse(
156 match.group(2), match.group(1)
157 )
158 else:
159 yield data["t"]
160 else:
161 yield data["t"]
162
163 @classmethod
164 async def upload_file(
165 cls, client: StreamSession, cookies: Cookies, file: bytes, filename: str = None
166 ) -> dict:
167 async with client.get(
168 f"{cls.url}/api/get_nonce",
169 cookies=cookies,
170 ) as response:
171 await raise_for_status(response)
172 upload_nonce = await response.text()
173 data = FormData()
174 content_type = is_accepted_format(file)
175 filename = (
176 f"image.{MEDIA_TYPE_MAP[content_type]}" if filename is None else filename
177 )
178 data.add_field("file", file, content_type=content_type, filename=filename)
179 async with client.post(
180 f"{cls.url}/api/upload",
181 data=data,
182 headers={
183 "X-Upload-Nonce": upload_nonce,
184 },
185 cookies=cookies,
186 ) as response:
187 await raise_for_status(response)
188 result = await response.json()
189 result["user_filename"] = filename
190 result["size"] = len(file)
191 return result
8 backend_url = "https://g4f.space/api/you.com"
9 working = True
Modified g4f/gui/server/website.py +10 -10
@@ -192,6 +192,8 @@ class Website:
192 192 try:
193 193 provider = ProviderLoader.from_name(name)
194 194 url = getattr(provider, "url", None)
195 screenshot_url = getattr(provider, "screenshot_url", None)
196 login_url = getattr(provider, "login_url", None)
195 197 # Skip model list fetching here — it's slow for 108+ providers.
196 198 # Models are loaded lazily in _provider_detail() for a single provider.
197 199 models = getattr(provider, "models", None)
@@ -211,6 +213,8 @@ class Website:
211 213 providers.append({
212 214 "name": name,
213 215 "url": url,
216 "screenshot_url": screenshot_url,
217 "login_url": login_url,
214 218 "models": models if isinstance(models, list) else list(models) if models else [],
215 219 "needs_auth": needs_auth,
216 220 "working": working,
@@ -249,6 +253,8 @@ class Website:
249 253 <div class="providers-list">
250 254 """
251 255 for p in providers:
256 if not p["working"]:
257 continue # Skip non-working providers
252 258 models_html = ""
253 259 if p["models"]:
254 260 models_list = ", ".join(p["models"][:5]) if isinstance(p["models"], list) else ""
@@ -361,15 +367,8 @@ class Website:
361 367 """
362 368
363 369 # Screenshot / logo section
364 screenshot_url = f"{(p.get('url', (p.get('base_url', p.get('baseUrl', '')))) or "").replace('https://', '').replace('http://', '').replace('api.', '').replace('www.', '').replace('console.', '').replace('api.', '').replace('router.', '').split('/')[0]}"
365 mapping_urls = {
366 "airforce": "api.airforce",
367 "openai": "openrouter.ai",
368 }
369 screenshot_url = mapping_urls.get(screenshot_url, screenshot_url or "g4f.dev")
370 if p.get("name", "") == "OperaAria" or p.get("name", "") == "CopilotApp":
371 screenshot_url = p["url"].replace("https://", "")
372 create_url = f"/screenshot?url={quote_plus('https://' + screenshot_url)}"
370 screenshot_url = p.get("screenshot_url") or p.get("url") or ""
371 create_url = f"/screenshot?url={quote_plus(screenshot_url)}"
373 372 logo_url = "https://g4f.space/logo/" + p.get("name", "").replace(
374 373 'MetaAIAccount', 'Facebook AI').replace(
375 374 'MetaAI', 'Facebook AI').replace(
@@ -390,7 +389,7 @@ class Website:
390 389 'groq', 'Groq')
391 390 screenshot_html = f"""
392 391 <div class="screenshot-section">
393 <img src="/screenshot/{quote_plus(screenshot_url)}.webp" data-src="{create_url}" alt="{escape(p['name'])} logo" class="provider-logo"
392 <img src="/screenshot/{quote_plus(screenshot_url.replace('https://', ''))}.webp" data-src="{create_url}" alt="{escape(p['name'])} logo" class="provider-logo"
394 393 style="max-width:100%;border-radius:8px;border:1px solid var(--card-border)" />
395 394 <img src="{logo_url}" alt="{escape(p['name'])} logo" class="provider-logo" style="max-width:100%;border-radius:8px;border:1px solid var(--card-border)" />
396 395 <p class="screenshot-caption">Load screenshot from {escape(p['url'] or 'N/A')}</p>
@@ -420,6 +419,7 @@ class Website:
420 419 }}
421 420 }};
422 421 img.onmouseleave = img.onmouseenter;
422 img.ontouchstart = img.onmouseenter;
423 423 img.onerror = () => {{
424 424 input.placeholder = 'Ask {escape(p.get("label", p["name"]))}';
425 425 if (img.src.includes(createSrc)) {{
Modified g4f/mcp/pa_provider.py +35 -3
@@ -71,11 +71,15 @@ from pathlib import Path
71 71 from typing import Any, Dict, FrozenSet, List, Optional, Tuple, Type
72 72 from .. import debug
73 73 from ..files import secure_filename
74 from ..config import CONFIG_DIR
74 75
75 76 # ---------------------------------------------------------------------------
76 77 # Workspace directory
77 78 # ---------------------------------------------------------------------------
78 79
80 #: Private directory for secret data (outside the shared workspace).
81 SECRET_DIR: Path = CONFIG_DIR / "secret"
82
79 83
80 84 def get_workspace_dir() -> Path:
81 85 """Return the workspace directory ``~/.g4f/workspace``, creating it if needed."""
@@ -84,6 +88,27 @@ def get_workspace_dir() -> Path:
84 88 return workspace
85 89
86 90
91 def _migrate_secret_dir(old_secret_ws: Path, new_secret_ws: Path) -> None:
92 """Move any existing secret files from the workspace into the private dir."""
93 if not old_secret_ws.exists():
94 return
95 new_secret_ws.mkdir(parents=True, exist_ok=True)
96 for item in old_secret_ws.iterdir():
97 target = new_secret_ws / item.name
98 if target.exists():
99 continue
100 item.rename(target)
101 # Remove the old empty directory tree
102 try:
103 old_secret_ws.rmdir()
104 # Also remove the now-empty "secret" parent if it's empty
105 old_secret_parent = get_workspace_dir() / "secret"
106 if old_secret_parent.exists() and not any(old_secret_parent.iterdir()):
107 old_secret_parent.rmdir()
108 except OSError:
109 pass # Directory not empty — leave it
110
111
87 112 def get_user_workspace_dir(user_id: str) -> Path:
88 113 """Return a per-user workspace subdirectory ``~/.g4f/workspace/users/<user_id>``.
89 114
@@ -100,16 +125,23 @@ def get_user_workspace_dir(user_id: str) -> Path:
100 125
101 126
102 127 def get_secret_workspace_dir(user_id: str) -> Path:
103 """Return a per-user *secret* workspace ``~/.g4f/workspace/secret/<user_id>``.
128 """Return a per-user *secret* directory ``~/.g4f/secret/<user_id>``.
104 129
105 130 This is used when a workspace secret is provided. Files are saved here
106 131 when the user is logged in; reads fall back to the root workspace when
107 132 the file does not exist in the secret workspace.
133
134 Secrets are stored **outside** the shared workspace (under
135 ``~/.g4f/secret/``) so they are never synced or exposed alongside
136 workspace files — similar to how cookies live under ``~/.g4f/cookies/``.
108 137 """
109 138 if not user_id:
110 139 return get_workspace_dir()
111 140 safe_id = re.sub(r"[^a-zA-Z0-9_\-]+", "_", user_id).strip("_") or "anonymous"
112 secret_workspace = get_workspace_dir() / "secret" / safe_id
141 secret_workspace = SECRET_DIR / safe_id
142 # Migrate any existing data from the old workspace-relative location
143 old_location = get_workspace_dir() / "secret" / safe_id
144 _migrate_secret_dir(old_location, secret_workspace)
113 145 secret_workspace.mkdir(parents=True, exist_ok=True)
114 146 return secret_workspace
115 147
@@ -269,7 +301,7 @@ def get_secret_conversation_dir(user_id: str) -> Path:
269 301 """Return the directory used to store secret conversations for *user_id*.
270 302
271 303 Conversations are saved as individual JSON files under
272 ``~/.g4f/workspace/secret/<user_id>/conversations/``. An index file
304 ``~/.g4f/secret/<user_id>/conversations/``. An index file
273 305 ``index.json`` lists all stored conversation IDs.
274 306 """
275 307 secret_ws = get_secret_workspace_dir(user_id)