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

XFEstudio/gpt4free

Add AsyncAuthedProvider in Copilot Add orginal url to downloaded image Support ssl argument in StreamSession Report Provider and Errors in RetryProvider Support ssl argument in OpenaiTemplate Remove model duplication in OpenaiChat Disable ChatGpt provider and remove it from models.py Update slim requirements Support provider names as model name in Image generation Add model qwen-2.5-1m-demo to models.py

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

代码差异

20 个文件 +170 -129
Modified g4f/Provider/ChatGpt.py +1 -1
@@ -76,7 +76,7 @@ def init_session(user_agent):
76 76 class ChatGpt(AbstractProvider, ProviderModelMixin):
77 77 label = "ChatGpt"
78 78 url = "https://chatgpt.com"
79 working = True
79 working = False
80 80 supports_message_history = True
81 81 supports_system_message = True
82 82 supports_stream = True
Modified g4f/Provider/Copilot.py +0 -1
@@ -92,7 +92,6 @@ class Copilot(AbstractProvider, ProviderModelMixin):
92 92 cls._access_token, cls._cookies = asyncio.run(get_access_token_and_cookies(cls.url, proxy))
93 93 else:
94 94 raise h
95 yield Parameters(**{"api_key": cls._access_token, "cookies": cls._cookies if isinstance(cls._cookies, dict) else {c.name: c.value for c in cls._cookies}})
96 95 websocket_url = f"{websocket_url}&accessToken={quote(cls._access_token)}"
97 96 headers = {"authorization": f"Bearer {cls._access_token}"}
98 97
Modified g4f/Provider/needs_auth/CopilotAccount.py +45 -3
@@ -1,8 +1,16 @@
1 1 from __future__ import annotations
2 2
3 from ..Copilot import Copilot
3 import os
4 from typing import AsyncIterator
4 5
5 class CopilotAccount(Copilot):
6 from ..base_provider import AsyncAuthedProvider
7 from ..Copilot import Copilot, readHAR, has_nodriver, get_access_token_and_cookies
8 from ...providers.response import AuthResult, RequestLogin
9 from ...typing import AsyncResult, Messages
10 from ...errors import NoValidHarFileError
11 from ... import debug
12
13 class CopilotAccount(AsyncAuthedProvider, Copilot):
6 14 needs_auth = True
7 15 use_nodriver = True
8 16 parent = "Copilot"
@@ -12,4 +20,38 @@ class CopilotAccount(Copilot):
12 20 image_models = models
13 21 model_aliases = {
14 22 "dall-e-3": default_model
15 }
23 }
24
25 @classmethod
26 async def on_auth_async(cls, proxy: str = None, **kwargs) -> AsyncIterator:
27 if cls._access_token is None:
28 try:
29 cls._access_token, cls._cookies = readHAR(cls.url)
30 except NoValidHarFileError as h:
31 debug.log(f"Copilot: {h}")
32 if has_nodriver:
33 login_url = os.environ.get("G4F_LOGIN_URL")
34 if login_url:
35 yield RequestLogin(cls.label, login_url)
36 cls._access_token, cls._cookies = await get_access_token_and_cookies(cls.url, proxy)
37 else:
38 raise h
39 yield AuthResult(
40 api_key=cls._access_token,
41 cookies=cls._cookies,
42 )
43
44 @classmethod
45 async def create_authed(
46 cls,
47 model: str,
48 messages: Messages,
49 auth_result: AuthResult,
50 **kwargs
51 ) -> AsyncResult:
52 Copilot._access_token = getattr(auth_result, "api_key")
53 Copilot._cookies = getattr(auth_result, "cookies")
54 Copilot.needs_auth = cls.needs_auth
55 for chunk in Copilot.create_completion(model, messages, **kwargs):
56 yield chunk
57 auth_result.cookies = Copilot._cookies if isinstance(Copilot._cookies, dict) else {c.name: c.value for c in Copilot._cookies}
Modified g4f/Provider/needs_auth/OpenaiAccount.py +1 -6
@@ -4,9 +4,4 @@ from .OpenaiChat import OpenaiChat
4 4
5 5 class OpenaiAccount(OpenaiChat):
6 6 needs_auth = True
7 parent = "OpenaiChat"
8 default_model = "gpt-4o"
9 default_vision_model = default_model
10 default_image_model = OpenaiChat.default_image_model
11 image_models = [default_model, default_image_model, "gpt-4"]
12 fallback_models = [*OpenaiChat.fallback_models, default_image_model]
7 parent = "OpenaiChat"
Modified g4f/Provider/needs_auth/OpenaiChat.py +3 -29
@@ -98,8 +98,9 @@ class OpenaiChat(AsyncAuthedProvider, ProviderModelMixin):
98 98 default_model = "auto"
99 99 default_image_model = "dall-e-3"
100 100 image_models = [default_image_model]
101 fallback_models = [default_model, "gpt-4", "gpt-4o", "gpt-4o-mini", "gpt-4o-canmore", "o1", "o1-preview", "o1-mini"] +image_models
102 vision_models = fallback_models
101 text_models = [default_model, "gpt-4", "gpt-4o", "gpt-4o-mini", "gpt-4o-canmore", "o1", "o1-preview", "o1-mini"]
102 vision_models = text_models
103 models = text_models + image_models
103 104 synthesize_content_type = "audio/mpeg"
104 105
105 106 _api_key: str = None
@@ -120,33 +121,6 @@ class OpenaiChat(AsyncAuthedProvider, ProviderModelMixin):
120 121 turnstile_token=RequestConfig.turnstile_token
121 122 )
122 123
123 @classmethod
124 def get_models(cls, proxy: str = None, timeout: int = 180) -> List[str]:
125 if not cls.models:
126 # try:
127 # headers = {
128 # **(cls.get_default_headers() if cls._headers is None else cls._headers),
129 # "accept": "application/json",
130 # }
131 # with Session(
132 # proxy=proxy,
133 # impersonate="chrome",
134 # timeout=timeout,
135 # headers=headers
136 # ) as session:
137 # response = session.get(
138 # f"{cls.url}/backend-anon/models"
139 # if cls._api_key is None else
140 # f"{cls.url}/backend-api/models"
141 # )
142 # raise_for_status(response)
143 # data = response.json()
144 # cls.models = [model.get("slug") for model in data.get("models")]
145 # except Exception as e:
146 # debug.log(f"OpenaiChat: Failed to get models: {type(e).__name__}: {e}")
147 cls.models = cls.fallback_models
148 return cls.models
149
150 124 @classmethod
151 125 async def upload_images(
152 126 cls,
Modified g4f/Provider/needs_auth/OpenaiTemplate.py +6 -5
@@ -20,6 +20,7 @@ class OpenaiTemplate(AsyncGeneratorProvider, ProviderModelMixin, RaiseErrorMixin
20 20 default_model = ""
21 21 fallback_models = []
22 22 sort_models = True
23 ssl = None
23 24
24 25 @classmethod
25 26 def get_models(cls, api_key: str = None, api_base: str = None) -> list[str]:
@@ -30,7 +31,7 @@ class OpenaiTemplate(AsyncGeneratorProvider, ProviderModelMixin, RaiseErrorMixin
30 31 api_base = cls.api_base
31 32 if api_key is not None:
32 33 headers["authorization"] = f"Bearer {api_key}"
33 response = requests.get(f"{api_base}/models", headers=headers)
34 response = requests.get(f"{api_base}/models", headers=headers, verify=cls.ssl)
34 35 raise_for_status(response)
35 36 data = response.json()
36 37 data = data.get("data") if isinstance(data, dict) else data
@@ -79,12 +80,12 @@ class OpenaiTemplate(AsyncGeneratorProvider, ProviderModelMixin, RaiseErrorMixin
79 80 api_base = cls.api_base
80 81
81 82 # Proxy for image generation feature
82 if model in cls.image_models:
83 if model and model in cls.image_models:
83 84 data = {
84 85 "prompt": messages[-1]["content"] if prompt is None else prompt,
85 86 "model": model,
86 87 }
87 async with session.post(f"{api_base.rstrip('/')}/images/generations", json=data) as response:
88 async with session.post(f"{api_base.rstrip('/')}/images/generations", json=data, ssl=cls.ssl) as response:
88 89 data = await response.json()
89 90 cls.raise_error(data)
90 91 await raise_for_status(response)
@@ -119,7 +120,7 @@ class OpenaiTemplate(AsyncGeneratorProvider, ProviderModelMixin, RaiseErrorMixin
119 120 )
120 121 if api_endpoint is None:
121 122 api_endpoint = f"{api_base.rstrip('/')}/chat/completions"
122 async with session.post(api_endpoint, json=data) as response:
123 async with session.post(api_endpoint, json=data, ssl=cls.ssl) as response:
123 124 content_type = response.headers.get("content-type", "text/event-stream" if stream else "application/json")
124 125 if content_type.startswith("application/json"):
125 126 data = await response.json()
@@ -180,7 +181,7 @@ class OpenaiTemplate(AsyncGeneratorProvider, ProviderModelMixin, RaiseErrorMixin
180 181 "Content-Type": "application/json",
181 182 **(
182 183 {"Authorization": f"Bearer {api_key}"}
183 if api_key is not None else {}
184 if api_key else {}
184 185 ),
185 186 **({} if headers is None else headers)
186 187 }
Modified g4f/client/__init__.py +4 -0
@@ -70,6 +70,8 @@ def iter_response(
70 70 continue
71 71 elif isinstance(chunk, SynthesizeData) or not chunk:
72 72 continue
73 elif isinstance(chunk, Exception):
74 continue
73 75
74 76 chunk = str(chunk)
75 77 content += chunk
@@ -149,6 +151,8 @@ async def async_iter_response(
149 151 continue
150 152 elif isinstance(chunk, SynthesizeData) or not chunk:
151 153 continue
154 elif isinstance(chunk, Exception):
155 continue
152 156
153 157 chunk = str(chunk)
154 158 content += chunk
Modified g4f/client/image_models.py +6 -5
@@ -1,14 +1,15 @@
1 1 from __future__ import annotations
2 2
3 3 from ..models import ModelUtils
4 from ..Provider import ProviderUtils
4 5
5 6 class ImageModels():
6 7 def __init__(self, client):
7 8 self.client = client
8 self.models = ModelUtils.convert
9 9
10 10 def get(self, name, default=None):
11 model = self.models.get(name)
12 if model and model.best_provider:
13 return model.best_provider
14 return default
11 if name in ModelUtils.convert:
12 return ModelUtils.convert[name].best_provider
13 if name in ProviderUtils.convert:
14 return ProviderUtils.convert[name]
15 return default
Modified g4f/gui/client/static/js/chat.v1.js +16 -10
@@ -722,12 +722,13 @@ async function add_message_chunk(message, message_id, provider, scroll, finish_m
722 722 `;
723 723 } else if (message.type == "message") {
724 724 console.error(message.message)
725 await api("log", {...message, provider: provider_storage[message_id]});
725 726 } else if (message.type == "error") {
726 727 content_map.update_timeouts.forEach((timeoutId)=>clearTimeout(timeoutId));
727 728 content_map.update_timeouts = [];
728 error_storage[message_id] = message.error
729 console.error(message.error);
730 content_map.inner.innerHTML += markdown_render(`**An error occured:** ${message.error}`);
729 error_storage[message_id] = message.message
730 console.error(message.message);
731 content_map.inner.innerHTML += markdown_render(`**An error occured:** ${message.message}`);
731 732 let p = document.createElement("p");
732 733 p.innerText = message.error;
733 734 log_storage.appendChild(p);
@@ -865,19 +866,23 @@ const ask_gpt = async (message_id, message_index = -1, regenerate = false, provi
865 866 }
866 867 if (message_storage[message_id]) {
867 868 const message_provider = message_id in provider_storage ? provider_storage[message_id] : null;
868 let usage;
869 let usage = {};
869 870 if (usage_storage[message_id]) {
870 871 usage = usage_storage[message_id];
871 872 delete usage_storage[message_id];
872 873 }
873 // Calculate usage if we have no usage result jet
874 if (document.getElementById("track_usage").checked && !usage && window.GPTTokenizer_cl100k_base) {
874 usage = {
875 model: message_provider?.model,
876 provider: message_provider?.name,
877 ...usage
878 }
879 // Calculate usage if we don't have it jet
880 if (document.getElementById("track_usage").checked && !usage.prompt_tokens && window.GPTTokenizer_cl100k_base) {
875 881 const prompt_token_model = model?.startsWith("gpt-3") ? "gpt-3.5-turbo" : "gpt-4"
876 882 const prompt_tokens = GPTTokenizer_cl100k_base?.encodeChat(messages, prompt_token_model).length;
877 883 const completion_tokens = count_tokens(message_provider?.model, message_storage[message_id]);
878 884 usage = {
879 model: message_provider?.model,
880 provider: message_provider?.name,
885 ...usage,
881 886 prompt_tokens: prompt_tokens,
882 887 completion_tokens: completion_tokens,
883 888 total_tokens: prompt_tokens + completion_tokens
@@ -1748,9 +1753,10 @@ function update_message(content_map, message_id, content = null, scroll = true)
1748 1753 content = content.substring(0, lastIndex) + '<span class="cursor"></span>' + lastElement;
1749 1754 }
1750 1755 }
1751 content_map.inner.innerHTML = content;
1752 1756 if (error_storage[message_id]) {
1753 content_map.inner.innerHTML += markdown_render(`**An error occured:** ${error_storage[message_id]}`);
1757 content_map.inner.innerHTML = message + markdown_render(`**An error occured:** ${error_storage[message_id]}`);
1758 } else {
1759 content_map.inner.innerHTML = content;
1754 1760 }
1755 1761 content_map.count.innerText = count_words_and_tokens(message_storage[message_id], provider_storage[message_id]?.model);
1756 1762 highlight(content_map.inner);
Modified g4f/gui/server/api.py +22 -29
@@ -12,9 +12,9 @@ from ...image import ImagePreview, ImageResponse, copy_images, ensure_images_dir
12 12 from ...tools.run_tools import iter_run_tools
13 13 from ...Provider import ProviderUtils, __providers__
14 14 from ...providers.base_provider import ProviderModelMixin
15 from ...providers.retry_provider import IterListProvider
15 from ...providers.retry_provider import BaseRetryProvider
16 16 from ...providers.response import BaseConversation, JsonConversation, FinishReason, Usage, Reasoning
17 from ...providers.response import SynthesizeData, TitleGeneration, RequestLogin, Parameters
17 from ...providers.response import SynthesizeData, TitleGeneration, RequestLogin, Parameters, ProviderInfo
18 18 from ... import version, models
19 19 from ... import ChatCompletion, get_model_and_provider
20 20 from ... import debug
@@ -154,41 +154,32 @@ class Api:
154 154 )
155 155 except Exception as e:
156 156 logger.exception(e)
157 yield self._format_json('error', get_error_message(e))
157 yield self._format_json('error', type(e).__name__, message=get_error_message(e))
158 158 return
159 params = {
160 **(provider_handler.get_parameters(as_json=True) if hasattr(provider_handler, "get_parameters") else {}),
161 "model": model,
162 "messages": kwargs.get("messages"),
163 }
164 if isinstance(kwargs.get("conversation"), JsonConversation):
165 params["conversation"] = kwargs.get("conversation").get_dict()
166 else:
167 params["conversation_id"] = conversation_id
168 if kwargs.get("api_key") is not None:
169 params["api_key"] = kwargs["api_key"]
170 yield self._format_json("parameters", params)
171 first = True
159 if not isinstance(provider_handler, BaseRetryProvider):
160 yield self.handle_provider(provider_handler, model)
161 if hasattr(provider_handler, "get_parameters"):
162 yield self._format_json("parameters", provider_handler.get_parameters(as_json=True))
172 163 try:
173 164 result = iter_run_tools(ChatCompletion.create, **{**kwargs, "model": model, "provider": provider_handler})
174 165 for chunk in result:
175 if first:
176 first = False
177 yield self.handle_provider(provider_handler, model)
178 if isinstance(chunk, BaseConversation):
166 if isinstance(chunk, ProviderInfo):
167 yield self.handle_provider(chunk, model)
168 provider = chunk.name
169 elif isinstance(chunk, BaseConversation):
179 170 if provider is not None:
180 171 if provider not in conversations:
181 172 conversations[provider] = {}
182 173 conversations[provider][conversation_id] = chunk
183 174 if isinstance(chunk, JsonConversation):
184 175 yield self._format_json("conversation", {
185 provider.__name__ if isinstance(provider, type) else provider: chunk.get_dict()
176 provider: chunk.get_dict()
186 177 })
187 178 else:
188 179 yield self._format_json("conversation_id", conversation_id)
189 180 elif isinstance(chunk, Exception):
190 181 logger.exception(chunk)
191 yield self._format_json("message", get_error_message(chunk))
182 yield self._format_json('message', get_error_message(chunk), error=type(chunk).__name__)
192 183 elif isinstance(chunk, ImagePreview):
193 184 yield self._format_json("preview", chunk.to_string())
194 185 elif isinstance(chunk, ImageResponse):
@@ -219,9 +210,11 @@ class Api:
219 210 debug.logs = []
220 211 except Exception as e:
221 212 logger.exception(e)
222 yield self._format_json('error', get_error_message(e))
223 if first:
224 yield self.handle_provider(provider_handler, model)
213 if debug.logs:
214 for log in debug.logs:
215 yield self._format_json("log", str(log))
216 debug.logs = []
217 yield self._format_json('error', type(e).__name__, message=get_error_message(e))
225 218
226 219 def _format_json(self, response_type: str, content = None, **kwargs):
227 220 if content is not None:
@@ -235,11 +228,11 @@ class Api:
235 228 }
236 229
237 230 def handle_provider(self, provider_handler, model):
238 if isinstance(provider_handler, IterListProvider) and provider_handler.last_provider is not None:
231 if isinstance(provider_handler, BaseRetryProvider) and provider_handler.last_provider is not None:
239 232 provider_handler = provider_handler.last_provider
240 if not model and hasattr(provider_handler, "last_model") and provider_handler.last_model is not None:
241 model = provider_handler.last_model
242 return self._format_json("provider", {**provider_handler.get_dict(), "model": model})
233 if model:
234 return self._format_json("provider", {**provider_handler.get_dict(), "model": model})
235 return self._format_json("provider", provider_handler.get_dict())
243 236
244 237 def get_error_message(exception: Exception) -> str:
245 238 return f"{type(exception).__name__}: {exception}"
Modified g4f/gui/server/backend_api.py +2 -1
@@ -277,7 +277,8 @@ class Backend_Api(Api):
277 277 return Response(filter_markdown(response, do_filter_markdown), mimetype='text/plain')
278 278 def cast_str():
279 279 for chunk in response:
280 yield str(chunk)
280 if not isinstance(chunk, Exception):
281 yield str(chunk)
281 282 return Response(cast_str(), mimetype='text/plain')
282 283 except Exception as e:
283 284 logger.exception(e)
Modified g4f/image.py +36 -24
@@ -242,36 +242,48 @@ def ensure_images_dir():
242 242 async def copy_images(
243 243 images: list[str],
244 244 cookies: Optional[Cookies] = None,
245 proxy: Optional[str] = None
245 proxy: Optional[str] = None,
246 add_url: bool = True,
247 target: str = None,
248 ssl: bool = None
246 249 ) -> list[str]:
250 if add_url:
251 add_url = not cookies
247 252 ensure_images_dir()
248 253 async with ClientSession(
249 254 connector=get_connector(proxy=proxy),
250 255 cookies=cookies
251 256 ) as session:
252 async def copy_image(image: str) -> str:
253 target = os.path.join(images_dir, f"{int(time.time())}_{str(uuid.uuid4())}")
254 if image.startswith("data:"):
255 with open(target, "wb") as f:
256 f.write(extract_data_uri(image))
257 else:
258 try:
259 async with session.get(image) as response:
260 response.raise_for_status()
261 with open(target, "wb") as f:
262 async for chunk in response.content.iter_chunked(4096):
263 f.write(chunk)
264 except ClientError as e:
265 debug.log(f"copy_images failed: {e.__class__.__name__}: {e}")
266 return image
267 with open(target, "rb") as f:
268 extension = is_accepted_format(f.read(12)).split("/")[-1]
269 extension = "jpg" if extension == "jpeg" else extension
270 new_target = f"{target}.{extension}"
271 os.rename(target, new_target)
272 return f"/images/{os.path.basename(new_target)}"
273
274 return await asyncio.gather(*[copy_image(image) for image in images])
257 async def copy_image(image: str, target: str = None) -> str:
258 if target is None or len(images) > 1:
259 target = os.path.join(images_dir, f"{int(time.time())}_{str(uuid.uuid4())}")
260 try:
261 if image.startswith("data:"):
262 with open(target, "wb") as f:
263 f.write(extract_data_uri(image))
264 else:
265 try:
266 async with session.get(image, ssl=ssl) as response:
267 response.raise_for_status()
268 with open(target, "wb") as f:
269 async for chunk in response.content.iter_chunked(4096):
270 f.write(chunk)
271 except ClientError as e:
272 debug.log(f"copy_images failed: {e.__class__.__name__}: {e}")
273 return image
274 if "." not in target:
275 with open(target, "rb") as f:
276 extension = is_accepted_format(f.read(12)).split("/")[-1]
277 extension = "jpg" if extension == "jpeg" else extension
278 new_target = f"{target}.{extension}"
279 os.rename(target, new_target)
280 target = new_target
281 finally:
282 if "." not in target and os.path.exists(target):
283 os.unlink(target)
284 return f"/images/{os.path.basename(target)}{'?url=' + image if add_url and not image.startswith('data:') else ''}"
285
286 return await asyncio.gather(*[copy_image(image, target) for image in images])
275 287
276 288 class ImageDataResponse():
277 289 def __init__(
Modified g4f/models.py +10 -7
Modified g4f/providers/base_provider.py +1 -1
Modified g4f/providers/response.py +4 -0
Modified g4f/providers/retry_provider.py +5 -1
Modified g4f/requests/curl_cffi.py +2 -2
Modified g4f/requests/raise_for_status.py +4 -2
Modified requirements-slim.txt +2 -1
Modified setup.py +0 -1