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

XFEstudio/gpt4free

refactor: replace constants module with config module

- Replaced imports of `STATIC_URL` from `..constants` to `..config` in: - `g4f/Provider/PollinationsAI.py` - `g4f/Provider/PollinationsImage.py` - Updated `client.py` to import `CONFIG_DIR` and `COOKIES_DIR` from `g4f.config` instead of defining platform-specific directories. - Changed the handling of conversation history in `ConversationManager`: - Updated `self.history` to retrieve data from `data.get("items", [])` instead of `data.get("history", [])`. - Modified the `stream_response` function to use `media` instead of `image` for handling media content. - Updated the `save_content` function to accept `media_content` of type `Optional[MediaResponse]` instead of `content`. - Adjusted the `run_client_args` function to handle media URLs and files more effectively, appending valid media to a list. - Removed the `constants.py` file and added a new `config.py` file to centralize configuration settings. - Updated the `CookiesConfig` class to set `cookies_dir` based on the existence of `CUSTOM_COOKIES_DIR`. - Adjusted the `render` function in `website.py` to correctly handle file paths and requests for HTML files. - Updated various references to use the new `config` module instead of the removed `constants` module.

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

代码差异

12 个文件 +103 -64
Modified g4f/Provider/PollinationsAI.py +1 -1
@@ -21,7 +21,7 @@ from ..image.copy_images import save_response_media
21 21 from ..image import use_aspect_ratio
22 22 from ..providers.response import FinishReason, Usage, ToolCalls, ImageResponse, Reasoning, TitleGeneration, SuggestedFollowups, ProviderInfo, AudioResponse
23 23 from ..tools.media import render_messages
24 from ..constants import STATIC_URL
24 from ..config import STATIC_URL
25 25 from .. import debug
26 26
27 27 DEFAULT_HEADERS = {
Modified g4f/Provider/PollinationsImage.py +1 -1
@@ -4,7 +4,7 @@ from typing import Optional
4 4
5 5 from .helper import format_media_prompt
6 6 from ..typing import AsyncResult, Messages, MediaListType
7 from ..constants import STATIC_URL
7 from ..config import STATIC_URL
8 8 from .PollinationsAI import PollinationsAI
9 9
10 10 class PollinationsImage(PollinationsAI):
Modified g4f/cli/client.py +52 -42
@@ -6,29 +6,20 @@ import asyncio
6 6 import json
7 7 import argparse
8 8 import traceback
9 import requests
9 10 from pathlib import Path
10 11 from typing import Optional, List, Dict
11 12 from g4f.client import AsyncClient
12 from g4f.providers.response import JsonConversation, is_content
13 from g4f.providers.response import JsonConversation, MediaResponse, is_content
13 14 from g4f.cookies import set_cookies_dir, read_cookie_files
14 15 from g4f.Provider import ProviderUtils
15 16 from g4f.image import extract_data_uri, is_accepted_format
16 17 from g4f.image.copy_images import get_media_dir
17 18 from g4f.client.helper import filter_markdown
19 from g4f.integration.markitdown import MarkItDown
20 from g4f.config import CONFIG_DIR, COOKIES_DIR
18 21 from g4f import debug
19 22
20 # Platform-appropriate directories
21 def get_config_dir() -> Path:
22 """Get platform-appropriate config directory."""
23 if sys.platform == "win32":
24 return Path(os.environ.get("APPDATA", Path.home() / "AppData" / "Roaming"))
25 elif sys.platform == "darwin":
26 return Path.home() / "Library" / "Application Support"
27 else: # Linux and other UNIX-like
28 return Path.home() / ".config"
29
30 CONFIG_DIR = get_config_dir() / "g4f-cli"
31 COOKIES_DIR = CONFIG_DIR / "cookies"
32 23 CONVERSATION_FILE = CONFIG_DIR / "conversation.json"
33 24
34 25 class ConversationManager:
@@ -59,7 +50,7 @@ class ConversationManager:
59 50 self.conversation = JsonConversation(**self.data.get(self.provider))
60 51 elif not self.provider and self.data:
61 52 self.conversation = JsonConversation(**self.data)
62 self.history = data.get("history", [])
53 self.history = data.get("items", [])
63 54 except (json.JSONDecodeError, KeyError) as e:
64 55 print(f"Error loading conversation: {e}", file=sys.stderr)
65 56 except Exception as e:
@@ -80,7 +71,7 @@ class ConversationManager:
80 71 "model": self.model,
81 72 "provider": self.provider,
82 73 "data": self.data,
83 "history": self.history
74 "items": self.history
84 75 }, f, indent=2, ensure_ascii=False)
85 76 except Exception as e:
86 77 print(f"Error saving conversation: {e}", file=sys.stderr)
@@ -101,9 +92,9 @@ async def stream_response(
101 92 instructions: Optional[str] = None
102 93 ) -> None:
103 94 """Stream the response from the API and update conversation."""
104 image = None
95 media = None
105 96 if isinstance(input_text, tuple):
106 image, input_text = input_text
97 media, input_text = input_text
107 98
108 99 if instructions:
109 100 # Add system instructions to conversation if provided
@@ -115,7 +106,7 @@ async def stream_response(
115 106 create_args = {
116 107 "messages": conversation.get_messages(),
117 108 "stream": True,
118 "image": image
109 "media": media
119 110 }
120 111
121 112 if conversation.model:
@@ -141,9 +132,10 @@ async def stream_response(
141 132 print("\n", end="")
142 133
143 134 conversation.conversation = getattr(last_chunk, 'conversation', None)
135 media_content = next(iter([chunk for chunk in response_content if isinstance(chunk, MediaResponse)]), None)
144 136 response_content = response_content[0] if len(response_content) == 1 else "".join([str(chunk) for chunk in response_content])
145 137 if output_file:
146 if save_content(response_content, output_file):
138 if save_content(response_content, media_content, output_file):
147 139 print(f"\nResponse saved to {output_file}")
148 140
149 141 if response_content:
@@ -152,13 +144,12 @@ async def stream_response(
152 144 else:
153 145 raise RuntimeError("No response received from the API")
154 146
155 def save_content(content, filepath: str, allowed_types = None):
156 if hasattr(content, "urls"):
157 import requests
158 for url in content.urls:
147 def save_content(content, media_content: Optional[MediaResponse], filepath: str, allowed_types = None):
148 if media_content is not None:
149 for url in media_content.urls:
159 150 if url.startswith("http://") or url.startswith("https://"):
160 151 try:
161 response = requests.get(url, cookies=content.get("cookies"), headers=content.get("headers"))
152 response = requests.get(url, cookies=media_content.get("cookies"), headers=media_content.get("headers"))
162 153 if response.status_code == 200:
163 154 with open(filepath, "wb") as f:
164 155 f.write(response.content)
@@ -279,25 +270,44 @@ async def run_args(input_text: str, args):
279 270
280 271 def run_client_args(args):
281 272 input_text = ""
282 if args.input and os.path.isfile(args.input[0]):
283 try:
284 with open(args.input[0], 'rb') as f:
285 if is_accepted_format(f.read(12)):
286 input_text = (Path(args.input[0]), " ".join(args.input[1:]))
287 except ValueError:
288 # If not a valid image, read as text
289 try:
290 with open(args.input[0], 'r', encoding='utf-8') as f:
291 file_content = f.read().strip()
292 except UnicodeDecodeError:
293 print(f"Error reading file {args.input[0]} as text. Ensure it is a valid text file.", file=sys.stderr)
294 sys.exit(1)
295 if len(args.input) > 1:
296 input_text = f"{' '.join(args.input[1:])}\n```{os.path.basename(args.input[0])}\n{file_content}\n```"
273 media = []
274 rest = 0
275 for idx, input_value in enumerate(args.input):
276 if input_value.startswith("http://") or input_value.startswith("https://"):
277 response = requests.head(input_value)
278 if not response.ok:
279 print(f"Error accessing URL {input_value}: {response.status_code}", file=sys.stderr)
280 break
281 if response.headers.get('Content-Type', '').startswith('image/'):
282 media.append(input_value)
297 283 else:
298 input_text = file_content
299 elif args.input:
300 input_text = (" ".join(args.input)).strip()
284 try:
285 md = MarkItDown()
286 text_content = md.convert_url(input_value).text_content
287 input_text += f"\n```\n{text_content}\n\nSource: {input_value}\n```\n"
288 except Exception as e:
289 print(f"Error processing URL {input_value}: {type(e).__name__}: {e}", file=sys.stderr)
290 break
291 elif os.path.isfile(input_value):
292 try:
293 with open(input_value, 'rb') as f:
294 if is_accepted_format(f.read(12)):
295 media.append(Path(input_value))
296 except ValueError:
297 # If not a valid image, read as text
298 try:
299 with open(input_value, 'r', encoding='utf-8') as f:
300 file_content = f.read().strip()
301 except UnicodeDecodeError:
302 print(f"Error reading file {input_value} as text. Ensure it is a valid text file.", file=sys.stderr)
303 break
304 input_text += f"\n```{input_value}\n{file_content}\n```\n"
305 else:
306 break
307 rest = idx + 1
308 input_text = (" ".join(args.input[rest:])).strip() + input_text
309 if media:
310 input_text = (media, input_text)
301 311 if not input_text:
302 312 input_text = sys.stdin.read().strip()
303 313 if not input_text:
Modified g4f/client/__init__.py +16 -7
@@ -50,11 +50,13 @@ def resolve_media(kwargs: dict, image = None, image_name: str = None) -> None:
50 50 kwargs["media"] = [(image, getattr(image, "name", image_name))]
51 51 elif "images" in kwargs:
52 52 kwargs["media"] = kwargs.pop("images")
53 if "media" in kwargs and not isinstance(kwargs["media"], list):
53 if kwargs.get("media") is None:
54 kwargs.pop("media", None)
55 elif not isinstance(kwargs["media"], list):
54 56 kwargs["media"] = [kwargs["media"]]
55 57 for idx, media in enumerate(kwargs.get("media", [])):
56 58 if not isinstance(media, (list, tuple)):
57 kwargs["media"][idx] = (media, os.path.basename(getattr(media, "name", "")))
59 kwargs["media"][idx] = (media, getattr(media, "name", None))
58 60
59 61 # Synchronous iter_response function
60 62 def iter_response(
@@ -433,12 +435,10 @@ class Images:
433 435 provider_handler = self.provider
434 436 if provider_handler is None:
435 437 provider_handler = self.client.models.get(model, default)
436 elif isinstance(provider, str):
437 provider_handler = convert_to_provider(provider)
438 438 else:
439 439 provider_handler = provider
440 if provider_handler is None:
441 return default
440 if isinstance(provider_handler, str):
441 provider_handler = convert_to_provider(provider_handler)
442 442 return provider_handler
443 443
444 444 async def async_generate(
@@ -538,13 +538,21 @@ class Images:
538 538 def create_variation(
539 539 self,
540 540 image: ImageType,
541 image_name: str = None,
542 prompt: str = "Create a variation of this image",
541 543 model: str = None,
542 544 provider: Optional[ProviderType] = None,
543 545 response_format: Optional[str] = None,
544 546 **kwargs
545 547 ) -> ImagesResponse:
546 548 return asyncio.run(self.async_create_variation(
547 image, model, provider, response_format, **kwargs
549 image=image,
550 image_name=image_name,
551 prompt=prompt,
552 model=model,
553 provider=provider,
554 response_format=response_format,
555 **kwargs
548 556 ))
549 557
550 558 async def async_create_variation(
@@ -619,6 +627,7 @@ class Images:
619 627 images = await asyncio.gather(*[get_b64_from_url(image) for image in response.get_list()])
620 628 else:
621 629 # Save locally for None (default) case
630 images = response.get_list()
622 631 if download_media or response.get("cookies") or response.get("headers"):
623 632 images = await copy_media(response.get_list(), response.get("cookies"), response.get("headers"), proxy, response.alt)
624 633 images = [Image.model_construct(url=image, revised_prompt=response.alt) for image in images]
Added g4f/config.py +24 -0
@@ -0,0 +1,24 @@
1 import os
2 import sys
3 from pathlib import Path
4
5 # Platform-appropriate directories
6 def get_config_dir() -> Path:
7 """Get platform-appropriate config directory."""
8 if sys.platform == "win32":
9 return Path(os.environ.get("APPDATA", Path.home() / "AppData" / "Roaming"))
10 elif sys.platform == "darwin":
11 return Path.home() / "Library" / "Application Support"
12 else: # Linux and other UNIX-like
13 return Path.home() / ".config"
14
15 CONFIG_DIR = get_config_dir() / "g4f"
16 COOKIES_DIR = CONFIG_DIR / "cookies"
17 CUSTOM_COOKIES_DIR = "./har_and_cookies"
18 PACKAGE_NAME = "g4f"
19 ORGANIZATION = "gpt4free"
20 GITHUB_REPOSITORY = f"xtekky/{ORGANIZATION}"
21 STATIC_DOMAIN = f"g4f.dev"
22 STATIC_URL = f"https://{STATIC_DOMAIN}/"
23 DIST_DIR = f"./{STATIC_DOMAIN}/dist"
24 DOWNLOAD_URL = f"https://raw.githubusercontent.com/{ORGANIZATION}/{STATIC_DOMAIN}/refs/heads/main/"
Deleted g4f/constants.py +0 -7
@@ -1,7 +0,0 @@
1 PACKAGE_NAME = "g4f"
2 ORGANIZATION = "gpt4free"
3 GITHUB_REPOSITORY = f"xtekky/{ORGANIZATION}"
4 STATIC_DOMAIN = f"g4f.dev"
5 STATIC_URL = f"https://{STATIC_DOMAIN}/"
6 DIST_DIR = f"./{STATIC_DOMAIN}/dist"
7 DOWNLOAD_URL = f"https://raw.githubusercontent.com/{ORGANIZATION}/{STATIC_DOMAIN}/refs/heads/main/"
Modified g4f/cookies.py +2 -1
@@ -44,11 +44,12 @@ except ImportError:
44 44
45 45 from .typing import Dict, Cookies
46 46 from .errors import MissingRequirementsError
47 from .config import COOKIES_DIR, CUSTOM_COOKIES_DIR
47 48 from . import debug
48 49
49 50 class CookiesConfig():
50 51 cookies: Dict[str, Cookies] = {}
51 cookies_dir: str = "./har_and_cookies"
52 cookies_dir: str = CUSTOM_COOKIES_DIR if os.path.exists(CUSTOM_COOKIES_DIR) else COOKIES_DIR
52 53
53 54 DOMAINS = [
54 55 ".bing.com",
Modified g4f/gui/server/website.py +4 -3
@@ -8,15 +8,16 @@ from flask import send_from_directory, redirect, request
8 8 from ...image.copy_images import secure_filename
9 9 from ...cookies import get_cookies_dir
10 10 from ...errors import VersionNotFoundError
11 from ...constants import STATIC_URL, DOWNLOAD_URL, DIST_DIR
11 from ...config import STATIC_URL, DOWNLOAD_URL, DIST_DIR
12 12 from ... import version
13 13
14 14 def redirect_home():
15 15 return redirect('/chat/')
16 16
17 17 def render(filename = "home"):
18 filename += ("" if "." in filename else ".html")
18 19 if os.path.exists(DIST_DIR) and not request.args.get("debug"):
19 path = os.path.abspath(os.path.join(os.path.dirname(DIST_DIR), (filename + ("" if "." in filename else ".html"))))
20 path = os.path.abspath(os.path.join(os.path.dirname(DIST_DIR), filename))
20 21 return send_from_directory(os.path.dirname(path), os.path.basename(path))
21 22 try:
22 23 latest_version = version.utils.latest_version
@@ -31,7 +32,7 @@ def render(filename = "home"):
31 32 is_temp = True
32 33 else:
33 34 os.makedirs(cache_dir, exist_ok=True)
34 response = requests.get(f"{DOWNLOAD_URL}{filename}.html")
35 response = requests.get(f"{DOWNLOAD_URL}{filename}")
35 36 if not response.ok:
36 37 found = None
37 38 for root, _, files in os.walk(cache_dir):
Modified g4f/image/__init__.py +1 -1
@@ -251,7 +251,7 @@ def to_bytes(image: ImageType) -> bytes:
251 251 elif image.startswith("http://") or image.startswith("https://"):
252 252 path: str = urlparse(image).path
253 253 if path.startswith("/files/"):
254 path = get_bucket_dir(path.split(path, "/")[1:])
254 path = get_bucket_dir(*path.split("/")[2:])
255 255 if os.path.exists(path):
256 256 return Path(path).read_bytes()
257 257 else:
Modified g4f/tools/files.py +1 -0
@@ -438,6 +438,7 @@ async def download_urls(
438 438 if text_content:
439 439 filename = get_filename_from_url(url)
440 440 target = bucket_dir / filename
441 text_content = f"{text_content.strip()}\n\nSource: {url}\n"
441 442 target.write_text(text_content, errors="replace")
442 443 return filename
443 444 except Exception as e:
Modified g4f/version.py +1 -1
@@ -6,7 +6,7 @@ from functools import cached_property
6 6 from importlib.metadata import version as get_package_version, PackageNotFoundError
7 7 from subprocess import check_output, CalledProcessError, PIPE
8 8 from .errors import VersionNotFoundError
9 from .constants import PACKAGE_NAME, GITHUB_REPOSITORY
9 from .config import PACKAGE_NAME, GITHUB_REPOSITORY
10 10 from . import debug
11 11
12 12 def get_pypi_version(package_name: str) -> str:
Deleted har_and_cookies/.gitkeep +0 -0
此文件没有可显示的逐行差异。