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

XFEstudio/gpt4free

Major Provider Updates and Model Support Enhancements (#2467)

* refactor(g4f/Provider/Airforce.py): improve model handling and filtering - Add hidden_models set to exclude specific models - Add evil alias for uncensored model handling - Extend filtering for model-specific response tokens - Add response buffering for streamed content - Update model fetching with error handling * refactor(g4f/Provider/Blackbox.py): improve caching and model handling - Add caching system for validated values with file-based storage - Rename 'flux' model to 'ImageGeneration' and update references - Add temperature, top_p and max_tokens parameters to generator - Simplify HTTP headers and remove redundant options - Add model alias mapping for ImageGeneration - Add file system utilities for cache management * feat(g4f/Provider/RobocodersAPI.py): add caching and error handling - Add file-based caching system for access tokens and sessions - Add robust error handling with specific error messages - Add automatic dialog continuation on resource limits - Add HTML parsing with BeautifulSoup for token extraction - Add debug logging for error tracking - Add timeout configuration for API requests * refactor(g4f/Provider/DarkAI.py): update DarkAI default model and aliases - Change default model from llama-3-405b to llama-3-70b - Remove llama-3-405b from supported models list - Remove llama-3.1-405b from model aliases * feat(g4f/Provider/Blackbox2.py): add image generation support - Add image model 'flux' with dedicated API endpoint - Refactor generator to support both text and image outputs - Extract headers into reusable static method - Add type hints for AsyncGenerator return type - Split generation logic into _generate_text and _generate_image methods - Add ImageResponse handling for image generation results BREAKING CHANGE: create_async_generator now returns AsyncGenerator instead of AsyncResult * refactor(g4f/Provider/ChatGptEs.py): update ChatGptEs model configuration - Update models list to include gpt-3.5-turbo - Remove chatgpt-4o-latest from supported models - Remove model_aliases mapping for gpt-4o * feat(g4f/Provider/DeepInfraChat.py): add Accept-Language header support - Add Accept-Language header for internationalization - Maintain existing header configuration - Improve request compatibility with language preferences * refactor(g4f/Provider/needs_auth/Gemini.py): add ProviderModelMixin inheritance - Add ProviderModelMixin to class inheritance - Import ProviderModelMixin from base_provider - Move BaseConversation import to base_provider imports * refactor(g4f/Provider/Liaobots.py): update model details and aliases - Add version suffix to o1 model IDs - Update model aliases for o1-preview and o1-mini - Standardize version format across model definitions * refactor(g4f/Provider/PollinationsAI.py): enhance model support and generation - Split generation logic into dedicated image/text methods - Add additional text models including sur and claude - Add width/height parameters for image generation - Add model existence validation - Add hasattr checks for model lists initialization * chore(gitignore): add provider cache directory - Add g4f/Provider/.cache to gitignore patterns * refactor(g4f/Provider/ReplicateHome.py): update model configuration - Update default model to gemma-2b-it - Add default_image_model configuration - Remove llava-13b from supported models - Simplify request headers * feat(g4f/models.py): expand provider and model support - Add new providers DarkAI and PollinationsAI - Add new models for Mistral, Flux and image generation - Update provider lists for existing models - Add P1 and Evil models with experimental providers BREAKING CHANGE: Remove llava-13b model support * refactor(Airforce): Update type hint for split_message return - Change return type of from to for consistency with import. - Maintain overall functionality and structure of the class. - Ensure compatibility with type hinting standards in Python. * refactor(g4f/Provider/Airforce.py): Update type hint for split_message return - Change return type of 'split_message' from 'list[str]' to 'List[str]' for consistency with import. - Maintain overall functionality and structure of the 'Airforce' class. - Ensure compatibility with type hinting standards in Python. * feat(g4f/Provider/RobocodersAPI.py): Add support for optional BeautifulSoup dependency - Introduce a check for the BeautifulSoup library and handle its absence gracefully. - Raise a if BeautifulSoup is not installed, prompting the user to install it. - Remove direct import of BeautifulSoup to avoid import errors when the library is missing. --------- Co-authored-by: kqlio67 <>

a358b28f
kqlio67 <166700875+kqlio67@users.noreply.github.com>
提交于

代码差异

16 个文件 +585 -258
Modified .gitignore +2 -1
@@ -65,4 +65,5 @@ x.txt
65 65 bench.py
66 66 to-reverse.txt
67 67 g4f/Provider/OpenaiChat2.py
68 generated_images/
68 generated_images/
69 g4f/Provider/.cache
Modified g4f/Provider/Airforce.py +76 -59
@@ -1,18 +1,19 @@
1 from __future__ import annotations
2 1 import json
3 2 import random
4 3 import re
5 4 import requests
6 from requests.packages.urllib3.exceptions import InsecureRequestWarning
7 5 from aiohttp import ClientSession
8
6 from typing import List
7 from requests.packages.urllib3.exceptions import InsecureRequestWarning
9 8 from ..typing import AsyncResult, Messages
10 9 from ..image import ImageResponse
11 10 from .base_provider import AsyncGeneratorProvider, ProviderModelMixin
12 11
13 requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
12 from .. import debug
14 13
15 def split_message(message: str, max_length: int = 1000) -> list[str]:
14 requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
15
16 def split_message(message: str, max_length: int = 1000) -> List[str]:
16 17 """Splits the message into parts up to (max_length)."""
17 18 chunks = []
18 19 while len(message) > max_length:
@@ -38,6 +39,8 @@ class Airforce(AsyncGeneratorProvider, ProviderModelMixin):
38 39
39 40 default_model = "gpt-4o-mini"
40 41 default_image_model = "flux"
42
43 hidden_models = {"Flux-1.1-Pro"}
41 44
42 45 additional_models_imagine = ["flux-1.1-pro", "dall-e-3"]
43 46
@@ -54,39 +57,38 @@ class Airforce(AsyncGeneratorProvider, ProviderModelMixin):
54 57 "llama-3.1-70b": "llama-3.1-70b-turbo",
55 58 "neural-7b": "neural-chat-7b-v3-1",
56 59 "zephyr-7b": "zephyr-7b-beta",
60 "evil": "any-uncensored",
57 61 "sdxl": "stable-diffusion-xl-base",
58 62 "flux-pro": "flux-1.1-pro",
59 63 }
60 64
61 @classmethod
62 def fetch_completions_models(cls):
63 response = requests.get('https://api.airforce/models', verify=False)
64 response.raise_for_status()
65 data = response.json()
66 return [model['id'] for model in data['data']]
67
68 @classmethod
69 def fetch_imagine_models(cls):
70 response = requests.get(
71 'https://api.airforce/v1/imagine2/models',
72 verify=False
73 )
74 response.raise_for_status()
75 return response.json()
76
77 @classmethod
78 def is_image_model(cls, model: str) -> bool:
79 return model in cls.image_models
80
81 65 @classmethod
82 66 def get_models(cls):
67 if not cls.image_models:
68 try:
69 url = "https://api.airforce/imagine2/models"
70 response = requests.get(url, verify=False)
71 response.raise_for_status()
72 cls.image_models = response.json()
73 cls.image_models.extend(cls.additional_models_imagine)
74 except Exception as e:
75 debug.log(f"Error fetching image models: {e}")
76
83 77 if not cls.models:
84 cls.image_models = cls.fetch_imagine_models() + cls.additional_models_imagine
85 cls.models = list(dict.fromkeys([cls.default_model] +
86 cls.fetch_completions_models() +
87 cls.image_models))
88 return cls.models
78 try:
79 url = "https://api.airforce/models"
80 response = requests.get(url, verify=False)
81 response.raise_for_status()
82 data = response.json()
83 cls.models = [model['id'] for model in data['data']]
84 cls.models.extend(cls.image_models)
85 cls.models = [model for model in cls.models if model not in cls.hidden_models]
86 except Exception as e:
87 debug.log(f"Error fetching text models: {e}")
88 cls.models = [cls.default_model]
89 89
90 return cls.models
91
90 92 @classmethod
91 93 async def check_api_key(cls, api_key: str) -> bool:
92 94 """
@@ -111,6 +113,37 @@ class Airforce(AsyncGeneratorProvider, ProviderModelMixin):
111 113 print(f"Error checking API key: {str(e)}")
112 114 return False
113 115
116 @classmethod
117 def _filter_content(cls, part_response: str) -> str:
118 """
119 Filters out unwanted content from the partial response.
120 """
121 part_response = re.sub(
122 r"One message exceeds the \d+chars per message limit\..+https:\/\/discord\.com\/invite\/\S+",
123 '',
124 part_response
125 )
126
127 part_response = re.sub(
128 r"Rate limit \(\d+\/minute\) exceeded\. Join our discord for more: .+https:\/\/discord\.com\/invite\/\S+",
129 '',
130 part_response
131 )
132
133 return part_response
134
135 @classmethod
136 def _filter_response(cls, response: str) -> str:
137 """
138 Filters the full response to remove system errors and other unwanted text.
139 """
140 filtered_response = re.sub(r"\[ERROR\] '\w{8}-\w{4}-\w{4}-\w{4}-\w{12}'", '', response) # any-uncensored
141 filtered_response = re.sub(r'<\|im_end\|>', '', filtered_response) # remove <|im_end|> token
142 filtered_response = re.sub(r'</s>', '', filtered_response) # neural-chat-7b-v3-1
143 filtered_response = re.sub(r'^(Assistant: |AI: |ANSWER: |Output: )', '', filtered_response) # phi-2
144 filtered_response = cls._filter_content(filtered_response)
145 return filtered_response
146
114 147 @classmethod
115 148 async def generate_image(
116 149 cls,
@@ -124,6 +157,7 @@ class Airforce(AsyncGeneratorProvider, ProviderModelMixin):
124 157 headers = {
125 158 "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:133.0) Gecko/20100101 Firefox/133.0",
126 159 "Accept": "image/avif,image/webp,image/png,image/svg+xml,image/*;q=0.8,*/*;q=0.5",
160 "Accept-Language": "en-US,en;q=0.5",
127 161 "Accept-Encoding": "gzip, deflate, br, zstd",
128 162 "Content-Type": "application/json",
129 163 "Authorization": f"Bearer {api_key}",
@@ -151,9 +185,13 @@ class Airforce(AsyncGeneratorProvider, ProviderModelMixin):
151 185 api_key: str,
152 186 proxy: str = None
153 187 ) -> AsyncResult:
188 """
189 Generates text, buffers the response, filters it, and returns the final result.
190 """
154 191 headers = {
155 192 "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:133.0) Gecko/20100101 Firefox/133.0",
156 193 "Accept": "application/json, text/event-stream",
194 "Accept-Language": "en-US,en;q=0.5",
157 195 "Accept-Encoding": "gzip, deflate, br, zstd",
158 196 "Content-Type": "application/json",
159 197 "Authorization": f"Bearer {api_key}",
@@ -175,6 +213,7 @@ class Airforce(AsyncGeneratorProvider, ProviderModelMixin):
175 213 response.raise_for_status()
176 214
177 215 if stream:
216 buffer = [] # Buffer to collect partial responses
178 217 async for line in response.content:
179 218 line = line.decode('utf-8').strip()
180 219 if line.startswith('data: '):
@@ -184,18 +223,20 @@ class Airforce(AsyncGeneratorProvider, ProviderModelMixin):
184 223 if 'choices' in chunk and chunk['choices']:
185 224 delta = chunk['choices'][0].get('delta', {})
186 225 if 'content' in delta:
187 filtered_content = cls._filter_response(delta['content'])
188 yield filtered_content
226 buffer.append(delta['content'])
189 227 except json.JSONDecodeError:
190 228 continue
229 # Combine the buffered response and filter it
230 filtered_response = cls._filter_response(''.join(buffer))
231 yield filtered_response
191 232 else:
192 233 # Non-streaming response
193 234 result = await response.json()
194 235 if 'choices' in result and result['choices']:
195 236 message = result['choices'][0].get('message', {})
196 237 content = message.get('content', '')
197 filtered_content = cls._filter_response(content)
198 yield filtered_content
238 filtered_response = cls._filter_response(content)
239 yield filtered_response
199 240
200 241 @classmethod
201 242 async def create_async_generator(
@@ -217,7 +258,7 @@ class Airforce(AsyncGeneratorProvider, ProviderModelMixin):
217 258 pass
218 259
219 260 model = cls.get_model(model)
220 if cls.is_image_model(model):
261 if model in cls.image_models:
221 262 if prompt is None:
222 263 prompt = messages[-1]['content']
223 264 if seed is None:
@@ -227,27 +268,3 @@ class Airforce(AsyncGeneratorProvider, ProviderModelMixin):
227 268 else:
228 269 async for result in cls.generate_text(model, messages, max_tokens, temperature, top_p, stream, api_key, proxy):
229 270 yield result
230
231 @classmethod
232 def _filter_content(cls, part_response: str) -> str:
233 part_response = re.sub(
234 r"One message exceeds the \d+chars per message limit\..+https:\/\/discord\.com\/invite\/\S+",
235 '',
236 part_response
237 )
238
239 part_response = re.sub(
240 r"Rate limit \(\d+\/minute\) exceeded\. Join our discord for more: .+https:\/\/discord\.com\/invite\/\S+",
241 '',
242 part_response
243 )
244
245 return part_response
246
247 @classmethod
248 def _filter_response(cls, response: str) -> str:
249 filtered_response = re.sub(r"\[ERROR\] '\w{8}-\w{4}-\w{4}-\w{4}-\w{12}'", '', response) # any-uncensored
250 filtered_response = re.sub(r'<\|im_end\|>', '', response) # hermes-2-pro-mistral-7b
251 filtered_response = re.sub(r'</s>', '', response) # neural-chat-7b-v3-1
252 filtered_response = cls._filter_content(filtered_response)
253 return filtered_response
Modified g4f/Provider/Blackbox.py +65 -25
@@ -7,6 +7,10 @@ import json
7 7 import re
8 8 import aiohttp
9 9
10 import os
11 import json
12 from pathlib import Path
13
10 14 from ..typing import AsyncResult, Messages, ImageType
11 15 from .base_provider import AsyncGeneratorProvider, ProviderModelMixin
12 16 from ..image import ImageResponse, to_data_uri
@@ -17,22 +21,22 @@ class Blackbox(AsyncGeneratorProvider, ProviderModelMixin):
17 21 label = "Blackbox AI"
18 22 url = "https://www.blackbox.ai"
19 23 api_endpoint = "https://www.blackbox.ai/api/chat"
24
20 25 working = True
21 26 supports_stream = True
22 27 supports_system_message = True
23 28 supports_message_history = True
24 _last_validated_value = None
25 29
26 30 default_model = 'blackboxai'
27 31 default_vision_model = default_model
28 32 default_image_model = 'flux'
29 image_models = ['flux', 'repomap']
33 image_models = ['ImageGeneration', 'repomap']
30 34 vision_models = [default_model, 'gpt-4o', 'gemini-pro', 'gemini-1.5-flash', 'llama-3.1-8b', 'llama-3.1-70b', 'llama-3.1-405b']
31 35
32 36 userSelectedModel = ['gpt-4o', 'gemini-pro', 'claude-sonnet-3.5', 'blackboxai-pro']
33 37
34 38 agentMode = {
35 'flux': {'mode': True, 'id': "ImageGenerationLV45LJp", 'name': "Image Generation"}
39 'ImageGeneration': {'mode': True, 'id': "ImageGenerationLV45LJp", 'name': "Image Generation"}
36 40 }
37 41
38 42 trendingAgentMode = {
@@ -95,22 +99,63 @@ class Blackbox(AsyncGeneratorProvider, ProviderModelMixin):
95 99 models = list(dict.fromkeys([default_model, *userSelectedModel, *list(agentMode.keys()), *list(trendingAgentMode.keys())]))
96 100
97 101 model_aliases = {
102 "gpt-4": "blackboxai",
103 "gpt-4": "gpt-4o",
104 "gpt-4o-mini": "gpt-4o",
98 105 "gpt-3.5-turbo": "blackboxai",
99 106 "gemini-flash": "gemini-1.5-flash",
100 "claude-3.5-sonnet": "claude-sonnet-3.5"
107 "claude-3.5-sonnet": "claude-sonnet-3.5",
108 "flux": "ImageGeneration",
101 109 }
102 110
103 111 @classmethod
104 async def fetch_validated(cls):
105 if cls._last_validated_value:
106 return cls._last_validated_value
112 def _get_cache_dir(cls) -> Path:
113 # Get the path to the current file
114 current_file = Path(__file__)
115 # Create the path to the .cache directory
116 cache_dir = current_file.parent / '.cache'
117 # Create a directory if it does not exist
118 cache_dir.mkdir(exist_ok=True)
119 return cache_dir
120
121 @classmethod
122 def _get_cache_file(cls) -> Path:
123 return cls._get_cache_dir() / 'blackbox.json'
124
125 @classmethod
126 def _load_cached_value(cls) -> str | None:
127 cache_file = cls._get_cache_file()
128 if cache_file.exists():
129 try:
130 with open(cache_file, 'r') as f:
131 data = json.load(f)
132 return data.get('validated_value')
133 except Exception as e:
134 print(f"Error reading cache file: {e}")
135 return None
136
137 @classmethod
138 def _save_cached_value(cls, value: str):
139 cache_file = cls._get_cache_file()
140 try:
141 with open(cache_file, 'w') as f:
142 json.dump({'validated_value': value}, f)
143 except Exception as e:
144 print(f"Error writing to cache file: {e}")
145
146 @classmethod
147 async def fetch_validated(cls):
148 # Let's try to load the value from the cache first
149 cached_value = cls._load_cached_value()
150 if cached_value:
151 return cached_value
107 152
108 153 async with aiohttp.ClientSession() as session:
109 154 try:
110 155 async with session.get(cls.url) as response:
111 156 if response.status != 200:
112 157 print("Failed to load the page.")
113 return cls._last_validated_value
158 return cached_value
114 159
115 160 page_content = await response.text()
116 161 js_files = re.findall(r'static/chunks/\d{4}-[a-fA-F0-9]+\.js', page_content)
@@ -125,12 +170,13 @@ class Blackbox(AsyncGeneratorProvider, ProviderModelMixin):
125 170 match = key_pattern.search(js_content)
126 171 if match:
127 172 validated_value = match.group(1)
128 cls._last_validated_value = validated_value
173 # Save the new value to the cache file
174 cls._save_cached_value(validated_value)
129 175 return validated_value
130 176 except Exception as e:
131 177 print(f"Error fetching validated value: {e}")
132 178
133 return cls._last_validated_value
179 return cached_value
134 180
135 181 @staticmethod
136 182 def generate_id(length=7):
@@ -162,12 +208,16 @@ class Blackbox(AsyncGeneratorProvider, ProviderModelMixin):
162 208 web_search: bool = False,
163 209 image: ImageType = None,
164 210 image_name: str = None,
211 top_p: float = 0.9,
212 temperature: float = 0.5,
213 max_tokens: int = 1024,
165 214 **kwargs
166 215 ) -> AsyncResult:
167 216 message_id = cls.generate_id()
168 217 messages = cls.add_prefix_to_messages(messages, model)
169 218 validated_value = await cls.fetch_validated()
170 219 formatted_message = format_prompt(messages)
220 model = cls.get_model(model)
171 221
172 222 messages = [{"id": message_id, "content": formatted_message, "role": "user"}]
173 223
@@ -185,20 +235,10 @@ class Blackbox(AsyncGeneratorProvider, ProviderModelMixin):
185 235
186 236 headers = {
187 237 'accept': '*/*',
188 'accept-language': 'en-US,en;q=0.9',
189 'cache-control': 'no-cache',
190 238 'content-type': 'application/json',
191 239 'origin': cls.url,
192 'pragma': 'no-cache',
193 'priority': 'u=1, i',
194 240 'referer': f'{cls.url}/',
195 'sec-ch-ua': '"Not?A_Brand";v="99", "Chromium";v="130"',
196 'sec-ch-ua-mobile': '?0',
197 'sec-ch-ua-platform': '"Linux"',
198 'sec-fetch-dest': 'empty',
199 'sec-fetch-mode': 'cors',
200 'sec-fetch-site': 'same-origin',
201 'user-agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36'
241 'user-agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36'
202 242 }
203 243
204 244 data = {
@@ -211,9 +251,9 @@ class Blackbox(AsyncGeneratorProvider, ProviderModelMixin):
211 251 "trendingAgentMode": cls.trendingAgentMode.get(model, {}) if model in cls.trendingAgentMode else {},
212 252 "isMicMode": False,
213 253 "userSystemPrompt": None,
214 "maxTokens": 1024,
215 "playgroundTopP": 0.9,
216 "playgroundTemperature": 0.5,
254 "maxTokens": max_tokens,
255 "playgroundTopP": top_p,
256 "playgroundTemperature": temperature,
217 257 "isChromeExt": False,
218 258 "githubToken": None,
219 259 "clickedAnswer2": False,
@@ -225,7 +265,7 @@ class Blackbox(AsyncGeneratorProvider, ProviderModelMixin):
225 265 "webSearchMode": web_search,
226 266 "validated": validated_value,
227 267 "imageGenerationMode": False,
228 "webSearchModePrompt": False
268 "webSearchModePrompt": web_search
229 269 }
230 270
231 271 async with ClientSession(headers=headers) as session:
Modified g4f/Provider/Blackbox2.py +70 -20
@@ -3,20 +3,30 @@ from __future__ import annotations
3 3 import random
4 4 import asyncio
5 5 from aiohttp import ClientSession
6 from typing import Union, AsyncGenerator
6 7
7 8 from ..typing import AsyncResult, Messages
9 from ..image import ImageResponse
8 10 from .base_provider import AsyncGeneratorProvider, ProviderModelMixin
11
9 12 from .. import debug
10 13
11 14 class Blackbox2(AsyncGeneratorProvider, ProviderModelMixin):
12 15 url = "https://www.blackbox.ai"
13 api_endpoint = "https://www.blackbox.ai/api/improve-prompt"
16 api_endpoints = {
17 "llama-3.1-70b": "https://www.blackbox.ai/api/improve-prompt",
18 "flux": "https://www.blackbox.ai/api/image-generator"
19 }
20
14 21 working = True
15 22 supports_system_message = True
16 23 supports_message_history = True
17 24 supports_stream = False
25
18 26 default_model = 'llama-3.1-70b'
19 models = [default_model]
27 chat_models = ['llama-3.1-70b']
28 image_models = ['flux']
29 models = [*chat_models, *image_models]
20 30
21 31 @classmethod
22 32 async def create_async_generator(
@@ -27,23 +37,27 @@ class Blackbox2(AsyncGeneratorProvider, ProviderModelMixin):
27 37 max_retries: int = 3,
28 38 delay: int = 1,
29 39 **kwargs
30 ) -> AsyncResult:
31 headers = {
32 'accept': '*/*',
33 'accept-language': 'en-US,en;q=0.9',
34 'content-type': 'text/plain;charset=UTF-8',
35 'dnt': '1',
36 'origin': 'https://www.blackbox.ai',
37 'priority': 'u=1, i',
38 'referer': 'https://www.blackbox.ai',
39 'sec-ch-ua': '"Chromium";v="131", "Not_A Brand";v="24"',
40 'sec-ch-ua-mobile': '?0',
41 'sec-ch-ua-platform': '"Linux"',
42 'sec-fetch-dest': 'empty',
43 'sec-fetch-mode': 'cors',
44 'sec-fetch-site': 'same-origin',
45 'user-agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36'
46 }
40 ) -> AsyncGenerator:
41 if model in cls.chat_models:
42 async for result in cls._generate_text(model, messages, proxy, max_retries, delay):
43 yield result
44 elif model in cls.image_models:
45 async for result in cls._generate_image(model, messages, proxy):
46 yield result
47 else:
48 raise ValueError(f"Unsupported model: {model}")
49
50 @classmethod
51 async def _generate_text(
52 cls,
53 model: str,
54 messages: Messages,
55 proxy: str = None,
56 max_retries: int = 3,
57 delay: int = 1
58 ) -> AsyncGenerator:
59 headers = cls._get_headers()
60 api_endpoint = cls.api_endpoints[model]
47 61
48 62 data = {
49 63 "messages": messages,
@@ -53,7 +67,7 @@ class Blackbox2(AsyncGeneratorProvider, ProviderModelMixin):
53 67 async with ClientSession(headers=headers) as session:
54 68 for attempt in range(max_retries):
55 69 try:
56 async with session.post(cls.api_endpoint, json=data, proxy=proxy) as response:
70 async with session.post(api_endpoint, json=data, proxy=proxy) as response:
57 71 response.raise_for_status()
58 72 response_data = await response.json()
59 73 if 'prompt' in response_data:
@@ -68,3 +82,39 @@ class Blackbox2(AsyncGeneratorProvider, ProviderModelMixin):
68 82 wait_time = delay * (2 ** attempt) + random.uniform(0, 1)
69 83 debug.log(f"Attempt {attempt + 1} failed. Retrying in {wait_time:.2f} seconds...")
70 84 await asyncio.sleep(wait_time)
85
86 @classmethod
87 async def _generate_image(
88 cls,
89 model: str,
90 messages: Messages,
91 proxy: str = None
92 ) -> AsyncGenerator:
93 headers = cls._get_headers()
94 api_endpoint = cls.api_endpoints[model]
95
96 async with ClientSession(headers=headers) as session:
97 prompt = messages[-1]["content"]
98 data = {
99 "query": prompt
100 }
101
102 async with session.post(api_endpoint, headers=headers, json=data, proxy=proxy) as response:
103 response.raise_for_status()
104 response_data = await response.json()
105
106 if 'markdown' in response_data:
107 image_url = response_data['markdown'].split('(')[1].split(')')[0]
108 yield ImageResponse(images=image_url, alt=prompt)
109
110 @staticmethod
111 def _get_headers() -> dict:
112 return {
113 'accept': '*/*',
114 'accept-language': 'en-US,en;q=0.9',
115 'content-type': 'text/plain;charset=UTF-8',
116 'origin': 'https://www.blackbox.ai',
117 'priority': 'u=1, i',
118 'referer': 'https://www.blackbox.ai',
119 'user-agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36'
120 }
Modified g4f/Provider/ChatGptEs.py +2 -5
@@ -12,17 +12,14 @@ from .helper import format_prompt
12 12 class ChatGptEs(AsyncGeneratorProvider, ProviderModelMixin):
13 13 url = "https://chatgpt.es"
14 14 api_endpoint = "https://chatgpt.es/wp-admin/admin-ajax.php"
15
15 16 working = True
16 17 supports_stream = True
17 18 supports_system_message = True
18 19 supports_message_history = True
19 20
20 21 default_model = 'gpt-4o'
21 models = ['gpt-4o', 'gpt-4o-mini', 'chatgpt-4o-latest']
22
23 model_aliases = {
24 "gpt-4o": "chatgpt-4o-latest",
25 }
22 models = ['gpt-3.5-turbo', 'gpt-4o', 'gpt-4o-mini']
26 23
27 24 @classmethod
28 25 def get_model(cls, model: str) -> str:
Modified g4f/Provider/DarkAI.py +1 -3
@@ -16,17 +16,15 @@ class DarkAI(AsyncGeneratorProvider, ProviderModelMixin):
16 16 supports_system_message = True
17 17 supports_message_history = True
18 18
19 default_model = 'llama-3-405b'
19 default_model = 'llama-3-70b'
20 20 models = [
21 21 'gpt-4o', # Uncensored
22 22 'gpt-3.5-turbo', # Uncensored
23 'llama-3-70b', # Uncensored
24 23 default_model,
25 24 ]
26 25
27 26 model_aliases = {
28 27 "llama-3.1-70b": "llama-3-70b",
29 "llama-3.1-405b": "llama-3-405b",
30 28 }
31 29
32 30 @classmethod
Modified g4f/Provider/DeepInfraChat.py +1 -0
@@ -43,6 +43,7 @@ class DeepInfraChat(AsyncGeneratorProvider, ProviderModelMixin):
43 43 **kwargs
44 44 ) -> AsyncResult:
45 45 headers = {
46 'Accept-Language': 'en-US,en;q=0.9',
46 47 'Content-Type': 'application/json',
47 48 'Origin': 'https://deepinfra.com',
48 49 'Referer': 'https://deepinfra.com/',
Modified g4f/Provider/Liaobots.py +7 -4
@@ -36,8 +36,8 @@ models = {
36 36 "tokenLimit": 126000,
37 37 "context": "128K",
38 38 },
39 "o1-preview": {
40 "id": "o1-preview",
39 "o1-preview-2024-09-12": {
40 "id": "o1-preview-2024-09-12",
41 41 "name": "o1-preview",
42 42 "model": "o1",
43 43 "provider": "OpenAI",
@@ -45,8 +45,8 @@ models = {
45 45 "tokenLimit": 100000,
46 46 "context": "128K",
47 47 },
48 "o1-mini": {
49 "id": "o1-mini",
48 "o1-mini-2024-09-12": {
49 "id": "o1-mini-2024-09-12",
50 50 "name": "o1-mini",
51 51 "model": "o1",
52 52 "provider": "OpenAI",
@@ -152,6 +152,9 @@ class Liaobots(AsyncGeneratorProvider, ProviderModelMixin):
152 152 "gpt-4o-mini": "gpt-4o-mini-2024-07-18",
153 153 "gpt-4": "gpt-4o-2024-08-06",
154 154
155 "o1-preview": "o1-preview-2024-09-12",
156 "o1-mini": "o1-mini-2024-09-12",
157
155 158 "claude-3-opus": "claude-3-opus-20240229",
156 159 "claude-3.5-sonnet": "claude-3-5-sonnet-20240620",
157 160 "claude-3.5-sonnet": "claude-3-5-sonnet-20241022",
Added g4f/Provider/PollinationsAI.py +107 -0
@@ -0,0 +1,107 @@
1 from __future__ import annotations
2
3 from urllib.parse import quote
4 import random
5 import requests
6 from aiohttp import ClientSession
7
8 from ..typing import AsyncResult, Messages
9 from ..image import ImageResponse
10 from ..requests.raise_for_status import raise_for_status
11 from ..requests.aiohttp import get_connector
12 from .needs_auth.OpenaiAPI import OpenaiAPI
13 from .helper import format_prompt
14
15 class PollinationsAI(OpenaiAPI):
16 label = "Pollinations.AI"
17 url = "https://pollinations.ai"
18
19 working = True
20 needs_auth = False
21 supports_stream = True
22
23 default_model = "openai"
24
25 additional_models_image = ["unity", "midijourney", "rtist"]
26 additional_models_text = ["sur", "sur-mistral", "claude"]
27
28 model_aliases = {
29 "gpt-4o": "openai",
30 "mistral-nemo": "mistral",
31 "llama-3.1-70b": "llama", #
32 "gpt-3.5-turbo": "searchgpt",
33 "gpt-4": "searchgpt",
34 "gpt-3.5-turbo": "claude",
35 "gpt-4": "claude",
36 "qwen-2.5-coder-32b": "qwen-coder",
37 "claude-3.5-sonnet": "sur",
38 }
39
40 @classmethod
41 def get_models(cls):
42 if not hasattr(cls, 'image_models'):
43 cls.image_models = []
44 if not cls.image_models:
45 url = "https://image.pollinations.ai/models"
46 response = requests.get(url)
47 raise_for_status(response)
48 cls.image_models = response.json()
49 cls.image_models.extend(cls.additional_models_image)
50 if not hasattr(cls, 'models'):
51 cls.models = []
52 if not cls.models:
53 url = "https://text.pollinations.ai/models"
54 response = requests.get(url)
55 raise_for_status(response)
56 cls.models = [model.get("name") for model in response.json()]
57 cls.models.extend(cls.image_models)
58 cls.models.extend(cls.additional_models_text)
59 return cls.models
60
61 @classmethod
62 async def create_async_generator(
63 cls,
64 model: str,
65 messages: Messages,
66 prompt: str = None,
67 api_base: str = "https://text.pollinations.ai/openai",
68 api_key: str = None,
69 proxy: str = None,
70 seed: str = None,
71 width: int = 1024,
72 height: int = 1024,
73 **kwargs
74 ) -> AsyncResult:
75 model = cls.get_model(model)
76 if model in cls.image_models:
77 async for response in cls._generate_image(model, messages, prompt, seed, width, height):
78 yield response
79 elif model in cls.models:
80 async for response in cls._generate_text(model, messages, api_base, api_key, proxy, **kwargs):
81 yield response
82 else:
83 raise ValueError(f"Unknown model: {model}")
84
85 @classmethod
86 async def _generate_image(cls, model: str, messages: Messages, prompt: str = None, seed: str = None, width: int = 1024, height: int = 1024):
87 if prompt is None:
88 prompt = messages[-1]["content"]
89 if seed is None:
90 seed = random.randint(0, 100000)
91 image = f"https://image.pollinations.ai/prompt/{quote(prompt)}?width={width}&height={height}&seed={int(seed)}&nofeed=true&nologo=true&model={quote(model)}"
92 yield ImageResponse(image, prompt)
93
94 @classmethod
95 async def _generate_text(cls, model: str, messages: Messages, api_base: str, api_key: str = None, proxy: str = None, **kwargs):
96 if api_key is None:
97 async with ClientSession(connector=get_connector(proxy=proxy)) as session:
98 prompt = format_prompt(messages)
99 async with session.get(f"https://text.pollinations.ai/{quote(prompt)}?model={quote(model)}") as response:
100 await raise_for_status(response)
101 async for line in response.content.iter_any():
102 yield line.decode(errors="ignore")
103 else:
104 async for chunk in super().create_async_generator(
105 model, messages, api_base=api_base, proxy=proxy, **kwargs
106 ):
107 yield chunk
Modified g4f/Provider/ReplicateHome.py +2 -13
@@ -19,7 +19,8 @@ class ReplicateHome(AsyncGeneratorProvider, ProviderModelMixin):
19 19 supports_system_message = True
20 20 supports_message_history = True
21 21
22 default_model = 'yorickvp/llava-13b'
22 default_model = 'google-deepmind/gemma-2b-it'
23 default_image_model = 'stability-ai/stable-diffusion-3'
23 24
24 25 image_models = [
25 26 'stability-ai/stable-diffusion-3',
@@ -29,7 +30,6 @@ class ReplicateHome(AsyncGeneratorProvider, ProviderModelMixin):
29 30
30 31 text_models = [
31 32 'google-deepmind/gemma-2b-it',
32 'yorickvp/llava-13b',
33 33 ]
34 34
35 35 models = text_models + image_models
@@ -42,7 +42,6 @@ class ReplicateHome(AsyncGeneratorProvider, ProviderModelMixin):
42 42
43 43 # text_models
44 44 "gemma-2b": "google-deepmind/gemma-2b-it",
45 "llava-13b": "yorickvp/llava-13b",
46 45 }
47 46
48 47 model_versions = {
@@ -53,7 +52,6 @@ class ReplicateHome(AsyncGeneratorProvider, ProviderModelMixin):
53 52
54 53 # text_models
55 54 "google-deepmind/gemma-2b-it": "dff94eaf770e1fc211e425a50b51baa8e4cac6c39ef074681f9e39d778773626",
56 "yorickvp/llava-13b": "80537f9eead1a5bfa72d5ac6ea6414379be41d4d4f6679fd776e9535d1eb58bb",
57 55 }
58 56
59 57 @classmethod
@@ -70,18 +68,9 @@ class ReplicateHome(AsyncGeneratorProvider, ProviderModelMixin):
70 68 headers = {
71 69 "accept": "*/*",
72 70 "accept-language": "en-US,en;q=0.9",
73 "cache-control": "no-cache",
74 71 "content-type": "application/json",
75 72 "origin": "https://replicate.com",
76 "pragma": "no-cache",
77 "priority": "u=1, i",
78 73 "referer": "https://replicate.com/",
79 "sec-ch-ua": '"Not;A=Brand";v="24", "Chromium";v="128"',
80 "sec-ch-ua-mobile": "?0",
81 "sec-ch-ua-platform": '"Linux"',
82 "sec-fetch-dest": "empty",
83 "sec-fetch-mode": "cors",
84 "sec-fetch-site": "same-site",
85 74 "user-agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36"
86 75 }
87 76
Modified g4f/Provider/RobocodersAPI.py +168 -21
@@ -2,10 +2,24 @@ from __future__ import annotations
2 2
3 3 import json
4 4 import aiohttp
5 from pathlib import Path
6
7 try:
8 from bs4 import BeautifulSoup
9 HAS_BEAUTIFULSOUP = True
10 except ImportError:
11 HAS_BEAUTIFULSOUP = False
12 BeautifulSoup = None
13
14 from aiohttp import ClientTimeout
15 from ..errors import MissingRequirementsError
5 16 from ..typing import AsyncResult, Messages
6 17 from .base_provider import AsyncGeneratorProvider, ProviderModelMixin
7 18 from .helper import format_prompt
8 19
20 from .. import debug
21
22
9 23 class RobocodersAPI(AsyncGeneratorProvider, ProviderModelMixin):
10 24 label = "API Robocoders AI"
11 25 url = "https://api.robocoders.ai/docs"
@@ -16,6 +30,9 @@ class RobocodersAPI(AsyncGeneratorProvider, ProviderModelMixin):
16 30 agent = [default_model, "RepoAgent", "FrontEndAgent"]
17 31 models = [*agent]
18 32
33 CACHE_DIR = Path(__file__).parent / ".cache"
34 CACHE_FILE = CACHE_DIR / "robocoders.json"
35
19 36 @classmethod
20 37 async def create_async_generator(
21 38 cls,
@@ -24,14 +41,14 @@ class RobocodersAPI(AsyncGeneratorProvider, ProviderModelMixin):
24 41 proxy: str = None,
25 42 **kwargs
26 43 ) -> AsyncResult:
27 async with aiohttp.ClientSession() as session:
28 access_token = await cls._get_access_token(session)
29 if not access_token:
30 raise Exception("Failed to get access token")
31
32 session_id = await cls._create_session(session, access_token)
33 if not session_id:
34 raise Exception("Failed to create session")
44
45 timeout = ClientTimeout(total=600)
46
47 async with aiohttp.ClientSession(timeout=timeout) as session:
48 # Load or create access token and session ID
49 access_token, session_id = await cls._get_or_create_access_and_session(session)
50 if not access_token or not session_id:
51 raise Exception("Failed to initialize API interaction")
35 52
36 53 headers = {
37 54 "Content-Type": "application/json",
@@ -45,38 +62,116 @@ class RobocodersAPI(AsyncGeneratorProvider, ProviderModelMixin):
45 62 "prompt": prompt,
46 63 "agent": model
47 64 }
48
65
49 66 async with session.post(cls.api_endpoint, headers=headers, json=data, proxy=proxy) as response:
50 if response.status != 200:
51 raise Exception(f"Error: {response.status}")
67 if response.status == 401: # Unauthorized, refresh token
68 cls._clear_cached_data()
69 raise Exception("Unauthorized: Invalid token, please retry.")
70 elif response.status == 422:
71 raise Exception("Validation Error: Invalid input.")
72 elif response.status >= 500:
73 raise Exception(f"Server Error: {response.status}")
74 elif response.status != 200:
75 raise Exception(f"Unexpected Error: {response.status}")
52 76
53 77 async for line in response.content:
54 78 if line:
55 79 try:
56 response_data = json.loads(line)
57 message = response_data.get('message', '')
80 # Decode bytes into a string
81 line_str = line.decode('utf-8')
82 response_data = json.loads(line_str)
83
84 # Get the message from the 'args.content' or 'message' field
85 message = (response_data.get('args', {}).get('content') or
86 response_data.get('message', ''))
87
58 88 if message:
59 89 yield message
90
91 # Check for reaching the resource limit
92 if (response_data.get('action') == 'message' and
93 response_data.get('args', {}).get('wait_for_response')):
94 # Automatically continue the dialog
95 continue_data = {
96 "sid": session_id,
97 "prompt": "continue",
98 "agent": model
99 }
100 async with session.post(
101 cls.api_endpoint,
102 headers=headers,
103 json=continue_data,
104 proxy=proxy
105 ) as continue_response:
106 if continue_response.status == 200:
107 async for continue_line in continue_response.content:
108 if continue_line:
109 try:
110 continue_line_str = continue_line.decode('utf-8')
111 continue_data = json.loads(continue_line_str)
112 continue_message = (
113 continue_data.get('args', {}).get('content') or
114 continue_data.get('message', '')
115 )
116 if continue_message:
117 yield continue_message
118 except json.JSONDecodeError:
119 debug.log(f"Failed to decode continue JSON: {continue_line}")
120 except Exception as e:
121 debug.log(f"Error processing continue response: {e}")
122
60 123 except json.JSONDecodeError:
61 pass
124 debug.log(f"Failed to decode JSON: {line}")
125 except Exception as e:
126 debug.log(f"Error processing response: {e}")
127
128 @staticmethod
129 async def _get_or_create_access_and_session(session: aiohttp.ClientSession):
130 RobocodersAPI.CACHE_DIR.mkdir(exist_ok=True) # Ensure cache directory exists
131
132 # Load data from cache
133 if RobocodersAPI.CACHE_FILE.exists():
134 with open(RobocodersAPI.CACHE_FILE, "r") as f:
135 data = json.load(f)
136 access_token = data.get("access_token")
137 session_id = data.get("sid")
138
139 # Validate loaded data
140 if access_token and session_id:
141 return access_token, session_id
142
143 # If data not valid, create new access token and session ID
144 access_token = await RobocodersAPI._fetch_and_cache_access_token(session)
145 session_id = await RobocodersAPI._create_and_cache_session(session, access_token)
146 return access_token, session_id
62 147
63 148 @staticmethod
64 async def _get_access_token(session: aiohttp.ClientSession) -> str:
149 async def _fetch_and_cache_access_token(session: aiohttp.ClientSession) -> str:
150 if not HAS_BEAUTIFULSOUP:
151 raise MissingRequirementsError('Install "beautifulsoup4" package | pip install -U beautifulsoup4')
152 return token
153
65 154 url_auth = 'https://api.robocoders.ai/auth'
66 155 headers_auth = {
67 156 'accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7',
68 'accept-language': 'en-US,en;q=0.9',
69 'user-agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36',
157 'user-agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
70 158 }
71 159
72 160 async with session.get(url_auth, headers=headers_auth) as response:
73 161 if response.status == 200:
74 text = await response.text()
75 return text.split('id="token">')[1].split('</pre>')[0].strip()
162 html = await response.text()
163 soup = BeautifulSoup(html, 'html.parser')
164 token_element = soup.find('pre', id='token')
165 if token_element:
166 token = token_element.text.strip()
167
168 # Cache the token
169 RobocodersAPI._save_cached_data({"access_token": token})
170 return token
76 171 return None
77 172
78 173 @staticmethod
79 async def _create_session(session: aiohttp.ClientSession, access_token: str) -> str:
174 async def _create_and_cache_session(session: aiohttp.ClientSession, access_token: str) -> str:
80 175 url_create_session = 'https://api.robocoders.ai/create-session'
81 176 headers_create_session = {
82 177 'Authorization': f'Bearer {access_token}'
@@ -85,6 +180,58 @@ class RobocodersAPI(AsyncGeneratorProvider, ProviderModelMixin):
85 180 async with session.get(url_create_session, headers=headers_create_session) as response:
86 181 if response.status == 200:
87 182 data = await response.json()
88 return data.get('sid')
183 session_id = data.get('sid')
184
185 # Cache session ID
186 RobocodersAPI._update_cached_data({"sid": session_id})
187 return session_id
188 elif response.status == 401:
189 RobocodersAPI._clear_cached_data()
190 raise Exception("Unauthorized: Invalid token during session creation.")
191 elif response.status == 422:
192 raise Exception("Validation Error: Check input parameters.")
89 193 return None
90 194
195 @staticmethod
196 def _save_cached_data(new_data: dict):
197 """Save new data to cache file"""
198 RobocodersAPI.CACHE_DIR.mkdir(exist_ok=True)
199 RobocodersAPI.CACHE_FILE.touch(exist_ok=True)
200 with open(RobocodersAPI.CACHE_FILE, "w") as f:
201 json.dump(new_data, f)
202
203 @staticmethod
204 def _update_cached_data(updated_data: dict):
205 """Update existing cache data with new values"""
206 data = {}
207 if RobocodersAPI.CACHE_FILE.exists():
208 with open(RobocodersAPI.CACHE_FILE, "r") as f:
209 try:
210 data = json.load(f)
211 except json.JSONDecodeError:
212 # If cache file is corrupted, start with empty dict
213 data = {}
214
215 data.update(updated_data)
216 with open(RobocodersAPI.CACHE_FILE, "w") as f:
217 json.dump(data, f)
218
219 @staticmethod
220 def _clear_cached_data():
221 """Remove cache file"""
222 try:
223 if RobocodersAPI.CACHE_FILE.exists():
224 RobocodersAPI.CACHE_FILE.unlink()
225 except Exception as e:
226 debug.log(f"Error clearing cache: {e}")
227
228 @staticmethod
229 def _get_cached_data() -> dict:
230 """Get all cached data"""
231 if RobocodersAPI.CACHE_FILE.exists():
232 try:
233 with open(RobocodersAPI.CACHE_FILE, "r") as f:
234 return json.load(f)
235 except json.JSONDecodeError:
236 return {}
237 return {}
Modified g4f/Provider/__init__.py +1 -0
@@ -30,6 +30,7 @@ from .MagickPen import MagickPen
30 30 from .PerplexityLabs import PerplexityLabs
31 31 from .Pi import Pi
32 32 from .Pizzagpt import Pizzagpt
33 from .PollinationsAI import PollinationsAI
33 34 from .Prodia import Prodia
34 35 from .Reka import Reka
35 36 from .ReplicateHome import ReplicateHome
Modified g4f/Provider/needs_auth/Gemini.py +3 -3
Deleted g4f/Provider/needs_auth/PollinationsAI.py +0 -70
Modified g4f/Provider/needs_auth/__init__.py +0 -1
Modified g4f/models.py +80 -33