XFEstudio/gpt4free
refactor: Refactor image generation parameters handling
- Updated the `PollinationsAI` class in `g4f/Provider/PollinationsAI.py`: - Changed `aspect_ratio` parameter handling to conditionally use default "1:1" if not specified. - Enhanced media handling by introducing `media` parameter in `_generate_image` method. - Updated parameter processing in `_generate_image_async` method for `model == "gptimage"`. - Updated `Api` class in `g4f/api/__init__.py`: - Simplified handling of `credentials` for `config.api_key`. - Updated `Images` class in `g4f/client/__init__.py`: - Added `download_media` parameter to `_process_image_response` method. - Enhanced `_process_image_response` method to conditionally download media based on `download_media` flag. - Updated `_process_image_response` method in `Images` class in `g4f/client/__init__.py`: - Enhanced handling of media response based on `download_media` flag. - Updated `is_valid_media` function in `g4f/image/__init__.py`: - Added typing annotations for clarity. - Updated `AnyProvider` class in `g4f/providers/any_provider.py`: - Improved handling of `api_key` dictionary to set `extra_body["api_key"]`. - Updated `IterListProvider` class in `g4f/providers/retry_provider.py`: - Enhanced handling of `model` and `api_key` parameters. - Updated `BaseProvider` class in `g4f/providers/types.py`: - Added `create_function` and `async_create_function` methods. - Updated `BaseRetryProvider` class in `g4f/providers/types.py`: - Enhanced handling of `model` and `api_key` parameters in provider iteration.
963c3a58
代码差异
@@ -252,7 +252,7 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin):
extra_body: dict = None,
# Image generation parameters
prompt: str = None,
aspect_ratio: str = "1:1",
aspect_ratio: str = None,
width: int = None,
height: int = None,
seed: Optional[int] = None,
@@ -294,6 +294,7 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin):
async for chunk in cls._generate_image(
model=model,
prompt=format_media_prompt(messages, prompt),
media=media,
proxy=proxy,
aspect_ratio=aspect_ratio,
width=width,
@@ -347,6 +348,7 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin):
cls,
model: str,
prompt: str,
media: MediaListType,
proxy: str,
aspect_ratio: str,
width: int,
@@ -362,20 +364,30 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin):
api_key: str,
timeout: int = 120
) -> AsyncResult:
if model == "gptimage":
n = 1
params = use_aspect_ratio({
"width": width,
"height": height,
params = {
"model": model,
"nologo": str(nologo).lower(),
"private": str(private).lower(),
"enhance": str(enhance).lower(),
"safe": str(safe).lower(),
}, aspect_ratio)
}
if model == "gptimage":
n = 1
# Only remote images are supported
image = [item[0] for item in media if isinstance(item[0], str) and item[0].startswith("http")]
params = {
**params,
"image": ",".join(image) if image else "",
}
else:
params = use_aspect_ratio({
"width": width,
"height": height,
**params
}, "1:1" if aspect_ratio is None else aspect_ratio)
query = "&".join(f"{k}={quote_plus(str(v))}" for k, v in params.items() if v is not None)
encoded_prompt = prompt
if model == "gptimage" and aspect_ratio != "1:1":
if model == "gptimage" and aspect_ratio is not None:
encoded_prompt = f"{encoded_prompt} aspect-ratio: {aspect_ratio}"
encoded_prompt = quote_plus(encoded_prompt)[:2048-len(cls.image_api_endpoint)-len(query)-8]
url = f"{cls.image_api_endpoint}prompt/{encoded_prompt}?{query}"
@@ -425,7 +425,7 @@ class Api:
try:
if config.provider is None:
config.provider = AppConfig.provider if provider is None else provider
if credentials is not None and credentials.credentials != "secret":
if config.api_key is None and credentials is not None and credentials.credentials != "secret":
config.api_key = credentials.credentials
conversation = None
@@ -618,9 +618,7 @@ class Api:
provider=config.provider if provider is None else provider,
prompt=config.input,
audio=filter_none(voice=config.voice, format=config.response_format, language=config.language),
**filter_none(
api_key=api_key,
)
api_key=api_key,
)
if isinstance(response.choices[0].message.content, AudioResponse):
response = response.choices[0].message.content.data
@@ -479,6 +479,7 @@ class Images:
response,
model,
provider_name,
kwargs.get("download_media", True),
response_format,
proxy
)
@@ -531,7 +532,7 @@ class Images:
urls.extend(item.urls)
if not urls:
return None
alt = getattr(items[0], "alt", items[0].options.get("text"))
alt = getattr(items[0], "alt", "")
return MediaResponse(urls, alt, items[0].options)
def create_variation(
@@ -580,13 +581,21 @@ class Images:
if error is not None:
raise error
raise NoMediaResponseError(f"No media response from {provider_name}")
return await self._process_image_response(response, model, provider_name, response_format, proxy)
return await self._process_image_response(
response,
model,
provider_name,
kwargs.get("download_media", True),
response_format,
proxy
)
async def _process_image_response(
self,
response: MediaResponse,
model: str,
provider: str,
download_media: bool,
response_format: Optional[str] = None,
proxy: str = None
) -> ImagesResponse:
@@ -609,9 +618,10 @@ class Images:
images = await asyncio.gather(*[get_b64_from_url(image) for image in response.get_list()])
else:
# Save locally for None (default) case
images = await copy_media(response.get_list(), response.get("cookies"), response.get("headers"), proxy, response.alt)
if download_media or response.get("cookies"):
images = await copy_media(response.get_list(), response.get("cookies"), response.get("headers"), proxy, response.alt)
images = [Image.model_construct(url=image, revised_prompt=response.alt) for image in images]
return ImagesResponse.model_construct(
created=int(time.time()),
data=images,
@@ -14,7 +14,7 @@ try:
except ImportError:
has_requirements = False
from ..typing import ImageType, Union, Image
from ..typing import ImageType, Image
from ..errors import MissingRequirementsError
EXTENSIONS_MAP: dict[str, str] = {
@@ -107,7 +107,7 @@ def is_data_an_media(data, filename: str = None) -> str:
return is_accepted_format(data)
return is_data_uri_an_image(data)
def is_valid_media(data, filename: str = None) -> str:
def is_valid_media(data: ImageType = None, filename: str = None) -> str:
if is_valid_audio(data, filename):
return True
if filename:
@@ -319,8 +319,9 @@ class AnyProvider(AsyncGeneratorProvider, ProviderModelMixin):
extra_providers = []
if isinstance(api_key, dict):
for provider in api_key:
if provider in __map__ and __map__[provider] not in MAIN_PROVIERS:
extra_providers.append(__map__[provider])
if api_key.get(provider):
if provider in __map__ and __map__[provider] not in MAIN_PROVIERS:
extra_providers.append(__map__[provider])
for provider in MAIN_PROVIERS + extra_providers:
if provider.working:
if not model or model in provider.get_models() or model in provider.model_aliases:
@@ -53,11 +53,16 @@ class IterListProvider(BaseRetryProvider):
for provider in self.get_providers(stream and not ignore_stream, ignored):
self.last_provider = provider
debug.log(f"Using {provider.__name__} provider")
yield ProviderInfo(**provider.get_dict(), model=model if model else getattr(provider, "default_model"))
if not model:
model = getattr(provider, "default_model", None)
model = provider.model_aliases.get(model, model) if hasattr(provider, "model_aliases") else model
debug.log(f"Using {provider.__name__} provider with model {model}")
yield ProviderInfo(**provider.get_dict(), model=model)
extra_body = kwargs.copy()
if isinstance(api_key, dict):
extra_body["api_key"] = api_key.get(provider.get_parent())
api_key = api_key.get(provider.get_parent())
if api_key:
extra_body["api_key"] = api_key
try:
response = provider.create_function(model, messages, stream=stream, **extra_body)
for chunk in response:
@@ -92,11 +97,16 @@ class IterListProvider(BaseRetryProvider):
for provider in self.get_providers(stream and not ignore_stream, ignored):
self.last_provider = provider
debug.log(f"Using {provider.__name__} provider" + (f" and {model} model" if model else ""))
yield ProviderInfo(**provider.get_dict(), model=model if model else getattr(provider, "default_model"))
if not model:
model = getattr(provider, "default_model", None)
model = provider.model_aliases.get(model, model) if hasattr(provider, "model_aliases") else model
debug.log(f"Using {provider.__name__} provider with model {model}")
yield ProviderInfo(**provider.get_dict(), model=model)
extra_body = kwargs.copy()
if isinstance(api_key, dict):
extra_body["api_key"] = api_key.get(provider.get_parent())
api_key = api_key.get(provider.get_parent())
if api_key:
extra_body["api_key"] = api_key
if conversation is not None and hasattr(conversation, provider.__name__):
extra_body["conversation"] = JsonConversation(**getattr(conversation, provider.__name__))
try:
@@ -42,6 +42,42 @@ class BaseProvider(ABC):
def get_parent(cls) -> str:
return getattr(cls, "parent", cls.__name__)
@abstractmethod
def create_function(
*args,
**kwargs
) -> CreateResult:
"""
Create a function to generate a response based on the model and messages.
Args:
model (str): The model to use.
messages (Messages): The messages to process.
stream (bool): Whether to stream the response.
Returns:
CreateResult: The result of the creation.
"""
raise NotImplementedError()
@staticmethod
def async_create_function(
*args,
**kwargs
) -> CreateResult:
"""
Asynchronously create a function to generate a response based on the model and messages.
Args:
model (str): The model to use.
messages (Messages): The messages to process.
stream (bool): Whether to stream the response.
Returns:
CreateResult: The result of the creation.
"""
raise NotImplementedError()
class BaseRetryProvider(BaseProvider):
"""
Base class for a provider that implements retry logic.