返回提交历史
Modified
g4f/gui/server/website.py
+85
-74
XFEstudio/gpt4free
perf(gui): cache provider metadata and precompiled template in website server
4c633be1
代码差异
1 个文件
+85
-74
@@ -4,6 +4,8 @@ import asyncio
4
4
import os
5
5
import inspect
6
6
import requests
7
import re
8
import time
7
9
from datetime import datetime
8
10
from urllib.parse import quote, unquote
9
11
from flask import send_from_directory, redirect, request
@@ -15,6 +17,12 @@ from ...config import STATIC_URL, DOWNLOAD_URL, DIST_DIR, GITHUB_URL
15
17
from ... import version
16
18
17
19
_gui_session = requests.Session()
20
_CONTENT_PATTERN = re.compile(r"<!-- CONTENT_START -->.*?<!-- CONTENT_END -->", re.DOTALL)
21
_providers_cache: list[dict] | None = None
22
_providers_cache_time: float = 0.0
23
_providers_cards_cache: str | None = None
24
_PROVIDERS_TTL: float = 300.0
25
_template_cache: dict[str, str] = {}
18
26
19
27
20
28
def redirect_home():
@@ -173,7 +181,12 @@ class Website:
173
181
return render("stats")
174
182
175
183
def _get_providers(self):
176
"""Load all providers and return a list of dicts with their attributes."""
184
"""Load all providers and return a list of dicts with their attributes (cached with 300s TTL)."""
185
global _providers_cache, _providers_cache_time
186
now = time.time()
187
if _providers_cache is not None and (now - _providers_cache_time) < _PROVIDERS_TTL:
188
return _providers_cache
189
177
190
from g4f.Provider import ProviderLoader
178
191
179
192
providers = []
@@ -206,65 +219,66 @@ class Website:
206
219
})
207
220
except Exception:
208
221
pass
222
_providers_cache = providers
223
_providers_cache_time = now
209
224
return providers
210
225
211
226
def _providers(self):
227
global _providers_cards_cache
212
228
providers = self._get_providers()
213
229
214
# Build HTML cards
215
cards_html = """
216
<div class="page-header">
217
<h1>Available Providers</h1>
218
<p>Browse the list of AI providers supported by G4F</p>
219
</div>
230
template_path = os.path.join(os.path.dirname(__file__), "providers.html")
231
if not os.path.exists(template_path):
232
return "Providers template not found"
220
233
221
<div class="providers-list">
222
"""
223
for p in providers:
224
models_html = ""
225
if p["models"]:
226
models_list = ", ".join(p["models"][:5]) if isinstance(p["models"], list) else ""
227
if len(p["models"]) > 5:
228
models_list += f" (+{len(p['models']) - 5} more)"
229
models_html = f"<div class='provider-details'><strong>Models:</strong> {models_list}</div>"
230
else:
231
models_html = "<div class='provider-details'><em>No specific models</em></div>"
232
233
url_html = f"<div class='provider-url'>{p['url']}</div>" if p["url"] else ""
234
auth_html = "<div class='provider-details'><strong>Auth:</strong> Required</div>" if p["needs_auth"] else ""
235
working_html = "<div class='provider-details'><strong>Status:</strong> Working</div>" if p["working"] else ""
236
237
cards_html += f"""
238
<div class="provider-card" onclick="window.location.href='/providers/{p['name']}'">
239
<div class="provider-name">{p['name']}</div>
240
{url_html}
241
{models_html}
242
{auth_html}
243
{working_html}
244
<div class="provider-actions">
245
<a href="/providers/{p['name']}" class="btn btn-primary">Details</a>
246
<a href="{p['url']}" target="_blank" class="btn btn-secondary">Website</a>
247
</div>
234
if template_path not in _template_cache:
235
with open(template_path, "r", encoding="utf-8") as f:
236
_template_cache[template_path] = f.read()
237
html = _template_cache[template_path]
238
239
if _providers_cards_cache is None or (time.time() - _providers_cache_time) >= _PROVIDERS_TTL:
240
# Build HTML cards
241
cards_html = """
242
<div class="page-header">
243
<h1>Available Providers</h1>
244
<p>Browse the list of AI providers supported by G4F</p>
248
245
</div>
249
"""
250
cards_html += "\n </div>"
251
246
252
# Read the template
253
template_path = os.path.join(os.path.dirname(__file__), "providers.html")
254
if os.path.exists(template_path):
255
with open(template_path, "r", encoding="utf-8") as f:
256
html = f.read()
257
# Replace content between markers
258
import re
259
html = re.sub(
260
r"<!-- CONTENT_START -->.*?<!-- CONTENT_END -->",
261
f"<!-- CONTENT_START -->{cards_html}<!-- CONTENT_END -->",
262
html,
263
flags=re.DOTALL,
264
)
265
return html
247
<div class="providers-list">
248
"""
249
for p in providers:
250
models_html = ""
251
if p["models"]:
252
models_list = ", ".join(p["models"][:5]) if isinstance(p["models"], list) else ""
253
if len(p["models"]) > 5:
254
models_list += f" (+{len(p['models']) - 5} more)"
255
models_html = f"<div class='provider-details'><strong>Models:</strong> {models_list}</div>"
256
else:
257
models_html = "<div class='provider-details'><em>No specific models</em></div>"
258
259
url_html = f"<div class='provider-url'>{p['url']}</div>" if p["url"] else ""
260
auth_html = "<div class='provider-details'><strong>Auth:</strong> Required</div>" if p["needs_auth"] else ""
261
working_html = "<div class='provider-details'><strong>Status:</strong> Working</div>" if p["working"] else ""
262
263
cards_html += f"""
264
<div class="provider-card" onclick="window.location.href='/providers/{p['name']}'">
265
<div class="provider-name">{p['name']}</div>
266
{url_html}
267
{models_html}
268
{auth_html}
269
{working_html}
270
<div class="provider-actions">
271
<a href="/providers/{p['name']}" class="btn btn-primary">Details</a>
272
<a href="{p['url']}" target="_blank" class="btn btn-secondary">Website</a>
273
</div>
274
</div>
275
"""
276
cards_html += "\n </div>"
277
_providers_cards_cache = cards_html
266
278
else:
267
return "Providers template not found"
279
cards_html = _providers_cards_cache
280
281
return _CONTENT_PATTERN.sub(f"<!-- CONTENT_START -->{cards_html}<!-- CONTENT_END -->", html)
268
282
269
283
def _provider_detail(self, name: str = ""):
270
284
from html import escape
@@ -288,15 +302,16 @@ class Website:
288
302
289
303
# Build models list HTML
290
304
if p["models"]:
291
if callable(p["models"]):
305
models = p["models"]
306
if callable(models):
292
307
try:
293
p["models"] = p["models"]()
308
models = models()
294
309
except Exception:
295
p["models"] = []
296
if inspect.isawaitable(p["models"]):
297
p["models"] = asyncio.run(p["models"])
310
models = []
311
if inspect.isawaitable(models):
312
models = []
298
313
models_html = "<ul class='model-list'>" + "".join(
299
f"<li>{escape(str(m))}</li>" for m in p["models"]
314
f"<li>{escape(str(m))}</li>" for m in (models if isinstance(models, list) else list(models) if models else [])
300
315
) + "</ul>"
301
316
else:
302
317
models_html = "<p><em>No specific models listed</em></p>"
@@ -439,25 +454,21 @@ class Website:
439
454
440
455
# Read the template and inject detail content
441
456
template_path = os.path.join(os.path.dirname(__file__), "providers.html")
442
if os.path.exists(template_path):
443
with open(template_path, "r", encoding="utf-8") as f:
444
html = f.read()
445
# Replace content between markers
446
import re
447
html = re.sub(
448
r"<!-- CONTENT_START -->.*?<!-- CONTENT_END -->",
449
f"<!-- CONTENT_START -->{detail_html}<!-- CONTENT_END -->",
450
html,
451
flags=re.DOTALL,
452
)
453
html = html.replace(
454
"<title>Providers</title>",
455
f"<title>{escape(p['name'])} – Provider Details</title>"
456
)
457
return html
458
else:
457
if not os.path.exists(template_path):
459
458
return "Providers template not found"
460
459
460
if template_path not in _template_cache:
461
with open(template_path, "r", encoding="utf-8") as f:
462
_template_cache[template_path] = f.read()
463
html = _template_cache[template_path]
464
465
html = _CONTENT_PATTERN.sub(f"<!-- CONTENT_START -->{detail_html}<!-- CONTENT_END -->", html)
466
html = html.replace(
467
"<title>Providers</title>",
468
f"<title>{escape(p['name'])} – Provider Details</title>"
469
)
470
return html
471
461
472
def _chat(self, filename=""):
462
473
filename = f"chat/{filename}" if filename else "chat/index"
463
474
return render(filename)