XFEstudio/gpt4free
refactor: update model mappings, error handling, and file utils
- Changed `generate_commit_message` return to `.strip("`").strip()` in `commit.py` - Added new model mappings in `PollinationsAI.py`, including `gpt-4.1`, `gpt-4.1-mini`, and `deepseek-r1-distill-*` - Removed `print` debug statement from `PollinationsAI.py` request payload - Replaced temp file handling in `MarkItDown.py` with `get_tempfile` utility - Added `get_tempfile` function to `files.py` for consistent tempfile creation - Added `gpt-4.1` to `text_models` list in `models.py` - Added `ModelNotSupportedError` to exception handling in `OpenaiChat.py` - Updated message content creation to use `to_string()` in `OpenaiChat.py` - Wrapped `get_model()` in try-except to ignore `ModelNotSupportedError` in `OpenaiChat.py` - Adjusted `convert` endpoint in `api/__init__.py` to accept optional `provider` param - Refactored `/api/markitdown` to reuse `convert()` handler in `api/__init__.py
e238ca3a
代码差异
@@ -201,7 +201,7 @@ def generate_commit_message(diff_text: str, model: str = DEFAULT_MODEL) -> Optio
spinner = None
content.append(chunk.choices[0].delta.content)
print(chunk.choices[0].delta.content, end="", flush=True)
return "".join(content).strip().strip("`")
return "".join(content).strip("`").strip()
except Exception as e:
# Stop spinner if it's running
if 'spinner' in locals() and spinner:
@@ -59,6 +59,11 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin):
"gpt-4o-mini": "openai",
"gpt-4": "openai-large",
"gpt-4o": "openai-large",
"gpt-4.1": "openai",
"gpt-4.1-nano": "openai",
"gpt-4.1-mini": "openai-large",
"gpt-4.1-xlarge": "openai-xlarge",
"o4-mini": "openai-reasoning",
"qwen-2.5-coder-32b": "qwen-coder",
"llama-3.3-70b": "llama",
"llama-4-scout": "llamascout",
@@ -67,10 +72,12 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin):
"llama-3.3-70b": "llama-scaleway",
"phi-4": "phi",
"deepseek-r1": "deepseek-reasoning-large",
"deepseek-r1": "deepseek-reasoning",
"deepseek-r1-distill-llama-70b": "deepseek-reasoning-large",
"deepseek-r1-distill-qwen-32b": "deepseek-reasoning",
"deepseek-v3": "deepseek",
"llama-3.2-11b": "llama-vision",
"gpt-4o-audio": "openai-audio",
"gpt-4o-audio-preview": "openai-audio",
### Image Models ###
"sdxl-turbo": "turbo",
@@ -331,7 +338,6 @@ class PollinationsAI(AsyncGeneratorProvider, ProviderModelMixin):
"cache": cache,
**extra_parameters
})
print(f"Requesting {url} with data: {data}")
async with session.post(url, json=data) as response:
await raise_for_status(response)
if response.headers["content-type"].startswith("text/plain"):
@@ -1,7 +1,5 @@
from __future__ import annotations
import tempfile
import shutil
import os
try:
@@ -11,6 +9,7 @@ except ImportError:
has_markitdown = False
from ...typing import AsyncResult, Messages, MediaListType
from ...tools.files import get_tempfile
from ..base_provider import AsyncGeneratorProvider, ProviderModelMixin
class MarkItDown(AsyncGeneratorProvider, ProviderModelMixin):
@@ -26,17 +25,15 @@ class MarkItDown(AsyncGeneratorProvider, ProviderModelMixin):
) -> AsyncResult:
md = MaItDo()
for file, filename in media:
text = None
try:
text = md.convert(file, stream_info=StreamInfo(filename=filename)).text_content
text = md.convert(file, stream_info=StreamInfo(filename=filename) if filename else None).text_content
except TypeError:
# Copy SpooledTemporaryFile to a NamedTemporaryFile
copyfile = tempfile.NamedTemporaryFile(suffix=filename, delete=False)
shutil.copyfileobj(file, copyfile)
copyfile.close()
file.close()
# Use the NamedTemporaryFile for conversion
text = md.convert(copyfile.name, stream_info=StreamInfo(filename=filename)).text_content
os.remove(copyfile.name)
copyfile = get_tempfile(file, filename)
try:
text = md.convert(copyfile).text_content
finally:
os.remove(copyfile)
text = text.split("### Audio Transcript:\n")[-1]
if text:
yield text
@@ -23,11 +23,11 @@ from ...requests.raise_for_status import raise_for_status
from ...requests import StreamSession
from ...requests import get_nodriver
from ...image import ImageRequest, to_image, to_bytes, is_accepted_format
from ...errors import MissingAuthError, NoValidHarFileError
from ...errors import MissingAuthError, NoValidHarFileError, ModelNotSupportedError
from ...providers.response import JsonConversation, FinishReason, SynthesizeData, AuthResult, ImageResponse, ImagePreview
from ...providers.response import Sources, TitleGeneration, RequestLogin, Reasoning
from ...tools.media import merge_media
from ..helper import format_cookies, format_image_prompt
from ..helper import format_cookies, format_image_prompt, to_string
from ..openai.models import default_model, default_image_model, models, image_models, text_models
from ..openai.har_file import get_request_config
from ..openai.har_file import RequestConfig, arkReq, arkose_url, start_url, conversation_url, backend_url, backend_anon_url
@@ -221,7 +221,7 @@ class OpenaiChat(AsyncAuthedProvider, ProviderModelMixin):
messages = [{
"id": str(uuid.uuid4()),
"author": {"role": message["role"]},
"content": {"content_type": "text", "parts": [message["content"]]},
"content": {"content_type": "text", "parts": [to_string(message["content"])]},
"metadata": {"serialization_metadata": {"custom_symbol_offsets": []}, **({"system_hints": system_hints} if system_hints else {})},
"create_time": time.time(),
} for message in messages]
@@ -356,7 +356,10 @@ class OpenaiChat(AsyncAuthedProvider, ProviderModelMixin):
except Exception as e:
debug.error("OpenaiChat: Upload image failed")
debug.error(e)
model = cls.get_model(model)
try:
model = cls.get_model(model)
except ModelNotSupportedError:
pass
if conversation is None:
conversation = Conversation(None, str(uuid.uuid4()), getattr(auth_result, "cookies", {}).get("oai-did"))
else:
@@ -1,6 +1,6 @@
default_model = "auto"
default_image_model = "dall-e-3"
image_models = [default_image_model]
text_models = [default_model, "gpt-4", "gpt-4.5", "gpt-4o", "gpt-4o-mini", "o1", "o1-preview", "o1-mini", "o3-mini", "o3-mini-high"]
text_models = [default_model, "gpt-4", "gpt-4.1", "gpt-4.5", "gpt-4o", "gpt-4o-mini", "o1", "o1-preview", "o1-mini", "o3-mini", "o3-mini-high"]
vision_models = text_models
models = text_models + image_models
@@ -494,28 +494,20 @@ class Api:
}
@self.app.post("/v1/audio/transcriptions", responses=responses)
@self.app.post("/api/{path_provider}/audio/transcriptions", responses=responses)
@self.app.post("/api/markitdown", responses=responses)
async def convert(
file: UploadFile,
model: Annotated[Optional[str], Form()] = None,
provider: Annotated[Optional[str], Form()] = "MarkItDown",
path_provider: str = None,
prompt: Annotated[Optional[str], Form()] = "Transcribe this audio",
api_key: Annotated[Optional[str], Form()] = None,
credentials: Annotated[HTTPAuthorizationCredentials, Depends(Api.security)] = None
model: Annotated[Optional[str], Form()] = None,
provider: Annotated[Optional[str], Form()] = None,
prompt: Annotated[Optional[str], Form()] = "Transcribe this audio"
):
if credentials is not None and credentials.credentials != "secret":
api_key = credentials.credentials
try:
response = await self.client.chat.completions.create(
messages=prompt,
model=model,
provider=provider if path_provider is None else path_provider,
media=[[file.file, file.filename]],
modalities=["text"],
**filter_none(
provider=provider if path_provider is None else path_provider,
api_key=api_key
)
modalities=["text"]
)
return {"text": response.choices[0].message.content, "model": response.model, "provider": response.provider}
except (ModelNotFoundError, ProviderNotFoundError) as e:
@@ -528,6 +520,12 @@ class Api:
logger.exception(e)
return ErrorResponse.from_exception(e, None, HTTP_500_INTERNAL_SERVER_ERROR)
@self.app.post("/api/markitdown", responses=responses)
async def markitdown(
file: UploadFile
):
return await convert(file, "MarkItDown")
responses = {
HTTP_200_OK: {"class": FileResponse},
HTTP_401_UNAUTHORIZED: {"model": ErrorResponseModel},
@@ -13,6 +13,8 @@ import zipfile
import asyncio
import hashlib
import base64
import tempfile
import shutil
try:
import PyPDF2
@@ -578,4 +580,11 @@ async def get_async_streaming(bucket_dir: str, delete_files = False, refine_chun
except Exception as e:
if event_stream:
yield f'data: {json.dumps({"error": {"message": str(e)}})}\n\n'
raise e
raise e
def get_tempfile(file, suffix):
copyfile = tempfile.NamedTemporaryFile(suffix=suffix, delete=False)
shutil.copyfileobj(file, copyfile)
copyfile.close()
file.close()
return copyfile.name