mirror of
https://github.com/1Panel-dev/MaxKB.git
synced 2025-12-25 17:22:55 +00:00
feat: Create model and configure advanced parameters
This commit is contained in:
parent
8b33c99235
commit
4d977fd765
|
|
@ -21,6 +21,8 @@ class ImageGenerateNodeSerializer(serializers.Serializer):
|
|||
|
||||
is_result = serializers.BooleanField(required=False, error_messages=ErrMessage.boolean('是否返回内容'))
|
||||
|
||||
model_params_setting = serializers.JSONField(required=False, default=dict, error_messages=ErrMessage.json("模型参数设置"))
|
||||
|
||||
|
||||
class IImageGenerateNode(INode):
|
||||
type = 'image-generate-node'
|
||||
|
|
@ -32,6 +34,7 @@ class IImageGenerateNode(INode):
|
|||
return self.execute(**self.node_params_serializer.data, **self.flow_params_serializer.data)
|
||||
|
||||
def execute(self, model_id, prompt, negative_prompt, dialogue_number, dialogue_type, history_chat_record, chat_id,
|
||||
model_params_setting,
|
||||
chat_record_id,
|
||||
**kwargs) -> NodeResult:
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -2,10 +2,13 @@
|
|||
from functools import reduce
|
||||
from typing import List
|
||||
|
||||
import requests
|
||||
from langchain_core.messages import BaseMessage, HumanMessage, AIMessage
|
||||
|
||||
from application.flow.i_step_node import NodeResult
|
||||
from application.flow.step_node.image_generate_step_node.i_image_generate_node import IImageGenerateNode
|
||||
from common.util.common import bytes_to_uploaded_file
|
||||
from dataset.serializers.file_serializers import FileSerializer
|
||||
from setting.models_provider.tools import get_model_instance_by_model_user_id
|
||||
|
||||
|
||||
|
|
@ -16,10 +19,12 @@ class BaseImageGenerateNode(IImageGenerateNode):
|
|||
self.answer_text = details.get('answer')
|
||||
|
||||
def execute(self, model_id, prompt, negative_prompt, dialogue_number, dialogue_type, history_chat_record, chat_id,
|
||||
model_params_setting,
|
||||
chat_record_id,
|
||||
**kwargs) -> NodeResult:
|
||||
|
||||
tti_model = get_model_instance_by_model_user_id(model_id, self.flow_params_serializer.data.get('user_id'))
|
||||
print(model_params_setting)
|
||||
application = self.workflow_manage.work_flow_post_handler.chat_info.application
|
||||
tti_model = get_model_instance_by_model_user_id(model_id, self.flow_params_serializer.data.get('user_id'), **model_params_setting)
|
||||
history_message = self.get_history_message(history_chat_record, dialogue_number)
|
||||
self.context['history_message'] = history_message
|
||||
question = self.generate_prompt_question(prompt)
|
||||
|
|
@ -28,10 +33,21 @@ class BaseImageGenerateNode(IImageGenerateNode):
|
|||
self.context['message_list'] = message_list
|
||||
self.context['dialogue_type'] = dialogue_type
|
||||
print(message_list)
|
||||
print(negative_prompt)
|
||||
image_urls = tti_model.generate_image(question, negative_prompt)
|
||||
self.context['image_list'] = image_urls
|
||||
answer = '\n'.join([f"" for path in image_urls])
|
||||
# 保存图片
|
||||
file_urls = []
|
||||
for image_url in image_urls:
|
||||
file_name = 'generated_image.png'
|
||||
file = bytes_to_uploaded_file(requests.get(image_url).content, file_name)
|
||||
meta = {
|
||||
'debug': False if application.id else True,
|
||||
'chat_id': chat_id,
|
||||
'application_id': str(application.id) if application.id else None,
|
||||
}
|
||||
file_url = FileSerializer(data={'file': file, 'meta': meta}).upload()
|
||||
file_urls.append(file_url)
|
||||
self.context['image_list'] = file_urls
|
||||
answer = '\n'.join([f"" for path in file_urls])
|
||||
return NodeResult({'answer': answer, 'chat_model': tti_model, 'message_list': message_list,
|
||||
'image': [{'file_id': path.split('/')[-1], 'file_url': path} for path in file_urls],
|
||||
'history_message': history_message, 'question': question}, {})
|
||||
|
|
|
|||
|
|
@ -10,14 +10,32 @@ from common.exception.app_exception import AppApiException
|
|||
from common.forms import BaseForm, TooltipLabel
|
||||
from setting.models_provider.base_model_provider import BaseModelCredential, ValidCode
|
||||
|
||||
class OpenAITTIModelParams(BaseForm):
|
||||
size = forms.TextInputField(
|
||||
TooltipLabel('图片尺寸', '指定生成图片的尺寸, 如: 1024x1024'),
|
||||
required=True, default_value='1024x1024')
|
||||
|
||||
quality = forms.TextInputField(
|
||||
class OpenAITTIModelParams(BaseForm):
|
||||
size = forms.SingleSelect(
|
||||
TooltipLabel('图片尺寸', '指定生成图片的尺寸, 如: 1024x1024'),
|
||||
required=True,
|
||||
default_value='1024x1024',
|
||||
option_list=[
|
||||
{'value': '1024x1024', 'label': '1024x1024'},
|
||||
{'value': '1024x1792', 'label': '1024x1792'},
|
||||
{'value': '1792x1024', 'label': '1792x1024'},
|
||||
],
|
||||
text_field='label',
|
||||
value_field='value'
|
||||
)
|
||||
|
||||
quality = forms.SingleSelect(
|
||||
TooltipLabel('图片质量', ''),
|
||||
required=True, default_value='standard')
|
||||
required=True,
|
||||
default_value='standard',
|
||||
option_list=[
|
||||
{'value': 'standard', 'label': 'standard'},
|
||||
{'value': 'hd', 'label': 'hd'},
|
||||
],
|
||||
text_field='label',
|
||||
value_field='value'
|
||||
)
|
||||
|
||||
n = forms.SliderField(
|
||||
TooltipLabel('图片数量', '指定生成图片的数量'),
|
||||
|
|
|
|||
|
|
@ -1,13 +1,8 @@
|
|||
from typing import Dict
|
||||
|
||||
import requests
|
||||
from langchain_core.messages import HumanMessage
|
||||
from langchain_openai import ChatOpenAI
|
||||
from openai import OpenAI
|
||||
|
||||
from common.config.tokenizer_manage_config import TokenizerManage
|
||||
from common.util.common import bytes_to_uploaded_file
|
||||
from dataset.serializers.file_serializers import FileSerializer
|
||||
from setting.models_provider.base_model_provider import MaxKBBaseModel
|
||||
from setting.models_provider.impl.base_tti import BaseTextToImage
|
||||
|
||||
|
|
@ -32,7 +27,7 @@ class OpenAITextToImage(MaxKBBaseModel, BaseTextToImage):
|
|||
|
||||
@staticmethod
|
||||
def new_instance(model_type, model_name, model_credential: Dict[str, object], **model_kwargs):
|
||||
optional_params = {'params': {}}
|
||||
optional_params = {'params': {'size': '1024x1024', 'quality': 'standard', 'n': 1}}
|
||||
for key, value in model_kwargs.items():
|
||||
if key not in ['model_id', 'use_local', 'streaming']:
|
||||
optional_params['params'][key] = value
|
||||
|
|
@ -43,6 +38,9 @@ class OpenAITextToImage(MaxKBBaseModel, BaseTextToImage):
|
|||
**optional_params,
|
||||
)
|
||||
|
||||
def is_cache_model(self):
|
||||
return False
|
||||
|
||||
def check_auth(self):
|
||||
chat = OpenAI(api_key=self.api_key, base_url=self.api_base)
|
||||
response_list = chat.models.with_raw_response.list()
|
||||
|
|
@ -50,18 +48,11 @@ class OpenAITextToImage(MaxKBBaseModel, BaseTextToImage):
|
|||
# self.generate_image('生成一个小猫图片')
|
||||
|
||||
def generate_image(self, prompt: str, negative_prompt: str = None):
|
||||
|
||||
chat = OpenAI(api_key=self.api_key, base_url=self.api_base)
|
||||
res = chat.images.generate(model=self.model, prompt=prompt, **self.params)
|
||||
|
||||
file_urls = []
|
||||
for content in res.data:
|
||||
url = content.url
|
||||
print(url)
|
||||
file_name = 'generated_image.png'
|
||||
file = bytes_to_uploaded_file(requests.get(url).content, file_name)
|
||||
meta = {'debug': True}
|
||||
file_url = FileSerializer(data={'file': file, 'meta': meta}).upload()
|
||||
file_urls.append(file_url)
|
||||
file_urls.append(url)
|
||||
|
||||
return file_urls
|
||||
|
|
|
|||
|
|
@ -19,9 +19,18 @@ from setting.models_provider.base_model_provider import BaseModelCredential, Val
|
|||
|
||||
|
||||
class QwenModelParams(BaseForm):
|
||||
size = forms.TextInputField(
|
||||
size = forms.SingleSelect(
|
||||
TooltipLabel('图片尺寸', '指定生成图片的尺寸, 如: 1024x1024'),
|
||||
required=True, default_value='1024x1024')
|
||||
required=True,
|
||||
default_value='1024*1024',
|
||||
option_list=[
|
||||
{'value': '1024*1024', 'label': '1024*1024'},
|
||||
{'value': '720*1280', 'label': '720*1280'},
|
||||
{'value': '768*1152', 'label': '768*1152'},
|
||||
{'value': '1280*720', 'label': '1280*720'},
|
||||
],
|
||||
text_field='label',
|
||||
value_field='value')
|
||||
n = forms.SliderField(
|
||||
TooltipLabel('图片数量', '指定生成图片的数量'),
|
||||
required=True, default_value=1,
|
||||
|
|
@ -29,9 +38,25 @@ class QwenModelParams(BaseForm):
|
|||
_max=4,
|
||||
_step=1,
|
||||
precision=0)
|
||||
style = forms.TextInputField(
|
||||
style = forms.SingleSelect(
|
||||
TooltipLabel('风格', '指定生成图片的风格'),
|
||||
required=True, default_value='<auto>')
|
||||
required=True,
|
||||
default_value='<auto>',
|
||||
option_list=[
|
||||
{'value': '<auto>', 'label': '默认值,由模型随机输出图像风格'},
|
||||
{'value': '<photography>', 'label': '摄影'},
|
||||
{'value': '<portrait>', 'label': '人像写真'},
|
||||
{'value': '<3d cartoon>', 'label': '3D卡通'},
|
||||
{'value': '<anime>', 'label': '动画'},
|
||||
{'value': '<oil painting>', 'label': '油画'},
|
||||
{'value': '<watercolor>', 'label': '水彩'},
|
||||
{'value': '<sketch>', 'label': '素描'},
|
||||
{'value': '<chinese painting>', 'label': '中国画'},
|
||||
{'value': '<flat illustration>', 'label': '扁平插画'},
|
||||
],
|
||||
text_field='label',
|
||||
value_field='value'
|
||||
)
|
||||
|
||||
|
||||
class QwenTextToImageModelCredential(BaseForm, BaseModelCredential):
|
||||
|
|
|
|||
|
|
@ -1,16 +1,11 @@
|
|||
# coding=utf-8
|
||||
from http import HTTPStatus
|
||||
from pathlib import PurePosixPath
|
||||
from typing import Dict
|
||||
from urllib.parse import unquote, urlparse
|
||||
|
||||
import requests
|
||||
from dashscope import ImageSynthesis
|
||||
from langchain_community.chat_models import ChatTongyi
|
||||
from langchain_core.messages import HumanMessage
|
||||
|
||||
from common.util.common import bytes_to_uploaded_file
|
||||
from dataset.serializers.file_serializers import FileSerializer
|
||||
from setting.models_provider.base_model_provider import MaxKBBaseModel
|
||||
from setting.models_provider.impl.base_tti import BaseTextToImage
|
||||
|
||||
|
|
@ -28,7 +23,7 @@ class QwenTextToImageModel(MaxKBBaseModel, BaseTextToImage):
|
|||
|
||||
@staticmethod
|
||||
def new_instance(model_type, model_name, model_credential: Dict[str, object], **model_kwargs):
|
||||
optional_params = {'params': {}}
|
||||
optional_params = {'params': {'size': '1024*1024', 'style': '<auto>', 'n': 1}}
|
||||
for key, value in model_kwargs.items():
|
||||
if key not in ['model_id', 'use_local', 'streaming']:
|
||||
optional_params['params'][key] = value
|
||||
|
|
@ -39,6 +34,9 @@ class QwenTextToImageModel(MaxKBBaseModel, BaseTextToImage):
|
|||
)
|
||||
return chat_tong_yi
|
||||
|
||||
def is_cache_model(self):
|
||||
return False
|
||||
|
||||
def check_auth(self):
|
||||
chat = ChatTongyi(api_key=self.api_key, model_name='qwen-max')
|
||||
chat.invoke([HumanMessage([{"type": "text", "text": "你好"}])])
|
||||
|
|
@ -53,11 +51,7 @@ class QwenTextToImageModel(MaxKBBaseModel, BaseTextToImage):
|
|||
file_urls = []
|
||||
if rsp.status_code == HTTPStatus.OK:
|
||||
for result in rsp.output.results:
|
||||
file_name = PurePosixPath(unquote(urlparse(result.url).path)).parts[-1]
|
||||
file = bytes_to_uploaded_file(requests.get(result.url).content, file_name)
|
||||
meta = {'debug': True}
|
||||
file_url = FileSerializer(data={'file': file, 'meta': meta}).upload()
|
||||
file_urls.append(file_url)
|
||||
file_urls.append(result.url)
|
||||
else:
|
||||
print('sync_call Failed, status_code: %s, code: %s, message: %s' %
|
||||
(rsp.status_code, rsp.code, rsp.message))
|
||||
|
|
|
|||
|
|
@ -3,15 +3,12 @@
|
|||
import json
|
||||
from typing import Dict
|
||||
|
||||
import requests
|
||||
from tencentcloud.common import credential
|
||||
from tencentcloud.common.exception.tencent_cloud_sdk_exception import TencentCloudSDKException
|
||||
from tencentcloud.common.profile.client_profile import ClientProfile
|
||||
from tencentcloud.common.profile.http_profile import HttpProfile
|
||||
from tencentcloud.hunyuan.v20230901 import hunyuan_client, models
|
||||
|
||||
from common.util.common import bytes_to_uploaded_file
|
||||
from dataset.serializers.file_serializers import FileSerializer
|
||||
from setting.models_provider.base_model_provider import MaxKBBaseModel
|
||||
from setting.models_provider.impl.base_tti import BaseTextToImage
|
||||
from setting.models_provider.impl.tencent_model_provider.model.hunyuan import ChatHunyuan
|
||||
|
|
@ -87,12 +84,8 @@ class TencentTextToImageModel(MaxKBBaseModel, BaseTextToImage):
|
|||
# 输出json格式的字符串回包
|
||||
print(resp.to_json_string())
|
||||
file_urls = []
|
||||
file_name = 'generated_image.png'
|
||||
file = bytes_to_uploaded_file(requests.get(resp.ResultImage).content, file_name)
|
||||
meta = {'debug': True}
|
||||
file_url = FileSerializer(data={'file': file, 'meta': meta}).upload()
|
||||
file_urls.append(file_url)
|
||||
|
||||
file_urls.append(resp.ResultImage)
|
||||
return file_urls
|
||||
except TencentCloudSDKException as err:
|
||||
print(err)
|
||||
|
||||
|
|
|
|||
|
|
@ -8,10 +8,22 @@ from setting.models_provider.base_model_provider import BaseModelCredential, Val
|
|||
|
||||
|
||||
class ZhiPuTTIModelParams(BaseForm):
|
||||
size = forms.TextInputField(
|
||||
size = forms.SingleSelect(
|
||||
TooltipLabel('图片尺寸',
|
||||
'图片尺寸,仅 cogview-3-plus 支持该参数。可选范围:[1024x1024,768x1344,864x1152,1344x768,1152x864,1440x720,720x1440],默认是1024x1024。'),
|
||||
required=True, default_value='1024x1024')
|
||||
required=True,
|
||||
default_value='1024x1024',
|
||||
option_list=[
|
||||
{'value': '1024x1024', 'label': '1024x1024'},
|
||||
{'value': '768x1344', 'label': '768x1344'},
|
||||
{'value': '864x1152', 'label': '864x1152'},
|
||||
{'value': '1344x768', 'label': '1344x768'},
|
||||
{'value': '1152x864', 'label': '1152x864'},
|
||||
{'value': '1440x720', 'label': '1440x720'},
|
||||
{'value': '720x1440', 'label': '720x1440'},
|
||||
],
|
||||
text_field='label',
|
||||
value_field='value')
|
||||
|
||||
|
||||
class ZhiPuTextToImageModelCredential(BaseForm, BaseModelCredential):
|
||||
|
|
|
|||
|
|
@ -1,13 +1,10 @@
|
|||
from typing import Dict
|
||||
|
||||
import requests
|
||||
from langchain_community.chat_models import ChatZhipuAI
|
||||
from langchain_core.messages import HumanMessage
|
||||
from zhipuai import ZhipuAI
|
||||
|
||||
from common.config.tokenizer_manage_config import TokenizerManage
|
||||
from common.util.common import bytes_to_uploaded_file
|
||||
from dataset.serializers.file_serializers import FileSerializer
|
||||
from setting.models_provider.base_model_provider import MaxKBBaseModel
|
||||
from setting.models_provider.impl.base_tti import BaseTextToImage
|
||||
|
||||
|
|
@ -30,7 +27,7 @@ class ZhiPuTextToImage(MaxKBBaseModel, BaseTextToImage):
|
|||
|
||||
@staticmethod
|
||||
def new_instance(model_type, model_name, model_credential: Dict[str, object], **model_kwargs):
|
||||
optional_params = {'params': {}}
|
||||
optional_params = {'params': {'size': '1024x1024'}}
|
||||
for key, value in model_kwargs.items():
|
||||
if key not in ['model_id', 'use_local', 'streaming']:
|
||||
optional_params['params'][key] = value
|
||||
|
|
@ -40,6 +37,9 @@ class ZhiPuTextToImage(MaxKBBaseModel, BaseTextToImage):
|
|||
**optional_params,
|
||||
)
|
||||
|
||||
def is_cache_model(self):
|
||||
return False
|
||||
|
||||
def check_auth(self):
|
||||
chat = ChatZhipuAI(
|
||||
zhipuai_api_key=self.api_key,
|
||||
|
|
@ -58,16 +58,11 @@ class ZhiPuTextToImage(MaxKBBaseModel, BaseTextToImage):
|
|||
response = chat.images.generations(
|
||||
model=self.model, # 填写需要调用的模型编码
|
||||
prompt=prompt, # 填写需要生成图片的文本
|
||||
**self.params # 填写额外参数
|
||||
**self.params # 填写额外参数
|
||||
)
|
||||
file_urls = []
|
||||
for content in response.data:
|
||||
url = content['url']
|
||||
print(url)
|
||||
file_name = url.split('/')[-1]
|
||||
file = bytes_to_uploaded_file(requests.get(url).content, file_name)
|
||||
meta = {'debug': True}
|
||||
file_url = FileSerializer(data={'file': file, 'meta': meta}).upload()
|
||||
file_urls.append(file_url)
|
||||
url = content.url
|
||||
file_urls.append(url)
|
||||
|
||||
return file_urls
|
||||
|
|
|
|||
|
|
@ -30,8 +30,10 @@ from setting.models_provider.constants.model_provider_constants import ModelProv
|
|||
|
||||
def get_default_model_params_setting(provider, model_type, model_name):
|
||||
credential = get_model_credential(provider, model_type, model_name)
|
||||
model_params_setting = credential.get_model_params_setting_form(model_name).to_form_list()
|
||||
return model_params_setting
|
||||
setting_form = credential.get_model_params_setting_form(model_name)
|
||||
if setting_form is not None:
|
||||
return setting_form.to_form_list()
|
||||
return []
|
||||
|
||||
|
||||
class ModelPullManage:
|
||||
|
|
@ -178,6 +180,8 @@ class ModelSerializer(serializers.Serializer):
|
|||
|
||||
model_name = serializers.CharField(required=True, error_messages=ErrMessage.char("基础模型"))
|
||||
|
||||
model_params_form = serializers.ListField(required=False, default=list, error_messages=ErrMessage.char("参数配置"))
|
||||
|
||||
credential = serializers.DictField(required=True, error_messages=ErrMessage.dict("认证信息"))
|
||||
|
||||
def is_valid(self, *, raise_exception=False):
|
||||
|
|
@ -207,11 +211,12 @@ class ModelSerializer(serializers.Serializer):
|
|||
model_type = self.data.get('model_type')
|
||||
model_name = self.data.get('model_name')
|
||||
permission_type = self.data.get('permission_type')
|
||||
model_params_form = self.data.get('model_params_form')
|
||||
model_credential_str = json.dumps(credential)
|
||||
model = Model(id=uuid.uuid1(), status=status, user_id=user_id, name=name,
|
||||
credential=rsa_long_encrypt(model_credential_str),
|
||||
provider=provider, model_type=model_type, model_name=model_name,
|
||||
model_params_form=get_default_model_params_setting(provider, model_type, model_name),
|
||||
model_params_form=model_params_form,
|
||||
permission_type=permission_type)
|
||||
model.save()
|
||||
if status == Status.DOWNLOAD:
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ urlpatterns = [
|
|||
path('provider/model_type_list', views.Provide.ModelTypeList.as_view(), name="provider/model_type_list"),
|
||||
path('provider/model_list', views.Provide.ModelList.as_view(),
|
||||
name="provider/model_name_list"),
|
||||
path('provider/model_params_form', views.Provide.ModelParamsForm.as_view(),
|
||||
name="provider/model_params_form"),
|
||||
path('provider/model_form', views.Provide.ModelForm.as_view(),
|
||||
name="provider/model_form"),
|
||||
path('model', views.Model.as_view(), name='model'),
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ from common.constants.permission_constants import PermissionConstants
|
|||
from common.response import result
|
||||
from common.util.common import query_params_to_single_dict
|
||||
from setting.models_provider.constants.model_provider_constants import ModelProvideConstants
|
||||
from setting.serializers.provider_serializers import ProviderSerializer, ModelSerializer
|
||||
from setting.serializers.provider_serializers import ProviderSerializer, ModelSerializer, get_default_model_params_setting
|
||||
from setting.swagger_api.provide_api import ProvideApi, ModelCreateApi, ModelQueryApi, ModelEditApi
|
||||
|
||||
|
||||
|
|
@ -207,6 +207,24 @@ class Provide(APIView):
|
|||
ModelProvideConstants[provider].value.get_model_list(
|
||||
model_type))
|
||||
|
||||
class ModelParamsForm(APIView):
|
||||
authentication_classes = [TokenAuth]
|
||||
|
||||
@action(methods=['GET'], detail=False)
|
||||
@swagger_auto_schema(operation_summary="获取模型默认参数",
|
||||
operation_id="获取模型创建表单",
|
||||
manual_parameters=ProvideApi.ModelList.get_request_params_api(),
|
||||
responses=result.get_api_array_response(ProvideApi.ModelList.get_response_body_api())
|
||||
, tags=["模型"]
|
||||
)
|
||||
@has_permissions(PermissionConstants.MODEL_READ)
|
||||
def get(self, request: Request):
|
||||
provider = request.query_params.get('provider')
|
||||
model_type = request.query_params.get('model_type')
|
||||
model_name = request.query_params.get('model_name')
|
||||
|
||||
return result.success(get_default_model_params_setting(provider, model_type, model_name))
|
||||
|
||||
class ModelForm(APIView):
|
||||
authentication_classes = [TokenAuth]
|
||||
|
||||
|
|
|
|||
|
|
@ -98,6 +98,15 @@ const listBaseModel: (
|
|||
return get(`${prefix_provider}/model_list`, { provider, model_type }, loading)
|
||||
}
|
||||
|
||||
const listBaseModelParamsForm: (
|
||||
provider: string,
|
||||
model_type: string,
|
||||
model_name: string,
|
||||
loading?: Ref<boolean>
|
||||
) => Promise<Result<Array<BaseModel>>> = (provider, model_type, model_name, loading) => {
|
||||
return get(`${prefix_provider}/model_params_form`, { provider, model_type, model_name}, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建模型
|
||||
* @param request 请求对象
|
||||
|
|
@ -187,6 +196,7 @@ export default {
|
|||
getModelCreateForm,
|
||||
listModelType,
|
||||
listBaseModel,
|
||||
listBaseModelParamsForm,
|
||||
createModel,
|
||||
updateModel,
|
||||
deleteModel,
|
||||
|
|
|
|||
|
|
@ -22,136 +22,192 @@
|
|||
>
|
||||
</el-breadcrumb>
|
||||
</template>
|
||||
|
||||
<DynamicsForm
|
||||
v-model="form_data"
|
||||
:render_data="model_form_field"
|
||||
:model="form_data"
|
||||
ref="dynamicsFormRef"
|
||||
label-position="top"
|
||||
require-asterisk-position="right"
|
||||
class="mb-24"
|
||||
label-width="auto"
|
||||
>
|
||||
<template #default>
|
||||
<el-form-item prop="name" :rules="base_form_data_rule.name">
|
||||
<template #label>
|
||||
<div class="flex align-center" style="display: inline-flex">
|
||||
<div class="mr-4">
|
||||
<span>模型名称 </span>
|
||||
</div>
|
||||
<el-tooltip effect="dark" placement="right">
|
||||
<template #content>
|
||||
<p>MaxKB 中自定义的模型名称</p>
|
||||
</template>
|
||||
<AppIcon iconName="app-warning" class="app-warning-icon"></AppIcon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
<el-input
|
||||
v-model="base_form_data.name"
|
||||
maxlength="64"
|
||||
show-word-limit
|
||||
placeholder="请给基础模型设置一个名称"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item prop="permission_type" :rules="base_form_data_rule.permission_type">
|
||||
<template #label>
|
||||
<span>权限</span>
|
||||
</template>
|
||||
<el-radio-group v-model="base_form_data.permission_type" class="card__radio">
|
||||
<el-row :gutter="16">
|
||||
<template v-for="(value, key) of PermissionType" :key="key">
|
||||
<el-col :span="12">
|
||||
<el-card
|
||||
shadow="never"
|
||||
class="mb-16"
|
||||
:class="base_form_data.permission_type === key ? 'active' : ''"
|
||||
>
|
||||
<el-radio :value="key" size="large">
|
||||
<p class="mb-4">{{ value }}</p>
|
||||
<el-text type="info">
|
||||
{{ PermissionDesc[key] }}
|
||||
</el-text>
|
||||
</el-radio>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item prop="model_type" :rules="base_form_data_rule.model_type">
|
||||
<template #label>
|
||||
<div class="flex align-center" style="display: inline-flex">
|
||||
<span class="mr-4">模型类型 </span>
|
||||
<el-tooltip effect="dark" placement="right">
|
||||
<template #content>
|
||||
<p>大语言模型:在应用中与AI对话的推理模型。</p>
|
||||
<p>向量模型:在知识库中导入文档进行向量化和向量检索召回分段时使用的向量模型。</p>
|
||||
<p>
|
||||
重排模型:在二次召回中根据召回的候选分段和用户问题的匹配度重新排序,从而得到更精确的结果。
|
||||
</p>
|
||||
<p>语音识别:在应用中开启语音识别后用于语音转文字的模型。</p>
|
||||
<p>语音合成:在应用中开启语音播放后用于文字转语音的模型。</p>
|
||||
</template>
|
||||
<AppIcon iconName="app-warning" class="app-warning-icon"></AppIcon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
<el-select
|
||||
v-loading="model_type_loading"
|
||||
@change="list_base_model($event, true)"
|
||||
v-model="base_form_data.model_type"
|
||||
class="w-full m-2"
|
||||
placeholder="请选择模型类型"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in model_type_list"
|
||||
:key="item.value"
|
||||
:label="item.key"
|
||||
:value="item.value"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item prop="model_name" :rules="base_form_data_rule.model_name">
|
||||
<template #label>
|
||||
<div class="flex align-center" style="display: inline-flex">
|
||||
<div class="mr-4">
|
||||
<span>基础模型 </span>
|
||||
<span class="danger">列表中未列出的模型,直接输入模型名称,回车即可添加</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-select
|
||||
@change="getModelForm($event)"
|
||||
v-loading="base_model_loading"
|
||||
v-model="base_form_data.model_name"
|
||||
class="w-full m-2"
|
||||
placeholder="自定义输入基础模型后回车即可"
|
||||
filterable
|
||||
allow-create
|
||||
default-first-option
|
||||
>
|
||||
<el-option v-for="item in base_model_list" :key="item.name" :value="item.name">
|
||||
<template #default>
|
||||
<el-tabs v-model="activeName">
|
||||
<el-tab-pane label="基础信息" name="base-info">
|
||||
<DynamicsForm
|
||||
v-model="form_data"
|
||||
:render_data="model_form_field"
|
||||
:model="form_data"
|
||||
ref="dynamicsFormRef"
|
||||
label-position="top"
|
||||
require-asterisk-position="right"
|
||||
class="mb-24"
|
||||
label-width="auto"
|
||||
>
|
||||
<template #default>
|
||||
<el-form-item prop="name" :rules="base_form_data_rule.name">
|
||||
<template #label>
|
||||
<div class="flex align-center" style="display: inline-flex">
|
||||
<div class="flex-between mr-4">
|
||||
<span>{{ item.name }} </span>
|
||||
<div class="mr-4">
|
||||
<span>模型名称 </span>
|
||||
</div>
|
||||
<el-tooltip effect="dark" placement="right" v-if="item.desc">
|
||||
<el-tooltip effect="dark" placement="right">
|
||||
<template #content>
|
||||
<p class="w-280">{{ item.desc }}</p>
|
||||
<p>MaxKB 中自定义的模型名称</p>
|
||||
</template>
|
||||
<AppIcon iconName="app-warning" class="app-warning-icon"></AppIcon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</template>
|
||||
</DynamicsForm>
|
||||
<el-input
|
||||
v-model="base_form_data.name"
|
||||
maxlength="64"
|
||||
show-word-limit
|
||||
placeholder="请给基础模型设置一个名称"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item prop="permission_type" :rules="base_form_data_rule.permission_type">
|
||||
<template #label>
|
||||
<span>权限</span>
|
||||
</template>
|
||||
<el-radio-group v-model="base_form_data.permission_type" class="card__radio">
|
||||
<el-row :gutter="16">
|
||||
<template v-for="(value, key) of PermissionType" :key="key">
|
||||
<el-col :span="12">
|
||||
<el-card
|
||||
shadow="never"
|
||||
class="mb-16"
|
||||
:class="base_form_data.permission_type === key ? 'active' : ''"
|
||||
>
|
||||
<el-radio :value="key" size="large">
|
||||
<p class="mb-4">{{ value }}</p>
|
||||
<el-text type="info">
|
||||
{{ PermissionDesc[key] }}
|
||||
</el-text>
|
||||
</el-radio>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</template>
|
||||
</el-row>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item prop="model_type" :rules="base_form_data_rule.model_type">
|
||||
<template #label>
|
||||
<div class="flex align-center" style="display: inline-flex">
|
||||
<span class="mr-4">模型类型 </span>
|
||||
<el-tooltip effect="dark" placement="right">
|
||||
<template #content>
|
||||
<p>大语言模型:在应用中与AI对话的推理模型。</p>
|
||||
<p>向量模型:在知识库中导入文档进行向量化和向量检索召回分段时使用的向量模型。</p>
|
||||
<p>
|
||||
重排模型:在二次召回中根据召回的候选分段和用户问题的匹配度重新排序,从而得到更精确的结果。
|
||||
</p>
|
||||
<p>语音识别:在应用中开启语音识别后用于语音转文字的模型。</p>
|
||||
<p>语音合成:在应用中开启语音播放后用于文字转语音的模型。</p>
|
||||
</template>
|
||||
<AppIcon iconName="app-warning" class="app-warning-icon"></AppIcon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
<el-select
|
||||
v-loading="model_type_loading"
|
||||
@change="list_base_model($event, true)"
|
||||
v-model="base_form_data.model_type"
|
||||
class="w-full m-2"
|
||||
placeholder="请选择模型类型"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in model_type_list"
|
||||
:key="item.value"
|
||||
:label="item.key"
|
||||
:value="item.value"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item prop="model_name" :rules="base_form_data_rule.model_name">
|
||||
<template #label>
|
||||
<div class="flex align-center" style="display: inline-flex">
|
||||
<div class="mr-4">
|
||||
<span>基础模型 </span>
|
||||
<span class="danger">列表中未列出的模型,直接输入模型名称,回车即可添加</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<el-select
|
||||
@change="getModelForm($event)"
|
||||
v-loading="base_model_loading"
|
||||
v-model="base_form_data.model_name"
|
||||
class="w-full m-2"
|
||||
placeholder="自定义输入基础模型后回车即可"
|
||||
filterable
|
||||
allow-create
|
||||
default-first-option
|
||||
>
|
||||
<el-option v-for="item in base_model_list" :key="item.name" :value="item.name">
|
||||
<template #default>
|
||||
<div class="flex align-center" style="display: inline-flex">
|
||||
<div class="flex-between mr-4">
|
||||
<span>{{ item.name }} </span>
|
||||
</div>
|
||||
<el-tooltip effect="dark" placement="right" v-if="item.desc">
|
||||
<template #content>
|
||||
<p class="w-280">{{ item.desc }}</p>
|
||||
</template>
|
||||
<AppIcon iconName="app-warning" class="app-warning-icon"></AppIcon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</template>
|
||||
</DynamicsForm>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="高级设置" name="advanced-info">
|
||||
<div class="flex-between">
|
||||
<h5>模型参数</h5>
|
||||
<el-button type="text" @click="openAddDrawer()" :disabled="form_data.model_type !== 'LLM' && form_data.model_type !== 'IMAGE' && form_data.model_type !== 'TTS' && form_data.model_type !== 'TTI'">
|
||||
<AppIcon iconName="Plus" class="add-icon" />添加
|
||||
</el-button>
|
||||
</div>
|
||||
<el-table :data="base_form_data.model_params_form" v-if="base_form_data.model_params_form?.length > 0" class="mb-16">
|
||||
<el-table-column prop="label" label="显示名称" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.label && row.label.input_type === 'TooltipLabel'">{{
|
||||
row.label.label
|
||||
}}</span>
|
||||
<span v-else>{{ row.label }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="field" label="参数" show-overflow-tooltip />
|
||||
<el-table-column label="组件类型" width="110px">
|
||||
<template #default="{ row }">
|
||||
<el-tag type="info" class="info-tag">{{
|
||||
input_type_list.find((item) => item.value === row.input_type)?.label
|
||||
}}</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="default_value" label="默认值" show-overflow-tooltip />
|
||||
<el-table-column label="必填">
|
||||
<template #default="{ row }">
|
||||
<div @click.stop>
|
||||
<el-switch disabled size="small" v-model="row.required" />
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="操作" align="left" width="80">
|
||||
<template #default="{ row, $index }">
|
||||
<span class="mr-4">
|
||||
<el-tooltip effect="dark" content="修改" placement="top">
|
||||
<el-button type="primary" text @click.stop="openAddDrawer(row, $index)">
|
||||
<el-icon><EditPen /></el-icon>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</span>
|
||||
<el-tooltip effect="dark" content="删除" placement="top">
|
||||
<el-button type="primary" text @click="deleteParam($index)">
|
||||
<el-icon>
|
||||
<Delete />
|
||||
</el-icon>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="close">取消</el-button>
|
||||
|
|
@ -159,6 +215,8 @@
|
|||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<AddParamDrawer ref="AddParamRef" @refresh="refresh" />
|
||||
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, computed } from 'vue'
|
||||
|
|
@ -168,8 +226,10 @@ import ModelApi from '@/api/model'
|
|||
import type { FormField } from '@/components/dynamics-form/type'
|
||||
import DynamicsForm from '@/components/dynamics-form/index.vue'
|
||||
import type { FormRules } from 'element-plus'
|
||||
import { MsgSuccess, MsgWarning } from '@/utils/message'
|
||||
import { MsgError, MsgSuccess, MsgWarning } from '@/utils/message'
|
||||
import { PermissionType, PermissionDesc } from '@/enums/model'
|
||||
import { input_type_list } from '@/components/dynamics-form/constructor/data'
|
||||
import AddParamDrawer from '@/views/template/component/AddParamDrawer.vue'
|
||||
|
||||
const providerValue = ref<Provider>()
|
||||
const dynamicsFormRef = ref<InstanceType<typeof DynamicsForm>>()
|
||||
|
|
@ -182,6 +242,9 @@ const model_type_list = ref<Array<KeyValue<string, string>>>([])
|
|||
const base_model_list = ref<Array<BaseModel>>()
|
||||
const model_form_field = ref<Array<FormField>>([])
|
||||
const dialogVisible = ref<boolean>(false)
|
||||
const activeName = ref('base-info')
|
||||
const AddParamRef = ref()
|
||||
|
||||
|
||||
const base_form_data_rule = ref<FormRules>({
|
||||
name: { required: true, trigger: 'blur', message: '模型名称不能为空' },
|
||||
|
|
@ -195,7 +258,8 @@ const base_form_data = ref<{
|
|||
permission_type: string
|
||||
model_type: string
|
||||
model_name: string
|
||||
}>({ name: '', model_type: '', model_name: '', permission_type: 'PRIVATE' })
|
||||
model_params_form: any
|
||||
}>({ name: '', model_type: '', model_name: '', permission_type: 'PRIVATE', model_params_form: [] })
|
||||
|
||||
const credential_form_data = ref<Dict<any>>({})
|
||||
|
||||
|
|
@ -206,7 +270,8 @@ const form_data = computed({
|
|||
name: base_form_data.value.name,
|
||||
model_type: base_form_data.value.model_type,
|
||||
model_name: base_form_data.value.model_name,
|
||||
permission_type: base_form_data.value.permission_type
|
||||
permission_type: base_form_data.value.permission_type,
|
||||
model_params_form: base_form_data.value.model_params_form
|
||||
}
|
||||
},
|
||||
set: (event: any) => {
|
||||
|
|
@ -230,6 +295,11 @@ const getModelForm = (model_name: string) => {
|
|||
// 渲染动态表单
|
||||
dynamicsFormRef.value?.render(model_form_field.value, undefined)
|
||||
})
|
||||
|
||||
ModelApi.listBaseModelParamsForm(providerValue.value.provider, form_data.value.model_type, model_name, base_model_loading)
|
||||
.then((ok) => {
|
||||
base_form_data.value.model_params_form = ok.data
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -255,7 +325,7 @@ const list_base_model = (model_type: any, change?: boolean) => {
|
|||
}
|
||||
|
||||
const close = () => {
|
||||
base_form_data.value = { name: '', model_type: '', model_name: '', permission_type: 'PRIVATE' }
|
||||
base_form_data.value = { name: '', model_type: '', model_name: '', permission_type: 'PRIVATE', model_params_form: [] }
|
||||
credential_form_data.value = {}
|
||||
model_form_field.value = []
|
||||
base_model_list.value = []
|
||||
|
|
@ -279,6 +349,44 @@ const submit = () => {
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
function openAddDrawer(data?: any, index?: any) {
|
||||
AddParamRef.value?.open(data, index)
|
||||
}
|
||||
|
||||
function deleteParam(index: any) {
|
||||
base_form_data.value.model_params_form.splice(index, 1)
|
||||
}
|
||||
|
||||
function refresh(data: any, index: any) {
|
||||
for (let i = 0; i < base_form_data.value.model_params_form.length; i++) {
|
||||
let field = base_form_data.value.model_params_form[i].field
|
||||
let label = base_form_data.value.model_params_form[i].label
|
||||
if (label && label.input_type === 'TooltipLabel') {
|
||||
label = label.label
|
||||
}
|
||||
let label2 = data.label
|
||||
if (label2 && label2.input_type === 'TooltipLabel') {
|
||||
label2 = label2.label
|
||||
}
|
||||
|
||||
if (field === data.field && index !== i) {
|
||||
MsgError('变量已存在: ' + data.field)
|
||||
return
|
||||
}
|
||||
if (label === label2 && index !== i) {
|
||||
MsgError('变量已存在: ' + label)
|
||||
return
|
||||
}
|
||||
}
|
||||
if (index !== null) {
|
||||
base_form_data.value.model_params_form.splice(index, 1, data)
|
||||
} else {
|
||||
base_form_data.value.model_params_form.push(data)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const toSelectProvider = () => {
|
||||
close()
|
||||
emit('change')
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ export const baseNode = {
|
|||
node_data: {
|
||||
name: '',
|
||||
desc: '',
|
||||
// @ts-ignore
|
||||
prologue: t('views.application.prompt.defaultPrologue')
|
||||
},
|
||||
config: {}
|
||||
|
|
|
|||
Loading…
Reference in New Issue