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

XFEstudio/gpt4free

Use loading icon

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

代码差异

9 个文件 +66 -33
Modified g4f/Provider/ARTA.py +8 -5
@@ -175,8 +175,9 @@ class ARTA(AsyncGeneratorProvider, ProviderModelMixin):
175 175
176 176 # Step 3: Check Generation Status
177 177 status_url = cls.status_check_url.format(record_id=record_id)
178 counter = 0
178 counter = 4
179 179 start_time = time.time()
180 last_status = None
180 181 while True:
181 182 async with session.get(status_url, headers=headers, proxy=proxy) as status_response:
182 183 status_data = await status_response.json()
@@ -189,10 +190,12 @@ class ARTA(AsyncGeneratorProvider, ProviderModelMixin):
189 190 yield ImageResponse(images=image_urls, alt=prompt)
190 191 return
191 192 elif status in ("IN_QUEUE", "IN_PROGRESS"):
192 yield Reasoning(label=("Waiting" if status == "IN_QUEUE" else "Generating"), status="." * counter)
193 if last_status != status:
194 last_status = status
195 if status == "IN_QUEUE":
196 yield Reasoning(label="Waiting", ticker="⌛" * counter)
197 else:
198 yield Reasoning(label="Generating", ticker="⚽" * counter)
193 199 await asyncio.sleep(2) # Poll every 2 seconds
194 counter += 1
195 if counter > 3:
196 counter = 1
197 200 else:
198 201 raise ResponseError(f"Image generation failed with status: {status}")
Modified g4f/Provider/PollinationsAI.py +3 -3
@@ -238,12 +238,12 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin):
238 238 "safe": str(safe).lower()
239 239 }, aspect_ratio)
240 240 query = "&".join(f"{k}={quote_plus(str(v))}" for k, v in params.items() if v is not None)
241 url = f"{cls.image_api_endpoint}prompt/{quote_plus(prompt)}?{query}"
242 url = url[:8192] # Limit URL length
241 prompt = quote_plus(prompt)[:8112] # Limit URL length
242 url = f"{cls.image_api_endpoint}prompt/{prompt}?{query}"
243 243 async with ClientSession(headers=DEFAULT_HEADERS, connector=get_connector(proxy=proxy)) as session:
244 244 async with session.get(url, allow_redirects=False) as response:
245 245 await raise_for_status(response)
246 yield ImageResponse(response.headers.get("location", str(response.url)), prompt)
246 yield ImageResponse(str(response.url), prompt)
247 247
248 248 @classmethod
249 249 async def _generate_text(
Modified g4f/gui/client/index.html +2 -1
@@ -61,8 +61,9 @@
61 61 const gpt_image = '<img src="/static/img/gpt.png" alt="your avatar">';
62 62 </script>
63 63 <script src="/static/js/highlight.min.js" async></script>
64
64 65 <script>window.conversation_id = "{{conversation_id}}"</script>
65 <script>window.chat_id = "{{chat_id}}"</script>
66 <script>window.chat_id = "{{chat_id}}"; window.share_url = "{{share_url}}";</script>
66 67 <title>G4F Chat</title>
67 68 </head>
68 69 <body>
Modified g4f/gui/client/static/css/style.css +7 -1
@@ -119,6 +119,7 @@ body:not(.white) a:visited{
119 119 color: var(--colour-3);
120 120 border: var(--colour-1) 1px solid;
121 121 border-radius: var(--border-radius-1);
122 z-index: 1;
122 123 }
123 124
124 125 .white .new_version {
@@ -351,6 +352,12 @@ body:not(.white) a:visited{
351 352
352 353 .message .reasoning_title {
353 354 cursor: pointer;
355 height: 22px;
356 overflow: hidden;
357 }
358
359 .message .reasoning_title strong {
360 float: left;
354 361 }
355 362
356 363 .message .user i {
@@ -1164,7 +1171,6 @@ ul {
1164 1171 .sidebar.shown {
1165 1172 width: 400px;
1166 1173 padding: 15px;
1167 margin-right: 10px;
1168 1174 }
1169 1175
1170 1176 /* style for hljs copy */
Modified g4f/gui/client/static/js/chat.v1.js +32 -19
@@ -94,7 +94,8 @@ function render_reasoning(reasoning, final = false) {
94 94 </div>` : "";
95 95 return `<div class="reasoning_body">
96 96 <div class="reasoning_title">
97 <strong>${reasoning.label ? reasoning.label :'Reasoning <i class="brain">🧠</i>'}:</strong> ${escapeHtml(reasoning.status)}
97 <strong>${reasoning.label ? reasoning.label :'Reasoning <i class="brain">🧠</i>'}: </strong>
98 ${reasoning.status ? escapeHtml(reasoning.status) : '&nbsp;<i class="fas fa-spinner fa-spin"></i>'}
98 99 </div>
99 100 ${inner_text}
100 101 </div>`;
@@ -891,9 +892,11 @@ async function add_message_chunk(message, message_id, provider, scroll, finish_m
891 892 } else if (message.type == "login") {
892 893 update_message(content_map, message_id, markdown_render(message.login), scroll);
893 894 } else if (message.type == "finish") {
894 finish_storage[message_id] = message.finish;
895 if (finish_message) {
896 await finish_message();
895 if (!finish_storage[message_id]) {
896 finish_storage[message_id] = message.finish;
897 if (finish_message) {
898 await finish_message();
899 }
897 900 }
898 901 } else if (message.type == "usage") {
899 902 usage_storage[message_id] = message.usage;
@@ -909,6 +912,7 @@ async function add_message_chunk(message, message_id, provider, scroll, finish_m
909 912 } if (message.token) {
910 913 reasoning_storage[message_id].text += message.token;
911 914 }
915 reasoning_storage[message_id].ticker = message.ticker;
912 916 update_message(content_map, message_id, render_reasoning(reasoning_storage[message_id]), scroll);
913 917 } else if (message.type == "parameters") {
914 918 if (!parameters_storage[provider]) {
@@ -1012,7 +1016,7 @@ const ask_gpt = async (message_id, message_index = -1, regenerate = false, provi
1012 1016 content_map.inner.innerHTML = html;
1013 1017 highlight(content_map.inner);
1014 1018 }
1015 if (message_storage[message_id] || reasoning_storage[message_id]) {
1019 if (message_storage[message_id] || reasoning_storage[message_id]?.status) {
1016 1020 const message_provider = message_id in provider_storage ? provider_storage[message_id] : null;
1017 1021 let usage = {};
1018 1022 if (usage_storage[message_id]) {
@@ -1164,11 +1168,6 @@ const ask_gpt = async (message_id, message_index = -1, regenerate = false, provi
1164 1168 }, Object.values(image_storage), message_id, scroll, finish_message);
1165 1169 } catch (e) {
1166 1170 console.error(e);
1167 if (e.name != "AbortError") {
1168 error_storage[message_id] = true;
1169 content_map.inner.innerHTML += markdown_render(`**An error occured:** ${e}`);
1170 }
1171 await finish_message();
1172 1171 }
1173 1172 };
1174 1173
@@ -1296,7 +1295,6 @@ const delete_conversation = async (conversation_id) => {
1296 1295 };
1297 1296
1298 1297 const set_conversation = async (conversation_id) => {
1299 window.chat_id = null;
1300 1298 if (title_ids_storage[conversation_id]) {
1301 1299 conversation_id = title_ids_storage[conversation_id];
1302 1300 }
@@ -1315,15 +1313,17 @@ const set_conversation = async (conversation_id) => {
1315 1313
1316 1314 const new_conversation = async () => {
1317 1315 history.pushState({}, null, `/chat/`);
1316 window.chat_id = null;
1318 1317 window.conversation_id = uuid();
1319 1318 document.title = window.title || document.title;
1319 document.querySelector(".chat-header").innerText = "New Conversation - G4F";
1320 1320
1321 1321 await clear_conversation();
1322 1322 if (chatPrompt) {
1323 1323 chatPrompt.value = document.getElementById("systemPrompt")?.value;
1324 1324 }
1325 1325 load_conversations();
1326 hide_sidebar();
1326 hide_sidebar(true);
1327 1327 say_hello();
1328 1328 };
1329 1329
@@ -1384,7 +1384,12 @@ const load_conversation = async (conversation, scroll=true) => {
1384 1384 if (title) {
1385 1385 document.title = title;
1386 1386 }
1387 document.querySelector(".chat-header").innerText = title;
1387 const chatHeader = document.querySelector(".chat-header");
1388 if (window.chat_id) {
1389 chatHeader.innerHTML = '<i class="fa-solid fa-qrcode"></i> ' + escapeHtml(title);
1390 } else {
1391 chatHeader.innerText = title;
1392 }
1388 1393
1389 1394 if (chatPrompt) {
1390 1395 chatPrompt.value = conversation.system || "";
@@ -1577,6 +1582,9 @@ async function save_conversation(conversation_id, conversation) {
1577 1582 `conversation:${conversation_id}`,
1578 1583 data
1579 1584 );
1585 if (conversation_id != window.start_id) {
1586 window.chat_id = null;
1587 }
1580 1588 }
1581 1589
1582 1590 async function get_messages(conversation_id) {
@@ -1628,7 +1636,7 @@ const remove_message = async (conversation_id, index) => {
1628 1636 conversation.items = new_items;
1629 1637 await save_conversation(conversation_id, conversation);
1630 1638 if (window.chat_id) {
1631 const url = `/backend-api/v2/chat/${window.chat_id}`;
1639 const url = `${window.share_url}/backend-api/v2/chat/${window.chat_id}`;
1632 1640 await fetch(url, {
1633 1641 method: 'POST',
1634 1642 headers: {'content-type': 'application/json'},
@@ -1704,7 +1712,7 @@ const add_message = async (
1704 1712 }
1705 1713 await save_conversation(conversation_id, conversation);
1706 1714 if (window.chat_id) {
1707 const url = `/backend-api/v2/chat/${window.chat_id}`;
1715 const url = `${window.share_url}/backend-api/v2/chat/${window.chat_id}`;
1708 1716 fetch(url, {
1709 1717 method: 'POST',
1710 1718 headers: {'content-type': 'application/json'},
@@ -2053,7 +2061,8 @@ window.addEventListener('load', async function() {
2053 2061 if (!window.conversation_id) {
2054 2062 window.conversation_id = window.chat_id;
2055 2063 }
2056 const response = await fetch(`/backend-api/v2/chat/${window.chat_id ? window.chat_id : window.conversation_id}`, {
2064 window.start_id = window.conversation_id
2065 const response = await fetch(`${window.share_url}/backend-api/v2/chat/${window.chat_id ? window.chat_id : window.conversation_id}`, {
2057 2066 headers: {'accept': 'application/json'},
2058 2067 });
2059 2068 if (!response.ok) {
@@ -2083,7 +2092,7 @@ window.addEventListener('load', async function() {
2083 2092 if (!refreshOnHide) {
2084 2093 return;
2085 2094 }
2086 const response = await fetch(`/backend-api/v2/chat/${window.chat_id}`, {
2095 const response = await fetch(`${window.share_url}/backend-api/v2/chat/${window.chat_id}`, {
2087 2096 headers: {'accept': 'application/json', 'if-none-match': conversation.updated},
2088 2097 });
2089 2098 if (response.status == 200) {
@@ -2741,8 +2750,12 @@ async function api(ressource, args=null, files=null, message_id=null, scroll=tru
2741 2750 await finish_message();
2742 2751 return;
2743 2752 } else {
2744 await read_response(response, message_id, args.provider || null, scroll, finish_message);
2745 await finish_message();
2753 try {
2754 await read_response(response, message_id, args.provider || null, scroll, finish_message);
2755 await finish_message();
2756 } catch (e) {
2757 console.error(e);
2758 }
2746 2759 return;
2747 2760 }
2748 2761 } else if (args) {
Modified g4f/gui/server/api.py +2 -2
@@ -214,12 +214,12 @@ class Api:
214 214 yield self._format_json(chunk.type, **chunk.get_dict())
215 215 else:
216 216 yield self._format_json("content", str(chunk))
217 yield from self._yield_logs()
218 217 except Exception as e:
219 218 logger.exception(e)
220 219 debug.error(e)
221 yield from self._yield_logs()
222 220 yield self._format_json('error', type(e).__name__, message=get_error_message(e))
221 finally:
222 yield from self._yield_logs()
223 223
224 224 def _yield_logs(self):
225 225 if debug.logs:
Modified g4f/gui/server/website.py +5 -1
@@ -1,3 +1,6 @@
1 from __future__ import annotations
2
3 import os
1 4 import uuid
2 5 from flask import render_template, redirect
3 6
@@ -48,7 +51,8 @@ class Website:
48 51 return render_template('index.html', conversation_id=conversation_id)
49 52
50 53 def _chat_id(self, chat_id, conversation_id: str = ""):
51 return render_template('index.html', chat_id=chat_id, conversation_id=conversation_id)
54 share_url = os.environ.get("G4F_SHARE_URL", "")
55 return render_template('index.html', share_url=share_url, chat_id=chat_id, conversation_id=conversation_id)
52 56
53 57 def _index(self):
54 58 return render_template('index.html', conversation_id=str(uuid.uuid4()))
Modified g4f/image/copy_images.py +1 -1
@@ -135,7 +135,7 @@ async def copy_media(
135 135 async with session.get(image, ssl=request_ssl, headers=request_headers) as response:
136 136 response.raise_for_status()
137 137 media_type = response.headers.get("content-type", "application/octet-stream")
138 if media_type != "application/octet-stream":
138 if media_type not in ("application/octet-stream", "binary/octet-stream"):
139 139 if not is_valid_media_type(media_type):
140 140 raise ValueError(f"Unsupported media type: {media_type}")
141 141 with open(target_path, "wb") as f:
Modified g4f/providers/response.py +6 -0
@@ -180,12 +180,14 @@ class Reasoning(ResponseType):
180 180 token: Optional[str] = None,
181 181 label: Optional[str] = None,
182 182 status: Optional[str] = None,
183 ticker: Optional[str] = None,
183 184 is_thinking: Optional[str] = None
184 185 ) -> None:
185 186 """Initialize with token, status, and thinking state."""
186 187 self.token = token
187 188 self.label = label
188 189 self.status = status
190 self.ticker = ticker
189 191 self.is_thinking = is_thinking
190 192
191 193 def __str__(self) -> str:
@@ -195,6 +197,8 @@ class Reasoning(ResponseType):
195 197 if self.token is not None:
196 198 return self.token
197 199 if self.status is not None:
200 if self.label is not None:
201 return f"{self.label}: {self.status}\n"
198 202 return f"{self.status}\n"
199 203 return ""
200 204
@@ -206,6 +210,8 @@ class Reasoning(ResponseType):
206 210 def get_dict(self) -> Dict:
207 211 """Return a dictionary representation of the reasoning."""
208 212 if self.label is not None:
213 if self.ticker is not None:
214 return {"label": self.label, "status": self.status, "ticker": self.ticker}
209 215 return {"label": self.label, "status": self.status}
210 216 if self.is_thinking is None:
211 217 if self.status is None: