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
This commit is contained in:
hlohaus 2025-04-26 17:50:48 +02:00
parent dc0705c082
commit e238ca3a58
7 changed files with 46 additions and 33 deletions

View file

@ -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:

View file

@ -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"):

View file

@ -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

View file

@ -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:

View file

@ -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

View file

@ -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},

View file

@ -13,6 +13,8 @@ import zipfile
import asyncio
import hashlib
import base64
import tempfile
import shutil
try:
import PyPDF2
@ -579,3 +581,10 @@ async def get_async_streaming(bucket_dir: str, delete_files = False, refine_chun
if event_stream:
yield f'data: {json.dumps({"error": {"message": str(e)}})}\n\n'
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