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

XFEstudio/gpt4free

Add login utl to HuggingFace in Web UI Add some HuggingSpace providers Add icon to home.html Remove duplicate lines from web search results Fix object async_generator can't be used in 'await' Fix for continue message

c908dba0
Heiner Lohaus <hlohaus@users.noreply.github.com>
提交于

代码差异

14 个文件 +206 -45
Modified g4f/Provider/Jmuz.py +3 -0
@@ -5,6 +5,7 @@ from .needs_auth.OpenaiAPI import OpenaiAPI
5 5
6 6 class Jmuz(OpenaiAPI):
7 7 label = "Jmuz"
8 login_url = None
8 9 api_base = "https://jmuz.me/gpt/api/v2"
9 10 api_key = "prod"
10 11
@@ -33,6 +34,8 @@ class Jmuz(OpenaiAPI):
33 34 model: str,
34 35 messages: Messages,
35 36 stream: bool = False,
37 aoi_key: str = None,
38 api_base: str = None,
36 39 **kwargs
37 40 ) -> AsyncResult:
38 41 model = cls.get_model(model)
Modified g4f/Provider/PollinationsAI.py +4 -4
@@ -170,13 +170,13 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin):
170 170 params = {k: v for k, v in params.items() if v is not None}
171 171
172 172 async with ClientSession(headers=headers) as session:
173 prompt = quote(messages[-1]["content"] if prompt is None else prompt)
173 prompt = messages[-1]["content"] if prompt is None else prompt
174 174 param_string = "&".join(f"{k}={v}" for k, v in params.items())
175 url = f"{cls.image_api_endpoint}/prompt/{prompt}?{param_string}"
175 url = f"{cls.image_api_endpoint}/prompt/{quote(prompt)}?{param_string}"
176 176
177 177 async with session.head(url, proxy=proxy) as response:
178 178 if response.status == 200:
179 image_response = ImageResponse(images=url, alt=messages[-1]["content"] if prompt is None else prompt)
179 image_response = ImageResponse(images=url, alt=prompt)
180 180 yield image_response
181 181
182 182 @classmethod
@@ -225,4 +225,4 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin):
225 225 content = json_response['choices'][0]['message']['content']
226 226 yield content
227 227 except json.JSONDecodeError:
228 yield decoded_chunk
228 pass
Added g4f/Provider/hf_space/Qwen_QVQ_72B.py +70 -0
@@ -0,0 +1,70 @@
1 from __future__ import annotations
2
3 import json
4 from aiohttp import ClientSession, FormData
5
6 from ...typing import AsyncResult, Messages, ImagesType
7 from ...requests import raise_for_status
8 from ...errors import ResponseError
9 from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin
10 from ..helper import format_prompt, get_random_string
11 from ...image import to_bytes, is_accepted_format
12
13 class Qwen_QVQ_72B(AsyncGeneratorProvider, ProviderModelMixin):
14 url = "https://qwen-qvq-72b-preview.hf.space"
15 api_endpoint = "/gradio_api/call/generate"
16
17 working = True
18
19 default_model = "Qwen/QwQ-32B-Preview"
20 models = [default_model]
21
22 @classmethod
23 async def create_async_generator(
24 cls, model: str, messages: Messages,
25 images: ImagesType = None,
26 api_key: str = None,
27 proxy: str = None,
28 **kwargs
29 ) -> AsyncResult:
30 headers = {
31 "Accept": "application/json",
32 }
33 if api_key is not None:
34 headers["Authorization"] = f"Bearer {api_key}"
35 async with ClientSession(headers=headers) as session:
36 if images:
37 data = FormData()
38 data_bytes = to_bytes(images[0][0])
39 data.add_field("files", data_bytes, content_type=is_accepted_format(data_bytes), filename=images[0][1])
40 url = f"https://qwen-qvq-72b-preview.hf.space/gradio_api/upload?upload_id={get_random_string()}"
41 async with session.post(url, data=data, proxy=proxy) as response:
42 await raise_for_status(response)
43 image = await response.json()
44 data = {"data": [{"path": image[0]}, format_prompt(messages)]}
45 else:
46 data = {"data": [None, format_prompt(messages)]}
47 async with session.post(f"{cls.url}{cls.api_endpoint}", json=data, proxy=proxy) as response:
48 await raise_for_status(response)
49 event_id = (await response.json()).get("event_id")
50 async with session.get(f"{cls.url}{cls.api_endpoint}/{event_id}") as event_response:
51 await raise_for_status(event_response)
52 event = None
53 text_position = 0
54 async for chunk in event_response.content:
55 if chunk.startswith(b"event: "):
56 event = chunk[7:].decode(errors="replace").strip()
57 if chunk.startswith(b"data: "):
58 if event == "error":
59 raise ResponseError(f"GPU token limit exceeded: {chunk.decode(errors='replace')}")
60 if event in ("complete", "generating"):
61 try:
62 data = json.loads(chunk[6:])
63 except (json.JSONDecodeError, KeyError, TypeError) as e:
64 raise RuntimeError(f"Failed to read response: {chunk.decode(errors='replace')}", e)
65 if event == "generating":
66 if isinstance(data[0], str):
67 yield data[0][text_position:]
68 text_position = len(data[0])
69 else:
70 break
Added g4f/Provider/hf_space/StableDiffusion35Large.py +71 -0
@@ -0,0 +1,71 @@
1 from __future__ import annotations
2
3 import json
4 from aiohttp import ClientSession
5
6 from ...typing import AsyncResult, Messages
7 from ...image import ImageResponse, ImagePreview
8 from ...errors import ResponseError
9 from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin
10
11 class StableDiffusion35Large(AsyncGeneratorProvider, ProviderModelMixin):
12 url = "https://stabilityai-stable-diffusion-3-5-large.hf.space"
13 api_endpoint = "/gradio_api/call/infer"
14
15 working = True
16
17 default_model = 'stable-diffusion-3.5-large'
18 models = [default_model]
19 image_models = [default_model]
20
21 @classmethod
22 async def create_async_generator(
23 cls, model: str, messages: Messages,
24 prompt: str = None,
25 negative_prompt: str = None,
26 api_key: str = None,
27 proxy: str = None,
28 width: int = 1024,
29 height: int = 1024,
30 guidance_scale: float = 4.5,
31 num_inference_steps: int = 50,
32 seed: int = 0,
33 randomize_seed: bool = True,
34 **kwargs
35 ) -> AsyncResult:
36 headers = {
37 "Content-Type": "application/json",
38 "Accept": "application/json",
39 }
40 if api_key is not None:
41 headers["Authorization"] = f"Bearer {api_key}"
42 async with ClientSession(headers=headers) as session:
43 prompt = messages[-1]["content"] if prompt is None else prompt
44 data = {
45 "data": [prompt, negative_prompt, seed, randomize_seed, width, height, guidance_scale, num_inference_steps]
46 }
47 async with session.post(f"{cls.url}{cls.api_endpoint}", json=data, proxy=proxy) as response:
48 response.raise_for_status()
49 event_id = (await response.json()).get("event_id")
50 async with session.get(f"{cls.url}{cls.api_endpoint}/{event_id}") as event_response:
51 event_response.raise_for_status()
52 event = None
53 async for chunk in event_response.content:
54 if chunk.startswith(b"event: "):
55 event = chunk[7:].decode(errors="replace").strip()
56 if chunk.startswith(b"data: "):
57 if event == "error":
58 raise ResponseError(f"GPU token limit exceeded: {chunk.decode(errors='replace')}")
59 if event in ("complete", "generating"):
60 try:
61 data = json.loads(chunk[6:])
62 if data is None:
63 continue
64 url = data[0]["url"]
65 except (json.JSONDecodeError, KeyError, TypeError) as e:
66 raise RuntimeError(f"Failed to parse image URL: {chunk.decode(errors='replace')}", e)
67 if event == "generating":
68 yield ImagePreview(url, prompt)
69 else:
70 yield ImageResponse(url, prompt)
71 break
Modified g4f/Provider/hf_space/__init__.py +9 -3
@@ -1,18 +1,22 @@
1 1 from __future__ import annotations
2 2
3 from ...typing import AsyncResult, Messages
3 from ...typing import AsyncResult, Messages, ImagesType
4 4 from ...errors import ResponseError
5 5 from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin
6 6
7 7 from .BlackForestLabsFlux1Dev import BlackForestLabsFlux1Dev
8 8 from .BlackForestLabsFlux1Schnell import BlackForestLabsFlux1Schnell
9 9 from .VoodoohopFlux1Schnell import VoodoohopFlux1Schnell
10 from .StableDiffusion35Large import StableDiffusion35Large
11 from .Qwen_QVQ_72B import Qwen_QVQ_72B
10 12
11 13 class HuggingSpace(AsyncGeneratorProvider, ProviderModelMixin):
12 14 url = "https://huggingface.co/spaces"
15 parent = "HuggingFace"
13 16 working = True
14 17 default_model = BlackForestLabsFlux1Dev.default_model
15 providers = [BlackForestLabsFlux1Dev, BlackForestLabsFlux1Schnell, VoodoohopFlux1Schnell]
18 default_vision_model = Qwen_QVQ_72B.default_model
19 providers = [BlackForestLabsFlux1Dev, BlackForestLabsFlux1Schnell, VoodoohopFlux1Schnell, StableDiffusion35Large, Qwen_QVQ_72B]
16 20
17 21 @classmethod
18 22 def get_parameters(cls, **kwargs) -> dict:
@@ -33,8 +37,10 @@ class HuggingSpace(AsyncGeneratorProvider, ProviderModelMixin):
33 37
34 38 @classmethod
35 39 async def create_async_generator(
36 cls, model: str, messages: Messages, **kwargs
40 cls, model: str, messages: Messages, images: ImagesType = None, **kwargs
37 41 ) -> AsyncResult:
42 if not model and images is not None:
43 model = cls.default_vision_model
38 44 is_started = False
39 45 for provider in cls.providers:
40 46 if model in provider.model_aliases:
Modified g4f/Provider/needs_auth/HuggingFace.py +4 -3
@@ -17,6 +17,7 @@ from .HuggingChat import HuggingChat
17 17
18 18 class HuggingFace(AsyncGeneratorProvider, ProviderModelMixin):
19 19 url = "https://huggingface.co"
20 login_url = "https://huggingface.co/settings/tokens"
20 21 working = True
21 22 supports_message_history = True
22 23 default_model = HuggingChat.default_model
@@ -149,14 +150,14 @@ class HuggingFace(AsyncGeneratorProvider, ProviderModelMixin):
149 150 def format_prompt_mistral(messages: Messages, do_continue: bool = False) -> str:
150 151 system_messages = [message["content"] for message in messages if message["role"] == "system"]
151 152 question = " ".join([messages[-1]["content"], *system_messages])
152 history = "".join([
153 history = "\n".join([
153 154 f"<s>[INST]{messages[idx-1]['content']} [/INST] {message['content']}</s>"
154 155 for idx, message in enumerate(messages)
155 156 if message["role"] == "assistant"
156 157 ])
157 158 if do_continue:
158 159 return history[:-len('</s>')]
159 return f"{history}<s>[INST] {question} [/INST]"
160 return f"{history}\n<s>[INST] {question} [/INST]"
160 161
161 162 def format_prompt_qwen(messages: Messages, do_continue: bool = False) -> str:
162 163 prompt = "".join([
@@ -185,7 +186,7 @@ def format_prompt_custom(messages: Messages, end_token: str = "</s>", do_continu
185 186 def get_inputs(messages: Messages, model_data: dict, model_type: str, do_continue: bool = False) -> str:
186 187 if model_type in ("gpt2", "gpt_neo", "gemma", "gemma2"):
187 188 inputs = format_prompt(messages, do_continue=do_continue)
188 elif model_type in ("mistral"):
189 elif model_type == "mistral" and model_data.get("author") == "mistralai":
189 190 inputs = format_prompt_mistral(messages, do_continue)
190 191 elif "config" in model_data and "tokenizer_config" in model_data["config"] and "eos_token" in model_data["config"]["tokenizer_config"]:
191 192 eos_token = model_data["config"]["tokenizer_config"]["eos_token"]
Modified g4f/Provider/needs_auth/HuggingFaceAPI.py +2 -2
@@ -5,8 +5,8 @@ from .HuggingChat import HuggingChat
5 5
6 6 class HuggingFaceAPI(OpenaiAPI):
7 7 label = "HuggingFace (Inference API)"
8 url = "https://api-inference.huggingface.co"
9 login_url = "https://huggingface.co/settings/tokens"
8 parent = "HuggingFace"
9 url = "https://api-inference.huggingface.com"
10 10 api_base = "https://api-inference.huggingface.co/v1"
11 11 working = True
12 12 default_model = "meta-llama/Llama-3.2-11B-Vision-Instruct"
Modified g4f/gui/client/home.html +5 -1
@@ -5,6 +5,10 @@
5 5 <meta charset="UTF-8">
6 6 <meta name="viewport" content="width=device-width, initial-scale=1.0">
7 7 <title>G4F GUI</title>
8 <link rel="apple-touch-icon" sizes="180x180" href="/static/img/apple-touch-icon.png">
9 <link rel="icon" type="image/png" sizes="32x32" href="/static/img/favicon-32x32.png">
10 <link rel="icon" type="image/png" sizes="16x16" href="/static/img/favicon-16x16.png">
11 <link rel="manifest" href="/static/img/site.webmanifest">
8 12 <style>
9 13 :root {
10 14 --colour-1: #000000;
@@ -287,7 +291,7 @@
287 291
288 292 (async () => {
289 293 const today = new Date().toJSON().slice(0, 10);
290 const max = 100;
294 const max = 5;
291 295 const cache_id = Math.floor(Math.random() * max);
292 296 let prompt;
293 297 if (cache_id % 2 == 0) {
Modified g4f/gui/client/index.html +1 -1
@@ -152,7 +152,7 @@
152 152 </div>
153 153 <div class="field box hidden">
154 154 <label for="BingCreateImages-api_key" class="label" title="">Microsoft Designer in Bing:</label>
155 <textarea id="BingCreateImages-api_key" name="BingCreateImages[api_key]" placeholder="&quot;_U&quot; cookie"></textarea>
155 <input type="text" id="BingCreateImages-api_key" name="BingCreateImages[api_key]" placeholder="&quot;_U&quot; cookie"/>
156 156 </div>
157 157 </div>
158 158 <div class="bottom_buttons">
Modified g4f/gui/client/static/js/chat.v1.js +10 -9
@@ -1110,7 +1110,7 @@ const load_conversation = async (conversation_id, scroll=true) => {
1110 1110 if (lastLine.endsWith("[aborted]") || lastLine.endsWith("[error]")) {
1111 1111 reason = "error";
1112 1112 // Has an even number of start or end code tags
1113 } else if (reason = "stop" && buffer.split("```").length - 1 % 2 === 1) {
1113 } else if (reason == "stop" && buffer.split("```").length - 1 % 2 === 1) {
1114 1114 reason = "length";
1115 1115 }
1116 1116 if (reason == "length" || reason == "max_tokens" || reason == "error") {
@@ -1724,19 +1724,19 @@ async function on_api() {
1724 1724 option.dataset.parent = provider.parent;
1725 1725 providerSelect.appendChild(option);
1726 1726
1727 if (provider.login_url) {
1727 if (provider.parent) {
1728 if (!login_urls[provider.parent]) {
1729 login_urls[provider.parent] = [provider.label, provider.login_url, [provider.name]];
1730 } else {
1731 login_urls[provider.parent][2].push(provider.name);
1732 }
1733 } else if (provider.login_url) {
1728 1734 if (!login_urls[provider.name]) {
1729 1735 login_urls[provider.name] = [provider.label, provider.login_url, []];
1730 1736 } else {
1731 1737 login_urls[provider.name][0] = provider.label;
1732 1738 login_urls[provider.name][1] = provider.login_url;
1733 1739 }
1734 } else if (provider.parent) {
1735 if (!login_urls[provider.parent]) {
1736 login_urls[provider.parent] = [provider.label, provider.login_url, [provider.name]];
1737 } else {
1738 login_urls[provider.parent][2].push(provider.name);
1739 }
1740 1740 }
1741 1741 });
1742 1742 for (let [name, [label, login_url, childs]] of Object.entries(login_urls)) {
@@ -1746,9 +1746,10 @@ async function on_api() {
1746 1746 option = document.createElement("div");
1747 1747 option.classList.add("field", "box", "hidden");
1748 1748 childs = childs.map((child)=>`${child}-api_key`).join(" ");
1749 console.log(childs);
1749 1750 option.innerHTML = `
1750 1751 <label for="${name}-api_key" class="label" title="">${label}:</label>
1751 <textarea id="${name}-api_key" name="${name}[api_key]" class="${childs}" placeholder="api_key"></textarea>
1752 <input type="text" id="${name}-api_key" name="${name}[api_key]" class="${childs}" placeholder="api_key"/>
1752 1753 <a href="${login_url}" target="_blank" title="Login to ${label}">Get API key</a>
1753 1754 `;
1754 1755 settings.querySelector(".paper").appendChild(option);
Modified g4f/gui/server/backend_api.py +3 -3
@@ -120,7 +120,7 @@ class Backend_Api(Api):
120 120 tool_calls.append({
121 121 "function": {
122 122 "name": "search_tool",
123 "arguments": {"query": web_search, "instructions": ""} if web_search != "true" else {}
123 "arguments": {"query": web_search, "instructions": "", "max_words": 1000} if web_search != "true" else {}
124 124 },
125 125 "type": "function"
126 126 })
@@ -173,7 +173,7 @@ class Backend_Api(Api):
173 173 @app.route('/backend-api/v2/files/<bucket_id>', methods=['GET', 'DELETE'])
174 174 def manage_files(bucket_id: str):
175 175 bucket_id = secure_filename(bucket_id)
176 bucket_dir = get_bucket_dir(secure_filename(bucket_id))
176 bucket_dir = get_bucket_dir(bucket_id)
177 177
178 178 if not os.path.isdir(bucket_dir):
179 179 return jsonify({"error": {"message": "Bucket directory not found"}}), 404
@@ -231,7 +231,7 @@ class Backend_Api(Api):
231 231 if not file_data:
232 232 return jsonify({"error": {"message": "No file data received"}}), 400
233 233
234 with open(str(file_path), 'wb') as f:
234 with file_path.open('wb') as f:
235 235 f.write(file_data)
236 236
237 237 return jsonify({"message": f"File '{filename}' uploaded successfully to bucket '{bucket_id}'"}), 201
Modified g4f/providers/asyncio.py +3 -10
@@ -70,15 +70,8 @@ def to_sync_generator(generator: AsyncIterator, stream: bool = True) -> Iterator
70 70
71 71 # Helper function to convert a synchronous iterator to an async iterator
72 72 async def to_async_iterator(iterator: Iterator) -> AsyncIterator:
73 if isinstance(iterator, str):
74 yield iterator
75 elif hasattr(iterator, "__await__"):
76 yield await iterator
77 elif hasattr(iterator, "__aiter__"):
73 try:
78 74 async for item in iterator:
79 75 yield item
80 elif hasattr(iterator, "__iter__"):
81 for item in iterator:
82 yield item
83 else:
84 yield iterator
76 except TypeError:
77 yield await iterator
Modified g4f/providers/base_provider.py +3 -2
Modified g4f/tools/web_search.py +18 -7