返回提交历史
Modified
g4f/Provider/PollinationsAI.py
+3
-3
Deleted
g4f/Provider/Startnest.py
+0
-215
Deleted
g4f/Provider/StringableInference.py
+0
-31
Modified
g4f/Provider/Yupp.py
+2
-2
Modified
g4f/Provider/__init__.py
+0
-2
Modified
g4f/Provider/local/Ollama.py
+3
-2
Modified
g4f/Provider/needs_auth/Azure.py
+1
-1
Modified
g4f/Provider/needs_auth/Claude.py
+1
-1
Modified
g4f/Provider/needs_auth/GeminiPro.py
+1
-1
Modified
g4f/Provider/needs_auth/Groq.py
+1
-0
Modified
g4f/Provider/needs_auth/Nvidia.py
+1
-0
Modified
g4f/Provider/needs_auth/OpenRouter.py
+1
-0
Modified
g4f/client/__init__.py
+1
-1
Modified
g4f/models.py
+1
-4
XFEstudio/gpt4free
fix: Update API endpoints to use new g4f.space URLs and remove deprecated providers
f7cb4e69
代码差异
14 个文件
+16
-263
@@ -44,9 +44,9 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin):
44
44
gen_text_api_endpoint = "https://gen.pollinations.ai/v1/chat/completions"
45
45
image_models_endpoint = "https://gen.pollinations.ai/image/models"
46
46
text_models_endpoint = "https://gen.pollinations.ai/text/models"
47
balance_endpoint = "https://api.gpt4free.workers.dev/api/pollinations/account/balance"
48
worker_api_endpoint = "https://api.gpt4free.workers.dev/api/pollinations/chat/completions"
49
worker_models_endpoint = "https://api.gpt4free.workers.dev/api/pollinations/text/models"
47
balance_endpoint = "https://g4f.space/api/pollinations/account/balance"
48
worker_api_endpoint = "https://g4f.space/api/pollinations/chat/completions"
49
worker_models_endpoint = "https://g4f.space/api/pollinations/models"
50
50
51
51
# Models configuration
52
52
default_model = "openai"
@@ -1,215 +0,0 @@
1
from __future__ import annotations
2
3
from aiohttp import ClientSession
4
import json
5
import time
6
import hashlib
7
8
from ..typing import AsyncResult, Messages, MediaListType
9
from .base_provider import AsyncGeneratorProvider, ProviderModelMixin
10
from .helper import format_prompt
11
from ..tools.media import merge_media
12
from ..image import to_data_uri
13
from ..providers.response import FinishReason
14
15
16
class Startnest(AsyncGeneratorProvider, ProviderModelMixin):
17
label = "Startnest"
18
url = "https://play.google.com/store/apps/details?id=starnest.aitype.aikeyboard.chatbot.chatgpt"
19
api_endpoint = "https://api.startnest.uk/api/completions/stream"
20
21
working = False
22
needs_auth = False
23
supports_stream = True
24
supports_system_message = True
25
supports_message_history = True
26
27
default_model = 'gpt-4o-mini'
28
models = [default_model]
29
vision_models = models
30
31
@classmethod
32
def generate_signature(cls, timestamp: int) -> str:
33
"""
34
Generate signature for authorization header
35
You may need to adjust this based on the actual signature algorithm
36
"""
37
# This is a placeholder - the actual signature generation might involve:
38
# - A secret key
39
# - Specific string formatting
40
# - Different hash input
41
42
# Example implementation (adjust as needed):
43
kid = "36ccfe00-78fc-4cab-9c5b-5460b0c78513"
44
algorithm = "sha256"
45
validity = 90
46
user_id = ""
47
48
# The actual signature generation logic needs to be determined
49
# This is just a placeholder that creates a hash from timestamp
50
signature_input = f"{kid}{timestamp}{validity}".encode()
51
signature_value = hashlib.sha256(signature_input).hexdigest()
52
53
return f"Signature kid={kid}&algorithm={algorithm}×tamp={timestamp}&validity={validity}&userId={user_id}&value={signature_value}"
54
55
@classmethod
56
async def create_async_generator(
57
cls,
58
model: str,
59
messages: Messages,
60
proxy: str = None,
61
media: MediaListType = None,
62
stream: bool = True,
63
max_tokens: int = None,
64
**kwargs
65
) -> AsyncResult:
66
model = cls.get_model(model)
67
68
# Generate current timestamp
69
timestamp = int(time.time())
70
71
headers = {
72
"Accept-Encoding": "gzip",
73
"app_name": "AIKEYBOARD",
74
"Authorization": cls.generate_signature(timestamp),
75
"Connection": "Keep-Alive",
76
"Content-Type": "application/json; charset=UTF-8",
77
"Host": "api.startnest.uk",
78
"User-Agent": "okhttp/4.9.0",
79
}
80
81
async with ClientSession() as session:
82
# Merge media with messages
83
media = list(merge_media(media, messages))
84
85
# Convert messages to the required format
86
formatted_messages = []
87
for i, msg in enumerate(messages):
88
if isinstance(msg, dict):
89
role = msg.get("role", "user")
90
content = msg.get("content", "")
91
92
# Create content array
93
content_array = []
94
95
# Add images if this is the last user message and media exists
96
if media and role == "user" and i == len(messages) - 1:
97
for image, _ in media:
98
image_data_uri = to_data_uri(image)
99
content_array.append({
100
"image_url": {
101
"url": image_data_uri
102
},
103
"type": "image_url"
104
})
105
106
# Add text content
107
if content:
108
content_array.append({
109
"text": content,
110
"type": "text"
111
})
112
113
formatted_messages.append({
114
"role": role,
115
"content": content_array
116
})
117
118
# If only one message and no media, use format_prompt as requested
119
if len(messages) == 1 and not media:
120
prompt_text = format_prompt(messages)
121
formatted_messages = [{
122
"role": "user",
123
"content": [{"text": prompt_text, "type": "text"}]
124
}]
125
126
data = {
127
"isVip": True,
128
"max_tokens": max_tokens,
129
"messages": formatted_messages,
130
"stream": stream
131
}
132
133
# Add advanceToolType if media is present
134
if media:
135
data["advanceToolType"] = "upload_and_ask"
136
137
async with session.post(cls.api_endpoint, json=data, headers=headers, proxy=proxy) as response:
138
response.raise_for_status()
139
140
if stream:
141
# Handle streaming response (SSE format)
142
async for line in response.content:
143
if line:
144
line = line.decode('utf-8').strip()
145
if line.startswith("data: "):
146
data_str = line[6:]
147
if data_str == "[DONE]":
148
break
149
try:
150
json_data = json.loads(data_str)
151
if "choices" in json_data and len(json_data["choices"]) > 0:
152
choice = json_data["choices"][0]
153
154
# Handle content
155
delta = choice.get("delta", {})
156
content = delta.get("content", "")
157
if content:
158
yield content
159
160
# Handle finish_reason
161
if "finish_reason" in choice and choice["finish_reason"] is not None:
162
yield FinishReason(choice["finish_reason"])
163
break
164
165
except json.JSONDecodeError:
166
continue
167
else:
168
# Handle non-streaming response (regular JSON)
169
response_text = await response.text()
170
try:
171
json_data = json.loads(response_text)
172
if "choices" in json_data and len(json_data["choices"]) > 0:
173
choice = json_data["choices"][0]
174
if "message" in choice and "content" in choice["message"]:
175
content = choice["message"]["content"]
176
if content:
177
yield content.strip()
178
179
# Handle finish_reason for non-streaming
180
if "finish_reason" in choice and choice["finish_reason"] is not None:
181
yield FinishReason(choice["finish_reason"])
182
return
183
184
except json.JSONDecodeError:
185
# If it's still SSE format even when stream=False, handle it
186
lines = response_text.strip().split('\n')
187
full_content = []
188
finish_reason_value = None
189
190
for line in lines:
191
if line.startswith("data: "):
192
data_str = line[6:]
193
if data_str == "[DONE]":
194
break
195
try:
196
json_data = json.loads(data_str)
197
if "choices" in json_data and len(json_data["choices"]) > 0:
198
choice = json_data["choices"][0]
199
delta = choice.get("delta", {})
200
content = delta.get("content", "")
201
if content:
202
full_content.append(content)
203
204
# Store finish_reason
205
if "finish_reason" in choice and choice["finish_reason"] is not None:
206
finish_reason_value = choice["finish_reason"]
207
208
except json.JSONDecodeError:
209
continue
210
211
if full_content:
212
yield ''.join(full_content)
213
214
if finish_reason_value:
215
yield FinishReason(finish_reason_value)
@@ -1,31 +0,0 @@
1
from __future__ import annotations
2
3
import secrets
4
import string
5
6
from .template import OpenaiTemplate
7
8
class StringableInference(OpenaiTemplate):
9
label = "Stringable Inference"
10
url = "https://stringable-inference.onrender.com"
11
base_url = "https://stringableinf.com/api"
12
api_endpoint = "https://stringableinf.com/api/v1/chat/completions"
13
14
working = False
15
active_by_default = True
16
default_model = "deepseek-v3.2"
17
default_vision_model = "gpt-oss-120b"
18
19
@classmethod
20
def get_headers(cls, stream: bool, api_key: str = None, headers: dict = None) -> dict:
21
return {
22
"Accept": "text/event-stream" if stream else "application/json",
23
"Content-Type": "application/json",
24
"HTTP-Referer": "https://g4f.dev/",
25
"X-Title": "G4F Python",
26
**(
27
{"Authorization": f"Bearer {api_key}"}
28
if api_key else {}
29
),
30
**({} if headers is None else headers)
31
}
@@ -305,8 +305,8 @@ class Yupp(AsyncGeneratorProvider, ProviderModelMixin):
305
305
}
306
306
)
307
307
resp.raise_for_status()
308
data = resp.json()
309
return data[0]["result"]["data"]["json"]["signed_url"]
308
data = resp.json()[0]["result"]["data"]["json"]
309
return data.get("signed_url", data.get("signedURL"))
310
310
311
311
@classmethod
312
312
async def get_signed_image(cls, scraper: CloudScraper, image_id: str) -> str:
@@ -49,9 +49,7 @@ from .OperaAria import OperaAria
49
49
from .Perplexity import Perplexity
50
50
from .PollinationsAI import PollinationsAI
51
51
from .PollinationsImage import PollinationsImage
52
from .Startnest import Startnest
53
52
from .Qwen import Qwen
54
from .StringableInference import StringableInference
55
53
from .TeachAnything import TeachAnything
56
54
from .WeWordle import WeWordle
57
55
from .Yqcloud import Yqcloud
@@ -13,6 +13,7 @@ from ...typing import AsyncResult, Messages
13
13
class Ollama(OpenaiTemplate):
14
14
label = "Ollama 🦙"
15
15
url = "https://ollama.com"
16
base_url = "https://g4f.space/api/ollama"
16
17
login_url = "https://ollama.com/settings/keys"
17
18
needs_auth = False
18
19
working = True
@@ -34,7 +35,7 @@ class Ollama(OpenaiTemplate):
34
35
cls.live += 1
35
36
cls.models = [model["name"] for model in models]
36
37
if base_url is None:
37
host = os.getenv("OLLAMA_HOST", "127.0.0.1")
38
host = os.getenv("OLLAMA_HOST", "localhost")
38
39
port = os.getenv("OLLAMA_PORT", "11434")
39
40
url = f"http://{host}:{port}/api/tags"
40
41
else:
@@ -66,7 +67,7 @@ class Ollama(OpenaiTemplate):
66
67
base_url: str = f"http://{host}:{port}/v1"
67
68
if model in cls.local_models:
68
69
async with StreamSession(headers={"Authorization": f"Bearer {api_key}"}, proxy=proxy) as session:
69
async with session.post(f"{base_url}/api/chat", json={
70
async with session.post(f"{base_url.replace('/v1', '')}/api/chat", json={
70
71
"model": model,
71
72
"messages": messages,
72
73
}) as response:
@@ -14,7 +14,7 @@ from ..helper import format_media_prompt
14
14
class Azure(OpenaiTemplate):
15
15
label = "Azure ☁️"
16
16
url = "https://ai.azure.com"
17
base_url = "https://g4f.dev/api/azure"
17
base_url = "https://g4f.space/api/azure"
18
18
working = True
19
19
active_by_default = False
20
20
login_url = "https://discord.gg/qXA4Wf4Fsm"
@@ -9,7 +9,7 @@ from ..template import OpenaiTemplate
9
9
class Claude(OpenaiTemplate):
10
10
label = "Claude 💥"
11
11
url = "https://claude.ai"
12
base_url = "https://g4f.dev/api/claude"
12
base_url = "https://g4f.space/api/claude"
13
13
working = True
14
14
active_by_default = True
15
15
login_url = "https://discord.gg/qXA4Wf4Fsm"
@@ -7,7 +7,7 @@ class GeminiPro(OpenaiTemplate):
7
7
url = "https://ai.google.dev"
8
8
login_url = "https://aistudio.google.com/u/0/apikey"
9
9
base_url = "https://generativelanguage.googleapis.com/v1beta/openai"
10
backup_url = "https://g4f.dev/custom/srv_mjnryskw9fe0567fa267"
10
backup_url = "https://g4f.space/api/gemini-v1beta"
11
11
active_by_default = True
12
12
working = True
13
13
default_model = "models/gemini-2.5-flash"
@@ -7,6 +7,7 @@ class Groq(OpenaiTemplate):
7
7
url = "https://console.groq.com/playground"
8
8
login_url = "https://console.groq.com/keys"
9
9
base_url = "https://api.groq.com/openai/v1"
10
backup_url = "https://g4f.space/api/groq"
10
11
working = True
11
12
active_by_default = True
12
13
default_model = DEFAULT_MODEL
@@ -6,6 +6,7 @@ from ...config import DEFAULT_MODEL
6
6
class Nvidia(OpenaiTemplate):
7
7
label = "Nvidia"
8
8
base_url = "https://integrate.api.nvidia.com/v1"
9
backup_url = "https://g4f.space/api/nvidia"
9
10
login_url = "https://google.com"
10
11
url = "https://build.nvidia.com"
11
12
working = True
@@ -13,6 +13,7 @@ class OpenRouter(OpenaiTemplate):
13
13
14
14
class OpenRouterFree(OpenRouter):
15
15
label = "OpenRouter (free)"
16
base_url = "https://g4f.space/api/openrouter"
16
17
max_tokens = 4096
17
18
active_by_default = True
18
19