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

XFEstudio/gpt4free

fix: Update API endpoints to use new g4f.space URLs and remove deprecated providers

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

代码差异

14 个文件 +16 -263
Modified g4f/Provider/PollinationsAI.py +3 -3
@@ -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"
Deleted g4f/Provider/Startnest.py +0 -215
@@ -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}&timestamp={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)
Deleted g4f/Provider/StringableInference.py +0 -31
@@ -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 }
Modified g4f/Provider/Yupp.py +2 -2
@@ -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:
Modified g4f/Provider/__init__.py +0 -2
@@ -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
Modified g4f/Provider/local/Ollama.py +3 -2
@@ -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:
Modified g4f/Provider/needs_auth/Azure.py +1 -1
@@ -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"
Modified g4f/Provider/needs_auth/Claude.py +1 -1
@@ -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"
Modified g4f/Provider/needs_auth/GeminiPro.py +1 -1
@@ -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"
Modified g4f/Provider/needs_auth/Groq.py +1 -0
@@ -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
Modified g4f/Provider/needs_auth/Nvidia.py +1 -0
@@ -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
Modified g4f/Provider/needs_auth/OpenRouter.py +1 -0
@@ -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
Modified g4f/client/__init__.py +1 -1
Modified g4f/models.py +1 -4