chore: 添加虚拟环境到仓库

- 添加 backend_service/venv 虚拟环境
- 包含所有Python依赖包
- 注意:虚拟环境约393MB,包含12655个文件
This commit is contained in:
2025-12-03 10:19:25 +08:00
parent a6c2027caa
commit c4f851d387
12655 changed files with 3009376 additions and 0 deletions

View File

@@ -0,0 +1,20 @@
# Copyright (c) Alibaba, Inc. and its affiliates.
from .conversation import Conversation, History, HistoryItem
from .generation import Generation, AioGeneration
from .image_synthesis import ImageSynthesis, AioImageSynthesis
from .multimodal_conversation import MultiModalConversation, AioMultiModalConversation
from .video_synthesis import VideoSynthesis, AioVideoSynthesis
__all__ = [
Generation,
AioGeneration,
Conversation,
HistoryItem,
History,
ImageSynthesis,
AioImageSynthesis,
MultiModalConversation,
AioMultiModalConversation,
VideoSynthesis,
AioVideoSynthesis,
]

View File

@@ -0,0 +1,282 @@
# Copyright (c) Alibaba, Inc. and its affiliates.
import json
from typing import Any, Dict, Generator, List, Union
import dashscope
from dashscope.aigc.generation import Generation
from dashscope.api_entities.chat_completion_types import (ChatCompletion,
ChatCompletionChunk)
from dashscope.api_entities.dashscope_response import (GenerationResponse,
Message)
from dashscope.client.base_api import BaseAioApi, CreateMixin
from dashscope.common.error import InputRequired, ModelRequired
from dashscope.common.utils import _get_task_group_and_task
class Completions(CreateMixin):
"""Support openai compatible chat completion interface.
"""
SUB_PATH = ''
@classmethod
def create(
cls,
*,
model: str,
messages: List[Message],
stream: bool = False,
temperature: float = None,
top_p: float = None,
top_k: int = None,
stop: Union[List[str], List[List[int]]] = None,
max_tokens: int = None,
repetition_penalty: float = None,
api_key: str = None,
workspace: str = None,
extra_headers: Dict = None,
extra_body: Dict = None,
**kwargs
) -> Union[ChatCompletion, Generator[ChatCompletionChunk, None, None]]:
"""Call openai compatible chat completion model service.
Args:
model (str): The requested model, such as qwen-long
messages (list): The generation messages.
examples:
[{'role': 'user',
'content': 'The weather is fine today.'},
{'role': 'assistant', 'content': 'Suitable for outings'}]
stream(bool, `optional`): Enable server-sent events
(ref: https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events) # noqa E501
the result will back partially[qwen-turbo,bailian-v1].
temperature(float, `optional`): Used to control the degree
of randomness and diversity. Specifically, the temperature
value controls the degree to which the probability distribution
of each candidate word is smoothed when generating text.
A higher temperature value will reduce the peak value of
the probability, allowing more low-probability words to be
selected, and the generated results will be more diverse;
while a lower temperature value will enhance the peak value
of the probability, making it easier for high-probability
words to be selected, the generated results are more
deterministic.
top_p(float, `optional`): A sampling strategy, called nucleus
sampling, where the model considers the results of the
tokens with top_p probability mass. So 0.1 means only
the tokens comprising the top 10% probability mass are
considered.
top_k(int, `optional`): The size of the sample candidate set when generated. # noqa E501
For example, when the value is 50, only the 50 highest-scoring tokens # noqa E501
in a single generation form a randomly sampled candidate set. # noqa E501
The larger the value, the higher the randomness generated; # noqa E501
the smaller the value, the higher the certainty generated. # noqa E501
The default value is 0, which means the top_k policy is # noqa E501
not enabled. At this time, only the top_p policy takes effect. # noqa E501
stop(list[str] or list[list[int]], `optional`): Used to control the generation to stop # noqa E501
when encountering setting str or token ids, the result will not include # noqa E501
stop words or tokens.
max_tokens(int, `optional`): The maximum token num expected to be output. It should be # noqa E501
noted that the length generated by the model will only be less than max_tokens, # noqa E501
not necessarily equal to it. If max_tokens is set too large, the service will # noqa E501
directly prompt that the length exceeds the limit. It is generally # noqa E501
not recommended to set this value.
repetition_penalty(float, `optional`): Used to control the repeatability when generating models. # noqa E501
Increasing repetition_penalty can reduce the duplication of model generation. # noqa E501
1.0 means no punishment.
api_key (str, optional): The api api_key, can be None,
if None, will get by default rule.
workspace (str, optional): The bailian workspace id.
**kwargs:
timeout: set request timeout.
Raises:
InvalidInput: The history and auto_history are mutually exclusive.
Returns:
Union[ChatCompletion,
Generator[ChatCompletionChunk, None, None]]: If
stream is True, return Generator, otherwise ChatCompletion.
"""
if messages is None or not messages:
raise InputRequired('Messages is required!')
if model is None or not model:
raise ModelRequired('Model is required!')
data = {}
data['model'] = model
data['messages'] = messages
if temperature is not None:
data['temperature'] = temperature
if top_p is not None:
data['top_p'] = top_p
if top_k is not None:
data['top_k'] = top_k
if stop is not None:
data['stop'] = stop
if max_tokens is not None:
data[max_tokens] = max_tokens
if repetition_penalty is not None:
data['repetition_penalty'] = repetition_penalty
if extra_body is not None and extra_body:
data = {**data, **extra_body}
if extra_headers is not None and extra_headers:
kwargs = {
'headers': extra_headers
} if kwargs else {
**kwargs,
**{
'headers': extra_headers
}
}
response = super().call(data=data,
path='chat/completions',
base_address=dashscope.base_compatible_api_url,
api_key=api_key,
flattened_output=True,
stream=stream,
workspace=workspace,
**kwargs)
if stream:
return (ChatCompletionChunk(**item) for _, item in response)
else:
return ChatCompletion(**response)
class AioGeneration(BaseAioApi):
task = 'text-generation'
"""API for AI-Generated Content(AIGC) models.
"""
class Models:
"""@deprecated, use qwen_turbo instead"""
qwen_v1 = 'qwen-v1'
"""@deprecated, use qwen_plus instead"""
qwen_plus_v1 = 'qwen-plus-v1'
bailian_v1 = 'bailian-v1'
dolly_12b_v2 = 'dolly-12b-v2'
qwen_turbo = 'qwen-turbo'
qwen_plus = 'qwen-plus'
qwen_max = 'qwen-max'
@classmethod
async def call(
cls,
model: str,
prompt: Any = None,
history: list = None,
api_key: str = None,
messages: List[Message] = None,
plugins: Union[str, Dict[str, Any]] = None,
workspace: str = None,
**kwargs
) -> Union[GenerationResponse, Generator[GenerationResponse, None, None]]:
"""Call generation model service.
Args:
model (str): The requested model, such as qwen-turbo
prompt (Any): The input prompt.
history (list):The user provided history, deprecated
examples:
[{'user':'The weather is fine today.',
'bot': 'Suitable for outings'}].
Defaults to None.
api_key (str, optional): The api api_key, can be None,
if None, will get by default rule(TODO: api key doc).
messages (list): The generation messages.
examples:
[{'role': 'user',
'content': 'The weather is fine today.'},
{'role': 'assistant', 'content': 'Suitable for outings'}]
plugins (Any): The plugin config. Can be plugins config str, or dict.
**kwargs:
stream(bool, `optional`): Enable server-sent events
(ref: https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events) # noqa E501
the result will back partially[qwen-turbo,bailian-v1].
temperature(float, `optional`): Used to control the degree
of randomness and diversity. Specifically, the temperature
value controls the degree to which the probability distribution
of each candidate word is smoothed when generating text.
A higher temperature value will reduce the peak value of
the probability, allowing more low-probability words to be
selected, and the generated results will be more diverse;
while a lower temperature value will enhance the peak value
of the probability, making it easier for high-probability
words to be selected, the generated results are more
deterministic, range(0, 2) .[qwen-turbo,qwen-plus].
top_p(float, `optional`): A sampling strategy, called nucleus
sampling, where the model considers the results of the
tokens with top_p probability mass. So 0.1 means only
the tokens comprising the top 10% probability mass are
considered[qwen-turbo,bailian-v1].
top_k(int, `optional`): The size of the sample candidate set when generated. # noqa E501
For example, when the value is 50, only the 50 highest-scoring tokens # noqa E501
in a single generation form a randomly sampled candidate set. # noqa E501
The larger the value, the higher the randomness generated; # noqa E501
the smaller the value, the higher the certainty generated. # noqa E501
The default value is 0, which means the top_k policy is # noqa E501
not enabled. At this time, only the top_p policy takes effect. # noqa E501
enable_search(bool, `optional`): Whether to enable web search(quark). # noqa E501
Currently works best only on the first round of conversation.
Default to False, support model: [qwen-turbo].
customized_model_id(str, required) The enterprise-specific
large model id, which needs to be generated from the
operation background of the enterprise-specific
large model product, support model: [bailian-v1].
result_format(str, `optional`): [message|text] Set result result format. # noqa E501
Default result is text
incremental_output(bool, `optional`): Used to control the streaming output mode. # noqa E501
If true, the subsequent output will include the previously input content. # noqa E501
Otherwise, the subsequent output will not include the previously output # noqa E501
content. Default false.
stop(list[str] or list[list[int]], `optional`): Used to control the generation to stop # noqa E501
when encountering setting str or token ids, the result will not include # noqa E501
stop words or tokens.
max_tokens(int, `optional`): The maximum token num expected to be output. It should be # noqa E501
noted that the length generated by the model will only be less than max_tokens, # noqa E501
not necessarily equal to it. If max_tokens is set too large, the service will # noqa E501
directly prompt that the length exceeds the limit. It is generally # noqa E501
not recommended to set this value.
repetition_penalty(float, `optional`): Used to control the repeatability when generating models. # noqa E501
Increasing repetition_penalty can reduce the duplication of model generation. # noqa E501
1.0 means no punishment.
workspace (str): The dashscope workspace id.
Raises:
InvalidInput: The history and auto_history are mutually exclusive.
Returns:
Union[GenerationResponse,
Generator[GenerationResponse, None, None]]: If
stream is True, return Generator, otherwise GenerationResponse.
"""
if (prompt is None or not prompt) and (messages is None
or not messages):
raise InputRequired('prompt or messages is required!')
if model is None or not model:
raise ModelRequired('Model is required!')
task_group, function = _get_task_group_and_task(__name__)
if plugins is not None:
headers = kwargs.pop('headers', {})
if isinstance(plugins, str):
headers['X-DashScope-Plugin'] = plugins
else:
headers['X-DashScope-Plugin'] = json.dumps(plugins)
kwargs['headers'] = headers
input, parameters = Generation._build_input_parameters(
model, prompt, history, messages, **kwargs)
response = await super().call(model=model,
task_group=task_group,
task=Generation.task,
function=function,
api_key=api_key,
input=input,
workspace=workspace,
**parameters)
is_stream = kwargs.get('stream', False)
if is_stream:
return (GenerationResponse.from_api_response(rsp)
async for rsp in response)
else:
return GenerationResponse.from_api_response(response)

View File

@@ -0,0 +1,145 @@
# Copyright (c) Alibaba, Inc. and its affiliates.
from typing import Generator, List, Union
from dashscope.api_entities.dashscope_response import (DashScopeAPIResponse,
DictMixin, Role)
from dashscope.client.base_api import BaseApi
from dashscope.common.constants import MESSAGE, SCENE
from dashscope.common.error import InputRequired, ModelRequired
from dashscope.common.utils import _get_task_group_and_task
class MessageParam(DictMixin):
role: str
def __init__(self, role: str, **kwargs):
super().__init__(role=role, **kwargs)
class UserRoleMessageParam(MessageParam):
content: str
def __init__(self, content: str, **kwargs):
super().__init__(role=Role.USER, content=content, **kwargs)
class AttachmentRoleMessageParam(MessageParam):
meta: dict
def __init__(self, meta: dict, **kwargs):
super().__init__(role=Role.ATTACHMENT, meta=meta, **kwargs)
class OtherRoleContentMessageParam(MessageParam):
content: str
def __init__(self, role: str, content: str, **kwargs):
super().__init__(role=role, content=content, **kwargs)
class OtherRoleMetaMessageParam(MessageParam):
meta: dict
def __init__(self, role: str, meta: dict, **kwargs):
super().__init__(role=role, meta=meta, **kwargs)
class CodeGeneration(BaseApi):
function = 'generation'
"""API for AI-Generated Content(AIGC) models.
"""
class Models:
tongyi_lingma_v1 = 'tongyi-lingma-v1'
class Scenes:
custom = 'custom'
nl2code = 'nl2code'
code2comment = 'code2comment'
code2explain = 'code2explain'
commit2msg = 'commit2msg'
unit_test = 'unittest'
code_qa = 'codeqa'
nl2sql = 'nl2sql'
@classmethod
def call(
cls,
model: str,
scene: str = None,
api_key: str = None,
message: List[MessageParam] = None,
workspace: str = None,
**kwargs
) -> Union[DashScopeAPIResponse, Generator[DashScopeAPIResponse, None,
None]]:
"""Call generation model service.
Args:
model (str): The requested model, such as tongyi-lingma-v1
scene (str): Scene type, single choice, such as custom
examples:
customUser defined prompt
nl2codeNatural language generated code
code2commentannotation
code2explainexplain
commit2msgAutomatically generate commit
uinttestGenerating Unit Tests
codeqaCode Q&A
nl2sqlGenerate SQL code using natural language
api_key (str, optional): The api api_key, can be None,
if None, will get by default rule(TODO: api key doc).
message (list): The generation messages.
scene == custom, examples:
[{"role": "user", "content": "根据下面的功能描述生成一个python函数。代码的功能是计算给定路径下所有文件的总大小。"}] # noqa E501
scene == nl2code, examples:
[{"role": "user", "content": "计算给定路径下所有文件的总大小"}, {"role": "attachment", "meta": {"language": "java"}}] # noqa E501
scene == code2comment, examples:
[{"role": "user", "content": "1. 生成中文注释\n2. 仅生成代码部分,不需要额外解释函数功能\n"}, {"role": "attachment", "meta": {"code": "\t\t@Override\n\t\tpublic CancelExportTaskResponse cancelExportTask(\n\t\t\t\tCancelExportTask cancelExportTask) {\n\t\t\tAmazonEC2SkeletonInterface ec2Service = ServiceProvider.getInstance().getServiceImpl(AmazonEC2SkeletonInterface.class);\n\t\t\treturn ec2Service.cancelExportTask(cancelExportTask);\n\t\t}", "language": "java"}}] # noqa E501
scene == code2explain, examples:
[{"role": "user", "content": "要求不低于200字"}, {"role": "attachment", "meta": {"code": "@Override\n public int getHeaderCacheSize()\n {\n return 0;\n }\n\n", "language": "java"}}] # noqa E501
scene == commit2msg, examples:
[{"role": "attachment", "meta": {"diff_list": [{"diff": "--- src/com/siondream/core/PlatformResolver.java\n+++ src/com/siondream/core/PlatformResolver.java\n@@ -1,11 +1,8 @@\npackage com.siondream.core;\n-\n-import com.badlogic.gdx.files.FileHandle;\n\npublic interface PlatformResolver {\npublic void openURL(String url);\npublic void rateApp();\npublic void sendFeedback();\n-\tpublic FileHandle[] listFolder(String path);\n}\n", "old_file_path": "src/com/siondream/core/PlatformResolver.java", "new_file_path": "src/com/siondream/core/PlatformResolver.java"}]}}] # noqa E501
scene == unittest, examples:
[{"role": "attachment", "meta": {"code": "public static <T> TimestampMap<T> parseTimestampMap(Class<T> typeClass, String input, DateTimeZone timeZone) throws IllegalArgumentException {\n if (typeClass == null) {\n throw new IllegalArgumentException(\"typeClass required\");\n }\n\n if (input == null) {\n return null;\n }\n\n TimestampMap result;\n\n typeClass = AttributeUtils.getStandardizedType(typeClass);\n if (typeClass.equals(String.class)) {\n result = new TimestampStringMap();\n } else if (typeClass.equals(Byte.class)) {\n result = new TimestampByteMap();\n } else if (typeClass.equals(Short.class)) {\n result = new TimestampShortMap();\n } else if (typeClass.equals(Integer.class)) {\n result = new TimestampIntegerMap();\n } else if (typeClass.equals(Long.class)) {\n result = new TimestampLongMap();\n } else if (typeClass.equals(Float.class)) {\n result = new TimestampFloatMap();\n } else if (typeClass.equals(Double.class)) {\n result = new TimestampDoubleMap();\n } else if (typeClass.equals(Boolean.class)) {\n result = new TimestampBooleanMap();\n } else if (typeClass.equals(Character.class)) {\n result = new TimestampCharMap();\n } else {\n throw new IllegalArgumentException(\"Unsupported type \" + typeClass.getClass().getCanonicalName());\n }\n\n if (input.equalsIgnoreCase(EMPTY_VALUE)) {\n return result;\n }\n\n StringReader reader = new StringReader(input + ' ');// Add 1 space so\n // reader.skip\n // function always\n // works when\n // necessary (end of\n // string not\n // reached).\n\n try {\n int r;\n char c;\n while ((r = reader.read()) != -1) {\n c = (char) r;\n switch (c) {\n case LEFT_BOUND_SQUARE_BRACKET:\n case LEFT_BOUND_BRACKET:\n parseTimestampAndValue(typeClass, reader, result, timeZone);\n break;\n default:\n // Ignore other chars outside of bounds\n }\n }\n } catch (IOException ex) {\n throw new RuntimeException(\"Unexpected expection while parsing timestamps\", ex);\n }\n\n return result;\n }", "language": "java"}}] # noqa E501
scene == codeqa, examples:
[{"role": "user", "content": "I'm writing a small web server in Python, using BaseHTTPServer and a custom subclass of BaseHTTPServer.BaseHTTPRequestHandler. Is it possible to make this listen on more than one port?\nWhat I'm doing now:\nclass MyRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):\n def doGET\n [...]\n\nclass ThreadingHTTPServer(ThreadingMixIn, HTTPServer): \n pass\n\nserver = ThreadingHTTPServer(('localhost', 80), MyRequestHandler)\nserver.serve_forever()"}] # noqa E501
scene == nl2sql, examples:
[{"role": "user", "content": "小明的总分数是多少"}, {"role": "attachment", "meta": {"synonym_infos": {"学生姓名": "姓名|名字|名称", "学生分数": "分数|得分"}, "recall_infos": [{"content": "student_score.id='小明'", "score": "0.83"}], "schema_infos": [{"table_id": "student_score", "table_desc": "学生分数表", "columns": [{"col_name": "id", "col_caption": "学生id", "col_desc": "例值为:1,2,3", "col_type": "string"}, {"col_name": "name", "col_caption": "学生姓名", "col_desc": "例值为:张三,李四,小明", "col_type": "string"}, {"col_name": "score", "col_caption": "学生分数", "col_desc": "例值为:98,100,66", "col_type": "string"}]}]}}] # noqa E501
workspace (str): The dashscope workspace id.
**kwargs:
n(int, `optional`): The number of output results, currently only supports 1, with a default value of 1 # noqa E501
Returns:
Union[DashScopeAPIResponse,
Generator[DashScopeAPIResponse, None, None]]: If
stream is True, return Generator, otherwise DashScopeAPIResponse.
"""
if (scene is None or not scene) or (message is None or not message):
raise InputRequired('scene and message is required!')
if model is None or not model:
raise ModelRequired('Model is required!')
task_group, task = _get_task_group_and_task(__name__)
input, parameters = cls._build_input_parameters(
model, scene, message, **kwargs)
response = super().call(model=model,
task_group=task_group,
task=task,
function=CodeGeneration.function,
api_key=api_key,
input=input,
workspace=workspace,
**parameters)
is_stream = kwargs.get('stream', False)
if is_stream:
return (rsp for rsp in response)
else:
return response
@classmethod
def _build_input_parameters(cls, model, scene, message, **kwargs):
parameters = {'n': kwargs.pop('n', 1)}
input = {SCENE: scene, MESSAGE: message}
return input, {**parameters, **kwargs}

View File

@@ -0,0 +1,314 @@
# Copyright (c) Alibaba, Inc. and its affiliates.
import json
from copy import deepcopy
from http import HTTPStatus
from typing import Any, Dict, Generator, List, Union
from dashscope.api_entities.dashscope_response import (ConversationResponse,
Message, Role)
from dashscope.client.base_api import BaseApi
from dashscope.common.constants import DEPRECATED_MESSAGE, HISTORY, PROMPT
from dashscope.common.error import InputRequired, InvalidInput, ModelRequired
from dashscope.common.logging import logger
from dashscope.common.utils import _get_task_group_and_task
class HistoryItem(dict):
"""A conversation history item.
"""
def __init__(self, role: str, text: str = None, **kwargs):
"""Init a history item.
Args:
role (str): The role name.
text (str): The text history. Default ot None.
kwargs: The key/value pair of history content.
Raises:
InvalidInput: The key and value must pair.
"""
logger.warning(DEPRECATED_MESSAGE)
self.role = role
dict.__init__(self, {role: []})
if text is not None:
self[self.role].append({'text': text})
for k, v in kwargs.items():
self[self.role].append({k: v})
def add(self, key: str, content: Any):
"""Add a key/value to history.
Args:
key (str): The key of the content.
value (Any): The history content.
"""
self[self.role].append({key: content})
class History(list):
"""Manage the conversation history.
"""
def __init__(self, items: List[HistoryItem] = None):
"""Init a history with list of HistoryItems.
Args:
items (List[HistoryItem], optional): The history items.
Defaults to None.
"""
if items is not None:
logger.warning(DEPRECATED_MESSAGE)
list.__init__(items)
else:
list.__init__([])
def _history_to_qwen_format(history: History, n_history: int):
"""Convert history to simple format.
[{"user":"您好", "bot":"我是你的助手,很高兴为您服务"},
{"user":"user input", "bot":"bot output"}]
"""
simple_history = []
user = None
bot = None
if n_history != -1 and len(history) >= 2 * n_history:
history = history[len(history) - 2 * n_history:]
for item in history:
if 'user' in item:
user = item['user'][0]['text']
if 'bot' in item:
bot = item['bot'][0]['text']
if user is not None and bot is not None:
simple_history.append({'user': user, 'bot': bot})
user = None
bot = None
return simple_history
class Conversation(BaseApi):
"""Conversational robot interface.
"""
task = 'generation'
class Models:
"""@deprecated, use qwen_turbo instead"""
qwen_v1 = 'qwen-v1'
"""@deprecated, use qwen_plus instead"""
qwen_plus_v1 = 'qwen-plus-v1'
qwen_turbo = 'qwen-turbo'
qwen_plus = 'qwen-plus'
qwen_max = 'qwen-max'
def __init__(self, history: History = None) -> None:
"""Init a chat.
Args:
history (dict): The conversation initialization settings.
will be recorded in the system history list.
Defaults to None.
"""
super().__init__()
if history is None:
self.history = History()
else:
logger.warning(DEPRECATED_MESSAGE)
self.history = history
def call(
self,
model: str,
prompt: Any = None,
history: History = None,
auto_history: bool = False,
n_history: int = -1,
api_key: str = None,
messages: List[Message] = None,
plugins: Union[str, Dict[str, Any]] = None,
workspace: str = None,
**kwargs
) -> Union[ConversationResponse, Generator[ConversationResponse, None,
None]]:
"""Call conversational robot generator a response.
Args:
model (str): The request model.
prompt (Any): The input prompt.
history(History): The user provided history.
Only works for this call, will not be recorded in the
system history list. The ``history`` and ``auto_history``
are mutually exclusive. Default to None.
auto_history (bool): Call with the automatically maintenance
conversation history list. The ``history`` and ``auto_history``
are mutually exclusive.
n_history (int): Number of latest history in conversation,
-1 all history. Default to -1
api_key (str, optional): The api api_key, if not present,
will get by default rule(TODO: api key doc). Defaults to None.
messages (list): The generation messages.
examples:
[{'role': 'user',
'content': 'The weather is fine today.'},
{'role': 'assistant', 'content': 'Suitable for outings'}]
plugins (Any): The plugin config, Can be plugins config str, or dict.
**kwargs(qwen-turbo, qwen-plus):
stream(bool, `optional`): Enable server-sent events
(ref: https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events) # noqa E501
the result will back partially.
temperature(float, `optional`): Used to control the degree
of randomness and diversity. Specifically, the temperature
value controls the degree to which the probability distribution
of each candidate word is smoothed when generating text.
A higher temperature value will reduce the peak value of
the probability, allowing more low-probability words to be
selected, and the generated results will be more diverse;
while a lower temperature value will enhance the peak value
of the probability, making it easier for high-probability
words to be selected, the generated results are more
deterministic,range(0, 2) .[qwen-turbo,qwen-plus].
top_p(float, `optional`): A sampling strategy, called nucleus
sampling, where the model considers the results of the
tokens with top_p probability mass. So 0.1 means only
the tokens comprising the top 10% probability mass are
considered.
top_k(int, `optional`): The size of the sample candidate set when generated. # noqa E501
For example, when the value is 50, only the 50 highest-scoring tokens # noqa E501
in a single generation form a randomly sampled candidate set. # noqa E501
The larger the value, the higher the randomness generated; # noqa E501
the smaller the value, the higher the certainty generated. # noqa E501
The default value is 0, which means the top_k policy is # noqa E501
not enabled. At this time, only the top_p policy takes effect. # noqa E501
enable_search(bool, `optional`): Whether to enable web search(quark). # noqa E501
Currently works best only on the first round of conversation.
Default to False, support model: [qwen-turbo].
customized_model_id(str, required) The enterprise-specific
large model id, which needs to be generated from the
operation background of the enterprise-specific
large model product, support model: [bailian-v1].
result_format(str, `optional`): [message|text] Set result result format. # noqa E501
Default result is text
incremental_output(bool, `optional`): Used to control the streaming output mode. # noqa E501
If true, the subsequent output will include the previously input content. # noqa E501
Otherwise, the subsequent output will not include the previously output # noqa E501
content. Default false.
stop(list[str] or list[list[int]], `optional`): Used to control the generation to stop # noqa E501
when encountering setting str or token ids, the result will not include # noqa E501
stop words or tokens.
max_tokens(int, `optional`): The maximum token num expected to be output. It should be # noqa E501
noted that the length generated by the model will only be less than max_tokens, # noqa E501
not necessarily equal to it. If max_tokens is set too large, the service will # noqa E501
directly prompt that the length exceeds the limit. It is generally # noqa E501
not recommended to set this value.
repetition_penalty(float, `optional`): Used to control the repeatability when generating models. # noqa E501
Increasing repetition_penalty can reduce the duplication of model generation. # noqa E501
1.0 means no punishment.
workspace (str): The dashscope workspace id.
Raises:
InputRequired: The prompt cannot be empty.
InvalidInput: The history and auto_history are mutually exclusive.
Returns:
Union[ConversationResponse,
Generator[ConversationResponse, None, None]]: If
stream is True, return Generator, otherwise ConversationResponse.
"""
if ((prompt is None or not prompt)
and ((messages is None or not messages))):
raise InputRequired('prompt or messages is required!')
if model is None or not model:
raise ModelRequired('Model is required!')
task_group, _ = _get_task_group_and_task(__name__)
if plugins is not None:
headers = kwargs.pop('headers', {})
if isinstance(plugins, str):
headers['X-DashScope-Plugin'] = plugins
else:
headers['X-DashScope-Plugin'] = json.dumps(plugins)
kwargs['headers'] = headers
input, parameters = self._build_input_parameters(
model, prompt, history, auto_history, n_history, messages,
**kwargs)
response = super().call(model=model,
task_group=task_group,
task='text-generation',
function='generation',
api_key=api_key,
input=input,
workspace=workspace,
**parameters)
is_stream = kwargs.get('stream', False)
return self._handle_response(prompt, response, is_stream)
def _handle_stream_response(self, prompt, responses):
for rsp in responses:
rsp = ConversationResponse.from_api_response(rsp)
yield rsp
if rsp.status_code == HTTPStatus.OK and rsp.output.choices is None:
user_item = HistoryItem('user', text=prompt)
bot_history_item = HistoryItem('bot', text=rsp.output.text)
self.history.append(user_item)
self.history.append(bot_history_item)
def _handle_response(self, prompt, response, is_stream):
if is_stream:
return (rsp
for rsp in self._handle_stream_response(prompt, response))
else:
response = ConversationResponse.from_api_response(response)
if (response.status_code == HTTPStatus.OK
and response.output.choices is None):
user_item = HistoryItem('user', text=prompt)
bot_history_item = HistoryItem('bot',
text=response.output['text'])
self.history.append(user_item)
self.history.append(bot_history_item)
return response
def _build_input_parameters(self, model, prompt, history, auto_history,
n_history, messages, **kwargs):
if model == Conversation.Models.qwen_v1:
logger.warning(
'Model %s is deprecated, use %s instead!' %
(Conversation.Models.qwen_v1, Conversation.Models.qwen_turbo))
if model == Conversation.Models.qwen_plus_v1:
logger.warning('Model %s is deprecated, use %s instead!' %
(Conversation.Models.qwen_plus_v1,
Conversation.Models.qwen_plus))
parameters = {}
if history is not None and auto_history:
raise InvalidInput('auto_history is True, history must None')
if history is not None: # use user provided history or system.
logger.warning(DEPRECATED_MESSAGE)
input = {
PROMPT:
prompt,
HISTORY:
_history_to_qwen_format(history, n_history) if history else [],
}
elif auto_history:
logger.warning(DEPRECATED_MESSAGE)
input = {
PROMPT: prompt,
HISTORY: _history_to_qwen_format(self.history, n_history)
}
elif messages:
msgs = deepcopy(messages)
if prompt is not None and prompt:
msgs.append({'role': Role.USER, 'content': prompt})
input = {'messages': msgs}
else:
input = {
PROMPT: prompt,
}
# parameters
if model.startswith('qwen'):
enable_search = kwargs.pop('enable_search', False)
if enable_search:
parameters['enable_search'] = enable_search
return input, {**parameters, **kwargs}

View File

@@ -0,0 +1,407 @@
# Copyright (c) Alibaba, Inc. and its affiliates.
import copy
import json
from typing import Any, Dict, Generator, List, Union, AsyncGenerator
from dashscope.api_entities.dashscope_response import (GenerationResponse,
Message, Role)
from dashscope.client.base_api import BaseAioApi, BaseApi
from dashscope.common.constants import (CUSTOMIZED_MODEL_ID,
DEPRECATED_MESSAGE, HISTORY, MESSAGES,
PROMPT)
from dashscope.common.error import InputRequired, ModelRequired
from dashscope.common.logging import logger
from dashscope.common.utils import _get_task_group_and_task
from dashscope.utils.param_utils import ParamUtil
from dashscope.utils.message_utils import merge_single_response
class Generation(BaseApi):
task = 'text-generation'
"""API for AI-Generated Content(AIGC) models.
"""
class Models:
"""@deprecated, use qwen_turbo instead"""
qwen_v1 = 'qwen-v1'
"""@deprecated, use qwen_plus instead"""
qwen_plus_v1 = 'qwen-plus-v1'
bailian_v1 = 'bailian-v1'
dolly_12b_v2 = 'dolly-12b-v2'
qwen_turbo = 'qwen-turbo'
qwen_plus = 'qwen-plus'
qwen_max = 'qwen-max'
@classmethod
def call(
cls,
model: str,
prompt: Any = None,
history: list = None,
api_key: str = None,
messages: List[Message] = None,
plugins: Union[str, Dict[str, Any]] = None,
workspace: str = None,
**kwargs
) -> Union[GenerationResponse, Generator[GenerationResponse, None, None]]:
"""Call generation model service.
Args:
model (str): The requested model, such as qwen-turbo
prompt (Any): The input prompt.
history (list):The user provided history, deprecated
examples:
[{'user':'The weather is fine today.',
'bot': 'Suitable for outings'}].
Defaults to None.
api_key (str, optional): The api api_key, can be None,
if None, will get by default rule(TODO: api key doc).
messages (list): The generation messages.
examples:
[{'role': 'user',
'content': 'The weather is fine today.'},
{'role': 'assistant', 'content': 'Suitable for outings'}]
plugins (Any): The plugin config. Can be plugins config str, or dict.
**kwargs:
stream(bool, `optional`): Enable server-sent events
(ref: https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events) # noqa E501
the result will back partially[qwen-turbo,bailian-v1].
temperature(float, `optional`): Used to control the degree
of randomness and diversity. Specifically, the temperature
value controls the degree to which the probability distribution
of each candidate word is smoothed when generating text.
A higher temperature value will reduce the peak value of
the probability, allowing more low-probability words to be
selected, and the generated results will be more diverse;
while a lower temperature value will enhance the peak value
of the probability, making it easier for high-probability
words to be selected, the generated results are more
deterministic, range(0, 2) .[qwen-turbo,qwen-plus].
top_p(float, `optional`): A sampling strategy, called nucleus
sampling, where the model considers the results of the
tokens with top_p probability mass. So 0.1 means only
the tokens comprising the top 10% probability mass are
considered[qwen-turbo,bailian-v1].
top_k(int, `optional`): The size of the sample candidate set when generated. # noqa E501
For example, when the value is 50, only the 50 highest-scoring tokens # noqa E501
in a single generation form a randomly sampled candidate set. # noqa E501
The larger the value, the higher the randomness generated; # noqa E501
the smaller the value, the higher the certainty generated. # noqa E501
The default value is 0, which means the top_k policy is # noqa E501
not enabled. At this time, only the top_p policy takes effect. # noqa E501
enable_search(bool, `optional`): Whether to enable web search(quark). # noqa E501
Currently works best only on the first round of conversation.
Default to False, support model: [qwen-turbo].
customized_model_id(str, required) The enterprise-specific
large model id, which needs to be generated from the
operation background of the enterprise-specific
large model product, support model: [bailian-v1].
result_format(str, `optional`): [message|text] Set result result format. # noqa E501
Default result is text
incremental_output(bool, `optional`): Used to control the streaming output mode. # noqa E501
If true, the subsequent output will include the previously input content. # noqa E501
Otherwise, the subsequent output will not include the previously output # noqa E501
content. Default false.
stop(list[str] or list[list[int]], `optional`): Used to control the generation to stop # noqa E501
when encountering setting str or token ids, the result will not include # noqa E501
stop words or tokens.
max_tokens(int, `optional`): The maximum token num expected to be output. It should be # noqa E501
noted that the length generated by the model will only be less than max_tokens, # noqa E501
not necessarily equal to it. If max_tokens is set too large, the service will # noqa E501
directly prompt that the length exceeds the limit. It is generally # noqa E501
not recommended to set this value.
repetition_penalty(float, `optional`): Used to control the repeatability when generating models. # noqa E501
Increasing repetition_penalty can reduce the duplication of model generation. # noqa E501
1.0 means no punishment.
workspace (str): The dashscope workspace id.
Raises:
InvalidInput: The history and auto_history are mutually exclusive.
Returns:
Union[GenerationResponse,
Generator[GenerationResponse, None, None]]: If
stream is True, return Generator, otherwise GenerationResponse.
"""
if (prompt is None or not prompt) and (messages is None
or not messages):
raise InputRequired('prompt or messages is required!')
if model is None or not model:
raise ModelRequired('Model is required!')
task_group, function = _get_task_group_and_task(__name__)
if plugins is not None:
headers = kwargs.pop('headers', {})
if isinstance(plugins, str):
headers['X-DashScope-Plugin'] = plugins
else:
headers['X-DashScope-Plugin'] = json.dumps(plugins)
kwargs['headers'] = headers
input, parameters = cls._build_input_parameters(
model, prompt, history, messages, **kwargs)
is_stream = parameters.get('stream', False)
# Check if we need to merge incremental output
is_incremental_output = kwargs.get('incremental_output', None)
to_merge_incremental_output = False
if (ParamUtil.should_modify_incremental_output(model) and
is_stream and is_incremental_output is False):
to_merge_incremental_output = True
parameters['incremental_output'] = True
# Pass incremental_to_full flag via headers user-agent
if 'headers' not in parameters:
parameters['headers'] = {}
flag = '1' if to_merge_incremental_output else '0'
parameters['headers']['user-agent'] = f'incremental_to_full/{flag}'
response = super().call(model=model,
task_group=task_group,
task=Generation.task,
function=function,
api_key=api_key,
input=input,
workspace=workspace,
**parameters)
if is_stream:
if to_merge_incremental_output:
# Extract n parameter for merge logic
n = parameters.get('n', 1)
return cls._merge_generation_response(response, n)
else:
return (GenerationResponse.from_api_response(rsp)
for rsp in response)
else:
return GenerationResponse.from_api_response(response)
@classmethod
def _build_input_parameters(cls, model, prompt, history, messages,
**kwargs):
if model == Generation.Models.qwen_v1:
logger.warning(
'Model %s is deprecated, use %s instead!' %
(Generation.Models.qwen_v1, Generation.Models.qwen_turbo))
if model == Generation.Models.qwen_plus_v1:
logger.warning(
'Model %s is deprecated, use %s instead!' %
(Generation.Models.qwen_plus_v1, Generation.Models.qwen_plus))
parameters = {}
input = {}
if history is not None:
logger.warning(DEPRECATED_MESSAGE)
input[HISTORY] = history
if prompt is not None and prompt:
input[PROMPT] = prompt
elif messages is not None:
msgs = copy.deepcopy(messages)
if prompt is not None and prompt:
msgs.append({'role': Role.USER, 'content': prompt})
input = {MESSAGES: msgs}
else:
input[PROMPT] = prompt
if model.startswith('qwen'):
enable_search = kwargs.pop('enable_search', False)
if enable_search:
parameters['enable_search'] = enable_search
elif model.startswith('bailian'):
customized_model_id = kwargs.pop('customized_model_id', None)
if customized_model_id is None:
raise InputRequired('customized_model_id is required for %s' %
model)
input[CUSTOMIZED_MODEL_ID] = customized_model_id
return input, {**parameters, **kwargs}
@classmethod
def _merge_generation_response(cls, response, n=1) -> Generator[GenerationResponse, None, None]:
"""Merge incremental response chunks to simulate non-incremental output."""
accumulated_data = {}
for rsp in response:
parsed_response = GenerationResponse.from_api_response(rsp)
result = merge_single_response(parsed_response, accumulated_data, n)
if result is True:
yield parsed_response
elif isinstance(result, list):
# Multiple responses to yield (for n>1 non-stop cases)
for resp in result:
yield resp
class AioGeneration(BaseAioApi):
task = 'text-generation'
"""API for AI-Generated Content(AIGC) models.
"""
class Models:
"""@deprecated, use qwen_turbo instead"""
qwen_v1 = 'qwen-v1'
"""@deprecated, use qwen_plus instead"""
qwen_plus_v1 = 'qwen-plus-v1'
bailian_v1 = 'bailian-v1'
dolly_12b_v2 = 'dolly-12b-v2'
qwen_turbo = 'qwen-turbo'
qwen_plus = 'qwen-plus'
qwen_max = 'qwen-max'
@classmethod
async def call(
cls,
model: str,
prompt: Any = None,
history: list = None,
api_key: str = None,
messages: List[Message] = None,
plugins: Union[str, Dict[str, Any]] = None,
workspace: str = None,
**kwargs
) -> Union[GenerationResponse, AsyncGenerator[GenerationResponse, None]]:
"""Call generation model service.
Args:
model (str): The requested model, such as qwen-turbo
prompt (Any): The input prompt.
history (list):The user provided history, deprecated
examples:
[{'user':'The weather is fine today.',
'bot': 'Suitable for outings'}].
Defaults to None.
api_key (str, optional): The api api_key, can be None,
if None, will get by default rule(TODO: api key doc).
messages (list): The generation messages.
examples:
[{'role': 'user',
'content': 'The weather is fine today.'},
{'role': 'assistant', 'content': 'Suitable for outings'}]
plugins (Any): The plugin config. Can be plugins config str, or dict.
**kwargs:
stream(bool, `optional`): Enable server-sent events
(ref: https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events) # noqa E501
the result will back partially[qwen-turbo,bailian-v1].
temperature(float, `optional`): Used to control the degree
of randomness and diversity. Specifically, the temperature
value controls the degree to which the probability distribution
of each candidate word is smoothed when generating text.
A higher temperature value will reduce the peak value of
the probability, allowing more low-probability words to be
selected, and the generated results will be more diverse;
while a lower temperature value will enhance the peak value
of the probability, making it easier for high-probability
words to be selected, the generated results are more
deterministic, range(0, 2) .[qwen-turbo,qwen-plus].
top_p(float, `optional`): A sampling strategy, called nucleus
sampling, where the model considers the results of the
tokens with top_p probability mass. So 0.1 means only
the tokens comprising the top 10% probability mass are
considered[qwen-turbo,bailian-v1].
top_k(int, `optional`): The size of the sample candidate set when generated. # noqa E501
For example, when the value is 50, only the 50 highest-scoring tokens # noqa E501
in a single generation form a randomly sampled candidate set. # noqa E501
The larger the value, the higher the randomness generated; # noqa E501
the smaller the value, the higher the certainty generated. # noqa E501
The default value is 0, which means the top_k policy is # noqa E501
not enabled. At this time, only the top_p policy takes effect. # noqa E501
enable_search(bool, `optional`): Whether to enable web search(quark). # noqa E501
Currently works best only on the first round of conversation.
Default to False, support model: [qwen-turbo].
customized_model_id(str, required) The enterprise-specific
large model id, which needs to be generated from the
operation background of the enterprise-specific
large model product, support model: [bailian-v1].
result_format(str, `optional`): [message|text] Set result result format. # noqa E501
Default result is text
incremental_output(bool, `optional`): Used to control the streaming output mode. # noqa E501
If true, the subsequent output will include the previously input content. # noqa E501
Otherwise, the subsequent output will not include the previously output # noqa E501
content. Default false.
stop(list[str] or list[list[int]], `optional`): Used to control the generation to stop # noqa E501
when encountering setting str or token ids, the result will not include # noqa E501
stop words or tokens.
max_tokens(int, `optional`): The maximum token num expected to be output. It should be # noqa E501
noted that the length generated by the model will only be less than max_tokens, # noqa E501
not necessarily equal to it. If max_tokens is set too large, the service will # noqa E501
directly prompt that the length exceeds the limit. It is generally # noqa E501
not recommended to set this value.
repetition_penalty(float, `optional`): Used to control the repeatability when generating models. # noqa E501
Increasing repetition_penalty can reduce the duplication of model generation. # noqa E501
1.0 means no punishment.
workspace (str): The dashscope workspace id.
Raises:
InvalidInput: The history and auto_history are mutually exclusive.
Returns:
Union[GenerationResponse,
AsyncGenerator[GenerationResponse, None]]: If
stream is True, return AsyncGenerator, otherwise GenerationResponse.
"""
if (prompt is None or not prompt) and (messages is None
or not messages):
raise InputRequired('prompt or messages is required!')
if model is None or not model:
raise ModelRequired('Model is required!')
task_group, function = _get_task_group_and_task(__name__)
if plugins is not None:
headers = kwargs.pop('headers', {})
if isinstance(plugins, str):
headers['X-DashScope-Plugin'] = plugins
else:
headers['X-DashScope-Plugin'] = json.dumps(plugins)
kwargs['headers'] = headers
input, parameters = Generation._build_input_parameters(
model, prompt, history, messages, **kwargs)
is_stream = parameters.get('stream', False)
# Check if we need to merge incremental output
is_incremental_output = kwargs.get('incremental_output', None)
to_merge_incremental_output = False
if (ParamUtil.should_modify_incremental_output(model) and
is_stream and is_incremental_output is False):
to_merge_incremental_output = True
parameters['incremental_output'] = True
# Pass incremental_to_full flag via headers user-agent
if 'headers' not in parameters:
parameters['headers'] = {}
flag = '1' if to_merge_incremental_output else '0'
parameters['headers']['user-agent'] = f'incremental_to_full/{flag}'
response = await super().call(model=model,
task_group=task_group,
task=Generation.task,
function=function,
api_key=api_key,
input=input,
workspace=workspace,
**parameters)
if is_stream:
if to_merge_incremental_output:
# Extract n parameter for merge logic
n = parameters.get('n', 1)
return cls._merge_generation_response(response, n)
else:
return cls._stream_responses(response)
else:
return GenerationResponse.from_api_response(response)
@classmethod
async def _stream_responses(cls, response) -> AsyncGenerator[GenerationResponse, None]:
"""Convert async response stream to GenerationResponse stream."""
# Type hint: when stream=True, response is actually an AsyncIterable
async for rsp in response: # type: ignore
yield GenerationResponse.from_api_response(rsp)
@classmethod
async def _merge_generation_response(cls, response, n=1) -> AsyncGenerator[GenerationResponse, None]:
"""Async version of merge incremental response chunks."""
accumulated_data = {}
async for rsp in response: # type: ignore
parsed_response = GenerationResponse.from_api_response(rsp)
result = merge_single_response(parsed_response, accumulated_data, n)
if result is True:
yield parsed_response
elif isinstance(result, list):
# Multiple responses to yield (for n>1 non-stop cases)
for resp in result:
yield resp

View File

@@ -0,0 +1,630 @@
# Copyright (c) Alibaba, Inc. and its affiliates.
from typing import Any, Dict, List, Union
from dashscope.api_entities.dashscope_response import (DashScopeAPIResponse,
ImageSynthesisResponse)
from dashscope.client.base_api import BaseAsyncApi, BaseApi, BaseAsyncAioApi, BaseAioApi
from dashscope.common.constants import IMAGES, NEGATIVE_PROMPT, PROMPT
from dashscope.common.error import InputRequired
from dashscope.common.utils import _get_task_group_and_task
from dashscope.utils.oss_utils import check_and_upload_local
class ImageSynthesis(BaseAsyncApi):
task = 'text2image'
"""API for image synthesis.
"""
class Models:
wanx_v1 = 'wanx-v1'
wanx_sketch_to_image_v1 = 'wanx-sketch-to-image-v1'
wanx_2_1_imageedit = 'wanx2.1-imageedit'
@classmethod
def call(cls,
model: str,
prompt: Any,
negative_prompt: Any = None,
images: List[str] = None,
api_key: str = None,
sketch_image_url: str = None,
ref_img: str = None,
workspace: str = None,
extra_input: Dict = None,
task: str = None,
function: str = None,
mask_image_url: str = None,
base_image_url: str = None,
**kwargs) -> ImageSynthesisResponse:
"""Call image(s) synthesis service and get result.
Args:
model (str): The model, reference ``Models``.
prompt (Any): The prompt for image(s) synthesis.
negative_prompt (Any): The negative_prompt. Defaults to None.
images (List[str]): The input list of images url,
currently not supported.
api_key (str, optional): The api api_key. Defaults to None.
sketch_image_url (str, optional): Only for wanx-sketch-to-image-v1,
can be local file.
Defaults to None.
workspace (str): The dashscope workspace id.
extra_input (Dict): The extra input parameters.
task (str): The task of api, ref doc.
function (str): The specific functions to be achieved. like:
colorization,super_resolution,expand,remove_watermaker,doodle,
description_edit_with_mask,description_edit,stylization_local,stylization_all
base_image_url (str): Enter the URL address of the target edited image.
mask_image_url (str): Provide the URL address of the image of the marked area by the user. It should be consistent with the image resolution of the base_image_url.
**kwargs:
n(int, `optional`): Number of images to synthesis.
size(str, `optional`): The output image(s) size(width*height).
similarity(float, `optional`): The similarity between the
output image and the input image
sketch_weight(int, optional): How much the input sketch
affects the output image[0-10], only for wanx-sketch-to-image-v1. # noqa E501
Default 10.
realisticness(int, optional): The realisticness of the output
image[0-10], only for wanx-sketch-to-image-v1. Default 5
Raises:
InputRequired: The prompt cannot be empty.
Returns:
ImageSynthesisResponse: The image(s) synthesis result.
"""
return super().call(model,
prompt,
negative_prompt,
images,
api_key=api_key,
sketch_image_url=sketch_image_url,
ref_img=ref_img,
workspace=workspace,
extra_input=extra_input,
task=task,
function=function,
mask_image_url=mask_image_url,
base_image_url=base_image_url,
**kwargs)
@classmethod
def sync_call(cls,
model: str,
prompt: Any,
negative_prompt: Any = None,
images: List[str] = None,
api_key: str = None,
sketch_image_url: str = None,
ref_img: str = None,
workspace: str = None,
extra_input: Dict = None,
task: str = None,
function: str = None,
mask_image_url: str = None,
base_image_url: str = None,
**kwargs) -> ImageSynthesisResponse:
"""
Note: This method currently now only supports wan2.2-t2i-flash and wan2.2-t2i-plus.
Using other models will result in an errorMore raw image models may be added for use later
"""
task_group, f = _get_task_group_and_task(__name__)
inputs, kwargs, task = cls._get_input(model, prompt, negative_prompt,
images, api_key, sketch_image_url,
ref_img, extra_input, task, function,
mask_image_url, base_image_url, **kwargs)
response = BaseApi.call(model, inputs, task_group, task, f, api_key, workspace, **kwargs)
return ImageSynthesisResponse.from_api_response(response)
@classmethod
def _get_input(cls,
model: str,
prompt: Any,
negative_prompt: Any = None,
images: List[str] = None,
api_key: str = None,
sketch_image_url: str = None,
ref_img: str = None,
extra_input: Dict = None,
task: str = None,
function: str = None,
mask_image_url: str = None,
base_image_url: str = None,
**kwargs):
if prompt is None or not prompt:
raise InputRequired('prompt is required!')
inputs = {PROMPT: prompt}
has_upload = False
if negative_prompt is not None:
inputs[NEGATIVE_PROMPT] = negative_prompt
if images is not None and images and len(images) > 0:
new_images = []
for image in images:
is_upload, new_image = check_and_upload_local(
model, image, api_key)
if is_upload:
has_upload = True
new_images.append(new_image)
inputs[IMAGES] = new_images
if sketch_image_url is not None and sketch_image_url:
is_upload, sketch_image_url = check_and_upload_local(
model, sketch_image_url, api_key)
if is_upload:
has_upload = True
inputs['sketch_image_url'] = sketch_image_url
if ref_img is not None and ref_img:
is_upload, ref_img = check_and_upload_local(
model, ref_img, api_key)
if is_upload:
has_upload = True
inputs['ref_img'] = ref_img
if function is not None and function:
inputs['function'] = function
if mask_image_url is not None and mask_image_url:
is_upload, res_mask_image_url = check_and_upload_local(
model, mask_image_url, api_key)
if is_upload:
has_upload = True
inputs['mask_image_url'] = res_mask_image_url
if base_image_url is not None and base_image_url:
is_upload, res_base_image_url = check_and_upload_local(
model, base_image_url, api_key)
if is_upload:
has_upload = True
inputs['base_image_url'] = res_base_image_url
if extra_input is not None and extra_input:
inputs = {**inputs, **extra_input}
if has_upload:
headers = kwargs.pop('headers', {})
headers['X-DashScope-OssResourceResolve'] = 'enable'
kwargs['headers'] = headers
def __get_i2i_task(task, model) -> str:
# 处理task参数优先使用有效的task值
if task is not None and task != "":
return task
# 根据model确定任务类型
if model is not None and model != "":
if 'imageedit' in model or "wan2.5-i2i" in model:
return 'image2image'
# 默认返回文本到图像任务
return ImageSynthesis.task
task = __get_i2i_task(task, model)
return inputs, kwargs, task
@classmethod
def async_call(cls,
model: str,
prompt: Any,
negative_prompt: Any = None,
images: List[str] = None,
api_key: str = None,
sketch_image_url: str = None,
ref_img: str = None,
workspace: str = None,
extra_input: Dict = None,
task: str = None,
function: str = None,
mask_image_url: str = None,
base_image_url: str = None,
**kwargs) -> ImageSynthesisResponse:
"""Create a image(s) synthesis task, and return task information.
Args:
model (str): The model, reference ``Models``.
prompt (Any): The prompt for image(s) synthesis.
negative_prompt (Any): The negative_prompt. Defaults to None.
images (List[str]): The input list of images url.
api_key (str, optional): The api api_key. Defaults to None.
sketch_image_url (str, optional): Only for wanx-sketch-to-image-v1.
Defaults to None.
workspace (str): The dashscope workspace id.
extra_input (Dict): The extra input parameters.
task (str): The task of api, ref doc.
function (str): The specific functions to be achieved. like:
colorization,super_resolution,expand,remove_watermaker,doodle,
description_edit_with_mask,description_edit,stylization_local,stylization_all
base_image_url (str): Enter the URL address of the target edited image.
mask_image_url (str): Provide the URL address of the image of the marked area by the user. It should be consistent with the image resolution of the base_image_url.
**kwargs(wanx-v1):
n(int, `optional`): Number of images to synthesis.
size: The output image(s) size, Default 1024*1024
similarity(float, `optional`): The similarity between the
output image and the input image.
sketch_weight(int, optional): How much the input sketch
affects the output image[0-10], only for wanx-sketch-to-image-v1. # noqa E501
Default 10.
realisticness(int, optional): The realisticness of the output
image[0-10], only for wanx-sketch-to-image-v1. Default 5
Raises:
InputRequired: The prompt cannot be empty.
Returns:
DashScopeAPIResponse: The image synthesis
task id in the response.
"""
task_group, f = _get_task_group_and_task(__name__)
inputs, kwargs, task = cls._get_input(model, prompt, negative_prompt,
images, api_key, sketch_image_url,
ref_img, extra_input, task, function,
mask_image_url, base_image_url, **kwargs)
response = super().async_call(
model=model,
task_group=task_group,
task=task,
function=f,
api_key=api_key,
input=inputs,
workspace=workspace,
**kwargs)
return ImageSynthesisResponse.from_api_response(response)
@classmethod
def fetch(cls,
task: Union[str, ImageSynthesisResponse],
api_key: str = None,
workspace: str = None) -> ImageSynthesisResponse:
"""Fetch image(s) synthesis task status or result.
Args:
task (Union[str, ImageSynthesisResponse]): The task_id or
ImageSynthesisResponse return by async_call().
api_key (str, optional): The api api_key. Defaults to None.
workspace (str): The dashscope workspace id.
Returns:
ImageSynthesisResponse: The task status or result.
"""
response = super().fetch(task, api_key=api_key, workspace=workspace)
return ImageSynthesisResponse.from_api_response(response)
@classmethod
def wait(cls,
task: Union[str, ImageSynthesisResponse],
api_key: str = None,
workspace: str = None) -> ImageSynthesisResponse:
"""Wait for image(s) synthesis task to complete, and return the result.
Args:
task (Union[str, ImageSynthesisResponse]): The task_id or
ImageSynthesisResponse return by async_call().
api_key (str, optional): The api api_key. Defaults to None.
workspace (str): The dashscope workspace id.
Returns:
ImageSynthesisResponse: The task result.
"""
response = super().wait(task, api_key, workspace=workspace)
return ImageSynthesisResponse.from_api_response(response)
@classmethod
def cancel(cls,
task: Union[str, ImageSynthesisResponse],
api_key: str = None,
workspace: str = None) -> DashScopeAPIResponse:
"""Cancel image synthesis task.
Only tasks whose status is PENDING can be canceled.
Args:
task (Union[str, ImageSynthesisResponse]): The task_id or
ImageSynthesisResponse return by async_call().
api_key (str, optional): The api api_key. Defaults to None.
workspace (str): The dashscope workspace id.
Returns:
DashScopeAPIResponse: The response data.
"""
return super().cancel(task, api_key, workspace=workspace)
@classmethod
def list(cls,
start_time: str = None,
end_time: str = None,
model_name: str = None,
api_key_id: str = None,
region: str = None,
status: str = None,
page_no: int = 1,
page_size: int = 10,
api_key: str = None,
workspace: str = None,
**kwargs) -> DashScopeAPIResponse:
"""List async tasks.
Args:
start_time (str, optional): The tasks start time,
for example: 20230420000000. Defaults to None.
end_time (str, optional): The tasks end time,
for example: 20230420000000. Defaults to None.
model_name (str, optional): The tasks model name. Defaults to None.
api_key_id (str, optional): The tasks api-key-id. Defaults to None.
region (str, optional): The service region,
for example: cn-beijing. Defaults to None.
status (str, optional): The status of tasks[PENDING,
RUNNING, SUCCEEDED, FAILED, CANCELED]. Defaults to None.
page_no (int, optional): The page number. Defaults to 1.
page_size (int, optional): The page size. Defaults to 10.
api_key (str, optional): The user api-key. Defaults to None.
workspace (str): The dashscope workspace id.
Returns:
DashScopeAPIResponse: The response data.
"""
return super().list(start_time=start_time,
end_time=end_time,
model_name=model_name,
api_key_id=api_key_id,
region=region,
status=status,
page_no=page_no,
page_size=page_size,
api_key=api_key,
workspace=workspace,
**kwargs)
class AioImageSynthesis(BaseAsyncAioApi):
@classmethod
async def call(cls,
model: str,
prompt: Any,
negative_prompt: Any = None,
images: List[str] = None,
api_key: str = None,
sketch_image_url: str = None,
ref_img: str = None,
workspace: str = None,
extra_input: Dict = None,
task: str = None,
function: str = None,
mask_image_url: str = None,
base_image_url: str = None,
**kwargs) -> ImageSynthesisResponse:
"""Call image(s) synthesis service and get result.
Args:
model (str): The model, reference ``Models``.
prompt (Any): The prompt for image(s) synthesis.
negative_prompt (Any): The negative_prompt. Defaults to None.
images (List[str]): The input list of images url,
currently not supported.
api_key (str, optional): The api api_key. Defaults to None.
sketch_image_url (str, optional): Only for wanx-sketch-to-image-v1,
can be local file.
Defaults to None.
workspace (str): The dashscope workspace id.
extra_input (Dict): The extra input parameters.
task (str): The task of api, ref doc.
function (str): The specific functions to be achieved. like:
colorization,super_resolution,expand,remove_watermaker,doodle,
description_edit_with_mask,description_edit,stylization_local,stylization_all
base_image_url (str): Enter the URL address of the target edited image.
mask_image_url (str): Provide the URL address of the image of the marked area by the user. It should be consistent with the image resolution of the base_image_url.
**kwargs:
n(int, `optional`): Number of images to synthesis.
size(str, `optional`): The output image(s) size(width*height).
similarity(float, `optional`): The similarity between the
output image and the input image
sketch_weight(int, optional): How much the input sketch
affects the output image[0-10], only for wanx-sketch-to-image-v1. # noqa E501
Default 10.
realisticness(int, optional): The realisticness of the output
image[0-10], only for wanx-sketch-to-image-v1. Default 5
Raises:
InputRequired: The prompt cannot be empty.
Returns:
ImageSynthesisResponse: The image(s) synthesis result.
"""
task_group, f = _get_task_group_and_task(__name__)
inputs, kwargs, task = ImageSynthesis._get_input(model, prompt, negative_prompt,
images, api_key, sketch_image_url,
ref_img, extra_input, task, function,
mask_image_url, base_image_url, **kwargs)
response = await super().call(model, inputs, task_group, task, f, api_key, workspace, **kwargs)
return ImageSynthesisResponse.from_api_response(response)
@classmethod
async def sync_call(cls,
model: str,
prompt: Any,
negative_prompt: Any = None,
images: List[str] = None,
api_key: str = None,
sketch_image_url: str = None,
ref_img: str = None,
workspace: str = None,
extra_input: Dict = None,
task: str = None,
function: str = None,
mask_image_url: str = None,
base_image_url: str = None,
**kwargs) -> ImageSynthesisResponse:
"""
Note: This method currently now only supports wan2.2-t2i-flash and wan2.2-t2i-plus.
Using other models will result in an errorMore raw image models may be added for use later
"""
task_group, f = _get_task_group_and_task(__name__)
inputs, kwargs, task = ImageSynthesis._get_input(model, prompt, negative_prompt,
images, api_key, sketch_image_url,
ref_img, extra_input, task, function,
mask_image_url, base_image_url, **kwargs)
response = await BaseAioApi.call(model, inputs, task_group, task, f, api_key, workspace, **kwargs)
return ImageSynthesisResponse.from_api_response(response)
@classmethod
async def async_call(cls,
model: str,
prompt: Any,
negative_prompt: Any = None,
images: List[str] = None,
api_key: str = None,
sketch_image_url: str = None,
ref_img: str = None,
workspace: str = None,
extra_input: Dict = None,
task: str = None,
function: str = None,
mask_image_url: str = None,
base_image_url: str = None,
**kwargs) -> ImageSynthesisResponse:
"""Create a image(s) synthesis task, and return task information.
Args:
model (str): The model, reference ``Models``.
prompt (Any): The prompt for image(s) synthesis.
negative_prompt (Any): The negative_prompt. Defaults to None.
images (List[str]): The input list of images url.
api_key (str, optional): The api api_key. Defaults to None.
sketch_image_url (str, optional): Only for wanx-sketch-to-image-v1.
Defaults to None.
workspace (str): The dashscope workspace id.
extra_input (Dict): The extra input parameters.
task (str): The task of api, ref doc.
function (str): The specific functions to be achieved. like:
colorization,super_resolution,expand,remove_watermaker,doodle,
description_edit_with_mask,description_edit,stylization_local,stylization_all
base_image_url (str): Enter the URL address of the target edited image.
mask_image_url (str): Provide the URL address of the image of the marked area by the user. It should be consistent with the image resolution of the base_image_url.
**kwargs(wanx-v1):
n(int, `optional`): Number of images to synthesis.
size: The output image(s) size, Default 1024*1024
similarity(float, `optional`): The similarity between the
output image and the input image.
sketch_weight(int, optional): How much the input sketch
affects the output image[0-10], only for wanx-sketch-to-image-v1. # noqa E501
Default 10.
realisticness(int, optional): The realisticness of the output
image[0-10], only for wanx-sketch-to-image-v1. Default 5
Raises:
InputRequired: The prompt cannot be empty.
Returns:
DashScopeAPIResponse: The image synthesis
task id in the response.
"""
task_group, f = _get_task_group_and_task(__name__)
inputs, kwargs, task = ImageSynthesis._get_input(model, prompt, negative_prompt,
images, api_key, sketch_image_url,
ref_img, extra_input, task, function,
mask_image_url, base_image_url, **kwargs)
response = await super().async_call(model, inputs, task_group, task, f, api_key, workspace, **kwargs)
return ImageSynthesisResponse.from_api_response(response)
@classmethod
async def fetch(cls,
task: Union[str, ImageSynthesisResponse],
api_key: str = None,
workspace: str = None,
**kwargs,) -> ImageSynthesisResponse:
"""Fetch image(s) synthesis task status or result.
Args:
task (Union[str, ImageSynthesisResponse]): The task_id or
ImageSynthesisResponse return by async_call().
api_key (str, optional): The api api_key. Defaults to None.
workspace (str): The dashscope workspace id.
Returns:
ImageSynthesisResponse: The task status or result.
"""
response = await super().fetch(task, api_key=api_key, workspace=workspace)
return ImageSynthesisResponse.from_api_response(response)
@classmethod
async def wait(cls,
task: Union[str, ImageSynthesisResponse],
api_key: str = None,
workspace: str = None,
**kwargs) -> ImageSynthesisResponse:
"""Wait for image(s) synthesis task to complete, and return the result.
Args:
task (Union[str, ImageSynthesisResponse]): The task_id or
ImageSynthesisResponse return by async_call().
api_key (str, optional): The api api_key. Defaults to None.
workspace (str): The dashscope workspace id.
Returns:
ImageSynthesisResponse: The task result.
"""
response = await super().wait(task, api_key, workspace=workspace)
return ImageSynthesisResponse.from_api_response(response)
@classmethod
async def cancel(cls,
task: Union[str, ImageSynthesisResponse],
api_key: str = None,
workspace: str = None,
**kwargs,) -> DashScopeAPIResponse:
"""Cancel image synthesis task.
Only tasks whose status is PENDING can be canceled.
Args:
task (Union[str, ImageSynthesisResponse]): The task_id or
ImageSynthesisResponse return by async_call().
api_key (str, optional): The api api_key. Defaults to None.
workspace (str): The dashscope workspace id.
Returns:
DashScopeAPIResponse: The response data.
"""
return await super().cancel(task, api_key, workspace=workspace)
@classmethod
async def list(cls,
start_time: str = None,
end_time: str = None,
model_name: str = None,
api_key_id: str = None,
region: str = None,
status: str = None,
page_no: int = 1,
page_size: int = 10,
api_key: str = None,
workspace: str = None,
**kwargs) -> DashScopeAPIResponse:
"""List async tasks.
Args:
start_time (str, optional): The tasks start time,
for example: 20230420000000. Defaults to None.
end_time (str, optional): The tasks end time,
for example: 20230420000000. Defaults to None.
model_name (str, optional): The tasks model name. Defaults to None.
api_key_id (str, optional): The tasks api-key-id. Defaults to None.
region (str, optional): The service region,
for example: cn-beijing. Defaults to None.
status (str, optional): The status of tasks[PENDING,
RUNNING, SUCCEEDED, FAILED, CANCELED]. Defaults to None.
page_no (int, optional): The page number. Defaults to 1.
page_size (int, optional): The page size. Defaults to 10.
api_key (str, optional): The user api-key. Defaults to None.
workspace (str): The dashscope workspace id.
Returns:
DashScopeAPIResponse: The response data.
"""
return await super().list(start_time=start_time,
end_time=end_time,
model_name=model_name,
api_key_id=api_key_id,
region=region,
status=status,
page_no=page_no,
page_size=page_size,
api_key=api_key,
workspace=workspace,
**kwargs)

View File

@@ -0,0 +1,371 @@
# Copyright (c) Alibaba, Inc. and its affiliates.
import copy
from typing import AsyncGenerator, Generator, List, Union
from dashscope.api_entities.dashscope_response import \
MultiModalConversationResponse
from dashscope.client.base_api import BaseAioApi, BaseApi
from dashscope.common.error import InputRequired, ModelRequired
from dashscope.common.utils import _get_task_group_and_task
from dashscope.utils.oss_utils import preprocess_message_element
from dashscope.utils.param_utils import ParamUtil
from dashscope.utils.message_utils import merge_multimodal_single_response
class MultiModalConversation(BaseApi):
"""MultiModal conversational robot interface.
"""
task = 'multimodal-generation'
function = 'generation'
class Models:
qwen_vl_chat_v1 = 'qwen-vl-chat-v1'
@classmethod
def call(
cls,
model: str,
messages: List = None,
api_key: str = None,
workspace: str = None,
text: str = None,
voice: str = None,
language_type: str = None,
**kwargs
) -> Union[MultiModalConversationResponse, Generator[
MultiModalConversationResponse, None, None]]:
"""Call the conversation model service.
Args:
model (str): The requested model, such as 'qwen-multimodal-v1'
messages (list): The generation messages.
examples:
[
{
"role": "system",
"content": [
{"text": "你是达摩院的生活助手机器人。"}
]
},
{
"role": "user",
"content": [
{"image": "http://XXXX"},
{"text": "这个图片是哪里?"},
]
}
]
api_key (str, optional): The api api_key, can be None,
if None, will retrieve by rule [1].
[1]: https://help.aliyun.com/zh/dashscope/developer-reference/api-key-settings. # noqa E501
workspace (str): The dashscope workspace id.
text (str): The text to generate.
voice (str): The voice name of qwen tts, include 'Cherry'/'Ethan'/'Sunny'/'Dylan' and so on,
you can get the total voice list : https://help.aliyun.com/zh/model-studio/qwen-tts.
language_type (str): The synthesized language type, default is 'auto', useful for [qwen3-tts].
**kwargs:
stream(bool, `optional`): Enable server-sent events
(ref: https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events) # noqa E501
the result will back partially[qwen-turbo,bailian-v1].
max_length(int, `optional`): The maximum length of tokens to
generate. The token count of your prompt plus max_length
cannot exceed the model's context length. Most models
have a context length of 2000 tokens[qwen-turbo,bailian-v1]. # noqa E501
top_p(float, `optional`): A sampling strategy, called nucleus
sampling, where the model considers the results of the
tokens with top_p probability mass. So 0.1 means only
the tokens comprising the top 10% probability mass are
considered[qwen-turbo,bailian-v1].
top_k(float, `optional`):
Raises:
InvalidInput: The history and auto_history are mutually exclusive.
Returns:
Union[MultiModalConversationResponse,
Generator[MultiModalConversationResponse, None, None]]: If
stream is True, return Generator, otherwise MultiModalConversationResponse.
"""
if model is None or not model:
raise ModelRequired('Model is required!')
task_group, _ = _get_task_group_and_task(__name__)
input = {}
msg_copy = None
if messages is not None and messages:
msg_copy = copy.deepcopy(messages)
has_upload = cls._preprocess_messages(model, msg_copy, api_key)
if has_upload:
headers = kwargs.pop('headers', {})
headers['X-DashScope-OssResourceResolve'] = 'enable'
kwargs['headers'] = headers
if text is not None and text:
input.update({'text': text})
if voice is not None and voice:
input.update({'voice': voice})
if language_type is not None and language_type:
input.update({'language_type': language_type})
if msg_copy is not None:
input.update({'messages': msg_copy})
# Check if we need to merge incremental output
is_incremental_output = kwargs.get('incremental_output', None)
to_merge_incremental_output = False
is_stream = kwargs.get('stream', False)
if (ParamUtil.should_modify_incremental_output(model) and
is_stream and is_incremental_output is not None and is_incremental_output is False):
to_merge_incremental_output = True
kwargs['incremental_output'] = True
# Pass incremental_to_full flag via headers user-agent
if 'headers' not in kwargs:
kwargs['headers'] = {}
flag = '1' if to_merge_incremental_output else '0'
kwargs['headers']['user-agent'] = f'incremental_to_full/{flag}'
response = super().call(model=model,
task_group=task_group,
task=MultiModalConversation.task,
function=MultiModalConversation.function,
api_key=api_key,
input=input,
workspace=workspace,
**kwargs)
if is_stream:
if to_merge_incremental_output:
# Extract n parameter for merge logic
n = kwargs.get('n', 1)
return cls._merge_multimodal_response(response, n)
else:
return (MultiModalConversationResponse.from_api_response(rsp)
for rsp in response)
else:
return MultiModalConversationResponse.from_api_response(response)
@classmethod
def _preprocess_messages(cls, model: str, messages: List[dict],
api_key: str):
"""
messages = [
{
"role": "user",
"content": [
{"image": ""},
{"text": ""},
]
}
]
"""
has_upload = False
for message in messages:
content = message['content']
for elem in content:
if not isinstance(elem,
(int, float, bool, str, bytes, bytearray)):
is_upload = preprocess_message_element(
model, elem, api_key)
if is_upload and not has_upload:
has_upload = True
return has_upload
@classmethod
def _merge_multimodal_response(cls, response, n=1) -> Generator[MultiModalConversationResponse, None, None]:
"""Merge incremental response chunks to simulate non-incremental output."""
accumulated_data = {}
for rsp in response:
parsed_response = MultiModalConversationResponse.from_api_response(rsp)
result = merge_multimodal_single_response(parsed_response, accumulated_data, n)
if result is True:
yield parsed_response
elif isinstance(result, list):
# Multiple responses to yield (for n>1 non-stop cases)
for resp in result:
yield resp
class AioMultiModalConversation(BaseAioApi):
"""Async MultiModal conversational robot interface.
"""
task = 'multimodal-generation'
function = 'generation'
class Models:
qwen_vl_chat_v1 = 'qwen-vl-chat-v1'
@classmethod
async def call(
cls,
model: str,
messages: List = None,
api_key: str = None,
workspace: str = None,
text: str = None,
voice: str = None,
language_type: str = None,
**kwargs
) -> Union[MultiModalConversationResponse, AsyncGenerator[
MultiModalConversationResponse, None]]:
"""Call the conversation model service asynchronously.
Args:
model (str): The requested model, such as 'qwen-multimodal-v1'
messages (list): The generation messages.
examples:
[
{
"role": "system",
"content": [
{"text": "你是达摩院的生活助手机器人。"}
]
},
{
"role": "user",
"content": [
{"image": "http://XXXX"},
{"text": "这个图片是哪里?"},
]
}
]
api_key (str, optional): The api api_key, can be None,
if None, will retrieve by rule [1].
[1]: https://help.aliyun.com/zh/dashscope/developer-reference/api-key-settings. # noqa E501
workspace (str): The dashscope workspace id.
text (str): The text to generate.
voice (str): The voice name of qwen tts, include 'Cherry'/'Ethan'/'Sunny'/'Dylan' and so on,
you can get the total voice list : https://help.aliyun.com/zh/model-studio/qwen-tts.
language_type (str): The synthesized language type, default is 'auto', useful for [qwen3-tts].
**kwargs:
stream(bool, `optional`): Enable server-sent events
(ref: https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events) # noqa E501
the result will back partially[qwen-turbo,bailian-v1].
max_length(int, `optional`): The maximum length of tokens to
generate. The token count of your prompt plus max_length
cannot exceed the model's context length. Most models
have a context length of 2000 tokens[qwen-turbo,bailian-v1]. # noqa E501
top_p(float, `optional`): A sampling strategy, called nucleus
sampling, where the model considers the results of the
tokens with top_p probability mass. So 0.1 means only
the tokens comprising the top 10% probability mass are
considered[qwen-turbo,bailian-v1].
top_k(float, `optional`):
Raises:
InvalidInput: The history and auto_history are mutually exclusive.
Returns:
Union[MultiModalConversationResponse,
AsyncGenerator[MultiModalConversationResponse, None]]: If
stream is True, return AsyncGenerator, otherwise MultiModalConversationResponse.
"""
if model is None or not model:
raise ModelRequired('Model is required!')
task_group, _ = _get_task_group_and_task(__name__)
input = {}
msg_copy = None
if messages is not None and messages:
msg_copy = copy.deepcopy(messages)
has_upload = cls._preprocess_messages(model, msg_copy, api_key)
if has_upload:
headers = kwargs.pop('headers', {})
headers['X-DashScope-OssResourceResolve'] = 'enable'
kwargs['headers'] = headers
if text is not None and text:
input.update({'text': text})
if voice is not None and voice:
input.update({'voice': voice})
if language_type is not None and language_type:
input.update({'language_type': language_type})
if msg_copy is not None:
input.update({'messages': msg_copy})
# Check if we need to merge incremental output
is_incremental_output = kwargs.get('incremental_output', None)
to_merge_incremental_output = False
is_stream = kwargs.get('stream', False)
if (ParamUtil.should_modify_incremental_output(model) and
is_stream and is_incremental_output is not None and is_incremental_output is False):
to_merge_incremental_output = True
kwargs['incremental_output'] = True
# Pass incremental_to_full flag via headers user-agent
if 'headers' not in kwargs:
kwargs['headers'] = {}
flag = '1' if to_merge_incremental_output else '0'
kwargs['headers']['user-agent'] = (
kwargs['headers'].get('user-agent', '') +
f'; incremental_to_full/{flag}'
)
response = await super().call(model=model,
task_group=task_group,
task=AioMultiModalConversation.task,
function=AioMultiModalConversation.function,
api_key=api_key,
input=input,
workspace=workspace,
**kwargs)
if is_stream:
if to_merge_incremental_output:
# Extract n parameter for merge logic
n = kwargs.get('n', 1)
return cls._merge_multimodal_response(response, n)
else:
return cls._stream_responses(response)
else:
return MultiModalConversationResponse.from_api_response(response)
@classmethod
def _preprocess_messages(cls, model: str, messages: List[dict],
api_key: str):
"""
messages = [
{
"role": "user",
"content": [
{"image": ""},
{"text": ""},
]
}
]
"""
has_upload = False
for message in messages:
content = message['content']
for elem in content:
if not isinstance(elem,
(int, float, bool, str, bytes, bytearray)):
is_upload = preprocess_message_element(
model, elem, api_key)
if is_upload and not has_upload:
has_upload = True
return has_upload
@classmethod
async def _stream_responses(cls, response) -> AsyncGenerator[MultiModalConversationResponse, None]:
"""Convert async response stream to MultiModalConversationResponse stream."""
# Type hint: when stream=True, response is actually an AsyncIterable
async for rsp in response: # type: ignore
yield MultiModalConversationResponse.from_api_response(rsp)
@classmethod
async def _merge_multimodal_response(cls, response, n=1) -> AsyncGenerator[MultiModalConversationResponse, None]:
"""Async version of merge incremental response chunks."""
accumulated_data = {}
async for rsp in response:
parsed_response = MultiModalConversationResponse.from_api_response(rsp)
result = merge_multimodal_single_response(parsed_response, accumulated_data, n)
if result is True:
yield parsed_response
elif isinstance(result, list):
# Multiple responses to yield (for n>1 non-stop cases)
for resp in result:
yield resp

View File

@@ -0,0 +1,573 @@
# Copyright (c) Alibaba, Inc. and its affiliates.
from typing import Any, Dict, Union
from dashscope.api_entities.dashscope_response import (DashScopeAPIResponse,
VideoSynthesisResponse)
from dashscope.client.base_api import BaseAsyncApi, BaseAsyncAioApi
from dashscope.common.constants import PROMPT
from dashscope.common.utils import _get_task_group_and_task
from dashscope.utils.oss_utils import check_and_upload_local
class VideoSynthesis(BaseAsyncApi):
task = 'video-generation'
"""API for video synthesis.
"""
class Models:
"""@deprecated, use wanx2.1-t2v-plus instead"""
wanx_txt2video_pro = 'wanx-txt2video-pro'
"""@deprecated, use wanx2.1-i2v-plus instead"""
wanx_img2video_pro = 'wanx-img2video-pro'
wanx_2_1_t2v_turbo = 'wanx2.1-t2v-turbo'
wanx_2_1_t2v_plus = 'wanx2.1-t2v-plus'
wanx_2_1_i2v_plus = 'wanx2.1-i2v-plus'
wanx_2_1_i2v_turbo = 'wanx2.1-i2v-turbo'
wanx_2_1_kf2v_plus = 'wanx2.1-kf2v-plus'
wanx_kf2v = 'wanx-kf2v'
@classmethod
def call(cls,
model: str,
prompt: Any = None,
# """@deprecated, use prompt_extend in parameters """
extend_prompt: bool = True,
negative_prompt: str = None,
template: str = None,
img_url: str = None,
audio_url: str = None,
api_key: str = None,
extra_input: Dict = None,
workspace: str = None,
task: str = None,
head_frame: str = None,
tail_frame: str = None,
first_frame_url: str = None,
last_frame_url: str = None,
**kwargs) -> VideoSynthesisResponse:
"""Call video synthesis service and get result.
Args:
model (str): The model, reference ``Models``.
prompt (Any): The prompt for video synthesis.
extend_prompt (bool): @deprecated, use prompt_extend in parameters
negative_prompt (str): The negative prompt is the opposite of the prompt meaning.
template (str): LoRa input, such as gufeng, katong, etc.
img_url (str): The input image url, Generate the URL of the image referenced by the video.
audio_url (str): The input audio url
api_key (str, optional): The api api_key. Defaults to None.
workspace (str): The dashscope workspace id.
extra_input (Dict): The extra input parameters.
task (str): The task of api, ref doc.
first_frame_url (str): The URL of the first frame image for generating the video.
last_frame_url (str): The URL of the last frame image for generating the video.
**kwargs:
size(str, `optional`): The output video size(width*height).
duration(int, optional): The duration. Duration of video generation. The default value is 5, in seconds.
seed(int, optional): The seed. The random seed for video generation. The default value is 5.
Raises:
InputRequired: The prompt cannot be empty.
Returns:
VideoSynthesisResponse: The video synthesis result.
"""
return super().call(model,
prompt,
img_url=img_url,
audio_url=audio_url,
api_key=api_key,
extend_prompt=extend_prompt,
negative_prompt=negative_prompt,
template=template,
workspace=workspace,
extra_input=extra_input,
task=task,
head_frame=head_frame,
tail_frame=tail_frame,
first_frame_url=first_frame_url,
last_frame_url=last_frame_url,
**kwargs)
@classmethod
def _get_input(cls,
model: str,
prompt: Any = None,
img_url: str = None,
audio_url: str = None,
# """@deprecated, use prompt_extend in parameters """
extend_prompt: bool = True,
negative_prompt: str = None,
template: str = None,
api_key: str = None,
extra_input: Dict = None,
task: str = None,
function: str = None,
head_frame: str = None,
tail_frame: str = None,
first_frame_url: str = None,
last_frame_url: str = None,
**kwargs):
inputs = {PROMPT: prompt, 'extend_prompt': extend_prompt}
if negative_prompt:
inputs['negative_prompt'] = negative_prompt
if template:
inputs['template'] = template
if function:
inputs['function'] = function
has_upload = False
if img_url is not None and img_url:
is_upload, res_img_url = check_and_upload_local(
model, img_url, api_key)
if is_upload:
has_upload = True
inputs['img_url'] = res_img_url
if audio_url is not None and audio_url:
is_upload, res_audio_url = check_and_upload_local(
model, audio_url, api_key)
if is_upload:
has_upload = True
inputs['audio_url'] = res_audio_url
if head_frame is not None and head_frame:
is_upload, res_head_frame = check_and_upload_local(
model, head_frame, api_key)
if is_upload:
has_upload = True
inputs['head_frame'] = res_head_frame
if tail_frame is not None and tail_frame:
is_upload, res_tail_frame = check_and_upload_local(
model, tail_frame, api_key)
if is_upload:
has_upload = True
inputs['tail_frame'] = res_tail_frame
if first_frame_url is not None and first_frame_url:
is_upload, res_first_frame_url = check_and_upload_local(
model, first_frame_url, api_key)
if is_upload:
has_upload = True
inputs['first_frame_url'] = res_first_frame_url
if last_frame_url is not None and last_frame_url:
is_upload, res_last_frame_url = check_and_upload_local(
model, last_frame_url, api_key)
if is_upload:
has_upload = True
inputs['last_frame_url'] = res_last_frame_url
if extra_input is not None and extra_input:
inputs = {**inputs, **extra_input}
if has_upload:
headers = kwargs.pop('headers', {})
headers['X-DashScope-OssResourceResolve'] = 'enable'
kwargs['headers'] = headers
if task is None:
task = VideoSynthesis.task
if model is not None and model and 'kf2v' in model:
task = 'image2video'
return inputs, kwargs, task
@classmethod
def async_call(cls,
model: str,
prompt: Any = None,
img_url: str = None,
audio_url: str = None,
# """@deprecated, use prompt_extend in parameters """
extend_prompt: bool = True,
negative_prompt: str = None,
template: str = None,
api_key: str = None,
extra_input: Dict = None,
workspace: str = None,
task: str = None,
head_frame: str = None,
tail_frame: str = None,
first_frame_url: str = None,
last_frame_url: str = None,
**kwargs) -> VideoSynthesisResponse:
"""Create a video synthesis task, and return task information.
Args:
model (str): The model, reference ``Models``.
prompt (Any): The prompt for video synthesis.
extend_prompt (bool): @deprecated, use prompt_extend in parameters
negative_prompt (str): The negative prompt is the opposite of the prompt meaning.
template (str): LoRa input, such as gufeng, katong, etc.
img_url (str): The input image url, Generate the URL of the image referenced by the video.
audio_url (str): The input audio url.
api_key (str, optional): The api api_key. Defaults to None.
workspace (str): The dashscope workspace id.
extra_input (Dict): The extra input parameters.
task (str): The task of api, ref doc.
first_frame_url (str): The URL of the first frame image for generating the video.
last_frame_url (str): The URL of the last frame image for generating the video.
**kwargs:
size(str, `optional`): The output video size(width*height).
duration(int, optional): The duration. Duration of video generation. The default value is 5, in seconds.
seed(int, optional): The seed. The random seed for video generation. The default value is 5.
Raises:
InputRequired: The prompt cannot be empty.
Returns:
DashScopeAPIResponse: The video synthesis
task id in the response.
"""
task_group, function = _get_task_group_and_task(__name__)
inputs, kwargs, task = cls._get_input(
model, prompt, img_url, audio_url, extend_prompt, negative_prompt, template, api_key,
extra_input, task, function, head_frame, tail_frame,
first_frame_url, last_frame_url, **kwargs)
response = super().async_call(
model=model,
task_group=task_group,
task=VideoSynthesis.task if task is None else task,
function=function,
api_key=api_key,
input=inputs,
workspace=workspace,
**kwargs)
return VideoSynthesisResponse.from_api_response(response)
@classmethod
def fetch(cls,
task: Union[str, VideoSynthesisResponse],
api_key: str = None,
workspace: str = None) -> VideoSynthesisResponse:
"""Fetch video synthesis task status or result.
Args:
task (Union[str, VideoSynthesisResponse]): The task_id or
VideoSynthesisResponse return by async_call().
api_key (str, optional): The api api_key. Defaults to None.
workspace (str): The dashscope workspace id.
Returns:
VideoSynthesisResponse: The task status or result.
"""
response = super().fetch(task, api_key=api_key, workspace=workspace)
return VideoSynthesisResponse.from_api_response(response)
@classmethod
def wait(cls,
task: Union[str, VideoSynthesisResponse],
api_key: str = None,
workspace: str = None) -> VideoSynthesisResponse:
"""Wait for video synthesis task to complete, and return the result.
Args:
task (Union[str, VideoSynthesisResponse]): The task_id or
VideoSynthesisResponse return by async_call().
api_key (str, optional): The api api_key. Defaults to None.
workspace (str): The dashscope workspace id.
Returns:
VideoSynthesisResponse: The task result.
"""
response = super().wait(task, api_key, workspace=workspace)
return VideoSynthesisResponse.from_api_response(response)
@classmethod
def cancel(cls,
task: Union[str, VideoSynthesisResponse],
api_key: str = None,
workspace: str = None) -> DashScopeAPIResponse:
"""Cancel video synthesis task.
Only tasks whose status is PENDING can be canceled.
Args:
task (Union[str, VideoSynthesisResponse]): The task_id or
VideoSynthesisResponse return by async_call().
api_key (str, optional): The api api_key. Defaults to None.
workspace (str): The dashscope workspace id.
Returns:
DashScopeAPIResponse: The response data.
"""
return super().cancel(task, api_key, workspace=workspace)
@classmethod
def list(cls,
start_time: str = None,
end_time: str = None,
model_name: str = None,
api_key_id: str = None,
region: str = None,
status: str = None,
page_no: int = 1,
page_size: int = 10,
api_key: str = None,
workspace: str = None,
**kwargs) -> DashScopeAPIResponse:
"""List async tasks.
Args:
start_time (str, optional): The tasks start time,
for example: 20230420000000. Defaults to None.
end_time (str, optional): The tasks end time,
for example: 20230420000000. Defaults to None.
model_name (str, optional): The tasks model name. Defaults to None.
api_key_id (str, optional): The tasks api-key-id. Defaults to None.
region (str, optional): The service region,
for example: cn-beijing. Defaults to None.
status (str, optional): The status of tasks[PENDING,
RUNNING, SUCCEEDED, FAILED, CANCELED]. Defaults to None.
page_no (int, optional): The page number. Defaults to 1.
page_size (int, optional): The page size. Defaults to 10.
api_key (str, optional): The user api-key. Defaults to None.
workspace (str): The dashscope workspace id.
Returns:
DashScopeAPIResponse: The response data.
"""
return super().list(start_time=start_time,
end_time=end_time,
model_name=model_name,
api_key_id=api_key_id,
region=region,
status=status,
page_no=page_no,
page_size=page_size,
api_key=api_key,
workspace=workspace,
**kwargs)
class AioVideoSynthesis(BaseAsyncAioApi):
@classmethod
async def call(cls,
model: str,
prompt: Any = None,
img_url: str = None,
audio_url: str = None,
# """@deprecated, use prompt_extend in parameters """
extend_prompt: bool = True,
negative_prompt: str = None,
template: str = None,
api_key: str = None,
extra_input: Dict = None,
workspace: str = None,
task: str = None,
head_frame: str = None,
tail_frame: str = None,
first_frame_url: str = None,
last_frame_url: str = None,
**kwargs) -> VideoSynthesisResponse:
"""Call video synthesis service and get result.
Args:
model (str): The model, reference ``Models``.
prompt (Any): The prompt for video synthesis.
extend_prompt (bool): @deprecated, use prompt_extend in parameters
negative_prompt (str): The negative prompt is the opposite of the prompt meaning.
template (str): LoRa input, such as gufeng, katong, etc.
img_url (str): The input image url, Generate the URL of the image referenced by the video.
audio_url (str): The input audio url.
api_key (str, optional): The api api_key. Defaults to None.
workspace (str): The dashscope workspace id.
extra_input (Dict): The extra input parameters.
task (str): The task of api, ref doc.
first_frame_url (str): The URL of the first frame image for generating the video.
last_frame_url (str): The URL of the last frame image for generating the video.
**kwargs:
size(str, `optional`): The output video size(width*height).
duration(int, optional): The duration. Duration of video generation. The default value is 5, in seconds.
seed(int, optional): The seed. The random seed for video generation. The default value is 5.
Raises:
InputRequired: The prompt cannot be empty.
Returns:
VideoSynthesisResponse: The video synthesis result.
"""
task_group, f = _get_task_group_and_task(__name__)
inputs, kwargs, task = VideoSynthesis._get_input(
model, prompt, img_url, audio_url, extend_prompt, negative_prompt, template, api_key,
extra_input, task, f, head_frame, tail_frame,
first_frame_url, last_frame_url, **kwargs)
response = await super().call(model, inputs, task_group, task, f, api_key, workspace, **kwargs)
return VideoSynthesisResponse.from_api_response(response)
@classmethod
async def async_call(cls,
model: str,
prompt: Any = None,
img_url: str = None,
audio_url: str = None,
# """@deprecated, use prompt_extend in parameters """
extend_prompt: bool = True,
negative_prompt: str = None,
template: str = None,
api_key: str = None,
extra_input: Dict = None,
workspace: str = None,
task: str = None,
head_frame: str = None,
tail_frame: str = None,
first_frame_url: str = None,
last_frame_url: str = None,
**kwargs) -> VideoSynthesisResponse:
"""Create a video synthesis task, and return task information.
Args:
model (str): The model, reference ``Models``.
prompt (Any): The prompt for video synthesis.
extend_prompt (bool): @deprecated, use prompt_extend in parameters
negative_prompt (str): The negative prompt is the opposite of the prompt meaning.
template (str): LoRa input, such as gufeng, katong, etc.
img_url (str): The input image url, Generate the URL of the image referenced by the video.
audio_url (str): The input audio url.
api_key (str, optional): The api api_key. Defaults to None.
workspace (str): The dashscope workspace id.
extra_input (Dict): The extra input parameters.
task (str): The task of api, ref doc.
first_frame_url (str): The URL of the first frame image for generating the video.
last_frame_url (str): The URL of the last frame image for generating the video.
**kwargs:
size(str, `optional`): The output video size(width*height).
duration(int, optional): The duration. Duration of video generation. The default value is 5, in seconds.
seed(int, optional): The seed. The random seed for video generation. The default value is 5.
Raises:
InputRequired: The prompt cannot be empty.
Returns:
DashScopeAPIResponse: The video synthesis
task id in the response.
"""
task_group, function = _get_task_group_and_task(__name__)
inputs, kwargs, task = VideoSynthesis._get_input(
model, prompt, img_url, audio_url, extend_prompt, negative_prompt, template, api_key,
extra_input, task, function, head_frame, tail_frame,
first_frame_url, last_frame_url, **kwargs)
response = await super().async_call(
model=model,
task_group=task_group,
task=VideoSynthesis.task if task is None else task,
function=function,
api_key=api_key,
input=inputs,
workspace=workspace,
**kwargs)
return VideoSynthesisResponse.from_api_response(response)
@classmethod
async def fetch(cls,
task: Union[str, VideoSynthesisResponse],
api_key: str = None,
workspace: str = None,
**kwargs) -> VideoSynthesisResponse:
"""Fetch video synthesis task status or result.
Args:
task (Union[str, VideoSynthesisResponse]): The task_id or
VideoSynthesisResponse return by async_call().
api_key (str, optional): The api api_key. Defaults to None.
workspace (str): The dashscope workspace id.
Returns:
VideoSynthesisResponse: The task status or result.
"""
response = await super().fetch(task, api_key=api_key, workspace=workspace)
return VideoSynthesisResponse.from_api_response(response)
@classmethod
async def wait(cls,
task: Union[str, VideoSynthesisResponse],
api_key: str = None,
workspace: str = None,
**kwargs) -> VideoSynthesisResponse:
"""Wait for video synthesis task to complete, and return the result.
Args:
task (Union[str, VideoSynthesisResponse]): The task_id or
VideoSynthesisResponse return by async_call().
api_key (str, optional): The api api_key. Defaults to None.
workspace (str): The dashscope workspace id.
Returns:
VideoSynthesisResponse: The task result.
"""
response = await super().wait(task, api_key, workspace=workspace)
return VideoSynthesisResponse.from_api_response(response)
@classmethod
async def cancel(cls,
task: Union[str, VideoSynthesisResponse],
api_key: str = None,
workspace: str = None,
**kwargs) -> DashScopeAPIResponse:
"""Cancel video synthesis task.
Only tasks whose status is PENDING can be canceled.
Args:
task (Union[str, VideoSynthesisResponse]): The task_id or
VideoSynthesisResponse return by async_call().
api_key (str, optional): The api api_key. Defaults to None.
workspace (str): The dashscope workspace id.
Returns:
DashScopeAPIResponse: The response data.
"""
return await super().cancel(task, api_key, workspace=workspace)
@classmethod
async def list(cls,
start_time: str = None,
end_time: str = None,
model_name: str = None,
api_key_id: str = None,
region: str = None,
status: str = None,
page_no: int = 1,
page_size: int = 10,
api_key: str = None,
workspace: str = None,
**kwargs) -> DashScopeAPIResponse:
"""List async tasks.
Args:
start_time (str, optional): The tasks start time,
for example: 20230420000000. Defaults to None.
end_time (str, optional): The tasks end time,
for example: 20230420000000. Defaults to None.
model_name (str, optional): The tasks model name. Defaults to None.
api_key_id (str, optional): The tasks api-key-id. Defaults to None.
region (str, optional): The service region,
for example: cn-beijing. Defaults to None.
status (str, optional): The status of tasks[PENDING,
RUNNING, SUCCEEDED, FAILED, CANCELED]. Defaults to None.
page_no (int, optional): The page number. Defaults to 1.
page_size (int, optional): The page size. Defaults to 10.
api_key (str, optional): The user api-key. Defaults to None.
workspace (str): The dashscope workspace id.
Returns:
DashScopeAPIResponse: The response data.
"""
return await super().list(start_time=start_time,
end_time=end_time,
model_name=model_name,
api_key_id=api_key_id,
region=region,
status=status,
page_no=page_no,
page_size=page_size,
api_key=api_key,
workspace=workspace,
**kwargs)