Compare commits

..

43 Commits

Author SHA1 Message Date
wangdan-fit2cloud 5f50059443 fix: Adjust application‘s similarity text
Some checks are pending
sync2gitee / repo-sync (push) Waiting to run
2025-07-24 10:33:34 +08:00
shaohuzhang1 d5148ddadf
fix: When the execution parameter of the function library is not None, no verification is performed (#3729)
Some checks are pending
sync2gitee / repo-sync (push) Waiting to run
2025-07-23 18:59:46 +08:00
shaohuzhang1 b838a14bd8
fix: Session timeout setting (#3728)
Some checks are pending
sync2gitee / repo-sync (push) Waiting to run
2025-07-23 18:12:40 +08:00
shaohuzhang1 5ba725ba18
refactor: recycle db connection to avoid "the connection is closed" exception. (#3726) (#3727) 2025-07-23 17:40:39 +08:00
shaohuzhang1 d1cd01f555
refactor: recycle db connection to avoid "the connection is closed" exception. (#3726) 2025-07-23 17:15:38 +08:00
wangdan-fit2cloud 6f4df54917 fix: draggable issue 2025-07-23 16:46:36 +08:00
shaohuzhang1 9d790f1eda
fix: When referencing workflow fields, if the node is not executed, return None data (#3724) 2025-07-23 16:42:36 +08:00
zhangzhanwei dd5622d2bb fix: Swagger doc 2025-07-23 16:35:46 +08:00
zhangzhanwei 7eaf860869 fix: Swagger 2025-07-23 15:56:22 +08:00
CaptainB bd0f44efd1 fix: validate required fields in FunctionNodeForm
--bug=1055176 --user=刘瑞斌 【github#2996】函数引用前置节点参数,前置节点删除后,函数参数为空可以发布应用 https://www.tapd.cn/57709429/s/1739785
2025-07-23 15:15:32 +08:00
shaohuzhang1 23fcb0e94e
fix: After the application opens a session, after a period of time, the conversation record ID will report an error (#3712) 2025-07-23 13:56:27 +08:00
wangdan-fit2cloud 3083d48dff perf: Similarity copywriting optimization 2025-07-23 13:02:06 +08:00
shaohuzhang1 ef549c7c89
fix: Anchor point positioning error of the discriminator node (#3710)
Some checks are pending
sync2gitee / repo-sync (push) Waiting to run
2025-07-22 19:00:35 +08:00
shaohuzhang1 bc6a5a8869
fix: Non streaming sessions cannot count token consumption #3635 (#3709) 2025-07-22 18:43:10 +08:00
wangdan-fit2cloud 14d011d61d fix: Firefox browser compatibility with drag and drop upload issues
Some checks are pending
sync2gitee / repo-sync (push) Waiting to run
2025-07-22 17:36:54 +08:00
wangdan-fit2cloud 753bf5f777 fix: hit test title style optimize(#3533) 2025-07-22 17:13:07 +08:00
shaohuzhang1 8e3e46a96d
fix: Replace replaceAll with the replace function (#3706) #3656 2025-07-22 17:08:57 +08:00
shaohuzhang1 33762f26bf
fix: Password change prompt (#3705) 2025-07-22 16:23:15 +08:00
wangdan-fit2cloud 2adb872cdf fix: Application dialogue history error(#3223) 2025-07-22 16:05:07 +08:00
shaohuzhang1 a0b6aaa568
fix: Specifying a specific form parameter for the reply output will result in an error #3309 (#3703) 2025-07-22 15:49:59 +08:00
shaohuzhang1 01075166b8
docs: Missing thinking process related parameters in model_setting #3134 (#3700) 2025-07-22 12:05:24 +08:00
shaohuzhang1 ae30052dae
docs: Delete the conversation log history_day parameter #3159 (#3699) 2025-07-22 11:52:04 +08:00
shaohuzhang1 55a7d73f98
fix: The thinking process information of AI dialogue nodes is lost (#3698) 2025-07-22 11:26:52 +08:00
shaohuzhang1 0531a6ecc8
feat: Support session_timeout parameter (#3697) 2025-07-22 10:35:20 +08:00
shaohuzhang1 1ee0eac455
build: locales (#3696) 2025-07-22 10:20:46 +08:00
CaptainB 0ba9b97752 feat: add MySQL and PostgreSQL query templates with JSON serialization
Some checks are pending
sync2gitee / repo-sync (push) Waiting to run
2025-07-21 17:45:20 +08:00
wangdan-fit2cloud 02d6239a71 fix: Quick question in the opening statement, English word breaks.(#3158)
Some checks are pending
sync2gitee / repo-sync (push) Waiting to run
2025-07-21 16:45:47 +08:00
shaohuzhang1 8d3b3f8121
fix: Quick question in the opening statement, English word breaks.(#3158)
Co-authored-by: wangdan-fit2cloud <dan.wang@fit2cloud.com>
2025-07-21 16:20:38 +08:00
shaohuzhang1 4013606a93
fix: The password rule prompt is unclear #3547 (#3689) 2025-07-21 16:12:47 +08:00
shaohuzhang1 40be71d765
fix: Create document prompt error #3527 (#3688) 2025-07-21 15:24:49 +08:00
shaohuzhang1 8ecf5b52ed
fix: Interface permission verification error #3309 (#3687) 2025-07-21 15:18:40 +08:00
shaohuzhang1 abe51dc30c
fix: Quick question in the opening statement, English word breaks.(#3158)
Co-authored-by: wangdan-fit2cloud <dan.wang@fit2cloud.com>
2025-07-21 12:34:14 +08:00
CaptainB b7ba9fdf67 fix: change HitTest action methods from GET to PUT in API views 2025-07-21 12:12:26 +08:00
shaohuzhang1 bca56af788
fix: Interface permission verification error #3343 (#3683) 2025-07-21 11:38:41 +08:00
wxg0103 622a8e525c refactor: add early return for invalid document type in document_serializers.py
--bug=1057562 --user=王孝刚 【知识库】飞书知识库对接,设置命中处理方式保存报错 https://www.tapd.cn/57709429/s/1736833
2025-07-21 11:32:26 +08:00
wxg0103 f568c6800f refactor: add early return for invalid document type in document_serializers.py
--bug=1057562 --user=王孝刚 【知识库】飞书知识库对接,设置命中处理方式保存报错 https://www.tapd.cn/57709429/s/1736833
2025-07-21 11:31:03 +08:00
CaptainB 93d1958fef fix: validate transport type in MCP server configuration
--bug=1056812 --user=刘瑞斌 【github##3232】【应用编排】mcp节点的transport配置填写http,错误提示不对 https://www.tapd.cn/57709429/s/1736690
2025-07-21 11:22:00 +08:00
wxg0103 90ee3c4d21 refactor: improve formatting in chat_serializers.py
--bug=1059060 --user=王孝刚 【github#3392】【应用】对话日志使用自定义时间段导出报错 https://www.tapd.cn/57709429/s/1736692
2025-07-21 11:20:15 +08:00
liqiang-fit2cloud 30ddab322f
Update README_CN.md
Some checks failed
sync2gitee / repo-sync (push) Has been cancelled
Typos Check / Spell Check with Typos (push) Has been cancelled
2025-07-16 10:30:40 +08:00
maninhill ee83139b96
chore: Update README_CN.md (#3621)
Some checks are pending
sync2gitee / repo-sync (push) Waiting to run
Typos Check / Spell Check with Typos (push) Waiting to run
2025-07-15 21:56:48 +08:00
maninhill 1268b2043a
chore: Update README_CN.md (#3619) 2025-07-15 21:35:03 +08:00
liqiang-fit2cloud f01a65f507 docs: Update README.md
Some checks are pending
sync2gitee / repo-sync (push) Waiting to run
Typos Check / Spell Check with Typos (push) Waiting to run
2025-07-15 16:26:03 +08:00
liqiang-fit2cloud efa196c58b docs: Update README.md 2025-07-15 16:23:46 +08:00
52 changed files with 380 additions and 406 deletions

View File

@ -24,7 +24,7 @@ MaxKB = Max Knowledge Brain, it is an open-source platform for building enterpri
Execute the script below to start a MaxKB container using Docker:
```bash
docker run -d --name=maxkb --restart=always -p 8080:8080 -v ~/.maxkb:/var/lib/postgresql/data -v ~/.python-packages:/opt/maxkb/app/sandbox/python-packages 1panel/maxkb
docker run -d --name=maxkb --restart=always -p 8080:8080 -v ~/.maxkb:/var/lib/postgresql/data -v ~/.python-packages:/opt/maxkb/app/sandbox/python-packages 1panel/maxkb:v1
```
Access MaxKB web interface at `http://your_server_ip:8080` with default admin credentials:
@ -32,7 +32,7 @@ Access MaxKB web interface at `http://your_server_ip:8080` with default admin cr
- username: admin
- password: MaxKB@123..
中国用户如遇到 Docker 镜像 Pull 失败问题,请参照该 [离线安装文档](https://maxkb.cn/docs/installation/offline_installtion/) 进行安装。
中国用户如遇到 Docker 镜像 Pull 失败问题,请参照该 [离线安装文档](https://maxkb.cn/docs/v1/installation/offline_installtion/) 进行安装。
## Screenshots

View File

@ -14,12 +14,12 @@
</p>
<hr/>
MaxKB = Max Knowledge Brain是一款强大易用的企业级智能体平台,支持 RAG 检索增强生成、工作流编排、MCP 工具调用能力。MaxKB 支持对接各种主流大语言模型,广泛应用于智能客服、企业内部知识库问答、员工助手、学术研究与教育等场景。
MaxKB = Max Knowledge Brain是一个强大易用的企业级智能体平台,致力于解决企业 AI 落地面临的技术门槛高、部署成本高、迭代周期长等问题助力企业在人工智能时代赢得先机。秉承“开箱即用伴随成长”的设计理念MaxKB 支持企业快速接入主流大模型高效构建专属知识库并提供从基础问答RAG、复杂流程自动化工作流到智能体Agent的渐进式升级路径全面赋能智能客服、智能办公助手等多种应用场景。
- **RAG 检索增强生成**:高效搭建本地 AI 知识库,支持直接上传文档 / 自动爬取在线文档,支持文本自动拆分、向量化,有效减少大模型幻觉,提升问答效果;
- **灵活编排**:内置强大的工作流引擎、函数库和 MCP 工具调用能力,支持编排 AI 工作过程,满足复杂业务场景下的需求;
- **无缝嵌入**:支持零编码快速嵌入到第三方业务系统,让已有系统快速拥有智能问答能力,提高用户满意度;
- **模型中立**支持对接各种大模型包括本地私有大模型DeepSeek R1 / Llama 3 / Qwen 2 等)、国内公共大模型(通义千问 / 腾讯混元 / 字节豆包 / 百度千帆 / 智谱 AI / Kimi 等和国外公共大模型OpenAI / Claude / Gemini 等)。
- **模型中立**支持对接各种大模型包括本地私有大模型DeepSeek R1 / Qwen 3 等)、国内公共大模型(通义千问 / 腾讯混元 / 字节豆包 / 百度千帆 / 智谱 AI / Kimi 等和国外公共大模型OpenAI / Claude / Gemini 等)。
MaxKB 三分钟视频介绍https://www.bilibili.com/video/BV18JypYeEkj/
@ -27,10 +27,10 @@ MaxKB 三分钟视频介绍https://www.bilibili.com/video/BV18JypYeEkj/
```
# Linux 机器
docker run -d --name=maxkb --restart=always -p 8080:8080 -v ~/.maxkb:/var/lib/postgresql/data -v ~/.python-packages:/opt/maxkb/app/sandbox/python-packages registry.fit2cloud.com/maxkb/maxkb
docker run -d --name=maxkb --restart=always -p 8080:8080 -v ~/.maxkb:/var/lib/postgresql/data -v ~/.python-packages:/opt/maxkb/app/sandbox/python-packages registry.fit2cloud.com/maxkb/maxkb:v1
# Windows 机器
docker run -d --name=maxkb --restart=always -p 8080:8080 -v C:/maxkb:/var/lib/postgresql/data -v C:/python-packages:/opt/maxkb/app/sandbox/python-packages registry.fit2cloud.com/maxkb/maxkb
docker run -d --name=maxkb --restart=always -p 8080:8080 -v C:/maxkb:/var/lib/postgresql/data -v C:/python-packages:/opt/maxkb/app/sandbox/python-packages registry.fit2cloud.com/maxkb/maxkb:v1
# 用户名: admin
# 密码: MaxKB@123..
@ -38,8 +38,8 @@ docker run -d --name=maxkb --restart=always -p 8080:8080 -v C:/maxkb:/var/lib/po
- 你也可以通过 [1Panel 应用商店](https://apps.fit2cloud.com/1panel) 快速部署 MaxKB
- 如果是内网环境,推荐使用 [离线安装包](https://community.fit2cloud.com/#/products/maxkb/downloads) 进行安装部署;
- MaxKB 产品版本分为社区版和专业版,详情请参见:[MaxKB 产品版本对比](https://maxkb.cn/pricing.html)
- 如果您需要向团队介绍 MaxKB可以使用这个 [官方 PPT 材料](https://maxkb.cn/download/introduce-maxkb_202503.pdf)。
- MaxKB 不同产品产品版本的对比请参见:[MaxKB 产品版本对比](https://maxkb.cn/price)
- 如果您需要向团队介绍 MaxKB可以使用这个 [官方 PPT 材料](https://fit2cloud.com/maxkb/download/introduce-maxkb_202507.pdf)。
如你有更多问题,可以查看使用手册,或者通过论坛与我们交流。

View File

@ -188,6 +188,7 @@ class BaseChatNode(IChatNode):
self.context['answer'] = details.get('answer')
self.context['question'] = details.get('question')
self.context['reasoning_content'] = details.get('reasoning_content')
self.context['model_setting'] = details.get('model_setting')
if self.node_params.get('is_result', False):
self.answer_text = details.get('answer')
@ -274,6 +275,7 @@ class BaseChatNode(IChatNode):
"index": index,
'run_time': self.context.get('run_time'),
'system': self.context.get('system'),
'model_setting': self.context.get('model_setting'),
'history_message': [{'content': message.content, 'role': message.type} for message in
(self.context.get('history_message') if self.context.get(
'history_message') is not None else [])],

View File

@ -45,6 +45,8 @@ def get_field_value(debug_field_list, name, is_required):
def valid_reference_value(_type, value, name):
if value is None:
return
if _type == 'int':
instance_type = int | float
elif _type == 'float':
@ -70,10 +72,17 @@ def convert_value(name: str, value, _type, is_required, source, node):
if not is_required and source == 'reference' and (value is None or len(value) == 0):
return None
if source == 'reference':
if value and isinstance(value, list) and len(value) == 0:
if not is_required:
return None
else:
raise Exception(f"字段:{name}类型:{_type}值:{value}必填参数")
value = node.workflow_manage.get_reference_field(
value[0],
value[1:])
valid_reference_value(_type, value, name)
if value is None:
return None
if _type == 'int':
return int(value)
if _type == 'float':

View File

@ -32,6 +32,8 @@ def write_context(step_variable: Dict, global_variable: Dict, node, workflow):
def valid_reference_value(_type, value, name):
if value is None:
return
if _type == 'int':
instance_type = int | float
elif _type == 'float':
@ -52,10 +54,17 @@ def convert_value(name: str, value, _type, is_required, source, node):
if not is_required and (value is None or (isinstance(value, str) and len(value) == 0)):
return None
if source == 'reference':
if value and isinstance(value, list) and len(value) == 0:
if not is_required:
return None
else:
raise Exception(f"字段:{name}类型:{_type}值:{value}必填参数")
value = node.workflow_manage.get_reference_field(
value[0],
value[1:])
valid_reference_value(_type, value, name)
if value is None:
return None
if _type == 'int':
return int(value)
if _type == 'float':

View File

@ -298,8 +298,8 @@ class WorkflowManage:
if global_fields is not None:
for global_field in global_fields:
global_field_list.append({**global_field, 'node_id': node_id, 'node_name': node_name})
field_list.sort(key=lambda f: len(f.get('node_name')), reverse=True)
global_field_list.sort(key=lambda f: len(f.get('node_name')), reverse=True)
field_list.sort(key=lambda f: len(f.get('node_name') + f.get('value')), reverse=True)
global_field_list.sort(key=lambda f: len(f.get('node_name') + f.get('value')), reverse=True)
self.field_list = field_list
self.global_field_list = global_field_list
@ -755,7 +755,10 @@ class WorkflowManage:
if node_id == 'global':
return INode.get_field(self.context, fields)
else:
return self.get_node_by_id(node_id).get_reference_field(fields)
node = self.get_node_by_id(node_id)
if node:
return node.get_reference_field(fields)
return None
def get_workflow_content(self):
context = {

View File

@ -16,6 +16,7 @@ import re
import uuid
from functools import reduce
from typing import Dict, List
from django.contrib.postgres.fields import ArrayField
from django.core import cache, validators
from django.core import signing
@ -24,8 +25,8 @@ from django.db.models import QuerySet
from django.db.models.expressions import RawSQL
from django.http import HttpResponse
from django.template import Template, Context
from django.utils.translation import gettext_lazy as _, get_language, to_locale
from langchain_mcp_adapters.client import MultiServerMCPClient
from mcp.client.sse import sse_client
from rest_framework import serializers, status
from rest_framework.utils.formatting import lazy_format
@ -38,7 +39,7 @@ from common.config.embedding_config import VectorStore
from common.constants.authentication_type import AuthenticationType
from common.db.search import get_dynamics_model, native_search, native_page_search
from common.db.sql_execute import select_list
from common.exception.app_exception import AppApiException, NotFound404, AppUnauthorizedFailed, ChatException
from common.exception.app_exception import AppApiException, NotFound404, AppUnauthorizedFailed
from common.field.common import UploadedImageField, UploadedFileField
from common.models.db_model_manage import DBModelManage
from common.response import result
@ -57,7 +58,6 @@ from setting.models_provider.tools import get_model_instance_by_model_user_id
from setting.serializers.provider_serializers import ModelSerializer
from smartdoc.conf import PROJECT_DIR
from users.models import User
from django.utils.translation import gettext_lazy as _, get_language, to_locale
chat_cache = cache.caches['chat_cache']
@ -1328,6 +1328,9 @@ class ApplicationSerializer(serializers.Serializer):
if '"stdio"' in self.data.get('mcp_servers'):
raise AppApiException(500, _('stdio is not supported'))
servers = json.loads(self.data.get('mcp_servers'))
for server, config in servers.items():
if config.get('transport') not in ['sse', 'streamable_http']:
raise AppApiException(500, _('Only support transport=sse or transport=streamable_http'))
async def get_mcp_tools(servers):
async with MultiServerMCPClient(servers) as client:

View File

@ -395,13 +395,14 @@ class ChatMessageSerializer(serializers.Serializer):
work_flow_manage = WorkflowManage(Flow.new_instance(chat_info.work_flow_version.work_flow),
{'history_chat_record': history_chat_record, 'question': message,
'chat_id': chat_info.chat_id, 'chat_record_id': str(
uuid.uuid1()) if chat_record is None else chat_record.id,
uuid.uuid1()) if chat_record is None else str(chat_record.id),
'stream': stream,
're_chat': re_chat,
'client_id': client_id,
'client_type': client_type,
'user_id': user_id}, WorkFlowPostHandler(chat_info, client_id, client_type),
base_to_response, form_data, image_list, document_list, audio_list, other_list,
base_to_response, form_data, image_list, document_list, audio_list,
other_list,
self.data.get('runtime_node_id'),
self.data.get('node_data'), chat_record, self.data.get('child_node'))
r = work_flow_manage.run()

View File

@ -222,7 +222,8 @@ class ChatSerializers(serializers.Serializer):
reference_paragraph,
"\n".join([
f"{improve_paragraph_list[index].get('title')}\n{improve_paragraph_list[index].get('content')}"
for index in range(len(improve_paragraph_list))]),
for index in range(len(improve_paragraph_list))
]) if improve_paragraph_list is not None else "",
row.get('asker').get('user_name'),
row.get('message_tokens') + row.get('answer_tokens'), row.get('run_time'),
str(row.get('create_time').astimezone(pytz.timezone(TIME_ZONE)).strftime('%Y-%m-%d %H:%M:%S')

View File

@ -38,15 +38,6 @@ class ApplicationApi(ApiMixin):
}
)
@staticmethod
def get_response_body_api():
return openapi.Schema(
type=openapi.TYPE_STRING,
title=_("Application authentication token"),
description=_("Application authentication token"),
default="token"
)
@staticmethod
def get_response_body_api():
return openapi.Schema(
@ -142,27 +133,6 @@ class ApplicationApi(ApiMixin):
}
)
@staticmethod
def get_response_body_api():
return openapi.Schema(
type=openapi.TYPE_OBJECT,
properties={
'id': openapi.Schema(type=openapi.TYPE_STRING, title=_("Primary key id"),
description=_("Primary key id")),
'secret_key': openapi.Schema(type=openapi.TYPE_STRING, title=_("Secret key"),
description=_("Secret key")),
'is_active': openapi.Schema(type=openapi.TYPE_BOOLEAN, title=_("Is activation"),
description=_("Is activation")),
'application_id': openapi.Schema(type=openapi.TYPE_STRING, title=_("Application ID"),
description=_("Application ID")),
'allow_cross_domain': openapi.Schema(type=openapi.TYPE_BOOLEAN,
title=_("Is cross-domain allowed"),
description=_("Is cross-domain allowed")),
'cross_domain_list': openapi.Schema(type=openapi.TYPE_ARRAY, title=_('Cross-domain list'),
items=openapi.Schema(type=openapi.TYPE_STRING))
}
)
class AccessToken(ApiMixin):
@staticmethod
def get_request_params_api():
@ -201,37 +171,6 @@ class ApplicationApi(ApiMixin):
}
)
@staticmethod
def get_response_body_api():
return openapi.Schema(
type=openapi.TYPE_OBJECT,
required=[],
properties={
'id': openapi.Schema(type=openapi.TYPE_STRING, title=_("Primary key id"),
description=_("Primary key id")),
'access_token': openapi.Schema(type=openapi.TYPE_STRING, title=_("Access Token"),
description=_("Access Token")),
'access_token_reset': openapi.Schema(type=openapi.TYPE_BOOLEAN, title=_("Reset Token"),
description=_("Reset Token")),
'is_active': openapi.Schema(type=openapi.TYPE_BOOLEAN, title=_("Is activation"),
description=_("Is activation")),
'access_num': openapi.Schema(type=openapi.TYPE_NUMBER, title=_("Number of visits"),
description=_("Number of visits")),
'white_active': openapi.Schema(type=openapi.TYPE_BOOLEAN, title=_("Whether to enable whitelist"),
description=_("Whether to enable whitelist")),
'white_list': openapi.Schema(type=openapi.TYPE_ARRAY,
items=openapi.Schema(type=openapi.TYPE_STRING), title=_("Whitelist"),
description=_("Whitelist")),
'show_source': openapi.Schema(type=openapi.TYPE_BOOLEAN,
title=_("Whether to display knowledge sources"),
description=_("Whether to display knowledge sources")),
'language': openapi.Schema(type=openapi.TYPE_STRING,
title=_("language"),
description=_("language"))
}
)
class Edit(ApiMixin):
@staticmethod
def get_request_body_api():
@ -363,7 +302,19 @@ class ApplicationApi(ApiMixin):
'no_references_prompt': openapi.Schema(type=openapi.TYPE_STRING,
title=_("No citation segmentation prompt"),
default="{question}",
description=_("No citation segmentation prompt"))
description=_("No citation segmentation prompt")),
'reasoning_content_enable': openapi.Schema(type=openapi.TYPE_BOOLEAN,
title=_("Reasoning enable"),
default=False,
description=_("Reasoning enable")),
'reasoning_content_end': openapi.Schema(type=openapi.TYPE_STRING,
title=_("Reasoning end tag"),
default="</think>",
description=_("Reasoning end tag")),
"reasoning_content_start": openapi.Schema(type=openapi.TYPE_STRING,
title=_("Reasoning start tag"),
default="<think>",
description=_("Reasoning start tag"))
}
)
@ -428,56 +379,6 @@ class ApplicationApi(ApiMixin):
}
)
@staticmethod
def get_response_body_api():
return openapi.Schema(
type=openapi.TYPE_OBJECT,
required=['id', 'name', 'desc', 'model_id', 'dialogue_number', 'dataset_setting', 'model_setting',
'problem_optimization', 'stt_model_enable', 'stt_model_enable', 'tts_type',
'work_flow'],
properties={
'id': openapi.Schema(type=openapi.TYPE_STRING, title=_("Primary key id"),
description=_("Primary key id")),
'name': openapi.Schema(type=openapi.TYPE_STRING, title=_("Application Name"),
description=_("Application Name")),
'desc': openapi.Schema(type=openapi.TYPE_STRING, title=_("Application Description"),
description=_("Application Description")),
'model_id': openapi.Schema(type=openapi.TYPE_STRING, title=_("Model id"),
description=_("Model id")),
"dialogue_number": openapi.Schema(type=openapi.TYPE_NUMBER,
title=_("Number of multi-round conversations"),
description=_("Number of multi-round conversations")),
'prologue': openapi.Schema(type=openapi.TYPE_STRING, title=_("Opening remarks"),
description=_("Opening remarks")),
'dataset_id_list': openapi.Schema(type=openapi.TYPE_ARRAY,
items=openapi.Schema(type=openapi.TYPE_STRING),
title=_("List of associated knowledge base IDs"),
description=_("List of associated knowledge base IDs")),
'dataset_setting': ApplicationApi.DatasetSetting.get_request_body_api(),
'model_setting': ApplicationApi.ModelSetting.get_request_body_api(),
'problem_optimization': openapi.Schema(type=openapi.TYPE_BOOLEAN, title=_("Problem Optimization"),
description=_("Problem Optimization"), default=True),
'type': openapi.Schema(type=openapi.TYPE_STRING, title=_("Application Type"),
description=_("Application Type SIMPLE | WORK_FLOW")),
'problem_optimization_prompt': openapi.Schema(type=openapi.TYPE_STRING,
title=_('Question optimization tips'),
description=_("Question optimization tips"),
default=_(
"() contains the user's question. Answer the guessed user's question based on the context ({question}) Requirement: Output a complete question and put it in the <data></data> tag")),
'tts_model_id': openapi.Schema(type=openapi.TYPE_STRING, title=_("Text-to-speech model ID"),
description=_("Text-to-speech model ID")),
'stt_model_id': openapi.Schema(type=openapi.TYPE_STRING, title=_("Speech-to-text model id"),
description=_("Speech-to-text model id")),
'stt_model_enable': openapi.Schema(type=openapi.TYPE_STRING, title=_("Is speech-to-text enabled"),
description=_("Is speech-to-text enabled")),
'tts_model_enable': openapi.Schema(type=openapi.TYPE_STRING, title=_("Is text-to-speech enabled"),
description=_("Is text-to-speech enabled")),
'tts_type': openapi.Schema(type=openapi.TYPE_STRING, title=_("Text-to-speech type"),
description=_("Text-to-speech type")),
'work_flow': ApplicationApi.WorkFlow.get_request_body_api(),
}
)
class Query(ApiMixin):
@staticmethod
def get_request_params_api():

View File

@ -319,15 +319,6 @@ class ChatApi(ApiMixin):
}
)
@staticmethod
def get_response_body_api():
return openapi.Schema(
type=openapi.TYPE_STRING,
title=_("Conversation ID"),
description=_("Conversation ID"),
default="chat_id"
)
@staticmethod
def get_request_params_api():
return [openapi.Parameter(name='application_id',
@ -335,11 +326,6 @@ class ChatApi(ApiMixin):
type=openapi.TYPE_STRING,
required=True,
description=_('Application ID')),
openapi.Parameter(name='history_day',
in_=openapi.IN_QUERY,
type=openapi.TYPE_NUMBER,
required=True,
description=_('Historical days')),
openapi.Parameter(name='abstract', in_=openapi.IN_QUERY, type=openapi.TYPE_STRING, required=False,
description=_("abstract")),
openapi.Parameter(name='min_star', in_=openapi.IN_QUERY, type=openapi.TYPE_INTEGER, required=False,

View File

@ -7,16 +7,6 @@
@desc:
"""
from django.core import cache
from django.http import HttpResponse
from django.utils.translation import gettext_lazy as _, gettext
from drf_yasg.utils import swagger_auto_schema
from langchain_core.prompts import PromptTemplate
from rest_framework.decorators import action
from rest_framework.parsers import MultiPartParser
from rest_framework.request import Request
from rest_framework.views import APIView
from application.serializers.application_serializers import ApplicationSerializer
from application.serializers.application_statistics_serializers import ApplicationStatisticsSerializer
from application.swagger_api.application_api import ApplicationApi
@ -31,6 +21,14 @@ from common.response import result
from common.swagger_api.common_api import CommonApi
from common.util.common import query_params_to_single_dict
from dataset.serializers.dataset_serializers import DataSetSerializers
from django.core import cache
from django.http import HttpResponse
from django.utils.translation import gettext_lazy as _
from drf_yasg.utils import swagger_auto_schema
from rest_framework.decorators import action
from rest_framework.parsers import MultiPartParser
from rest_framework.request import Request
from rest_framework.views import APIView
chat_cache = cache.caches['chat_cache']
@ -373,8 +371,7 @@ class Application(APIView):
operation_id=_("Modify application API_KEY"),
tags=[_('Application/API_KEY')],
manual_parameters=ApplicationApi.ApiKey.Operate.get_request_params_api(),
request_body=ApplicationApi.ApiKey.Operate.get_request_body_api(),
responses=result.get_api_response(ApplicationApi.ApiKey.Operate.get_response_body_api()))
request_body=ApplicationApi.ApiKey.Operate.get_request_body_api())
@has_permissions(ViewPermission(
[RoleConstants.ADMIN, RoleConstants.USER],
[lambda r, keywords: Permission(group=Group.APPLICATION, operate=Operate.MANAGE,
@ -416,8 +413,7 @@ class Application(APIView):
operation_id=_("Modify Application AccessToken"),
tags=[_('Application/Public Access')],
manual_parameters=ApplicationApi.AccessToken.get_request_params_api(),
request_body=ApplicationApi.AccessToken.get_request_body_api(),
responses=result.get_api_response(ApplicationApi.AccessToken.get_response_body_api()))
request_body=ApplicationApi.AccessToken.get_request_body_api())
@has_permissions(ViewPermission(
[RoleConstants.ADMIN, RoleConstants.USER],
[lambda r, keywords: Permission(group=Group.APPLICATION, operate=Operate.MANAGE,
@ -457,7 +453,6 @@ class Application(APIView):
@swagger_auto_schema(operation_summary=_("Application Certification"),
operation_id=_("Application Certification"),
request_body=ApplicationApi.Authentication.get_request_body_api(),
responses=result.get_api_response(ApplicationApi.Authentication.get_response_body_api()),
tags=[_("Application/Certification")],
security=[])
def post(self, request: Request):
@ -475,7 +470,6 @@ class Application(APIView):
@swagger_auto_schema(operation_summary=_("Create an application"),
operation_id=_("Create an application"),
request_body=ApplicationApi.Create.get_request_body_api(),
responses=result.get_api_response(ApplicationApi.Create.get_response_body_api()),
tags=[_('Application')])
@has_permissions(PermissionConstants.APPLICATION_CREATE, compare=CompareConstants.AND)
@log(menu='Application', operate="Create an application",
@ -498,7 +492,7 @@ class Application(APIView):
class HitTest(APIView):
authentication_classes = [TokenAuth]
@action(methods="GET", detail=False)
@action(methods="PUT", detail=False)
@swagger_auto_schema(operation_summary=_("Hit Test List"), operation_id=_("Hit Test List"),
manual_parameters=CommonApi.HitTestApi.get_request_params_api(),
responses=result.get_api_array_response(CommonApi.HitTestApi.get_response_body_api()),
@ -509,15 +503,15 @@ class Application(APIView):
[lambda r, keywords: Permission(group=Group.APPLICATION, operate=Operate.USE,
dynamic_tag=keywords.get('application_id'))],
compare=CompareConstants.AND))
def get(self, request: Request, application_id: str):
return result.success(
ApplicationSerializer.HitTest(data={'id': application_id, 'user_id': request.user.id,
"query_text": request.query_params.get("query_text"),
"top_number": request.query_params.get("top_number"),
'similarity': request.query_params.get('similarity'),
'search_mode': request.query_params.get(
'search_mode')}).hit_test(
))
def put(self, request: Request, application_id: str):
return result.success(ApplicationSerializer.HitTest(data={
'id': application_id,
'user_id': request.user.id,
"query_text": request.data.get("query_text"),
"top_number": request.data.get("top_number"),
'similarity': request.data.get('similarity'),
'search_mode': request.data.get('search_mode')}
).hit_test())
class Publish(APIView):
authentication_classes = [TokenAuth]

View File

@ -94,7 +94,6 @@ class ChatView(APIView):
@swagger_auto_schema(operation_summary=_("Get the workflow temporary session id"),
operation_id=_("Get the workflow temporary session id"),
request_body=ChatApi.OpenWorkFlowTemp.get_request_body_api(),
responses=result.get_api_response(ChatApi.OpenTempChat.get_response_body_api()),
tags=[_("Application/Chat")])
def post(self, request: Request):
return result.success(ChatSerializers.OpenWorkFlowChat(
@ -107,7 +106,6 @@ class ChatView(APIView):
@swagger_auto_schema(operation_summary=_("Get a temporary session id"),
operation_id=_("Get a temporary session id"),
request_body=ChatApi.OpenTempChat.get_request_body_api(),
responses=result.get_api_response(ChatApi.OpenTempChat.get_response_body_api()),
tags=[_("Application/Chat")])
@has_permissions(RoleConstants.ADMIN, RoleConstants.USER)
def post(self, request: Request):
@ -241,10 +239,9 @@ class ChatView(APIView):
@swagger_auto_schema(operation_summary=_("Client modifies dialogue summary"),
operation_id=_("Client modifies dialogue summary"),
request_body=ChatClientHistoryApi.Operate.ReAbstract.get_request_body_api(),
responses=result.get_default_response(),
tags=[_("Application/Conversation Log")])
@has_permissions(ViewPermission(
[RoleConstants.APPLICATION_ACCESS_TOKEN, RoleConstants.ADMIN, RoleConstants.USER],
[RoleConstants.APPLICATION_ACCESS_TOKEN],
[lambda r, keywords: Permission(group=Group.APPLICATION, operate=Operate.USE,
dynamic_tag=keywords.get('application_id'))],
compare=CompareConstants.AND),
@ -421,7 +418,6 @@ class ChatView(APIView):
operation_id=_("Add to Knowledge Base"),
manual_parameters=ImproveApi.get_request_params_api_post(),
request_body=ImproveApi.get_request_body_api_post(),
responses=result.get_default_response(),
tags=[_("Application/Conversation Log/Add to Knowledge Base")]
)
@has_permissions(

View File

@ -6,18 +6,18 @@
@date2024/3/14 03:02
@desc: 用户认证
"""
from django.core import cache
from django.db.models import QuerySet
from django.utils.translation import gettext_lazy as _
from common.auth.handle.auth_base_handle import AuthBaseHandle
from common.constants.authentication_type import AuthenticationType
from common.constants.permission_constants import RoleConstants, get_permission_list_by_role, Auth
from common.exception.app_exception import AppAuthenticationFailed
from smartdoc.settings import JWT_AUTH
from smartdoc.const import CONFIG
from users.models import User
from django.core import cache
from users.models.user import get_user_dynamics_permission
from django.utils.translation import gettext_lazy as _
token_cache = cache.caches['token_cache']
@ -35,7 +35,7 @@ class UserToken(AuthBaseHandle):
auth_details = get_token_details()
user = QuerySet(User).get(id=auth_details['id'])
# 续期
token_cache.touch(token, timeout=JWT_AUTH['JWT_EXPIRATION_DELTA'].total_seconds())
token_cache.touch(token, timeout=CONFIG.get_session_timeout())
rule = RoleConstants[user.role]
permission_list = get_permission_list_by_role(RoleConstants[user.role])
# 获取用户的应用和知识库的权限

View File

@ -526,7 +526,7 @@ class DataSetSerializers(serializers.ModelSerializer):
def get_request_body_api():
return openapi.Schema(
type=openapi.TYPE_OBJECT,
required=['name', 'desc'],
required=['name', 'desc', 'embedding_mode_id'],
properties={
'name': openapi.Schema(type=openapi.TYPE_STRING, title=_('dataset name'),
description=_('dataset name')),

View File

@ -141,7 +141,8 @@ class DocumentEditInstanceSerializer(ApiMixin, serializers.Serializer):
if 'meta' in self.data and self.data.get('meta') is not None:
dataset_meta_valid_map = self.get_meta_valid_map()
valid_class = dataset_meta_valid_map.get(document.type)
valid_class(data=self.data.get('meta')).is_valid(raise_exception=True)
if valid_class is not None:
valid_class(data=self.data.get('meta')).is_valid(raise_exception=True)
class DocumentWebInstanceSerializer(ApiMixin, serializers.Serializer):
@ -808,27 +809,40 @@ class DocumentSerializers(ApiMixin, serializers.Serializer):
def get_response_body_api():
return openapi.Schema(
type=openapi.TYPE_OBJECT,
required=['id', 'name', 'char_length', 'user_id', 'paragraph_count', 'is_active'
'update_time', 'create_time'],
required=['create_time', 'update_time', 'id', 'name', 'char_length', 'status', 'is_active',
'type', 'meta', 'dataset_id', 'hit_handling_method', 'directly_return_similarity',
'status_meta', 'paragraph_count'],
properties={
'create_time': openapi.Schema(type=openapi.TYPE_STRING, title=_('create time'),
description=_('create time'),
default="1970-01-01 00:00:00"),
'update_time': openapi.Schema(type=openapi.TYPE_STRING, title=_('update time'),
description=_('update time'),
default="1970-01-01 00:00:00"),
'id': openapi.Schema(type=openapi.TYPE_STRING, title="id",
description="id", default="xx"),
'name': openapi.Schema(type=openapi.TYPE_STRING, title=_('name'),
description=_('name'), default="xx"),
'char_length': openapi.Schema(type=openapi.TYPE_INTEGER, title=_('char length'),
description=_('char length'), default=10),
'user_id': openapi.Schema(type=openapi.TYPE_STRING, title=_('user id'), description=_('user id')),
'paragraph_count': openapi.Schema(type=openapi.TYPE_INTEGER, title="_('document count')",
description="_('document count')", default=1),
'status':openapi.Schema(type=openapi.TYPE_STRING, title=_('status'),
description=_('status'), default="xx"),
'is_active': openapi.Schema(type=openapi.TYPE_BOOLEAN, title=_('Is active'),
description=_('Is active'), default=True),
'update_time': openapi.Schema(type=openapi.TYPE_STRING, title=_('update time'),
description=_('update time'),
default="1970-01-01 00:00:00"),
'create_time': openapi.Schema(type=openapi.TYPE_STRING, title=_('create time'),
description=_('create time'),
default="1970-01-01 00:00:00"
)
'type': openapi.Schema(type=openapi.TYPE_STRING, title=_('type'),
description=_('type'), default="xx"),
'meta': openapi.Schema(type=openapi.TYPE_OBJECT, title=_('meta'),
description=_('meta'), default="{}"),
'dataset_id': openapi.Schema(type=openapi.TYPE_STRING, title=_('dataset_id'),
description=_('dataset_id'), default="xx"),
'hit_handling_method': openapi.Schema(type=openapi.TYPE_STRING, title=_('hit_handling_method'),
description=_('hit_handling_method'), default="xx"),
'directly_return_similarity': openapi.Schema(type=openapi.TYPE_NUMBER, title=_('directly_return_similarity'),
description=_('directly_return_similarity'), default="xx"),
'status_meta': openapi.Schema(type=openapi.TYPE_OBJECT, title=_('status_meta'),
description=_('status_meta'), default="{}"),
'paragraph_count': openapi.Schema(type=openapi.TYPE_INTEGER, title="_('document count')",
description="_('document count')", default=1),
}
)
@ -855,7 +869,7 @@ class DocumentSerializers(ApiMixin, serializers.Serializer):
class Create(ApiMixin, serializers.Serializer):
dataset_id = serializers.UUIDField(required=True, error_messages=ErrMessage.char(
_('document id')))
_('dataset id')))
def is_valid(self, *, raise_exception=False):
super().is_valid(raise_exception=True)
@ -983,7 +997,7 @@ class DocumentSerializers(ApiMixin, serializers.Serializer):
in_=openapi.IN_PATH,
type=openapi.TYPE_STRING,
required=True,
description=_('document id'))
description=_('dataset id'))
]
class Split(ApiMixin, serializers.Serializer):

View File

@ -7,13 +7,13 @@
@desc:
"""
from django.utils.translation import gettext_lazy as _
from drf_yasg.utils import swagger_auto_schema
from rest_framework.decorators import action
from rest_framework.parsers import MultiPartParser
from rest_framework.views import APIView
from rest_framework.views import Request
import dataset.models
from common.auth import TokenAuth, has_permissions
from common.constants.permission_constants import PermissionConstants, CompareConstants, Permission, Group, Operate, \
ViewPermission, RoleConstants
@ -25,7 +25,6 @@ from dataset.serializers.common_serializers import GenerateRelatedSerializer
from dataset.serializers.dataset_serializers import DataSetSerializers
from dataset.views.common import get_dataset_operation_object
from setting.serializers.provider_serializers import ModelSerializer
from django.utils.translation import gettext_lazy as _
class Dataset(APIView):
@ -141,21 +140,22 @@ class Dataset(APIView):
class HitTest(APIView):
authentication_classes = [TokenAuth]
@action(methods="GET", detail=False)
@action(methods="PUT", detail=False)
@swagger_auto_schema(operation_summary=_('Hit test list'), operation_id=_('Hit test list'),
manual_parameters=CommonApi.HitTestApi.get_request_params_api(),
responses=result.get_api_array_response(CommonApi.HitTestApi.get_response_body_api()),
tags=[_('Knowledge Base')])
@has_permissions(lambda r, keywords: Permission(group=Group.DATASET, operate=Operate.USE,
dynamic_tag=keywords.get('dataset_id')))
def get(self, request: Request, dataset_id: str):
return result.success(
DataSetSerializers.HitTest(data={'id': dataset_id, 'user_id': request.user.id,
"query_text": request.query_params.get("query_text"),
"top_number": request.query_params.get("top_number"),
'similarity': request.query_params.get('similarity'),
'search_mode': request.query_params.get('search_mode')}).hit_test(
))
def put(self, request: Request, dataset_id: str):
return result.success(DataSetSerializers.HitTest(data={
'id': dataset_id,
'user_id': request.user.id,
"query_text": request.data.get("query_text"),
"top_number": request.data.get("top_number"),
'similarity': request.data.get('similarity'),
'search_mode': request.data.get('search_mode')}
).hit_test())
class Embedding(APIView):
authentication_classes = [TokenAuth]
@ -181,7 +181,6 @@ class Dataset(APIView):
@swagger_auto_schema(operation_summary=_('Generate related'), operation_id=_('Generate related'),
manual_parameters=DataSetSerializers.Operate.get_request_params_api(),
request_body=GenerateRelatedSerializer.get_request_body_api(),
responses=result.get_default_response(),
tags=[_('Knowledge Base')]
)
@log(menu='document', operate="Generate related documents",

View File

@ -0,0 +1,127 @@
# Generated by Django 4.2.15 on 2025-03-13 07:21
from django.db import migrations
from django.db.models import Q
mysql_template = """
def query_mysql(host,port, user, password, database, sql):
import pymysql
import json
from pymysql.cursors import DictCursor
from datetime import datetime, date
def default_serializer(obj):
from decimal import Decimal
if isinstance(obj, (datetime, date)):
return obj.isoformat() # 将 datetime/date 转换为 ISO 格式字符串
elif isinstance(obj, Decimal):
return float(obj) # 将 Decimal 转换为 float
raise TypeError(f"Type {type(obj)} not serializable")
try:
# 创建连接
db = pymysql.connect(
host=host,
port=int(port),
user=user,
password=password,
database=database,
cursorclass=DictCursor # 使用字典游标
)
# 使用 cursor() 方法创建一个游标对象 cursor
cursor = db.cursor()
# 使用 execute() 方法执行 SQL 查询
cursor.execute(sql)
# 使用 fetchall() 方法获取所有数据
data = cursor.fetchall()
# 处理 bytes 类型的数据
for row in data:
for key, value in row.items():
if isinstance(value, bytes):
row[key] = value.decode("utf-8") # 转换为字符串
# 将数据序列化为 JSON
json_data = json.dumps(data, default=default_serializer, ensure_ascii=False)
return json_data
# 关闭数据库连接
db.close()
except Exception as e:
print(f"Error while connecting to MySQL: {e}")
raise e
"""
pgsql_template = """
def queryPgSQL(database, user, password, host, port, query):
import psycopg2
import json
from datetime import datetime
# 自定义 JSON 序列化函数
def default_serializer(obj):
from decimal import Decimal
if isinstance(obj, datetime):
return obj.isoformat() # 将 datetime 转换为 ISO 格式字符串
elif isinstance(obj, Decimal):
return float(obj) # 将 Decimal 转换为 float
raise TypeError(f"Type {type(obj)} not serializable")
# 数据库连接信息
conn_params = {
"dbname": database,
"user": user,
"password": password,
"host": host,
"port": port
}
try:
# 建立连接
conn = psycopg2.connect(**conn_params)
print("连接成功!")
# 创建游标对象
cursor = conn.cursor()
# 执行查询语句
cursor.execute(query)
# 获取查询结果
rows = cursor.fetchall()
# 处理 bytes 类型的数据
columns = [desc[0] for desc in cursor.description]
result = [dict(zip(columns, row)) for row in rows]
# 转换为 JSON 格式
json_result = json.dumps(result, default=default_serializer, ensure_ascii=False)
return json_result
except Exception as e:
print(f"发生错误:{e}")
raise e
finally:
# 关闭游标和连接
if cursor:
cursor.close()
if conn:
conn.close()
"""
def fix_type(apps, schema_editor):
FunctionLib = apps.get_model('function_lib', 'FunctionLib')
FunctionLib.objects.filter(
Q(id='22c21b76-0308-11f0-9694-5618c4394482') | Q(template_id='22c21b76-0308-11f0-9694-5618c4394482')
).update(code=mysql_template)
FunctionLib.objects.filter(
Q(id='bd1e8b88-0302-11f0-87bb-5618c4394482') | Q(template_id='bd1e8b88-0302-11f0-87bb-5618c4394482')
).update(code=pgsql_template)
class Migration(migrations.Migration):
dependencies = [
('function_lib', '0003_functionlib_function_type_functionlib_icon_and_more'),
]
operations = [
migrations.RunPython(fix_type)
]

View File

@ -33,11 +33,13 @@ from smartdoc.const import CONFIG
function_executor = FunctionExecutor(CONFIG.get('SANDBOX'))
class FlibInstance:
def __init__(self, function_lib: dict, version: str):
self.function_lib = function_lib
self.version = version
def encryption(message: str):
"""
加密敏感字段数据 加密方式是 如果密码是 1234567890 那么给前端则是 123******890
@ -68,7 +70,8 @@ def encryption(message: str):
class FunctionLibModelSerializer(serializers.ModelSerializer):
class Meta:
model = FunctionLib
fields = ['id', 'name', 'icon', 'desc', 'code', 'input_field_list','init_field_list', 'init_params', 'permission_type', 'is_active', 'user_id', 'template_id',
fields = ['id', 'name', 'icon', 'desc', 'code', 'input_field_list', 'init_field_list', 'init_params',
'permission_type', 'is_active', 'user_id', 'template_id',
'create_time', 'update_time']
@ -148,7 +151,6 @@ class FunctionLibSerializer(serializers.Serializer):
select_user_id = serializers.CharField(required=False, allow_null=True, allow_blank=True)
function_type = serializers.CharField(required=False, allow_null=True, allow_blank=True)
def get_query_set(self):
query_set = QuerySet(FunctionLib).filter(
(Q(user_id=self.data.get('user_id')) | Q(permission_type='PUBLIC')))
@ -269,7 +271,7 @@ class FunctionLibSerializer(serializers.Serializer):
def is_valid(self, *, raise_exception=False):
super().is_valid(raise_exception=True)
if not QuerySet(FunctionLib).filter(id=self.data.get('id')).exists():
if not QuerySet(FunctionLib).filter(user_id=self.data.get('user_id'), id=self.data.get('id')).exists():
raise AppApiException(500, _('Function does not exist'))
def delete(self, with_valid=True):
@ -285,7 +287,8 @@ class FunctionLibSerializer(serializers.Serializer):
if with_valid:
self.is_valid(raise_exception=True)
EditFunctionLib(data=instance).is_valid(raise_exception=True)
edit_field_list = ['name', 'desc', 'code', 'icon', 'input_field_list', 'init_field_list', 'init_params', 'permission_type', 'is_active']
edit_field_list = ['name', 'desc', 'code', 'icon', 'input_field_list', 'init_field_list', 'init_params',
'permission_type', 'is_active']
edit_dict = {field: instance.get(field) for field in edit_field_list if (
field in instance and instance.get(field) is not None)}
@ -317,7 +320,8 @@ class FunctionLibSerializer(serializers.Serializer):
if function_lib.init_params:
function_lib.init_params = json.loads(rsa_long_decrypt(function_lib.init_params))
if function_lib.init_field_list:
password_fields = [i["field"] for i in function_lib.init_field_list if i.get("input_type") == "PasswordInput"]
password_fields = [i["field"] for i in function_lib.init_field_list if
i.get("input_type") == "PasswordInput"]
if function_lib.init_params:
for k in function_lib.init_params:
if k in password_fields and function_lib.init_params[k]:

View File

@ -195,53 +195,6 @@ class FunctionLibApi(ApiMixin):
}
)
@staticmethod
def get_response_body_api():
return openapi.Schema(
type=openapi.TYPE_OBJECT,
required=['id', 'name', 'code', 'input_field_list', 'permission_type'],
properties={
'id': openapi.Schema(type=openapi.TYPE_STRING, title="", description=_('ID')),
'name': openapi.Schema(type=openapi.TYPE_STRING, title=_('function name'),
description=_('function name')),
'desc': openapi.Schema(type=openapi.TYPE_STRING, title=_('function description'),
description=_('function description')),
'code': openapi.Schema(type=openapi.TYPE_STRING, title=_('function content'),
description=_('function content')),
'permission_type': openapi.Schema(type=openapi.TYPE_STRING, title=_('permission'),
description=_('permission')),
'is_active': openapi.Schema(type=openapi.TYPE_BOOLEAN, title=_('Is active'),
description=_('Is active')),
'input_field_list': openapi.Schema(type=openapi.TYPE_ARRAY,
description=_('Input variable list'),
items=openapi.Schema(type=openapi.TYPE_OBJECT,
required=['name', 'is_required', 'source'],
properties={
'name': openapi.Schema(
type=openapi.TYPE_STRING,
title=_('variable name'),
description=_('variable name')),
'is_required': openapi.Schema(
type=openapi.TYPE_BOOLEAN,
title=_('required'),
description=_('required')),
'type': openapi.Schema(
type=openapi.TYPE_STRING,
title=_('type'),
description=_(
'Field type string|int|dict|array|float')
),
'source': openapi.Schema(
type=openapi.TYPE_STRING,
title=_('source'),
description=_(
'The source only supports custom|reference')),
}))
}
)
class Export(ApiMixin):
@staticmethod
def get_request_params_api():
@ -261,4 +214,4 @@ class FunctionLibApi(ApiMixin):
type=openapi.TYPE_FILE,
required=True,
description=_('Upload image files'))
]
]

View File

@ -44,7 +44,6 @@ class FunctionLibView(APIView):
@swagger_auto_schema(operation_summary=_('Create function'),
operation_id=_('Create function'),
request_body=FunctionLibApi.Create.get_request_body_api(),
responses=result.get_api_response(FunctionLibApi.Create.get_response_body_api()),
tags=[_('Function')])
@has_permissions(RoleConstants.ADMIN, RoleConstants.USER)
@log(menu='Function', operate="Create function",
@ -59,7 +58,6 @@ class FunctionLibView(APIView):
@swagger_auto_schema(operation_summary=_('Debug function'),
operation_id=_('Debug function'),
request_body=FunctionLibApi.Debug.get_request_body_api(),
responses=result.get_default_response(),
tags=[_('Function')])
@has_permissions(RoleConstants.ADMIN, RoleConstants.USER)
def post(self, request: Request):
@ -74,7 +72,6 @@ class FunctionLibView(APIView):
@swagger_auto_schema(operation_summary=_('Update function'),
operation_id=_('Update function'),
request_body=FunctionLibApi.Edit.get_request_body_api(),
responses=result.get_api_response(FunctionLibApi.Edit.get_request_body_api()),
tags=[_('Function')])
@has_permissions(RoleConstants.ADMIN, RoleConstants.USER)
@log(menu='Function', operate="Update function",
@ -87,7 +84,6 @@ class FunctionLibView(APIView):
@action(methods=['DELETE'], detail=False)
@swagger_auto_schema(operation_summary=_('Delete function'),
operation_id=_('Delete function'),
responses=result.get_default_response(),
tags=[_('Function')])
@has_permissions(RoleConstants.ADMIN, RoleConstants.USER)
@log(menu='Function', operate="Delete function",

View File

@ -26,7 +26,6 @@ class PyLintView(APIView):
@swagger_auto_schema(operation_summary=_('Check code'),
operation_id=_('Check code'),
request_body=PyLintApi.get_request_body_api(),
responses=result.get_api_response(PyLintApi.get_request_body_api()),
tags=[_('Function')])
@has_permissions(RoleConstants.ADMIN, RoleConstants.USER)
def post(self, request: Request):

View File

@ -7238,7 +7238,7 @@ msgstr ""
msgid ""
"The confirmation password must be 6-20 characters long and must be a "
"combination of letters, numbers, and special characters."
msgstr ""
msgstr "The confirmation password must be 6-20 characters long and must be a combination of letters, numbers, and special characters.(Special character support:_、!、@、#、$、(、) ……)"
#: community/apps/users/serializers/user_serializers.py:380
#, python-brace-format
@ -7499,4 +7499,13 @@ msgid "Captcha code error or expiration"
msgstr ""
msgid "captcha"
msgstr ""
msgid "Reasoning enable"
msgstr ""
msgid "Reasoning start tag"
msgstr ""
msgid "Reasoning end tag"
msgstr ""

View File

@ -7395,7 +7395,7 @@ msgstr "语言只支持:"
msgid ""
"The confirmation password must be 6-20 characters long and must be a "
"combination of letters, numbers, and special characters."
msgstr "确认密码长度6-20个字符必须字母、数字、特殊字符组合"
msgstr "确认密码长度6-20个字符必须字母、数字、特殊字符组合特殊字符支持_、!、@、#、$、(、) ……)"
#: community/apps/users/serializers/user_serializers.py:380
#, python-brace-format
@ -7662,4 +7662,13 @@ msgid "Captcha code error or expiration"
msgstr "验证码错误或过期"
msgid "captcha"
msgstr "验证码"
msgstr "验证码"
msgid "Reasoning enable"
msgstr "开启思考过程"
msgid "Reasoning start tag"
msgstr "思考过程开始标签"
msgid "Reasoning end tag"
msgstr "思考过程结束标签"

View File

@ -7405,7 +7405,7 @@ msgstr "語言只支持:"
msgid ""
"The confirmation password must be 6-20 characters long and must be a "
"combination of letters, numbers, and special characters."
msgstr "確認密碼長度6-20個字符必須字母、數字、特殊字符組合"
msgstr "確認密碼長度6-20個字符必須字母、數字、特殊字符組合特殊字元支持_、!、@、#、$、(、) ……)"
#: community/apps/users/serializers/user_serializers.py:380
#, python-brace-format
@ -7672,4 +7672,13 @@ msgid "Captcha code error or expiration"
msgstr "驗證碼錯誤或過期"
msgid "captcha"
msgstr "驗證碼"
msgstr "驗證碼"
msgid "Reasoning enable"
msgstr "開啟思考過程"
msgid "Reasoning start tag"
msgstr "思考過程開始標籤"
msgid "Reasoning end tag"
msgstr "思考過程結束標籤"

View File

@ -99,7 +99,7 @@ class BaseChatOpenAI(ChatOpenAI):
except Exception as e:
tokenizer = TokenizerManage.get_tokenizer()
return sum([len(tokenizer.encode(get_buffer_string([m]))) for m in messages])
return self.usage_metadata.get('input_tokens', 0)
return self.usage_metadata.get('input_tokens', self.usage_metadata.get('prompt_tokens', 0))
def get_num_tokens(self, text: str) -> int:
if self.usage_metadata is None or self.usage_metadata == {}:
@ -108,7 +108,8 @@ class BaseChatOpenAI(ChatOpenAI):
except Exception as e:
tokenizer = TokenizerManage.get_tokenizer()
return len(tokenizer.encode(text))
return self.get_last_generation_info().get('output_tokens', 0)
return self.get_last_generation_info().get('output_tokens',
self.get_last_generation_info().get('completion_tokens', 0))
def _stream(self, *args: Any, **kwargs: Any) -> Iterator[ChatGenerationChunk]:
kwargs['stream_usage'] = True

View File

@ -171,24 +171,6 @@ class TeamMemberSerializer(ApiMixin, serializers.Serializer):
}
)
@staticmethod
def get_response_body_api():
return openapi.Schema(
type=openapi.TYPE_OBJECT,
properties={
'id': openapi.Schema(type=openapi.TYPE_STRING, title=_('user id'), description=_('user id')),
'username': openapi.Schema(type=openapi.TYPE_STRING, title=_('Username'), description=_('Username')),
'email': openapi.Schema(type=openapi.TYPE_STRING, title=_('Email'), description=_('Email')),
'role': openapi.Schema(type=openapi.TYPE_STRING, title=_('Role'), description=_('Role')),
'is_active': openapi.Schema(type=openapi.TYPE_STRING, title=_('Is active'),
description=_('Is active')),
'team_id': openapi.Schema(type=openapi.TYPE_STRING, title=_('team id'), description=_('team id')),
'user_id': openapi.Schema(type=openapi.TYPE_STRING, title=_('user id'), description=_('user id')),
'type': openapi.Schema(type=openapi.TYPE_STRING, title=_('member type'),
description=_('member type manage|member')),
}
)
@transaction.atomic
def batch_add_member(self, user_id_list: List[str], with_valid=True):
"""

View File

@ -38,7 +38,6 @@ class TeamMember(APIView):
@swagger_auto_schema(operation_summary=_('Add member'),
operation_id=_('Add member'),
request_body=TeamMemberSerializer().get_request_body_api(),
responses=result.get_default_response(),
tags=[_('Team')])
@has_permissions(PermissionConstants.TEAM_CREATE)
@log(menu='Team', operate='Add member',
@ -54,7 +53,6 @@ class TeamMember(APIView):
@swagger_auto_schema(operation_summary=_('Add members in batches'),
operation_id=_('Add members in batches'),
request_body=TeamMemberSerializer.get_bach_request_body_api(),
responses=result.get_api_array_response(TeamMemberSerializer.get_response_body_api()),
tags=[_('Team')])
@has_permissions(PermissionConstants.TEAM_CREATE)
@log(menu='Team', operate='Add members in batches',
@ -80,7 +78,6 @@ class TeamMember(APIView):
@swagger_auto_schema(operation_summary=_('Update team member permissions'),
operation_id=_('Update team member permissions'),
request_body=UpdateTeamMemberPermissionSerializer().get_request_body_api(),
responses=result.get_default_response(),
manual_parameters=TeamMemberSerializer.Operate.get_request_params_api(),
tags=[_('Team')]
)
@ -96,7 +93,6 @@ class TeamMember(APIView):
@swagger_auto_schema(operation_summary=_('Remove member'),
operation_id=_('Remove member'),
manual_parameters=TeamMemberSerializer.Operate.get_request_params_api(),
responses=result.get_default_response(),
tags=[_('Team')]
)
@has_permissions(PermissionConstants.TEAM_DELETE)

View File

@ -31,8 +31,7 @@ class Model(APIView):
@action(methods=['POST'], detail=False)
@swagger_auto_schema(operation_summary=_('Create model'),
operation_id=_('Create model'),
request_body=ModelCreateApi.get_request_body_api(),
manual_parameters=result.get_api_response(ModelCreateApi.get_request_body_api())
request_body=ModelCreateApi.get_request_body_api()
, tags=[_('model')])
@has_permissions(PermissionConstants.MODEL_CREATE)
@log(menu='model', operate='Create model',
@ -46,8 +45,7 @@ class Model(APIView):
@action(methods=['PUT'], detail=False)
@swagger_auto_schema(operation_summary=_('Download model, trial only with Ollama platform'),
operation_id=_('Download model, trial only with Ollama platform'),
request_body=ModelCreateApi.get_request_body_api(),
responses=result.get_api_response(ModelCreateApi.get_request_body_api())
request_body=ModelCreateApi.get_request_body_api()
, tags=[_('model')])
@has_permissions(PermissionConstants.MODEL_CREATE)
def put(self, request: Request):
@ -125,8 +123,7 @@ class Model(APIView):
@action(methods=['PUT'], detail=False)
@swagger_auto_schema(operation_summary=_('Update model'),
operation_id=_('Update model'),
request_body=ModelEditApi.get_request_body_api(),
responses=result.get_api_response(ModelEditApi.get_request_body_api())
request_body=ModelEditApi.get_request_body_api()
, tags=[_('model')])
@has_permissions(PermissionConstants.MODEL_CREATE)
@log(menu='model', operate='Update model',
@ -169,8 +166,7 @@ class Provide(APIView):
@swagger_auto_schema(operation_summary=_('Call the supplier function to obtain form data'),
operation_id=_('Call the supplier function to obtain form data'),
manual_parameters=ProvideApi.get_request_params_api(),
request_body=ProvideApi.get_request_body_api(),
responses=result.get_api_response(ProvideApi.get_request_body_api())
request_body=ProvideApi.get_request_body_api()
, tags=[_('model')])
@has_permissions(PermissionConstants.MODEL_READ)
@log(menu='model', operate='Call the supplier function to obtain form data')

View File

@ -7,6 +7,7 @@
2. 程序需要, 用户不需要更改的写到settings中
3. 程序需要, 用户需要更改的写到本config中
"""
import datetime
import errno
import logging
import os
@ -112,13 +113,18 @@ class Config(dict):
"USER": self.get('DB_USER'),
"PASSWORD": self.get('DB_PASSWORD'),
"ENGINE": self.get('DB_ENGINE'),
"CONN_MAX_AGE": 0,
"POOL_OPTIONS": {
"POOL_SIZE": 20,
"MAX_OVERFLOW": int(self.get('DB_MAX_OVERFLOW')),
'RECYCLE': 30 * 60
"RECYCLE": 1800,
"TIMEOUT": 30
}
}
def get_session_timeout(self):
return datetime.timedelta(seconds=int(self.get('SESSION_TIMEOUT', 60 * 60 * 2)))
def get_language_code(self):
return self.get('LANGUAGE_CODE', 'zh-CN')

View File

@ -22,7 +22,7 @@ from common.constants.permission_constants import PermissionConstants, CompareCo
from common.log.log import log
from common.response import result
from common.util.common import encryption
from smartdoc.settings import JWT_AUTH
from smartdoc.const import CONFIG
from users.serializers.user_serializers import RegisterSerializer, LoginSerializer, CheckCodeSerializer, \
RePasswordSerializer, \
SendEmailSerializer, UserProfile, UserSerializer, UserManageSerializer, UserInstanceSerializer, SystemSerializer, \
@ -84,7 +84,7 @@ class SwitchUserLanguageView(APIView):
description=_("language")),
}
),
responses=result.get_default_response(),
responses=RePasswordSerializer().get_response_body_api(),
tags=[_("User management")])
@log(menu='User management', operate='Switch Language',
get_operation_object=lambda r, k: {'name': r.user.username})
@ -111,7 +111,7 @@ class ResetCurrentUserPasswordView(APIView):
description=_("Password"))
}
),
responses=result.get_default_response(),
responses=RePasswordSerializer().get_response_body_api(),
tags=[_("User management")])
@log(menu='User management', operate='Modify current user password',
get_operation_object=lambda r, k: {'name': r.user.username},
@ -199,7 +199,7 @@ class Login(APIView):
# 校验请求参数
user = login_request.is_valid(raise_exception=True)
token = login_request.get_user_token()
token_cache.set(token, user, timeout=JWT_AUTH['JWT_EXPIRATION_DELTA'])
token_cache.set(token, user, timeout=CONFIG.get_session_timeout())
return result.success(token)

View File

@ -38,6 +38,7 @@
"mermaid": "^10.9.0",
"mitt": "^3.0.0",
"moment": "^2.30.1",
"nanoid": "^5.1.5",
"npm": "^10.2.4",
"nprogress": "^0.2.0",
"pinia": "^2.1.6",
@ -53,8 +54,7 @@
"vue-draggable-plus": "^0.6.0",
"vue-i18n": "^9.13.1",
"vue-router": "^4.2.4",
"vue3-menus": "^1.1.2",
"vuedraggable": "^4.1.0"
"vue3-menus": "^1.1.2"
},
"devDependencies": {
"@rushstack/eslint-patch": "^1.3.2",

View File

@ -227,7 +227,7 @@ const getApplicationHitTest: (
data: any,
loading?: Ref<boolean>
) => Promise<Result<Array<any>>> = (application_id, data, loading) => {
return get(`${prefix}/${application_id}/hit_test`, data, loading)
return put(`${prefix}/${application_id}/hit_test`, data, undefined, loading)
}
/**

View File

@ -186,7 +186,7 @@ const getDatasetHitTest: (
data: any,
loading?: Ref<boolean>
) => Promise<Result<Array<any>>> = (dataset_id, data, loading) => {
return get(`${prefix}/${dataset_id}/hit_test`, data, loading)
return put(`${prefix}/${dataset_id}/hit_test`, data, undefined, loading)
}
/**

View File

@ -182,6 +182,7 @@
@keydown.enter="sendChatHandle($event)"
@paste="handlePaste"
@drop="handleDrop"
@dragover.prevent="handleDragOver"
/>
<div class="operate flex align-center">
@ -287,7 +288,7 @@
</div>
</template>
<script setup lang="ts">
import { ref, computed, onMounted, nextTick, watch } from 'vue'
import { ref, computed, onMounted, nextTick, watch, reactive } from 'vue'
import Recorder from 'recorder-core'
import TouchChat from './TouchChat.vue'
import applicationApi from '@/api/application'
@ -392,17 +393,7 @@ const checkMaxFilesLimit = () => {
uploadOtherList.value.length
)
}
const file_name_eq = (str: string, str1: string) => {
return (
str.replaceAll(' ', '') === str1.replaceAll(' ', '') ||
decodeHtmlEntities(str) === decodeHtmlEntities(str1)
)
}
function decodeHtmlEntities(str: string) {
const tempDiv = document.createElement('div')
tempDiv.innerHTML = str
return tempDiv.textContent || tempDiv.innerText || ''
}
const uploadFile = async (file: any, fileList: any) => {
const { maxFiles, fileLimit } = props.applicationDetails.file_upload_setting
//
@ -427,6 +418,7 @@ const uploadFile = async (file: any, fileList: any) => {
const formData = new FormData()
formData.append('file', file.raw, file.name)
//
file = reactive(file)
const extension = file.name.split('.').pop().toUpperCase() //
if (imageExtensions.includes(extension)) {
uploadImageList.value.push(file)
@ -460,41 +452,9 @@ const uploadFile = async (file: any, fileList: any) => {
)
.then((response) => {
fileList.splice(0, fileList.length)
uploadImageList.value.forEach((file: any) => {
const f = response.data.filter((f: any) => file_name_eq(f.name, file.name))
if (f.length > 0) {
file.url = f[0].url
file.file_id = f[0].file_id
}
})
uploadDocumentList.value.forEach((file: any) => {
const f = response.data.filter((f: any) => file_name_eq(f.name, file.name))
if (f.length > 0) {
file.url = f[0].url
file.file_id = f[0].file_id
}
})
uploadAudioList.value.forEach((file: any) => {
const f = response.data.filter((f: any) => file_name_eq(f.name, file.name))
if (f.length > 0) {
file.url = f[0].url
file.file_id = f[0].file_id
}
})
uploadVideoList.value.forEach((file: any) => {
const f = response.data.filter((f: any) => file_name_eq(f.name, file.name))
if (f.length > 0) {
file.url = f[0].url
file.file_id = f[0].file_id
}
})
uploadOtherList.value.forEach((file: any) => {
const f = response.data.filter((f: any) => file_name_eq(f.name, file.name))
if (f.length > 0) {
file.url = f[0].url
file.file_id = f[0].file_id
}
})
file.url = response.data[0].url
file.file_id = response.data[0].file_id
if (!inputValue.value && uploadImageList.value.length > 0) {
inputValue.value = t('chat.uploadFile.imageMessage')
}
@ -529,6 +489,7 @@ const handlePaste = (event: ClipboardEvent) => {
//
event.preventDefault()
}
//
const handleDrop = (event: DragEvent) => {
if (!props.applicationDetails.file_upload_enable) return
@ -548,6 +509,12 @@ const handleDrop = (event: DragEvent) => {
uploadFile(elFile, [elFile])
})
}
const handleDragOver = (event: DragEvent) => {
if (event.dataTransfer) {
event.dataTransfer.dropEffect = 'copy' // Firefox
}
}
// id
const intervalId = ref<any | null>(null)
//

View File

@ -58,7 +58,7 @@ const prologue = computed(() => {
]
let _temp = temp
for (const index in tag_list) {
_temp = _temp.replaceAll(tag_list[index], '')
_temp = _temp.replace(new RegExp(tag_list[index], 'g'), '')
}
const quick_question_list = _temp.match(/-\s.+/g)
let result = temp

View File

@ -163,13 +163,13 @@ const initialApiFormData = ref({})
const isUserInput = computed(
() =>
props.applicationDetails.work_flow?.nodes?.filter((v: any) => v.id === 'base-node')[0]
.properties.user_input_field_list.length > 0
?.properties.user_input_field_list.length > 0
)
const isAPIInput = computed(
() =>
props.type === 'debug-ai-chat' &&
props.applicationDetails.work_flow?.nodes?.filter((v: any) => v.id === 'base-node')[0]
.properties.api_input_field_list.length > 0
?.properties.api_input_field_list.length > 0
)
const showUserInputContent = computed(() => {
return (

View File

@ -9,7 +9,7 @@
class="problem-button mt-4 mb-4 flex"
:class="sendMessage ? 'cursor' : 'disabled'"
>
<el-icon class="mr-8" style="margin-top: 2px;">
<el-icon class="mr-8" style="margin-top: 2px">
<EditPen />
</el-icon>
{{ item.content }}
@ -237,7 +237,7 @@ const split_form_rander_ = (source: string, type: string) => {
padding: 12px;
box-sizing: border-box;
color: var(--el-text-color-regular);
word-break: break-all;
word-break: break-word;
&:hover {
background: var(--el-color-primary-light-9);

View File

@ -139,7 +139,7 @@ Response requirements:
hybridSearch: 'Hybrid Search',
hybridSearchTooltip:
'Hybrid search is a retrieval method based on both vector and text similarity, suitable for medium data volumes in the knowledge.',
similarityThreshold: 'Similarity higher than',
similarityThreshold: 'Similarity not lower than',
similarityTooltip: 'The higher the similarity, the stronger the correlation.',
topReferences: 'Top N Segments',
maxCharacters: 'Maximum Characters per Reference',

View File

@ -149,7 +149,7 @@ export default {
tooltip: 'When user asks a question, handle matched segments according to the set method.'
},
similarity: {
label: 'Similarity Higher Than',
label: 'Similarity not lower than',
placeholder: 'Directly return segment content',
requiredMessage: 'Please enter similarity value'
}

View File

@ -130,7 +130,7 @@ export default {
hybridSearch: '混合检索',
hybridSearchTooltip:
'混合检索是一种基于向量和文本相似度的检索方式,适用于知识库中的中等数据量场景。',
similarityThreshold: '相似度于',
similarityThreshold: '相似度不低于',
similarityTooltip: '相似度越高相关性越强。',
topReferences: '引用分段数 TOP',
maxCharacters: '最多引用字符数',

View File

@ -147,7 +147,7 @@ export default {
tooltip: '用户提问时,命中文档下的分段时按照设置的方式进行处理。'
},
similarity: {
label: '相似度于',
label: '相似度不低于',
placeholder: '直接返回分段内容',
requiredMessage: '请输入相似度'
}

View File

@ -129,7 +129,7 @@ export default {
hybridSearch: '混合檢索',
hybridSearchTooltip:
'混合檢索是一種基於向量和文本相似度的檢索方式,適用於知識庫中的中等數據量場景。',
similarityThreshold: '相似度於',
similarityThreshold: '相似度不低於',
similarityTooltip: '相似度越高相關性越強。',
topReferences: '引用分段數 TOP',
maxCharacters: '最多引用字元數',

View File

@ -146,7 +146,7 @@ export default {
tooltip: '用戶提問時,命中文檔下的分段時按照設置的方式進行處理。'
},
similarity: {
label: '相似度高于',
label: '相似度不低於',
placeholder: '直接返回分段内容',
requiredMessage: '请输入相似度'
}

View File

@ -1,5 +1,5 @@
import { MsgError } from '@/utils/message'
import { nanoid } from 'nanoid'
export function toThousands(num: any) {
return num?.toString().replace(/\d+/, function (n: any) {
return n.replace(/(\d)(?=(?:\d{3})+$)/g, '$1,')
@ -25,7 +25,7 @@ export function filesize(size: number) {
id
*/
export const randomId = function () {
return Math.floor(Math.random() * 10000) + ''
return nanoid()
}
/*
@ -48,7 +48,9 @@ const typeList: any = {
export function getImgUrl(name: string) {
const list = Object.values(typeList).flat()
const type = list.includes(fileType(name).toLowerCase()) ? fileType(name).toLowerCase() : 'unknown'
const type = list.includes(fileType(name).toLowerCase())
? fileType(name).toLowerCase()
: 'unknown'
return new URL(`../assets/fileType/${type}-icon.svg`, import.meta.url).href
}
// 是否是白名单后缀

View File

@ -3,7 +3,7 @@
<div class="header border-b flex-between p-12-24">
<div class="flex align-center">
<back-button @click="back"></back-button>
<h4>{{ detail?.name }}</h4>
<h4 class="ellipsis" style="max-width: 270px" :title="detail?.name">{{ detail?.name }}</h4>
<div v-if="showHistory && disablePublic">
<el-text type="info" class="ml-16 color-secondary"
>{{ $t('views.applicationWorkflow.info.previewVersion') }}
@ -101,7 +101,7 @@
/>
</div>
<h4>
<h4 class="ellipsis" style="max-width: 270px" :title="detail?.name">
{{ detail?.name || $t('views.application.applicationForm.form.appName.label') }}
</h4>
</div>
@ -279,7 +279,6 @@ async function publicHandle() {
return
}
applicationApi.putPublishApplication(id as String, obj, loading).then(() => {
application.asyncGetApplicationDetail(id, loading).then((res: any) => {
detail.value.name = res.data.name
MsgSuccess(t('views.applicationWorkflow.tip.publicSuccess'))

View File

@ -28,7 +28,9 @@
/>
</div>
<h4>{{ applicationDetail?.name }}</h4>
<h4 class="ellipsis-1" style="width: 50%" :title="applicationDetail?.name">
{{ applicationDetail?.name }}
</h4>
</div>
</div>
<div>
@ -263,7 +265,7 @@ function getChatRecord() {
currentChatId.value,
paginationConfig,
loading,
false
true
)
.then((res: any) => {
paginationConfig.total = res.data.total

View File

@ -27,7 +27,9 @@
/>
</div>
<h4>{{ applicationDetail?.name }}</h4>
<h4 class="ellipsis-1" style="width: 66%" :title="applicationDetail?.name">
{{ applicationDetail?.name }}
</h4>
</div>
</div>
<div>
@ -259,7 +261,7 @@ function getChatRecord() {
currentChatId.value,
paginationConfig,
loading,
false
true
)
.then((res: any) => {
paginationConfig.total = res.data.total

View File

@ -27,7 +27,9 @@
:size="32"
/>
</div>
<h4>{{ applicationDetail?.name }}</h4>
<h4 class="ellipsis-1" style="width: 66%" :title="applicationDetail?.name">
{{ applicationDetail?.name }}
</h4>
</div>
</div>
<div>
@ -313,7 +315,7 @@ function getChatRecord() {
currentChatId.value,
paginationConfig.value,
loading,
false
true
)
.then((res: any) => {
paginationConfig.value.total = res.data.total

View File

@ -8,15 +8,16 @@
</h4>
</template>
<div class="hit-test__main p-16" v-loading="loading">
<div class="question-title" :style="{ visibility: questionTitle ? 'visible' : 'hidden' }">
<div class="avatar">
<AppAvatar>
<img src="@/assets/user-icon.svg" style="width: 54%" alt="" />
</AppAvatar>
</div>
<div class="content">
<h4 class="text break-all">{{ questionTitle }}</h4>
</div>
<div
class="question-title flex align-center"
:style="{ visibility: questionTitle ? 'visible' : 'hidden' }"
>
<AppAvatar>
<img src="@/assets/user-icon.svg" style="width: 54%" alt="" />
</AppAvatar>
<h4 class="break-all ellipsis-1 ml-8" style="width: 66%" :title="questionTitle">
{{ questionTitle }}
</h4>
</div>
<el-scrollbar>
<div class="hit-test-height">
@ -349,20 +350,6 @@ onMounted(() => {})
</script>
<style lang="scss" scoped>
.hit-test {
.question-title {
.avatar {
float: left;
}
.content {
padding-left: 40px;
.text {
padding: 6px 0;
height: 34px;
box-sizing: border-box;
}
}
}
&__operate {
.operate-textarea {
box-shadow: 0px 6px 24px 0px rgba(31, 35, 41, 0.08);

View File

@ -12,7 +12,6 @@
ref="el"
v-bind:modelValue="form_data.branch"
:disabled="form_data.branch === 2"
:filter="'.no-drag'"
handle=".handle"
:animation="150"
ghostClass="ghost"

View File

@ -91,6 +91,7 @@ import { ref, computed, onMounted } from 'vue'
import { isLastNode } from '@/workflow/common/data'
import applicationApi from '@/api/application'
import { app } from '@/main'
import {t} from "@/locales";
const props = defineProps<{ nodeModel: any }>()
const nodeCascaderRef = ref()
@ -119,8 +120,16 @@ const chat_data = computed({
const FunctionNodeFormRef = ref<FormInstance>()
const validate = () => {
for (const item of chat_data.value.input_field_list) {
if (item.source === 'reference' && item.is_required && item.value[0] !== 'global') {
if (props.nodeModel.graphModel.nodes.filter((node: any) => node.id === item.value[0]).length === 0 ) {
item.value = []
return Promise.reject({node: props.nodeModel, errMessage: item.name + t('dynamicsForm.tip.requiredMessage')})
}
}
}
return FunctionNodeFormRef.value?.validate().catch((err) => {
return Promise.reject({ node: props.nodeModel, errMessage: err })
return Promise.reject({node: props.nodeModel, errMessage: err})
})
}