mirror of
https://github.com/modelscope/modelscope.git
synced 2025-12-16 16:27:45 +01:00
Fix/upload base64 (#1492)
This commit is contained in:
@@ -288,7 +288,7 @@ class HubApi:
|
||||
|
||||
# Add AIGC-specific fields to body
|
||||
body.update({
|
||||
'TagShowName': aigc_model.revision,
|
||||
'TagShowName': aigc_model.tag,
|
||||
'CoverImages': aigc_model.cover_images,
|
||||
'AigcType': aigc_model.aigc_type,
|
||||
'TagDescription': aigc_model.description,
|
||||
@@ -315,10 +315,9 @@ class HubApi:
|
||||
raise_on_error(d)
|
||||
model_repo_url = f'{endpoint}/models/{model_id}'
|
||||
|
||||
# TODO: to be aligned with the new api
|
||||
# Upload model files for AIGC models
|
||||
# if aigc_model is not None:
|
||||
# aigc_model.upload_to_repo(self, model_id, token)
|
||||
if aigc_model is not None:
|
||||
aigc_model.upload_to_repo(self, model_id, token)
|
||||
|
||||
return model_repo_url
|
||||
|
||||
|
||||
@@ -6,7 +6,8 @@ from typing import List, Optional
|
||||
import requests
|
||||
from tqdm.auto import tqdm
|
||||
|
||||
from modelscope.hub.utils.utils import MODELSCOPE_URL_SCHEME, get_domain
|
||||
from modelscope.hub.utils.utils import (MODELSCOPE_URL_SCHEME,
|
||||
encode_image_to_base64, get_domain)
|
||||
from modelscope.utils.logger import get_logger
|
||||
|
||||
logger = get_logger()
|
||||
@@ -61,7 +62,7 @@ class AigcModel:
|
||||
base_model_type: str,
|
||||
model_path: str,
|
||||
base_model_id: str = '',
|
||||
revision: Optional[str] = 'v1.0',
|
||||
tag: Optional[str] = 'v1.0',
|
||||
description: Optional[str] = 'this is an aigc model',
|
||||
cover_images: Optional[List[str]] = None,
|
||||
path_in_repo: Optional[str] = '',
|
||||
@@ -73,23 +74,46 @@ class AigcModel:
|
||||
model_path (str): The path of checkpoint/LoRA weight file or folder.
|
||||
aigc_type (str): AIGC model type. Recommended: 'Checkpoint', 'LoRA', 'VAE'.
|
||||
base_model_type (str): Vision foundation model type. Recommended values are in BASE_MODEL_TYPES.
|
||||
revision (str, optional): Revision for the AIGC model. Defaults to 'v1.0'.
|
||||
tag (str, optional): Tag for the AIGC model. Defaults to 'v1.0'.
|
||||
description (str, optional): Model description. Defaults to 'this is an aigc model'.
|
||||
cover_images (List[str], optional): List of cover image URLs.
|
||||
base_model_id (str, optional): Base model name. e.g., 'AI-ModelScope/FLUX.1-dev'.
|
||||
path_in_repo (str, optional): Path in the repository.
|
||||
trigger_words (List[str], optional): Trigger words for the AIGC Lora model.
|
||||
Note: Auto-upload during AIGC create is temporarily disabled by server. This parameter
|
||||
will not take effect at creation time.
|
||||
"""
|
||||
self.model_path = model_path
|
||||
self.aigc_type = aigc_type
|
||||
self.base_model_type = base_model_type
|
||||
self.revision = revision
|
||||
self.tag = tag
|
||||
self.description = description
|
||||
self.cover_images = cover_images if cover_images is not None else [
|
||||
DEFAULT_AIGC_COVER_IMAGE
|
||||
]
|
||||
# Process cover images - convert local paths to base64 data URLs
|
||||
if cover_images is not None:
|
||||
processed_cover_images = []
|
||||
for img in cover_images:
|
||||
if isinstance(img, str):
|
||||
# Check if it's a local file path (not a URL)
|
||||
if not (img.startswith('http://')
|
||||
or img.startswith('https://')
|
||||
or img.startswith('data:')):
|
||||
try:
|
||||
# Convert local path to base64 data URL
|
||||
processed_img = encode_image_to_base64(img)
|
||||
processed_cover_images.append(processed_img)
|
||||
logger.info('Converted local image to base64: %s',
|
||||
os.path.basename(img))
|
||||
except (FileNotFoundError, ValueError) as e:
|
||||
logger.warning(
|
||||
'Failed to process local image %s: %s. Using as-is.',
|
||||
img, e)
|
||||
processed_cover_images.append(img)
|
||||
else:
|
||||
# Keep URLs and data URLs as-is
|
||||
processed_cover_images.append(img)
|
||||
else:
|
||||
processed_cover_images.append(img)
|
||||
self.cover_images = processed_cover_images
|
||||
else:
|
||||
self.cover_images = [DEFAULT_AIGC_COVER_IMAGE]
|
||||
self.base_model_id = base_model_id
|
||||
self.path_in_repo = path_in_repo
|
||||
self.trigger_words = trigger_words
|
||||
@@ -216,7 +240,6 @@ class AigcModel:
|
||||
# Upload entire folder with path_in_repo support
|
||||
logger.info('Uploading directory: %s', self.model_path)
|
||||
api.upload_folder(
|
||||
revision=self.revision,
|
||||
repo_id=model_id,
|
||||
folder_path=self.model_path,
|
||||
path_in_repo=self.path_in_repo,
|
||||
@@ -226,7 +249,6 @@ class AigcModel:
|
||||
# Upload single file, target_file is guaranteed to be set by _process_model_path
|
||||
logger.info('Uploading file: %s', self.target_file)
|
||||
api.upload_file(
|
||||
revision=self.revision,
|
||||
path_or_fileobj=self.target_file,
|
||||
path_in_repo=self.path_in_repo + '/' + self.weight_filename
|
||||
if self.path_in_repo else self.weight_filename,
|
||||
@@ -313,7 +335,7 @@ class AigcModel:
|
||||
return {
|
||||
'aigc_type': self.aigc_type,
|
||||
'base_model_type': self.base_model_type,
|
||||
'revision': self.revision,
|
||||
'tag': self.tag,
|
||||
'description': self.description,
|
||||
'cover_images': self.cover_images,
|
||||
'base_model_id': self.base_model_id,
|
||||
|
||||
@@ -5,11 +5,11 @@ import hashlib
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import zoneinfo
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import Generator, List, Optional, Union
|
||||
|
||||
import zoneinfo
|
||||
from filelock import BaseFileLock, FileLock, SoftFileLock, Timeout
|
||||
|
||||
from modelscope.hub.constants import (DEFAULT_MODELSCOPE_DOMAIN,
|
||||
@@ -305,11 +305,9 @@ def weak_file_lock(lock_file: Union[str, Path],
|
||||
def convert_timestamp(time_stamp: Union[int, str, datetime],
|
||||
time_zone: str = 'Asia/Shanghai') -> Optional[datetime]:
|
||||
"""Convert a UNIX/string timestamp to a timezone-aware datetime object.
|
||||
|
||||
Args:
|
||||
time_stamp: UNIX timestamp (int), ISO string, or datetime object
|
||||
time_zone: Target timezone for non-UTC timestamps (default: 'Asia/Shanghai')
|
||||
|
||||
Returns:
|
||||
Timezone-aware datetime object or None if input is None
|
||||
"""
|
||||
@@ -380,3 +378,43 @@ def convert_timestamp(time_stamp: Union[int, str, datetime],
|
||||
raise TypeError(
|
||||
f"Unsupported type '{type(time_stamp)}'. Expected int, str, or datetime."
|
||||
)
|
||||
|
||||
|
||||
def encode_image_to_base64(image_path: str) -> str:
|
||||
"""
|
||||
Encode image file to base64 string.
|
||||
|
||||
Args:
|
||||
image_path (str): Path to the image file
|
||||
|
||||
Returns:
|
||||
str: Base64 encoded string with data URL prefix
|
||||
|
||||
Raises:
|
||||
FileNotFoundError: If image file doesn't exist
|
||||
ValueError: If file is not a valid image format
|
||||
"""
|
||||
import base64
|
||||
import mimetypes
|
||||
|
||||
# Expand user path
|
||||
image_path = os.path.expanduser(image_path)
|
||||
|
||||
if not os.path.exists(image_path):
|
||||
raise FileNotFoundError(f'Image file not found: {image_path}')
|
||||
|
||||
if not os.path.isfile(image_path):
|
||||
raise ValueError(f'Path is not a file: {image_path}')
|
||||
|
||||
# Get MIME type
|
||||
mime_type, _ = mimetypes.guess_type(image_path)
|
||||
if not mime_type or not mime_type.startswith('image/'):
|
||||
raise ValueError(f'File is not a valid image format: {image_path}')
|
||||
|
||||
# Read and encode file
|
||||
with open(image_path, 'rb') as image_file:
|
||||
image_data = image_file.read()
|
||||
base64_data = base64.b64encode(image_data).decode('utf-8')
|
||||
|
||||
# Return data URL format
|
||||
return f'data:{mime_type};base64,{base64_data}'
|
||||
|
||||
Reference in New Issue
Block a user