XFEstudio/gpt4free
refactor: simplify model alias management and update providers
- Removed specific model aliases for various models (e.g., "gpt-4.1-mini", "phi-4", "grok-3-mini"). - Replaced the `get_model` method with a simplified approach using `get_alias` for alias resolution. - Updated model alias management in `PollinationsAI` to handle model aliases in `get_models` method. - Refined the logic for adding models to `text_models` and `vision_models` in `PollinationsAI`. - Replaced `deepseek-v3` and other model aliases with direct models in `PollinationsAI`. - Modified `Ollama` class to handle local models and improve model fetching with API key. - Changed the `create_async_generator` in `Ollama` to support local model handling and proxy use. - Updated `Azure` class to remove unused `extra_body` argument and streamline stream handling. - Updated model providers in `g4f/models.py` to remove certain providers (e.g., `PollinationsAI`) from `best_provider` lists for some models.
28c4c61f
代码差异
@@ -88,21 +88,8 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin):
vision_models = [default_vision_model]
_models_loaded = False
model_aliases = {
"gpt-4.1-mini": "openai",
"gpt-4.1-nano": "openai-fast",
"gpt-4.1": "openai-large",
"o4-mini": "openai-reasoning",
"qwen-2.5-coder-32b": "qwen-coder",
"llama-3.3-70b": "llama",
"llama-4-scout": "llamascout",
"mistral-small-3.1-24b": "mistral",
"phi-4": "phi",
"deepseek-r1": "deepseek-reasoning",
"deepseek-v3-0324": "deepseek",
"deepseek-v3": "deepseek",
"grok-3-mini": "grok",
"grok-3-mini-high": "grok",
"gpt-4o-mini-audio": "openai-audio",
"sdxl-turbo": "turbo",
"gpt-image": "gptimage",
"flux-dev": "flux",
@@ -111,27 +98,11 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin):
"flux": "flux",
"flux-kontext": "kontext",
}
swap_models = {value: key for key, value in model_aliases.items()}
@classmethod
def get_model(cls, model: str) -> str:
"""Get the internal model name from the user-provided model name."""
if not model:
return cls.default_model
# Check if there's an alias for this model
if model in cls.model_aliases:
return cls.model_aliases[model]
# Check if the model exists directly in our model lists
if model in cls.text_models or model in cls.image_models or model in cls.audio_models:
return model
# If no match is found, raise an error
raise ModelNotFoundError(f"PollinationsAI: Model {model} not found")
@classmethod
def get_models(cls, **kwargs):
def get_alias(model: dict) -> str:
return model.get("aliases", model.get("name")).replace("-instruct", "").replace("qwen-", "qwen").replace("qwen", "qwen-")
if not cls._models_loaded:
try:
# Update of image models
@@ -166,25 +137,19 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin):
cls.audio_models.update({alias: {}})
cls.vision_models.extend([
cls.swap_models.get(model.get("name"), model.get("name"))
get_alias(model)
for model in models
if model.get("vision") and model not in cls.vision_models
if model.get("vision") and get_alias(model) not in cls.vision_models
])
for alias, model in cls.model_aliases.items():
if model in cls.vision_models and alias not in cls.vision_models:
cls.vision_models.append(alias)
# Create a set of unique text models starting with default model
text_models = cls.text_models.copy()
# Add models from the API response
for model in models:
model_name = model.get("name")
if model_name and "input_modalities" in model and "text" in model["input_modalities"]:
text_models.append(cls.swap_models.get(model_name, model_name))
# Convert to list and update text_models
cls.text_models = list(dict.fromkeys(text_models))
alias = get_alias(model)
if alias not in cls.text_models:
cls.text_models.append(alias)
if alias != model.get("name"):
cls.model_aliases[alias] = model.get("name")
elif model.get("name") not in cls.text_models:
cls.text_models.append(model.get("name"))
cls._models_loaded = True
@@ -259,10 +224,10 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin):
has_audio = True
break
model = cls.default_audio_model if has_audio else model
try:
model = cls.get_model(model) if model else None
except ModelNotFoundError:
pass
elif cls._models_loaded or cls.get_models():
if model in cls.model_aliases:
model = cls.model_aliases[model]
debug.log(f"Using model: {model}")
if model in cls.image_models:
async for chunk in cls._generate_image(
model="gptimage" if model == "transparent" else model,
@@ -1,22 +1,35 @@
from __future__ import annotations
import json
import requests
import os
from ..needs_auth.OpenaiAPI import OpenaiAPI
from ...requests import StreamSession, raise_for_status
from ...providers.response import Usage, Reasoning
from ...typing import AsyncResult, Messages
class Ollama(OpenaiAPI):
label = "Ollama"
url = "https://ollama.com"
login_url = None
login_url = "https://ollama.com/settings/keys"
api_endpoint = "https://ollama.com/api/chat"
needs_auth = False
working = True
active_by_default = False
active_by_default = True
local_models: list[str] = []
model_aliases = {
"gpt-oss-120b": "gpt-oss:120b",
"gpt-oss-20b": "gpt-oss:20b"
}
@classmethod
def get_models(cls, api_base: str = None, **kwargs):
def get_models(cls, api_key: str = None, api_base: str = None, **kwargs):
if not cls.models:
cls.models = []
if api_key:
models = requests.get("https://ollama.com/api/tags", {"headers": {"Authorization": f"Bearer {api_key}"}}).json()["models"]
cls.models = [model["name"] for model in models]
if api_base is None:
host = os.getenv("OLLAMA_HOST", "127.0.0.1")
port = os.getenv("OLLAMA_PORT", "11434")
@@ -26,23 +39,50 @@ class Ollama(OpenaiAPI):
try:
models = requests.get(url).json()["models"]
except requests.exceptions.RequestException as e:
return cls.fallback_models
cls.models = [model["name"] for model in models]
return cls.models
cls.local_models = [model["name"] for model in models]
cls.models = cls.models + cls.local_models
cls.default_model = next(iter(cls.models), None)
return cls.models
@classmethod
def create_async_generator(
async def create_async_generator(
cls,
model: str,
messages: Messages,
api_key: str = None,
api_base: str = None,
proxy: str = None,
**kwargs
) -> AsyncResult:
if api_base is None:
host = os.getenv("OLLAMA_HOST", "localhost")
port = os.getenv("OLLAMA_PORT", "11434")
api_base: str = f"http://{host}:{port}/v1"
return super().create_async_generator(
model, messages, api_base=api_base, **kwargs
)
if model in cls.local_models or not api_key:
for chunk in super().create_async_generator(
model, messages, api_base=api_base, proxy=proxy, **kwargs
):
yield chunk
else:
async with StreamSession(headers={"Authorization": f"Bearer {api_key}"}, proxy=proxy) as session:
async with session.post(cls.api_endpoint, json={
"model": model,
"messages": messages,
}) as response:
await raise_for_status(response)
last_data = {}
async for chunk in response.iter_lines():
data = json.loads(chunk)
last_data = data
thinking = data.get("message", {}).get("thinking", "")
if thinking:
yield Reasoning(thinking)
content = data.get("message", {}).get("content", "")
if content:
yield content
yield Usage(
prompt_tokens=last_data.get("prompt_eval_count", 0),
completion_tokens=last_data.get("eval_count", 0),
total_tokens=last_data.get("prompt_eval_count", 0) + last_data.get("eval_count", 0),
)
@@ -63,7 +63,6 @@ class Azure(OpenaiTemplate):
messages: Messages,
stream: bool = True,
media: MediaListType = None,
extra_body: dict = None,
api_key: str = None,
api_endpoint: str = None,
**kwargs
@@ -118,17 +117,19 @@ class Azure(OpenaiTemplate):
async with session.post(api_endpoint, data=form, json=data) as response:
data = await response.json()
await raise_for_status(response, data)
async for chunk in save_response_media(data["data"][0]["b64_json"], prompt, content_type=f"image/{output_format}"):
async for chunk in save_response_media(
data["data"][0]["b64_json"],
prompt,
content_type=f"image/{output_format.replace('jpg', 'jpeg')}"
):
yield chunk
return
if extra_body is None:
if model in cls.model_extra_body:
extra_body = cls.model_extra_body[model]
stream = False
else:
extra_body = {}
if model in cls.model_extra_body:
for key, value in cls.model_extra_body[model].items():
kwargs.setdefault(key, value)
stream = False
if stream:
extra_body.setdefault("stream_options", {"include_usage": True})
kwargs.setdefault("stream_options", {"include_usage": True})
try:
async for chunk in super().create_async_generator(
model=model,
@@ -137,7 +138,6 @@ class Azure(OpenaiTemplate):
media=media,
api_key=api_key,
api_endpoint=api_endpoint,
extra_body=extra_body,
**kwargs
):
yield chunk
@@ -80,7 +80,7 @@ class OpenaiTemplate(AsyncGeneratorProvider, ProviderModelMixin, RaiseErrorMixin
headers: dict = None,
impersonate: str = None,
download_media: bool = True,
extra_parameters: list[str] = ["tools", "parallel_tool_calls", "tool_choice", "reasoning_effort", "logit_bias", "modalities", "audio"],
extra_parameters: list[str] = ["tools", "parallel_tool_calls", "tool_choice", "reasoning_effort", "logit_bias", "modalities", "audio", "stream_options"],
extra_body: dict = None,
**kwargs
) -> AsyncResult:
@@ -88,6 +88,7 @@ class OpenaiTemplate(AsyncGeneratorProvider, ProviderModelMixin, RaiseErrorMixin
api_key = cls.api_key
if cls.needs_auth and api_key is None:
raise MissingAuthError('Add a "api_key"')
print(cls.get_headers(stream, api_key, headers))
async with StreamSession(
proxy=proxy,
headers=cls.get_headers(stream, api_key, headers),
@@ -135,9 +136,10 @@ class OpenaiTemplate(AsyncGeneratorProvider, ProviderModelMixin, RaiseErrorMixin
**extra_body
)
if api_endpoint is None:
api_endpoint = cls.api_endpoint
if api_endpoint is None:
if api_base:
api_endpoint = f"{api_base.rstrip('/')}/chat/completions"
if api_endpoint is None:
api_endpoint = cls.api_endpoint
async with session.post(api_endpoint, json=data, ssl=cls.ssl) as response:
async for chunk in read_response(response, stream, prompt, cls.get_dict(), download_media):
yield chunk
@@ -214,7 +214,7 @@ gpt_4o_mini = Model(
)
gpt_4o_mini_audio = AudioModel(
name = 'gpt-4o-mini-audio',
name = 'gpt-4o-mini-audio-preview',
base_provider = 'OpenAI',
best_provider = PollinationsAI
)
@@ -255,7 +255,7 @@ o3_mini_high = Model(
o4_mini = Model(
name = 'o4-mini',
base_provider = 'OpenAI',
best_provider = IterListProvider([PollinationsAI, OpenaiChat])
best_provider = OpenaiChat
)
o4_mini_high = Model(
@@ -274,7 +274,7 @@ gpt_4_1 = Model(
gpt_4_1_mini = Model(
name = 'gpt-4.1-mini',
base_provider = 'OpenAI',
best_provider = IterListProvider([Blackbox, OIVSCodeSer0501, PollinationsAI])
best_provider = IterListProvider([Blackbox, OIVSCodeSer0501])
)
gpt_4_1_nano = Model(
@@ -390,7 +390,7 @@ llama_3_2_90b = Model(
llama_3_3_70b = Model(
name = "llama-3.3-70b",
base_provider = "Meta Llama",
best_provider = IterListProvider([DeepInfraChat, LambdaChat, PollinationsAI, Together, HuggingChat, HuggingFace])
best_provider = IterListProvider([DeepInfraChat, LambdaChat, Together, HuggingChat, HuggingFace])
)
# llama-4
@@ -456,7 +456,7 @@ phi_3_5_mini = Model(
phi_4 = Model(
name = "phi-4",
base_provider = "Microsoft",
best_provider = IterListProvider([DeepInfraChat, PollinationsAI, HuggingSpace])
best_provider = IterListProvider([DeepInfraChat, HuggingSpace])
)
phi_4_multimodal = VisionModel(
@@ -753,7 +753,7 @@ qwq_32b = Model(
deepseek_v3 = Model(
name = 'deepseek-v3',
base_provider = 'DeepSeek',
best_provider = IterListProvider([DeepInfraChat, PollinationsAI, Together])
best_provider = IterListProvider([DeepInfraChat, Together])
)
# deepseek-r1
@@ -810,7 +810,7 @@ deepseek_prover_v2_671b = Model(
deepseek_v3_0324 = Model(
name = 'deepseek-v3-0324',
base_provider = 'DeepSeek',
best_provider = IterListProvider([DeepInfraChat, LambdaChat, PollinationsAI])
best_provider = IterListProvider([DeepInfraChat, LambdaChat])
)
deepseek_v3_0324_turbo = Model(
@@ -823,7 +823,7 @@ deepseek_v3_0324_turbo = Model(
deepseek_r1_0528 = Model(
name = 'deepseek-r1-0528',
base_provider = 'DeepSeek',
best_provider = IterListProvider([DeepInfraChat, LambdaChat])
best_provider = IterListProvider([DeepInfraChat, LambdaChat, PollinationsAI])
)
deepseek_r1_0528_turbo = Model(
@@ -852,12 +852,6 @@ grok_3 = Model(
best_provider = Grok
)
grok_3_mini = Model(
name = 'grok-3-mini',
base_provider = 'x.ai',
best_provider = PollinationsAI
)
grok_3_r1 = Model(
name = 'grok-3-r1',
base_provider = 'x.ai',