Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions apps/models_provider/impl/minimax_model_provider/credential/tti.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@
from django.utils.translation import gettext_lazy as _, gettext
from common import forms
from common.exception.app_exception import AppApiException
from common.forms import BaseForm, PasswordInputField, SingleSelect, SliderField, TooltipLabel
from common.forms import BaseForm, PasswordInputField, SliderField, TooltipLabel
from models_provider.base_model_provider import BaseModelCredential, ValidCode
from common.utils.logger import maxkb_logger


class MiniMaxModelParams(BaseForm):
"""
Parameters class for the Qwen Text-to-Image model.
Defines fields such as image size, number of images, and style.
Parameters for MiniMax image generation.
Defines the output count and optional subject reference input.
"""

n = SliderField(
Expand All @@ -26,6 +26,14 @@ class MiniMaxModelParams(BaseForm):
precision=0
)

subject_reference = forms.TextInputField(
TooltipLabel(
_('Subject reference image'),
_('Public image URL or base64 data URL used for image-to-image generation')
),
required=False,
)


class MiniMaxTextToImageModelCredential(BaseForm, BaseModelCredential):
"""
Expand Down Expand Up @@ -75,7 +83,7 @@ def is_valid(

try:
model = provider.get_model(model_type, model_name, model_credential, **model_params)
res = model.check_auth()
model.check_auth()
except Exception as e:
maxkb_logger.error(f'Exception: {e}', exc_info=True)
if isinstance(e, AppApiException):
Expand Down
18 changes: 13 additions & 5 deletions apps/models_provider/impl/minimax_model_provider/model/tti.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
# coding=utf-8
from http import HTTPStatus
from typing import Dict

import requests
from dashscope import ImageSynthesis, MultiModalConversation
from dashscope.aigc.image_generation import ImageGeneration

from common.utils.logger import maxkb_logger
from models_provider.base_model_provider import MaxKBBaseModel
Expand Down Expand Up @@ -47,14 +44,25 @@ def new_instance(model_type, model_name, model_credential: Dict[str, object], **
def check_auth(self):
return True

def generate_image(self, prompt: str, negative_prompt: str = None):
def generate_image(self, prompt: str, negative_prompt: str = None, subject_reference=None):
headers = {"Authorization": f"Bearer {self.api_key}"}

params = self.params or {}
subject_reference = (
subject_reference if subject_reference is not None else params.get('subject_reference')
)

payload = {
"model": self.model_name,
"prompt": prompt,
**self.params,
**{key: value for key, value in params.items() if key != 'subject_reference'},
}
if subject_reference:
if isinstance(subject_reference, str):
subject_reference = [{"type": "character", "image_file": subject_reference}]
elif not isinstance(subject_reference, list):
raise ValueError('subject_reference must be an image URL, data URL, or list of reference objects')
payload['subject_reference'] = subject_reference
try:
response = requests.post(f'{self.api_base}/image_generation', headers=headers, json=payload)
response.raise_for_status()
Expand Down
101 changes: 101 additions & 0 deletions tests/models_provider/test_minimax_tti.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import importlib.util
import sys
import types
import unittest
from pathlib import Path
from unittest.mock import Mock, patch


class DummyMaxKBBaseModel:
def __init__(self, **kwargs):
pass


class DummyBaseTextToImage:
pass


def load_tti_module():
requests_module = types.ModuleType('requests')
requests_module.post = Mock()

common_module = types.ModuleType('common')
common_utils_module = types.ModuleType('common.utils')
logger_module = types.ModuleType('common.utils.logger')
logger_module.maxkb_logger = Mock()

models_provider_module = types.ModuleType('models_provider')
base_model_provider_module = types.ModuleType('models_provider.base_model_provider')
base_model_provider_module.MaxKBBaseModel = DummyMaxKBBaseModel
impl_module = types.ModuleType('models_provider.impl')
base_tti_module = types.ModuleType('models_provider.impl.base_tti')
base_tti_module.BaseTextToImage = DummyBaseTextToImage

modules = {
'requests': requests_module,
'common': common_module,
'common.utils': common_utils_module,
'common.utils.logger': logger_module,
'models_provider': models_provider_module,
'models_provider.base_model_provider': base_model_provider_module,
'models_provider.impl': impl_module,
'models_provider.impl.base_tti': base_tti_module,
}

module_path = (
Path(__file__).resolve().parents[2]
/ 'apps/models_provider/impl/minimax_model_provider/model/tti.py'
)
spec = importlib.util.spec_from_file_location('minimax_tti_under_test', module_path)
module = importlib.util.module_from_spec(spec)
with patch.dict(sys.modules, modules):
spec.loader.exec_module(module)
return module, requests_module


class MiniMaxTextToImageModelTest(unittest.TestCase):
def setUp(self):
module, self.requests = load_tti_module()
response = Mock()
response.json.return_value = {'data': {'image_urls': ['https://example.com/generated.png']}}
self.requests.post.return_value = response
self.model = module.MiniMaxTextToImageModel(
api_key='test-key',
api_base='https://api.example.com/v1',
model_name='image-01',
params={'n': 1},
)

def test_adds_subject_reference_from_model_params(self):
reference_url = 'https://example.com/reference.png'
self.model.params['subject_reference'] = reference_url

result = self.model.generate_image('Create a portrait')

payload = self.requests.post.call_args.kwargs['json']
self.assertEqual(result, ['https://example.com/generated.png'])
self.assertEqual(
payload['subject_reference'],
[{'type': 'character', 'image_file': reference_url}],
)
self.assertEqual(payload['n'], 1)

def test_keeps_text_to_image_payload_without_reference(self):
self.model.generate_image('Create a landscape')

payload = self.requests.post.call_args.kwargs['json']
self.assertNotIn('subject_reference', payload)

def test_accepts_structured_subject_reference_argument(self):
subject_reference = [
{'type': 'character', 'image_file': 'data:image/png;base64,AAAA'},
]

self.model.generate_image('Create a portrait', subject_reference=subject_reference)

payload = self.requests.post.call_args.kwargs['json']
self.assertEqual(payload['subject_reference'], subject_reference)


if __name__ == '__main__':
unittest.main()
Loading