返回提交历史
Modified
g4f/Provider/PollinationsAI.py
+22
-4
Modified
g4f/providers/response.py
+10
-8
XFEstudio/gpt4free
Add Audio Generation and Response Support for AI Providers (#2788)
* feat(g4f/providers/response.py): add audio response support * feat(g4f/Provider/PollinationsAI.py): add audio generation support and enhance model handling * fix(g4f/providers/response.py): remove duplicate Audio class definition --------- Co-authored-by: kqlio67 <>
ce269256
代码差异
2 个文件
+32
-12
@@ -13,7 +13,7 @@ from ..image import to_data_uri
13
13
from ..errors import ModelNotFoundError
14
14
from ..requests.raise_for_status import raise_for_status
15
15
from ..requests.aiohttp import get_connector
16
from ..providers.response import ImageResponse, ImagePreview, FinishReason, Usage
16
from ..providers.response import ImageResponse, ImagePreview, FinishReason, Usage, Audio
17
17
from .. import debug
18
18
19
19
DEFAULT_HEADERS = {
@@ -32,7 +32,8 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin):
32
32
supports_message_history = True
33
33
34
34
# API endpoints
35
text_api_endpoint = "https://text.pollinations.ai/openai"
35
text_api_endpoint = "https://text.pollinations.ai"
36
openai_endpoint = "https://text.pollinations.ai/openai"
36
37
image_api_endpoint = "https://image.pollinations.ai/"
37
38
38
39
# Models configuration
@@ -88,10 +89,17 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin):
88
89
# Update of text models
89
90
text_response = requests.get("https://text.pollinations.ai/models")
90
91
text_response.raise_for_status()
92
models = text_response.json()
91
93
original_text_models = [
92
94
model.get("name")
93
for model in text_response.json()
95
for model in models
96
if model.get("type") == "chat"
94
97
]
98
cls.audio_models = {
99
model.get("name"): model.get("voices")
100
for model in models
101
if model.get("audio")
102
}
95
103
96
104
# Combining text models
97
105
combined_text = (
@@ -262,8 +270,18 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin):
262
270
"seed": seed,
263
271
"cache": cache
264
272
})
265
async with session.post(cls.text_api_endpoint, json=data) as response:
273
if "gemini" in model:
274
data.pop("seed")
275
if model in cls.audio_models:
276
data["voice"] = random.choice(cls.audio_models[model])
277
url = f"{cls.text_api_endpoint}"
278
else:
279
url = cls.openai_endpoint
280
async with session.post(url, json=data) as response:
266
281
await raise_for_status(response)
282
if response.headers["content-type"] == "audio/mpeg":
283
yield Audio(await response.read())
284
return
267
285
result = await response.json()
268
286
choice = result["choices"][0]
269
287
message = choice.get("message", {})
@@ -229,14 +229,6 @@ class Sources(ResponseType):
229
229
for idx, link in enumerate(self.list)
230
230
]))
231
231
232
class Audio(HiddenResponse):
233
def __init__(self, data: bytes) -> None:
234
self.data = data
235
236
def to_string(self) -> str:
237
data_base64 = base64.b64encode(self.data).decode()
238
return f"data:audio/mpeg;base64,{data_base64}"
239
240
232
class YouTube(ResponseType):
241
233
def __init__(self, ids: List[str]) -> None:
242
234
"""Initialize with a list of YouTube IDs."""
@@ -251,6 +243,16 @@ class YouTube(ResponseType):
251
243
for id in self.ids
252
244
]))
253
245
246
class Audio(HiddenResponse):
247
def __init__(self, data: bytes) -> None:
248
"""Initialize with audio data bytes."""
249
self.data = data
250
251
def to_string(self) -> str:
252
"""Return audio data as a base64-encoded data URI."""
253
data_base64 = base64.b64encode(self.data).decode()
254
return f"data:audio/mpeg;base64,{data_base64}"
255
254
256
class BaseConversation(ResponseType):
255
257
def __str__(self) -> str:
256
258
"""Return an empty string by default."""