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

XFEstudio/gpt4free

Support inlineData in GeminiPro

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

代码差异

5 个文件 +55 -15
Modified g4f/Provider/PollinationsAI.py +0 -2
@@ -339,7 +339,6 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin):
339 339 extra_parameters=extra_parameters,
340 340 referrer=referrer,
341 341 api_key=api_key,
342 download_media=download_media,
343 342 extra_body=extra_body,
344 343 **kwargs
345 344 ):
@@ -465,7 +464,6 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin):
465 464 extra_parameters: list[str],
466 465 referrer: str,
467 466 api_key: str,
468 download_media: bool,
469 467 extra_body: dict,
470 468 **kwargs
471 469 ) -> AsyncResult:
Modified g4f/Provider/needs_auth/GeminiPro.py +19 -9
@@ -9,10 +9,11 @@ from aiohttp import ClientSession, BaseConnector
9 9 from ...typing import AsyncResult, Messages, MediaListType
10 10 from ...image import to_bytes, is_data_an_media
11 11 from ...errors import MissingAuthError, ModelNotFoundError
12 from ...requests.raise_for_status import raise_for_status
12 from ...requests import raise_for_status, iter_lines
13 13 from ...providers.response import Usage, FinishReason
14 from ...image.copy_images import save_response_media
14 15 from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin
15 from ..helper import get_connector, to_string
16 from ..helper import get_connector, to_string, format_media_prompt
16 17 from ... import debug
17 18
18 19 class GeminiPro(AsyncGeneratorProvider, ProviderModelMixin):
@@ -123,6 +124,7 @@ class GeminiPro(AsyncGeneratorProvider, ProviderModelMixin):
123 124 "maxOutputTokens": kwargs.get("max_tokens"),
124 125 "topP": kwargs.get("top_p"),
125 126 "topK": kwargs.get("top_k"),
127 **{"responseModalities": ["AUDIO"]} if "tts" in model else {},
126 128 },
127 129 "tools": [{
128 130 "function_declarations": [{
@@ -152,16 +154,24 @@ class GeminiPro(AsyncGeneratorProvider, ProviderModelMixin):
152 154 raise RuntimeError(f"Response {response.status}: {data['error']['message']}")
153 155 if stream:
154 156 lines = []
155 async for chunk in response.content:
156 if chunk == b"[{\n":
157 lines = [b"{\n"]
158 elif chunk == b",\r\n" or chunk == b"]":
157 buffer = b""
158 async for chunk in iter_lines(response.content.iter_any()):
159 buffer += chunk
160 if chunk == b"[{":
161 lines = [b"{"]
162 elif chunk == b"," or chunk == b"]":
159 163 try:
160 164 data = b"".join(lines)
161 165 data = json.loads(data)
162 166 content = data["candidates"][0]["content"]
163 if "parts" in content:
164 yield content["parts"][0]["text"]
167 if "parts" in content and content["parts"]:
168 if "text" in content["parts"][0]:
169 yield content["parts"][0]["text"]
170 elif "inlineData" in content["parts"][0]:
171 async for media in save_response_media(
172 content["parts"][0]["inlineData"], format_media_prompt(messages)
173 ):
174 yield media
165 175 if "finishReason" in data["candidates"][0]:
166 176 yield FinishReason(data["candidates"][0]["finishReason"].lower())
167 177 usage = data.get("usageMetadata")
@@ -173,7 +183,7 @@ class GeminiPro(AsyncGeneratorProvider, ProviderModelMixin):
173 183 )
174 184 except Exception as e:
175 185 data = data.decode(errors="ignore") if isinstance(data, bytes) else data
176 raise RuntimeError(f"Read chunk failed: {data}") from e
186 raise RuntimeError(f"Read chunk failed") from e
177 187 lines = []
178 188 else:
179 189 lines.append(chunk)
Modified g4f/image/__init__.py +1 -1
@@ -257,7 +257,7 @@ def to_bytes(image: ImageType) -> bytes:
257 257 raise FileNotFoundError(f"File not found: {path}")
258 258 else:
259 259 raise ValueError("Invalid image format. Expected bytes, str, or PIL Image.")
260 elif isinstance(image, Image):
260 elif isinstance(image, Image.Image):
261 261 bytes_io = BytesIO()
262 262 image.save(bytes_io, image.format)
263 263 image.seek(0)
Modified g4f/image/copy_images.py +9 -2
@@ -60,11 +60,18 @@ def update_filename(response, filename: str) -> str:
60 60 timestamp = datetime.strptime(date, '%a, %d %b %Y %H:%M:%S %Z').timestamp()
61 61 return str(int(timestamp)) + "_" + filename.split("_", maxsplit=1)[-1]
62 62
63 async def save_response_media(response, prompt: str, tags: list[str]) -> AsyncIterator:
63 async def save_response_media(response, prompt: str, tags: list[str] = []) -> AsyncIterator:
64 64 """Save media from response to local file and return URL"""
65 if isinstance(response, dict):
66 content_type = response.get("mimeType")
67 response = response.get("data")
68 elif hasattr(response, "headers"):
69 content_type = response.headers["content-type"]
70 else:
71 content_type = "audio/mpeg"
72
65 73 if isinstance(response, str):
66 74 response = base64.b64decode(response)
67 content_type = response.headers["content-type"] if hasattr(response, "headers") else "audio/mpeg"
68 75 extension = MEDIA_TYPE_MAP.get(content_type)
69 76 if extension is None:
70 77 raise ValueError(f"Unsupported media type: {content_type}")
Modified g4f/requests/__init__.py +26 -1
@@ -207,4 +207,29 @@ async def see_stream(iter_lines: Iterator[bytes]) -> AsyncIterator[dict]:
207 207 if line.startswith(b"data: "):
208 208 if line[6:].startswith(b"[DONE]"):
209 209 break
210 yield json.loads(line[6:])
210 yield json.loads(line[6:])
211
212 async def iter_lines(iter_response: AsyncIterator[bytes], delimiter=None):
213 """
214 iterate streaming content line by line, separated by ``\\n``.
215
216 Copied from: https://requests.readthedocs.io/en/latest/_modules/requests/models/
217 which is under the License: Apache 2.0
218 """
219 pending = None
220
221 async for chunk in iter_response:
222 if pending is not None:
223 chunk = pending + chunk
224 lines = chunk.split(delimiter) if delimiter else chunk.splitlines()
225 pending = (
226 lines.pop()
227 if lines and lines[-1] and chunk and lines[-1][-1] == chunk[-1]
228 else None
229 )
230
231 for line in lines:
232 yield line
233
234 if pending is not None:
235 yield pending