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

XFEstudio/gpt4free

Improved ignored providers support, Add get_models to OpenaiAPI, HuggingFace and Groq Add xAI provider

ff66df14
Heiner Lohaus <hlohaus@users.noreply.github.com>
提交于

代码差异

16 个文件 +157 -121
Modified g4f/Provider/Blackbox2.py +1 -0
@@ -34,6 +34,7 @@ class Blackbox2(AsyncGeneratorProvider, ProviderModelMixin):
34 34 model: str,
35 35 messages: Messages,
36 36 proxy: str = None,
37 prompt: str = None,
37 38 max_retries: int = 3,
38 39 delay: int = 1,
39 40 **kwargs
Modified g4f/Provider/Copilot.py +5 -4
@@ -123,11 +123,12 @@ class Copilot(AbstractProvider, ProviderModelMixin):
123 123 prompt = format_prompt(messages)
124 124 if len(prompt) > 10000:
125 125 if len(messages) > 6:
126 prompt = format_prompt(messages[:3]+messages[-3:])
127 elif len(messages) > 2:
128 prompt = format_prompt(messages[:2]+messages[-1:])
126 prompt = format_prompt(messages[:3] + messages[-3:])
129 127 if len(prompt) > 10000:
130 prompt = messages[-1]["content"]
128 if len(messages) > 2:
129 prompt = format_prompt(messages[:2] + messages[-1:])
130 if len(prompt) > 10000:
131 prompt = messages[-1]["content"]
131 132 debug.log(f"Copilot: Trim messages to: {len(prompt)}")
132 133 debug.log(f"Copilot: Created conversation: {conversation_id}")
133 134 else:
Modified g4f/Provider/DarkAI.py +17 -29
@@ -4,38 +4,26 @@ import json
4 4 from aiohttp import ClientSession
5 5
6 6 from ..typing import AsyncResult, Messages
7 from ..requests.raise_for_status import raise_for_status
7 8 from .base_provider import AsyncGeneratorProvider, ProviderModelMixin
8 9 from .helper import format_prompt
9 10
10
11 11 class DarkAI(AsyncGeneratorProvider, ProviderModelMixin):
12 12 url = "https://darkai.foundation/chat"
13 13 api_endpoint = "https://darkai.foundation/chat"
14 14 working = True
15 15 supports_stream = True
16 supports_system_message = True
17 supports_message_history = True
18
16
19 17 default_model = 'llama-3-70b'
20 18 models = [
21 19 'gpt-4o', # Uncensored
22 20 'gpt-3.5-turbo', # Uncensored
23 21 default_model,
24 22 ]
25
26 23 model_aliases = {
27 24 "llama-3.1-70b": "llama-3-70b",
28 25 }
29 26
30 @classmethod
31 def get_model(cls, model: str) -> str:
32 if model in cls.models:
33 return model
34 elif model in cls.model_aliases:
35 return cls.model_aliases[model]
36 else:
37 return cls.default_model
38
39 27 @classmethod
40 28 async def create_async_generator(
41 29 cls,
@@ -45,7 +33,7 @@ class DarkAI(AsyncGeneratorProvider, ProviderModelMixin):
45 33 **kwargs
46 34 ) -> AsyncResult:
47 35 model = cls.get_model(model)
48
36
49 37 headers = {
50 38 "accept": "text/event-stream",
51 39 "content-type": "application/json",
@@ -58,24 +46,24 @@ class DarkAI(AsyncGeneratorProvider, ProviderModelMixin):
58 46 "model": model,
59 47 }
60 48 async with session.post(cls.api_endpoint, json=data, proxy=proxy) as response:
61 response.raise_for_status()
62 full_text = ""
63 async for chunk in response.content:
64 if chunk:
49 await raise_for_status(response)
50 first = True
51 async for line in response.content:
52 if line:
65 53 try:
66 chunk_str = chunk.decode().strip()
67 if chunk_str.startswith('data: '):
68 chunk_data = json.loads(chunk_str[6:])
54 line_str = line.decode().strip()
55 if line_str.startswith('data: '):
56 chunk_data = json.loads(line_str[6:])
69 57 if chunk_data['event'] == 'text-chunk':
70 full_text += chunk_data['data']['text']
58 chunk = chunk_data['data']['text']
59 if first:
60 chunk = chunk.lstrip()
61 if chunk:
62 first = False
63 yield chunk
71 64 elif chunk_data['event'] == 'stream-end':
72 if full_text:
73 yield full_text.strip()
74 65 return
75 66 except json.JSONDecodeError:
76 67 pass
77 68 except Exception:
78 pass
79
80 if full_text:
81 yield full_text.strip()
69 pass
Modified g4f/Provider/needs_auth/Cerebras.py +4 -19
@@ -1,6 +1,5 @@
1 1 from __future__ import annotations
2 2
3 import requests
4 3 from aiohttp import ClientSession
5 4
6 5 from .OpenaiAPI import OpenaiAPI
@@ -11,35 +10,21 @@ from ...cookies import get_cookies
11 10 class Cerebras(OpenaiAPI):
12 11 label = "Cerebras Inference"
13 12 url = "https://inference.cerebras.ai/"
13 api_base = "https://api.cerebras.ai/v1"
14 14 working = True
15 15 default_model = "llama3.1-70b"
16 fallback_models = [
16 models = [
17 17 "llama3.1-70b",
18 18 "llama3.1-8b",
19 19 ]
20 20 model_aliases = {"llama-3.1-70b": "llama3.1-70b", "llama-3.1-8b": "llama3.1-8b"}
21 21
22 @classmethod
23 def get_models(cls, api_key: str = None):
24 if not cls.models:
25 try:
26 headers = {}
27 if api_key:
28 headers["authorization"] = f"Bearer ${api_key}"
29 response = requests.get(f"https://api.cerebras.ai/v1/models", headers=headers)
30 raise_for_status(response)
31 data = response.json()
32 cls.models = [model.get("model") for model in data.get("models")]
33 except Exception:
34 cls.models = cls.fallback_models
35 return cls.models
36
37 22 @classmethod
38 23 async def create_async_generator(
39 24 cls,
40 25 model: str,
41 26 messages: Messages,
42 api_base: str = "https://api.cerebras.ai/v1",
27 api_base: str = api_base,
43 28 api_key: str = None,
44 29 cookies: Cookies = None,
45 30 **kwargs
@@ -62,4 +47,4 @@ class Cerebras(OpenaiAPI):
62 47 },
63 48 **kwargs
64 49 ):
65 yield chunk
50 yield chunk
Modified g4f/Provider/needs_auth/Groq.py +4 -3
@@ -6,9 +6,10 @@ from ...typing import AsyncResult, Messages
6 6 class Groq(OpenaiAPI):
7 7 label = "Groq"
8 8 url = "https://console.groq.com/playground"
9 api_base = "https://api.groq.com/openai/v1"
9 10 working = True
10 11 default_model = "mixtral-8x7b-32768"
11 models = [
12 fallback_models = [
12 13 "distil-whisper-large-v3-en",
13 14 "gemma2-9b-it",
14 15 "gemma-7b-it",
@@ -35,9 +36,9 @@ class Groq(OpenaiAPI):
35 36 cls,
36 37 model: str,
37 38 messages: Messages,
38 api_base: str = "https://api.groq.com/openai/v1",
39 api_base: str = api_base,
39 40 **kwargs
40 41 ) -> AsyncResult:
41 42 return super().create_async_generator(
42 43 model, messages, api_base=api_base, **kwargs
43 )
44 )
Modified g4f/Provider/needs_auth/HuggingFace.py +24 -12
@@ -6,8 +6,8 @@ import random
6 6 import requests
7 7
8 8 from ...typing import AsyncResult, Messages
9 from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin
10 from ...errors import ModelNotFoundError, ModelNotSupportedError
9 from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin, format_prompt
10 from ...errors import ModelNotFoundError, ModelNotSupportedError, ResponseError
11 11 from ...requests import StreamSession, raise_for_status
12 12 from ...image import ImageResponse
13 13
@@ -28,9 +28,11 @@ class HuggingFace(AsyncGeneratorProvider, ProviderModelMixin):
28 28 cls.models = [model["id"] for model in requests.get(url).json()]
29 29 cls.models.append("meta-llama/Llama-3.2-11B-Vision-Instruct")
30 30 cls.models.append("nvidia/Llama-3.1-Nemotron-70B-Instruct-HF")
31 cls.models.sort()
31 32 if not cls.image_models:
32 33 url = "https://huggingface.co/api/models?pipeline_tag=text-to-image"
33 34 cls.image_models = [model["id"] for model in requests.get(url).json() if model["trendingScore"] >= 20]
35 cls.image_models.sort()
34 36 cls.models.extend(cls.image_models)
35 37 return cls.models
36 38
@@ -89,19 +91,27 @@ class HuggingFace(AsyncGeneratorProvider, ProviderModelMixin):
89 91 ) as session:
90 92 if payload is None:
91 93 async with session.get(f"https://huggingface.co/api/models/{model}") as response:
94 await raise_for_status(response)
92 95 model_data = await response.json()
93 if "config" in model_data and "tokenizer_config" in model_data["config"] and "eos_token" in model_data["config"]["tokenizer_config"]:
96 model_type = None
97 if "config" in model_data and "model_type" in model_data["config"]:
98 model_type = model_data["config"]["model_type"]
99 if model_type in ("gpt2", "gpt_neo", "gemma", "gemma2"):
100 inputs = format_prompt(messages)
101 elif "config" in model_data and "tokenizer_config" in model_data["config"] and "eos_token" in model_data["config"]["tokenizer_config"]:
94 102 eos_token = model_data["config"]["tokenizer_config"]["eos_token"]
95 if eos_token == "</s>":
96 inputs = format_prompt_mistral(messages)
103 if eos_token in ("<|endoftext|>", "<eos>", "</s>"):
104 inputs = format_prompt_custom(messages, eos_token)
97 105 elif eos_token == "<|im_end|>":
98 106 inputs = format_prompt_qwen(messages)
99 107 elif eos_token == "<|eot_id|>":
100 108 inputs = format_prompt_llama(messages)
101 109 else:
102 inputs = format_prompt(messages)
110 inputs = format_prompt_default(messages)
103 111 else:
104 inputs = format_prompt(messages)
112 inputs = format_prompt_default(messages)
113 if model_type == "gpt2" and max_new_tokens >= 1024:
114 params["max_new_tokens"] = 512
105 115 payload = {"inputs": inputs, "parameters": params, "stream": stream}
106 116
107 117 async with session.post(f"{api_base.rstrip('/')}/models/{model}", json=payload) as response:
@@ -113,6 +123,8 @@ class HuggingFace(AsyncGeneratorProvider, ProviderModelMixin):
113 123 async for line in response.iter_lines():
114 124 if line.startswith(b"data:"):
115 125 data = json.loads(line[5:])
126 if "error" in data:
127 raise ResponseError(data["error"])
116 128 if not data["token"]["special"]:
117 129 chunk = data["token"]["text"]
118 130 if first:
@@ -128,7 +140,7 @@ class HuggingFace(AsyncGeneratorProvider, ProviderModelMixin):
128 140 else:
129 141 yield (await response.json())[0]["generated_text"].strip()
130 142
131 def format_prompt(messages: Messages) -> str:
143 def format_prompt_default(messages: Messages) -> str:
132 144 system_messages = [message["content"] for message in messages if message["role"] == "system"]
133 145 question = " ".join([messages[-1]["content"], *system_messages])
134 146 history = "".join([
@@ -146,9 +158,9 @@ def format_prompt_qwen(messages: Messages) -> str:
146 158 def format_prompt_llama(messages: Messages) -> str:
147 159 return "<|begin_of_text|>" + "".join([
148 160 f"<|start_header_id|>{message['role']}<|end_header_id|>\n\n{message['content']}\n<|eot_id|>\n" for message in messages
149 ]) + "<|start_header_id|>assistant<|end_header_id|>\\n\\n"
150
151 def format_prompt_mistral(messages: Messages) -> str:
161 ]) + "<|start_header_id|>assistant<|end_header_id|>\n\n"
162
163 def format_prompt_custom(messages: Messages, end_token: str = "</s>") -> str:
152 164 return "".join([
153 f"<|{message['role']}|>\n{message['content']}'</s>\n" for message in messages
165 f"<|{message['role']}|>\n{message['content']}{end_token}\n" for message in messages
154 166 ]) + "<|assistant|>\n"
Modified g4f/Provider/needs_auth/HuggingFaceAPI.py +2 -1
@@ -7,6 +7,7 @@ from ...typing import AsyncResult, Messages
7 7 class HuggingFaceAPI(OpenaiAPI):
8 8 label = "HuggingFace (Inference API)"
9 9 url = "https://api-inference.huggingface.co"
10 api_base = "https://api-inference.huggingface.co/v1"
10 11 working = True
11 12 default_model = "meta-llama/Llama-3.2-11B-Vision-Instruct"
12 13 default_vision_model = default_model
@@ -19,7 +20,7 @@ class HuggingFaceAPI(OpenaiAPI):
19 20 cls,
20 21 model: str,
21 22 messages: Messages,
22 api_base: str = "https://api-inference.huggingface.co/v1",
23 api_base: str = api_base,
23 24 max_tokens: int = 500,
24 25 **kwargs
25 26 ) -> AsyncResult:
Modified g4f/Provider/needs_auth/OpenaiAPI.py +28 -4
@@ -1,6 +1,7 @@
1 1 from __future__ import annotations
2 2
3 3 import json
4 import requests
4 5
5 6 from ..helper import filter_none
6 7 from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin, FinishReason
@@ -8,15 +9,35 @@ from ...typing import Union, Optional, AsyncResult, Messages, ImagesType
8 9 from ...requests import StreamSession, raise_for_status
9 10 from ...errors import MissingAuthError, ResponseError
10 11 from ...image import to_data_uri
12 from ... import debug
11 13
12 14 class OpenaiAPI(AsyncGeneratorProvider, ProviderModelMixin):
13 15 label = "OpenAI API"
14 16 url = "https://platform.openai.com"
17 api_base = "https://api.openai.com/v1"
15 18 working = True
16 19 needs_auth = True
17 20 supports_message_history = True
18 21 supports_system_message = True
19 22 default_model = ""
23 fallback_models = []
24
25 @classmethod
26 def get_models(cls, api_key: str = None):
27 if not cls.models:
28 try:
29 headers = {}
30 if api_key is not None:
31 headers["authorization"] = f"Bearer {api_key}"
32 response = requests.get(f"{cls.api_base}/models", headers=headers)
33 raise_for_status(response)
34 data = response.json()
35 cls.models = [model.get("id") for model in data.get("data")]
36 cls.models.sort()
37 except Exception as e:
38 debug.log(e)
39 cls.models = cls.fallback_models
40 return cls.models
20 41
21 42 @classmethod
22 43 async def create_async_generator(
@@ -27,7 +48,7 @@ class OpenaiAPI(AsyncGeneratorProvider, ProviderModelMixin):
27 48 timeout: int = 120,
28 49 images: ImagesType = None,
29 50 api_key: str = None,
30 api_base: str = "https://api.openai.com/v1",
51 api_base: str = api_base,
31 52 temperature: float = None,
32 53 max_tokens: int = None,
33 54 top_p: float = None,
@@ -47,14 +68,14 @@ class OpenaiAPI(AsyncGeneratorProvider, ProviderModelMixin):
47 68 *[{
48 69 "type": "image_url",
49 70 "image_url": {"url": to_data_uri(image)}
50 } for image, image_name in images],
71 } for image, _ in images],
51 72 {
52 73 "type": "text",
53 74 "text": messages[-1]["content"]
54 75 }
55 76 ]
56 77 async with StreamSession(
57 proxies={"all": proxy},
78 proxy=proxy,
58 79 headers=cls.get_headers(stream, api_key, headers),
59 80 timeout=timeout,
60 81 impersonate=impersonate,
@@ -111,7 +132,10 @@ class OpenaiAPI(AsyncGeneratorProvider, ProviderModelMixin):
111 132 if "error_message" in data:
112 133 raise ResponseError(data["error_message"])
113 134 elif "error" in data:
114 raise ResponseError(f'Error {data["error"]["code"]}: {data["error"]["message"]}')
135 if "code" in data["error"]:
136 raise ResponseError(f'Error {data["error"]["code"]}: {data["error"]["message"]}')
137 else:
138 raise ResponseError(data["error"]["message"])
115 139
116 140 @classmethod
117 141 def get_headers(cls, stream: bool, api_key: str = None, headers: dict = None) -> dict:
Modified g4f/Provider/needs_auth/__init__.py +1 -0
@@ -26,3 +26,4 @@ from .Replicate import Replicate
26 26 from .Theb import Theb
27 27 from .ThebApi import ThebApi
28 28 from .WhiteRabbitNeo import WhiteRabbitNeo
29 from .xAI import xAI
Added g4f/Provider/needs_auth/xAI.py +22 -0
@@ -0,0 +1,22 @@
1 from __future__ import annotations
2
3 from .OpenaiAPI import OpenaiAPI
4 from ...typing import AsyncResult, Messages
5
6 class xAI(OpenaiAPI):
7 label = "xAI"
8 url = "https://console.x.ai"
9 api_base = "https://api.x.ai/v1"
10 working = True
11
12 @classmethod
13 def create_async_generator(
14 cls,
15 model: str,
16 messages: Messages,
17 api_base: str = api_base,
18 **kwargs
19 ) -> AsyncResult:
20 return super().create_async_generator(
21 model, messages, api_base=api_base, **kwargs
22 )
Modified g4f/__init__.py +6 -9
@@ -2,16 +2,15 @@ from __future__ import annotations
2 2
3 3 import os
4 4 import logging
5 from typing import Union, Optional
5 from typing import Union, Optional, Coroutine
6 6
7 7 from . import debug, version
8 8 from .models import Model
9 9 from .client import Client, AsyncClient
10 10 from .typing import Messages, CreateResult, AsyncResult, ImageType
11 from .errors import StreamNotSupportedError, ModelNotAllowedError
11 from .errors import StreamNotSupportedError
12 12 from .cookies import get_cookies, set_cookies
13 13 from .providers.types import ProviderType
14 from .providers.base_provider import AsyncGeneratorProvider
15 14 from .client.service import get_model_and_provider, get_last_provider
16 15
17 16 #Configure "g4f" logger
@@ -30,14 +29,13 @@ class ChatCompletion:
30 29 stream : bool = False,
31 30 image : ImageType = None,
32 31 image_name: Optional[str] = None,
33 ignored: list[str] = None,
34 32 ignore_working: bool = False,
35 33 ignore_stream: bool = False,
36 34 **kwargs) -> Union[CreateResult, str]:
37 35 model, provider = get_model_and_provider(
38 36 model, provider, stream,
39 ignored, ignore_working,
40 ignore_stream or kwargs.get("ignore_stream_and_auth")
37 ignore_working,
38 ignore_stream
41 39 )
42 40 if image is not None:
43 41 kwargs["images"] = [(image, image_name)]
@@ -55,10 +53,9 @@ class ChatCompletion:
55 53 messages : Messages,
56 54 provider : Union[ProviderType, str, None] = None,
57 55 stream : bool = False,
58 ignored : list[str] = None,
59 56 ignore_working: bool = False,
60 **kwargs) -> Union[AsyncResult, str]:
61 model, provider = get_model_and_provider(model, provider, False, ignored, ignore_working)
57 **kwargs) -> Union[AsyncResult, Coroutine[str]]:
58 model, provider = get_model_and_provider(model, provider, False, ignore_working)
62 59
63 60 if stream:
64 61 if hasattr(provider, "create_async_generator"):
Modified g4f/api/__init__.py +6 -7
@@ -84,6 +84,11 @@ def create_app():
84 84 if not AppConfig.ignore_cookie_files:
85 85 read_cookie_files()
86 86
87 if AppConfig.ignored_providers:
88 for provider in AppConfig.ignored_providers:
89 if provider in ProviderUtils.convert:
90 ProviderUtils.convert[provider].working = False
91
87 92 return app
88 93
89 94 def create_app_debug():
@@ -151,7 +156,7 @@ class ErrorResponseMessageModel(BaseModel):
151 156
152 157 class FileResponseModel(BaseModel):
153 158 filename: str
154
159
155 160 class ErrorResponse(Response):
156 161 media_type = "application/json"
157 162
@@ -183,12 +188,6 @@ class AppConfig:
183 188 for key, value in data.items():
184 189 setattr(cls, key, value)
185 190
186 list_ignored_providers: list[str] = None
187
188 def set_list_ignored_providers(ignored: list[str]):
189 global list_ignored_providers
190 list_ignored_providers = ignored
191
192 191 class Api:
193 192 def __init__(self, app: FastAPI) -> None:
194 193 self.app = app
Modified g4f/client/service.py +2 -5
Modified g4f/cookies.py +15 -13
Modified g4f/gui/client/index.html +5 -1
Modified g4f/providers/retry_provider.py +15 -14