mirror of
https://github.com/1Panel-dev/MaxKB.git
synced 2025-12-26 18:22:46 +00:00
Compare commits
No commits in common. "main" and "v1.10.5-lts" have entirely different histories.
main
...
v1.10.5-lt
|
|
@ -6,12 +6,4 @@ updates:
|
|||
interval: "weekly"
|
||||
timezone: "Asia/Shanghai"
|
||||
day: "friday"
|
||||
target-branch: "v2"
|
||||
groups:
|
||||
python-dependencies:
|
||||
patterns:
|
||||
- "*"
|
||||
# ignore:
|
||||
# - dependency-name: "pymupdf"
|
||||
# versions: ["*"]
|
||||
|
||||
target-branch: "v2"
|
||||
|
|
@ -7,7 +7,7 @@ on:
|
|||
inputs:
|
||||
dockerImageTag:
|
||||
description: 'Image Tag'
|
||||
default: 'v1.10.7-dev'
|
||||
default: 'v1.10.3-dev'
|
||||
required: true
|
||||
dockerImageTagWithLatest:
|
||||
description: '是否发布latest tag(正式发版时选择,测试版本切勿选择)'
|
||||
|
|
@ -36,7 +36,7 @@ on:
|
|||
jobs:
|
||||
build-and-push-to-fit2cloud-registry:
|
||||
if: ${{ contains(github.event.inputs.registry, 'fit2cloud') }}
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Check Disk Space
|
||||
run: df -h
|
||||
|
|
@ -52,6 +52,10 @@ jobs:
|
|||
swap-storage: true
|
||||
- name: Check Disk Space
|
||||
run: df -h
|
||||
- name: Set Swap Space
|
||||
uses: pierotofy/set-swap-space@master
|
||||
with:
|
||||
swap-size-gb: 8
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
|
|
@ -64,17 +68,24 @@ jobs:
|
|||
TAG_NAME=${{ github.event.inputs.dockerImageTag }}
|
||||
TAG_NAME_WITH_LATEST=${{ github.event.inputs.dockerImageTagWithLatest }}
|
||||
if [[ ${TAG_NAME_WITH_LATEST} == 'true' ]]; then
|
||||
DOCKER_IMAGE_TAGS="--tag ${DOCKER_IMAGE}:${TAG_NAME} --tag ${DOCKER_IMAGE}:${TAG_NAME%%.*}"
|
||||
DOCKER_IMAGE_TAGS="--tag ${DOCKER_IMAGE}:${TAG_NAME} --tag ${DOCKER_IMAGE}:latest"
|
||||
else
|
||||
DOCKER_IMAGE_TAGS="--tag ${DOCKER_IMAGE}:${TAG_NAME}"
|
||||
fi
|
||||
echo ::set-output name=buildx_args::--platform ${DOCKER_PLATFORMS} --memory-swap -1 \
|
||||
--build-arg DOCKER_IMAGE_TAG=${{ github.event.inputs.dockerImageTag }} --build-arg BUILD_AT=$(TZ=Asia/Shanghai date +'%Y-%m-%dT%H:%M') --build-arg GITHUB_COMMIT=`git rev-parse --short HEAD` --no-cache \
|
||||
--build-arg DOCKER_IMAGE_TAG=${{ github.event.inputs.dockerImageTag }} --build-arg BUILD_AT=$(TZ=Asia/Shanghai date +'%Y-%m-%dT%H:%M') --build-arg GITHUB_COMMIT=${GITHUB_SHA::8} --no-cache \
|
||||
${DOCKER_IMAGE_TAGS} .
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
with:
|
||||
# Until https://github.com/tonistiigi/binfmt/issues/215
|
||||
image: tonistiigi/binfmt:qemu-v7.0.0-28
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
with:
|
||||
buildkitd-config-inline: |
|
||||
[worker.oci]
|
||||
max-parallelism = 1
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
|
|
@ -89,12 +100,11 @@ jobs:
|
|||
password: ${{ secrets.FIT2CLOUD_REGISTRY_PASSWORD }}
|
||||
- name: Docker Buildx (build-and-push)
|
||||
run: |
|
||||
sudo sync && echo 3 | sudo tee /proc/sys/vm/drop_caches && free -m
|
||||
docker buildx build --output "type=image,push=true" ${{ steps.prepare.outputs.buildx_args }} -f installer/Dockerfile
|
||||
|
||||
build-and-push-to-dockerhub:
|
||||
if: ${{ contains(github.event.inputs.registry, 'dockerhub') }}
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Check Disk Space
|
||||
run: df -h
|
||||
|
|
@ -110,6 +120,10 @@ jobs:
|
|||
swap-storage: true
|
||||
- name: Check Disk Space
|
||||
run: df -h
|
||||
- name: Set Swap Space
|
||||
uses: pierotofy/set-swap-space@master
|
||||
with:
|
||||
swap-size-gb: 8
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
|
|
@ -122,17 +136,24 @@ jobs:
|
|||
TAG_NAME=${{ github.event.inputs.dockerImageTag }}
|
||||
TAG_NAME_WITH_LATEST=${{ github.event.inputs.dockerImageTagWithLatest }}
|
||||
if [[ ${TAG_NAME_WITH_LATEST} == 'true' ]]; then
|
||||
DOCKER_IMAGE_TAGS="--tag ${DOCKER_IMAGE}:${TAG_NAME} --tag ${DOCKER_IMAGE}:${TAG_NAME%%.*}"
|
||||
DOCKER_IMAGE_TAGS="--tag ${DOCKER_IMAGE}:${TAG_NAME} --tag ${DOCKER_IMAGE}:latest"
|
||||
else
|
||||
DOCKER_IMAGE_TAGS="--tag ${DOCKER_IMAGE}:${TAG_NAME}"
|
||||
fi
|
||||
echo ::set-output name=buildx_args::--platform ${DOCKER_PLATFORMS} --memory-swap -1 \
|
||||
--build-arg DOCKER_IMAGE_TAG=${{ github.event.inputs.dockerImageTag }} --build-arg BUILD_AT=$(TZ=Asia/Shanghai date +'%Y-%m-%dT%H:%M') --build-arg GITHUB_COMMIT=`git rev-parse --short HEAD` --no-cache \
|
||||
--build-arg DOCKER_IMAGE_TAG=${{ github.event.inputs.dockerImageTag }} --build-arg BUILD_AT=$(TZ=Asia/Shanghai date +'%Y-%m-%dT%H:%M') --build-arg GITHUB_COMMIT=${GITHUB_SHA::8} --no-cache \
|
||||
${DOCKER_IMAGE_TAGS} .
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
with:
|
||||
# Until https://github.com/tonistiigi/binfmt/issues/215
|
||||
image: tonistiigi/binfmt:qemu-v7.0.0-28
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
with:
|
||||
buildkitd-config-inline: |
|
||||
[worker.oci]
|
||||
max-parallelism = 1
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
|
|
@ -146,5 +167,4 @@ jobs:
|
|||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
- name: Docker Buildx (build-and-push)
|
||||
run: |
|
||||
sudo sync && echo 3 | sudo tee /proc/sys/vm/drop_caches && free -m
|
||||
docker buildx build --output "type=image,push=true" ${{ steps.prepare.outputs.buildx_args }} -f installer/Dockerfile
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
<p align="center"><img src= "https://github.com/1Panel-dev/maxkb/assets/52996290/c0694996-0eed-40d8-b369-322bf2a380bf" alt="MaxKB" width="300" /></p>
|
||||
<h3 align="center">Open-source platform for building enterprise-grade agents</h3>
|
||||
<h3 align="center">强大易用的企业级智能体平台</h3>
|
||||
<h3 align="center">An Open-Source AI Assistant for Enterprise</h3>
|
||||
<p align="center"><a href="https://trendshift.io/repositories/9113" target="_blank"><img src="https://trendshift.io/api/badge/repositories/9113" alt="1Panel-dev%2FMaxKB | Trendshift" style="width: 250px; height: 55px;" width="250" height="55"/></a></p>
|
||||
<p align="center">
|
||||
<a href="https://www.gnu.org/licenses/gpl-3.0.html#license-text"><img src="https://img.shields.io/github/license/1Panel-dev/maxkb?color=%231890FF" alt="License: GPL v3"></a>
|
||||
|
|
@ -11,7 +10,7 @@
|
|||
</p>
|
||||
<hr/>
|
||||
|
||||
MaxKB = Max Knowledge Brain, it is an open-source platform for building enterprise-grade agents. MaxKB integrates Retrieval-Augmented Generation (RAG) pipelines, supports robust workflows, and provides advanced MCP tool-use capabilities. MaxKB is widely applied in scenarios such as intelligent customer service, corporate internal knowledge bases, academic research, and education.
|
||||
MaxKB = Max Knowledge Brain, it is a powerful and easy-to-use AI assistant that integrates Retrieval-Augmented Generation (RAG) pipelines, supports robust workflows, and provides advanced MCP tool-use capabilities. MaxKB is widely applied in scenarios such as intelligent customer service, corporate internal knowledge bases, academic research, and education.
|
||||
|
||||
- **RAG Pipeline**: Supports direct uploading of documents / automatic crawling of online documents, with features for automatic text splitting, vectorization. This effectively reduces hallucinations in large models, providing a superior smart Q&A interaction experience.
|
||||
- **Agentic Workflow**: Equipped with a powerful workflow engine, function library and MCP tool-use, enabling the orchestration of AI processes to meet the needs of complex business scenarios.
|
||||
|
|
@ -56,6 +55,8 @@ Access MaxKB web interface at `http://your_server_ip:8080` with default admin cr
|
|||
|
||||
## Feature Comparison
|
||||
|
||||
MaxKB is positioned as an Ready-to-use RAG (Retrieval-Augmented Generation) intelligent Q&A application, rather than a middleware platform for building large model applications. The following table is merely a comparison from a functional perspective.
|
||||
|
||||
<table style="width: 100%;">
|
||||
<tr>
|
||||
<th align="center">Feature</th>
|
||||
|
|
|
|||
13
README_CN.md
13
README_CN.md
|
|
@ -1,20 +1,19 @@
|
|||
<p align="center"><img src= "https://github.com/1Panel-dev/maxkb/assets/52996290/c0694996-0eed-40d8-b369-322bf2a380bf" alt="MaxKB" width="300" /></p>
|
||||
<h3 align="center">强大易用的企业级智能体平台</h3>
|
||||
<h3 align="center">强大易用的企业级 AI 助手</h3>
|
||||
<p align="center">
|
||||
<a href="https://trendshift.io/repositories/9113" target="_blank"><img src="https://trendshift.io/api/badge/repositories/9113" alt="1Panel-dev%2FMaxKB | Trendshift" style="width: 250px; height: auto;" /></a>
|
||||
<a href="https://market.aliyun.com/products/53690006/cmjj00067609.html?userCode=kmemb8jp" target="_blank"><img src="https://img.alicdn.com/imgextra/i2/O1CN01H5JIwY1rZ0OobDjnJ_!!6000000005644-2-tps-1000-216.png" alt="1Panel-dev%2FMaxKB | Aliyun" style="width: 250px; height: auto;" /></a>
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="README_EN.md"><img src="https://img.shields.io/badge/English_README-blue" alt="English README"></a>
|
||||
<a href="https://www.gnu.org/licenses/gpl-3.0.html#license-text"><img src="https://img.shields.io/github/license/1Panel-dev/maxkb?color=%231890FF" alt="License: GPL v3"></a>
|
||||
<a href="https://www.gnu.org/licenses/gpl-3.0.html#license-text"><img src="https://img.shields.io/github/license/1Panel-dev/maxkb" alt="License: GPL v3"></a>
|
||||
<a href="https://github.com/1Panel-dev/maxkb/releases/latest"><img src="https://img.shields.io/github/v/release/1Panel-dev/maxkb" alt="Latest release"></a>
|
||||
<a href="https://github.com/1Panel-dev/maxkb"><img src="https://img.shields.io/github/stars/1Panel-dev/maxkb?style=flat-square" alt="Stars"></a>
|
||||
<a href="https://hub.docker.com/r/1panel/maxkb"><img src="https://img.shields.io/docker/pulls/1panel/maxkb?label=downloads" alt="Download"></a>
|
||||
<a href="https://gitee.com/fit2cloud-feizhiyun/MaxKB"><img src="https://gitee.com/fit2cloud-feizhiyun/MaxKB/badge/star.svg?theme=gvp" alt="Gitee Stars"></a>
|
||||
<a href="https://gitcode.com/feizhiyun/MaxKB"><img src="https://gitcode.com/feizhiyun/MaxKB/star/badge.svg" alt="GitCode Stars"></a>
|
||||
<a href="https://github.com/1Panel-dev/maxkb"><img src="https://img.shields.io/github/stars/1Panel-dev/maxkb?style=flat-square" alt="Stars"></a>
|
||||
<a href="https://hub.docker.com/r/1panel/maxkb"><img src="https://img.shields.io/docker/pulls/1panel/maxkb?label=downloads" alt="Download"></a>
|
||||
</p>
|
||||
<hr/>
|
||||
|
||||
MaxKB = Max Knowledge Brain,是一款强大易用的企业级智能体平台,支持 RAG 检索增强生成、工作流编排、MCP 工具调用能力。MaxKB 支持对接各种主流大语言模型,广泛应用于智能客服、企业内部知识库问答、员工助手、学术研究与教育等场景。
|
||||
MaxKB = Max Knowledge Brain,是一款强大易用的企业级 AI 助手,支持 RAG 检索增强生成、工作流编排、MCP 工具调用能力。MaxKB 支持对接各种主流大语言模型,广泛应用于智能客服、企业内部知识库问答、员工助手、学术研究与教育等场景。
|
||||
|
||||
- **RAG 检索增强生成**:高效搭建本地 AI 知识库,支持直接上传文档 / 自动爬取在线文档,支持文本自动拆分、向量化,有效减少大模型幻觉,提升问答效果;
|
||||
- **灵活编排**:内置强大的工作流引擎、函数库和 MCP 工具调用能力,支持编排 AI 工作过程,满足复杂业务场景下的需求;
|
||||
|
|
|
|||
|
|
@ -40,7 +40,6 @@ tool_message_template = """
|
|||
|
||||
"""
|
||||
|
||||
|
||||
def _write_context(node_variable: Dict, workflow_variable: Dict, node: INode, workflow, answer: str,
|
||||
reasoning_content: str):
|
||||
chat_model = node_variable.get('chat_model')
|
||||
|
|
@ -103,6 +102,7 @@ def write_context_stream(node_variable: Dict, workflow_variable: Dict, node: INo
|
|||
_write_context(node_variable, workflow_variable, node, workflow, answer, reasoning_content)
|
||||
|
||||
|
||||
|
||||
async def _yield_mcp_response(chat_model, message_list, mcp_servers):
|
||||
async with MultiServerMCPClient(json.loads(mcp_servers)) as client:
|
||||
agent = create_react_agent(chat_model, client.get_tools())
|
||||
|
|
@ -115,7 +115,6 @@ async def _yield_mcp_response(chat_model, message_list, mcp_servers):
|
|||
if isinstance(chunk[0], AIMessageChunk):
|
||||
yield chunk[0]
|
||||
|
||||
|
||||
def mcp_response_generator(chat_model, message_list, mcp_servers):
|
||||
loop = asyncio.new_event_loop()
|
||||
try:
|
||||
|
|
@ -131,7 +130,6 @@ def mcp_response_generator(chat_model, message_list, mcp_servers):
|
|||
finally:
|
||||
loop.close()
|
||||
|
||||
|
||||
async def anext_async(agen):
|
||||
return await agen.__anext__()
|
||||
|
||||
|
|
@ -188,8 +186,7 @@ class BaseChatNode(IChatNode):
|
|||
self.context['answer'] = details.get('answer')
|
||||
self.context['question'] = details.get('question')
|
||||
self.context['reasoning_content'] = details.get('reasoning_content')
|
||||
if self.node_params.get('is_result', False):
|
||||
self.answer_text = details.get('answer')
|
||||
self.answer_text = details.get('answer')
|
||||
|
||||
def execute(self, model_id, system, prompt, dialogue_number, history_chat_record, stream, chat_id, chat_record_id,
|
||||
model_params_setting=None,
|
||||
|
|
@ -219,7 +216,7 @@ class BaseChatNode(IChatNode):
|
|||
message_list = self.generate_message_list(system, prompt, history_message)
|
||||
self.context['message_list'] = message_list
|
||||
|
||||
if mcp_enable and mcp_servers is not None and '"stdio"' not in mcp_servers:
|
||||
if mcp_enable and mcp_servers is not None:
|
||||
r = mcp_response_generator(chat_model, message_list, mcp_servers)
|
||||
return NodeResult(
|
||||
{'result': r, 'chat_model': chat_model, 'message_list': message_list,
|
||||
|
|
|
|||
|
|
@ -168,8 +168,7 @@ class BaseApplicationNode(IApplicationNode):
|
|||
self.context['question'] = details.get('question')
|
||||
self.context['type'] = details.get('type')
|
||||
self.context['reasoning_content'] = details.get('reasoning_content')
|
||||
if self.node_params.get('is_result', False):
|
||||
self.answer_text = details.get('answer')
|
||||
self.answer_text = details.get('answer')
|
||||
|
||||
def execute(self, application_id, message, chat_id, chat_record_id, stream, re_chat, client_id, client_type,
|
||||
app_document_list=None, app_image_list=None, app_audio_list=None, child_node=None, node_data=None,
|
||||
|
|
@ -179,8 +178,7 @@ class BaseApplicationNode(IApplicationNode):
|
|||
current_chat_id = string_to_uuid(chat_id + application_id)
|
||||
Chat.objects.get_or_create(id=current_chat_id, defaults={
|
||||
'application_id': application_id,
|
||||
'abstract': message[0:1024],
|
||||
'client_id': client_id,
|
||||
'abstract': message[0:1024]
|
||||
})
|
||||
if app_document_list is None:
|
||||
app_document_list = []
|
||||
|
|
|
|||
|
|
@ -15,9 +15,7 @@ from application.flow.step_node.direct_reply_node.i_reply_node import IReplyNode
|
|||
class BaseReplyNode(IReplyNode):
|
||||
def save_context(self, details, workflow_manage):
|
||||
self.context['answer'] = details.get('answer')
|
||||
if self.node_params.get('is_result', False):
|
||||
self.answer_text = details.get('answer')
|
||||
|
||||
self.answer_text = details.get('answer')
|
||||
def execute(self, reply_type, stream, fields=None, content=None, **kwargs) -> NodeResult:
|
||||
if reply_type == 'referencing':
|
||||
result = self.get_reference_content(fields)
|
||||
|
|
|
|||
|
|
@ -38,8 +38,7 @@ class BaseFormNode(IFormNode):
|
|||
self.context['start_time'] = details.get('start_time')
|
||||
self.context['form_data'] = form_data
|
||||
self.context['is_submit'] = details.get('is_submit')
|
||||
if self.node_params.get('is_result', False):
|
||||
self.answer_text = details.get('result')
|
||||
self.answer_text = details.get('result')
|
||||
if form_data is not None:
|
||||
for key in form_data:
|
||||
self.context[key] = form_data[key]
|
||||
|
|
@ -71,7 +70,7 @@ class BaseFormNode(IFormNode):
|
|||
"chat_record_id": self.flow_params_serializer.data.get("chat_record_id"),
|
||||
'form_data': self.context.get('form_data', {}),
|
||||
"is_submit": self.context.get("is_submit", False)}
|
||||
form = f'<form_rander>{json.dumps(form_setting, ensure_ascii=False)}</form_rander>'
|
||||
form = f'<form_rander>{json.dumps(form_setting,ensure_ascii=False)}</form_rander>'
|
||||
context = self.workflow_manage.get_workflow_content()
|
||||
form_content_format = self.workflow_manage.reset_prompt(form_content_format)
|
||||
prompt_template = PromptTemplate.from_template(form_content_format, template_format='jinja2')
|
||||
|
|
@ -86,7 +85,7 @@ class BaseFormNode(IFormNode):
|
|||
"chat_record_id": self.flow_params_serializer.data.get("chat_record_id"),
|
||||
'form_data': self.context.get('form_data', {}),
|
||||
"is_submit": self.context.get("is_submit", False)}
|
||||
form = f'<form_rander>{json.dumps(form_setting, ensure_ascii=False)}</form_rander>'
|
||||
form = f'<form_rander>{json.dumps(form_setting,ensure_ascii=False)}</form_rander>'
|
||||
context = self.workflow_manage.get_workflow_content()
|
||||
form_content_format = self.workflow_manage.reset_prompt(form_content_format)
|
||||
prompt_template = PromptTemplate.from_template(form_content_format, template_format='jinja2')
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ def valid_reference_value(_type, value, name):
|
|||
|
||||
|
||||
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)):
|
||||
if not is_required and value is None:
|
||||
return None
|
||||
if not is_required and source == 'reference' and (value is None or len(value) == 0):
|
||||
return None
|
||||
|
|
@ -113,8 +113,7 @@ def valid_function(function_lib, user_id):
|
|||
class BaseFunctionLibNodeNode(IFunctionLibNode):
|
||||
def save_context(self, details, workflow_manage):
|
||||
self.context['result'] = details.get('result')
|
||||
if self.node_params.get('is_result'):
|
||||
self.answer_text = str(details.get('result'))
|
||||
self.answer_text = str(details.get('result'))
|
||||
|
||||
def execute(self, function_lib_id, input_field_list, **kwargs) -> NodeResult:
|
||||
function_lib = QuerySet(FunctionLib).filter(id=function_lib_id).first()
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ def valid_reference_value(_type, value, name):
|
|||
|
||||
|
||||
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)):
|
||||
if not is_required and value is None:
|
||||
return None
|
||||
if source == 'reference':
|
||||
value = node.workflow_manage.get_reference_field(
|
||||
|
|
@ -84,8 +84,7 @@ def convert_value(name: str, value, _type, is_required, source, node):
|
|||
class BaseFunctionNodeNode(IFunctionNode):
|
||||
def save_context(self, details, workflow_manage):
|
||||
self.context['result'] = details.get('result')
|
||||
if self.node_params.get('is_result', False):
|
||||
self.answer_text = str(details.get('result'))
|
||||
self.answer_text = str(details.get('result'))
|
||||
|
||||
def execute(self, input_field_list, code, **kwargs) -> NodeResult:
|
||||
params = {field.get('name'): convert_value(field.get('name'), field.get('value'), field.get('type'),
|
||||
|
|
|
|||
|
|
@ -16,8 +16,7 @@ class BaseImageGenerateNode(IImageGenerateNode):
|
|||
def save_context(self, details, workflow_manage):
|
||||
self.context['answer'] = details.get('answer')
|
||||
self.context['question'] = details.get('question')
|
||||
if self.node_params.get('is_result', False):
|
||||
self.answer_text = details.get('answer')
|
||||
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,
|
||||
|
|
@ -25,8 +24,7 @@ class BaseImageGenerateNode(IImageGenerateNode):
|
|||
**kwargs) -> NodeResult:
|
||||
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)
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -69,8 +69,7 @@ class BaseImageUnderstandNode(IImageUnderstandNode):
|
|||
def save_context(self, details, workflow_manage):
|
||||
self.context['answer'] = details.get('answer')
|
||||
self.context['question'] = details.get('question')
|
||||
if self.node_params.get('is_result', False):
|
||||
self.answer_text = details.get('answer')
|
||||
self.answer_text = details.get('answer')
|
||||
|
||||
def execute(self, model_id, system, prompt, dialogue_number, dialogue_type, history_chat_record, stream, chat_id,
|
||||
model_params_setting,
|
||||
|
|
|
|||
|
|
@ -14,8 +14,7 @@ class BaseMcpNode(IMcpNode):
|
|||
self.context['result'] = details.get('result')
|
||||
self.context['tool_params'] = details.get('tool_params')
|
||||
self.context['mcp_tool'] = details.get('mcp_tool')
|
||||
if self.node_params.get('is_result', False):
|
||||
self.answer_text = details.get('result')
|
||||
self.answer_text = details.get('result')
|
||||
|
||||
def execute(self, mcp_servers, mcp_server, mcp_tool, tool_params, **kwargs) -> NodeResult:
|
||||
servers = json.loads(mcp_servers)
|
||||
|
|
@ -28,8 +27,7 @@ class BaseMcpNode(IMcpNode):
|
|||
return s
|
||||
|
||||
res = asyncio.run(call_tool(servers, mcp_server, mcp_tool, params))
|
||||
return NodeResult(
|
||||
{'result': [content.text for content in res.content], 'tool_params': params, 'mcp_tool': mcp_tool}, {})
|
||||
return NodeResult({'result': [content.text for content in res.content], 'tool_params': params, 'mcp_tool': mcp_tool}, {})
|
||||
|
||||
def handle_variables(self, tool_params):
|
||||
# 处理参数中的变量
|
||||
|
|
|
|||
|
|
@ -80,8 +80,7 @@ class BaseQuestionNode(IQuestionNode):
|
|||
self.context['answer'] = details.get('answer')
|
||||
self.context['message_tokens'] = details.get('message_tokens')
|
||||
self.context['answer_tokens'] = details.get('answer_tokens')
|
||||
if self.node_params.get('is_result', False):
|
||||
self.answer_text = details.get('answer')
|
||||
self.answer_text = details.get('answer')
|
||||
|
||||
def execute(self, model_id, system, prompt, dialogue_number, history_chat_record, stream, chat_id, chat_record_id,
|
||||
model_params_setting=None,
|
||||
|
|
|
|||
|
|
@ -18,8 +18,7 @@ class BaseSpeechToTextNode(ISpeechToTextNode):
|
|||
|
||||
def save_context(self, details, workflow_manage):
|
||||
self.context['answer'] = details.get('answer')
|
||||
if self.node_params.get('is_result', False):
|
||||
self.answer_text = details.get('answer')
|
||||
self.answer_text = details.get('answer')
|
||||
|
||||
def execute(self, stt_model_id, chat_id, audio, **kwargs) -> NodeResult:
|
||||
stt_model = get_model_instance_by_model_user_id(stt_model_id, self.flow_params_serializer.data.get('user_id'))
|
||||
|
|
|
|||
|
|
@ -45,8 +45,6 @@ class BaseStartStepNode(IStarNode):
|
|||
self.err_message = details.get('err_message')
|
||||
for key, value in workflow_variable.items():
|
||||
workflow_manage.context[key] = value
|
||||
for item in details.get('global_fields', []):
|
||||
workflow_manage.context[item.get('key')] = item.get('value')
|
||||
|
||||
def get_node_params_serializer_class(self) -> Type[serializers.Serializer]:
|
||||
pass
|
||||
|
|
|
|||
|
|
@ -37,8 +37,7 @@ def bytes_to_uploaded_file(file_bytes, file_name="generated_audio.mp3"):
|
|||
class BaseTextToSpeechNode(ITextToSpeechNode):
|
||||
def save_context(self, details, workflow_manage):
|
||||
self.context['answer'] = details.get('answer')
|
||||
if self.node_params.get('is_result', False):
|
||||
self.answer_text = details.get('answer')
|
||||
self.answer_text = details.get('answer')
|
||||
|
||||
def execute(self, tts_model_id, chat_id,
|
||||
content, model_params_setting=None,
|
||||
|
|
|
|||
|
|
@ -148,12 +148,10 @@ class ModelSettingSerializer(serializers.Serializer):
|
|||
error_messages=ErrMessage.char(_("Thinking process switch")))
|
||||
reasoning_content_start = serializers.CharField(required=False, allow_null=True, default="<think>",
|
||||
allow_blank=True, max_length=256,
|
||||
trim_whitespace=False,
|
||||
error_messages=ErrMessage.char(
|
||||
_("The thinking process begins to mark")))
|
||||
reasoning_content_end = serializers.CharField(required=False, allow_null=True, allow_blank=True, default="</think>",
|
||||
max_length=256,
|
||||
trim_whitespace=False,
|
||||
error_messages=ErrMessage.char(_("End of thinking process marker")))
|
||||
|
||||
|
||||
|
|
@ -164,7 +162,7 @@ class ApplicationWorkflowSerializer(serializers.Serializer):
|
|||
max_length=256, min_length=1,
|
||||
error_messages=ErrMessage.char(_("Application Description")))
|
||||
work_flow = serializers.DictField(required=False, error_messages=ErrMessage.dict(_("Workflow Objects")))
|
||||
prologue = serializers.CharField(required=False, allow_null=True, allow_blank=True, max_length=102400,
|
||||
prologue = serializers.CharField(required=False, allow_null=True, allow_blank=True, max_length=4096,
|
||||
error_messages=ErrMessage.char(_("Opening remarks")))
|
||||
|
||||
@staticmethod
|
||||
|
|
@ -227,7 +225,7 @@ class ApplicationSerializer(serializers.Serializer):
|
|||
min_value=0,
|
||||
max_value=1024,
|
||||
error_messages=ErrMessage.integer(_("Historical chat records")))
|
||||
prologue = serializers.CharField(required=False, allow_null=True, allow_blank=True, max_length=102400,
|
||||
prologue = serializers.CharField(required=False, allow_null=True, allow_blank=True, max_length=4096,
|
||||
error_messages=ErrMessage.char(_("Opening remarks")))
|
||||
dataset_id_list = serializers.ListSerializer(required=False, child=serializers.UUIDField(required=True),
|
||||
allow_null=True,
|
||||
|
|
@ -495,7 +493,7 @@ class ApplicationSerializer(serializers.Serializer):
|
|||
min_value=0,
|
||||
max_value=1024,
|
||||
error_messages=ErrMessage.integer(_("Historical chat records")))
|
||||
prologue = serializers.CharField(required=False, allow_null=True, allow_blank=True, max_length=102400,
|
||||
prologue = serializers.CharField(required=False, allow_null=True, allow_blank=True, max_length=4096,
|
||||
error_messages=ErrMessage.char(_("Opening remarks")))
|
||||
dataset_id_list = serializers.ListSerializer(required=False, child=serializers.UUIDField(required=True),
|
||||
error_messages=ErrMessage.list(_("Related Knowledge Base"))
|
||||
|
|
@ -1012,8 +1010,7 @@ class ApplicationSerializer(serializers.Serializer):
|
|||
'stt_autosend': application.stt_autosend,
|
||||
'file_upload_enable': application.file_upload_enable,
|
||||
'file_upload_setting': application.file_upload_setting,
|
||||
'work_flow': {'nodes': [node for node in ((application.work_flow or {}).get('nodes', []) or []) if
|
||||
node.get('id') == 'base-node']},
|
||||
'work_flow': application.work_flow,
|
||||
'show_source': application_access_token.show_source,
|
||||
'language': application_access_token.language,
|
||||
**application_setting_dict})
|
||||
|
|
@ -1325,8 +1322,6 @@ class ApplicationSerializer(serializers.Serializer):
|
|||
def get_mcp_servers(self, with_valid=True):
|
||||
if with_valid:
|
||||
self.is_valid(raise_exception=True)
|
||||
if '"stdio"' in self.data.get('mcp_servers'):
|
||||
raise AppApiException(500, _('stdio is not supported'))
|
||||
servers = json.loads(self.data.get('mcp_servers'))
|
||||
|
||||
async def get_mcp_tools(servers):
|
||||
|
|
|
|||
|
|
@ -174,14 +174,7 @@ class ChatSerializers(serializers.Serializer):
|
|||
condition = base_condition & min_trample_query
|
||||
else:
|
||||
condition = base_condition
|
||||
inner_queryset = QuerySet(Chat).filter(application_id=self.data.get("application_id"))
|
||||
if 'abstract' in self.data and self.data.get('abstract') is not None:
|
||||
inner_queryset = inner_queryset.filter(abstract__icontains=self.data.get('abstract'))
|
||||
|
||||
return {
|
||||
'inner_queryset': inner_queryset,
|
||||
'default_queryset': query_set.filter(condition).order_by("-application_chat.update_time")
|
||||
}
|
||||
return query_set.filter(condition).order_by("-application_chat.update_time")
|
||||
|
||||
def list(self, with_valid=True):
|
||||
if with_valid:
|
||||
|
|
|
|||
|
|
@ -23,8 +23,6 @@ FROM
|
|||
chat_id
|
||||
FROM
|
||||
application_chat_record
|
||||
WHERE chat_id IN (
|
||||
SELECT id FROM application_chat ${inner_queryset})
|
||||
GROUP BY
|
||||
application_chat_record.chat_id
|
||||
) chat_record_temp ON application_chat."id" = chat_record_temp.chat_id
|
||||
|
|
@ -37,5 +35,4 @@ FROM
|
|||
END as improve_paragraph_list
|
||||
FROM
|
||||
application_chat_record application_chat_record
|
||||
) application_chat_record_temp ON application_chat_record_temp.chat_id = application_chat."id"
|
||||
${default_queryset}
|
||||
) application_chat_record_temp ON application_chat_record_temp.chat_id = application_chat."id"
|
||||
|
|
@ -11,9 +11,6 @@ FROM
|
|||
chat_id
|
||||
FROM
|
||||
application_chat_record
|
||||
WHERE chat_id IN (
|
||||
SELECT id FROM application_chat ${inner_queryset})
|
||||
GROUP BY
|
||||
application_chat_record.chat_id
|
||||
) chat_record_temp ON application_chat."id" = chat_record_temp.chat_id
|
||||
${default_queryset}
|
||||
) chat_record_temp ON application_chat."id" = chat_record_temp.chat_id
|
||||
|
|
@ -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():
|
||||
|
|
@ -428,56 +367,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():
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
|
|
|
|||
|
|
@ -373,8 +373,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 +415,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 +455,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 +472,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",
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -11,50 +11,35 @@ import time
|
|||
|
||||
from common.cache.mem_cache import MemCache
|
||||
|
||||
_lock = threading.Lock()
|
||||
locks = {}
|
||||
lock = threading.Lock()
|
||||
|
||||
|
||||
class ModelManage:
|
||||
cache = MemCache('model', {})
|
||||
up_clear_time = time.time()
|
||||
|
||||
@staticmethod
|
||||
def _get_lock(_id):
|
||||
lock = locks.get(_id)
|
||||
if lock is None:
|
||||
with _lock:
|
||||
lock = locks.get(_id)
|
||||
if lock is None:
|
||||
lock = threading.Lock()
|
||||
locks[_id] = lock
|
||||
|
||||
return lock
|
||||
|
||||
@staticmethod
|
||||
def get_model(_id, get_model):
|
||||
model_instance = ModelManage.cache.get(_id)
|
||||
if model_instance is None:
|
||||
lock = ModelManage._get_lock(_id)
|
||||
with lock:
|
||||
model_instance = ModelManage.cache.get(_id)
|
||||
if model_instance is None:
|
||||
model_instance = get_model(_id)
|
||||
ModelManage.cache.set(_id, model_instance, timeout=60 * 60 * 8)
|
||||
else:
|
||||
if model_instance.is_cache_model():
|
||||
ModelManage.cache.touch(_id, timeout=60 * 60 * 8)
|
||||
else:
|
||||
# 获取锁
|
||||
lock.acquire()
|
||||
try:
|
||||
model_instance = ModelManage.cache.get(_id)
|
||||
if model_instance is None or not model_instance.is_cache_model():
|
||||
model_instance = get_model(_id)
|
||||
ModelManage.cache.set(_id, model_instance, timeout=60 * 60 * 8)
|
||||
ModelManage.clear_timeout_cache()
|
||||
return model_instance
|
||||
ModelManage.cache.set(_id, model_instance, timeout=60 * 30)
|
||||
return model_instance
|
||||
# 续期
|
||||
ModelManage.cache.touch(_id, timeout=60 * 30)
|
||||
ModelManage.clear_timeout_cache()
|
||||
return model_instance
|
||||
finally:
|
||||
# 释放锁
|
||||
lock.release()
|
||||
|
||||
@staticmethod
|
||||
def clear_timeout_cache():
|
||||
if time.time() - ModelManage.up_clear_time > 60 * 60:
|
||||
threading.Thread(target=lambda: ModelManage.cache.clear_timeout_data()).start()
|
||||
ModelManage.up_clear_time = time.time()
|
||||
if time.time() - ModelManage.up_clear_time > 60:
|
||||
ModelManage.cache.clear_timeout_data()
|
||||
|
||||
@staticmethod
|
||||
def delete_key(_id):
|
||||
|
|
|
|||
|
|
@ -238,8 +238,11 @@ class ListenerManagement:
|
|||
for key in params_dict:
|
||||
_value_ = params_dict[key]
|
||||
exec_sql = exec_sql.replace(key, str(_value_))
|
||||
with lock:
|
||||
lock.acquire()
|
||||
try:
|
||||
native_update(query_set, exec_sql)
|
||||
finally:
|
||||
lock.release()
|
||||
|
||||
@staticmethod
|
||||
def embedding_by_document(document_id, embedding_model: Embeddings, state_list=None):
|
||||
|
|
@ -269,6 +272,7 @@ class ListenerManagement:
|
|||
ListenerManagement.update_status(QuerySet(Document).filter(id=document_id), TaskType.EMBEDDING,
|
||||
State.STARTED)
|
||||
|
||||
|
||||
# 根据段落进行向量化处理
|
||||
page_desc(QuerySet(Paragraph)
|
||||
.annotate(
|
||||
|
|
|
|||
|
|
@ -22,4 +22,3 @@ from .table_checkbox import *
|
|||
from .radio_card_field import *
|
||||
from .label import *
|
||||
from .slider_field import *
|
||||
from .switch_field import *
|
||||
|
|
|
|||
|
|
@ -28,6 +28,6 @@ class SwitchField(BaseField):
|
|||
@param props_info:
|
||||
"""
|
||||
|
||||
super().__init__('SwitchInput', label, required, default_value, relation_show_field_dict,
|
||||
super().__init__('Switch', label, required, default_value, relation_show_field_dict,
|
||||
{},
|
||||
TriggerType.OPTION_LIST, attrs, props_info)
|
||||
|
|
|
|||
|
|
@ -19,24 +19,36 @@ class XlsxSplitHandle(BaseParseTableHandle):
|
|||
|
||||
def fill_merged_cells(self, sheet, image_dict):
|
||||
data = []
|
||||
|
||||
# 获取第一行作为标题行
|
||||
headers = []
|
||||
for idx, cell in enumerate(sheet[1]):
|
||||
if cell.value is None:
|
||||
headers.append(' ' * (idx + 1))
|
||||
else:
|
||||
headers.append(cell.value)
|
||||
|
||||
# 从第二行开始遍历每一行
|
||||
for row in sheet.iter_rows(values_only=False):
|
||||
row_data = []
|
||||
for row in sheet.iter_rows(min_row=2, values_only=False):
|
||||
row_data = {}
|
||||
for col_idx, cell in enumerate(row):
|
||||
cell_value = cell.value
|
||||
|
||||
# 如果单元格为空,并且该单元格在合并单元格内,获取合并单元格的值
|
||||
if cell_value is None:
|
||||
for merged_range in sheet.merged_cells.ranges:
|
||||
if cell.coordinate in merged_range:
|
||||
cell_value = sheet[merged_range.min_row][merged_range.min_col - 1].value
|
||||
break
|
||||
|
||||
image = image_dict.get(cell_value, None)
|
||||
if image is not None:
|
||||
cell_value = f''
|
||||
|
||||
# 使用标题作为键,单元格的值作为值存入字典
|
||||
row_data.insert(col_idx, cell_value)
|
||||
row_data[headers[col_idx]] = cell_value
|
||||
data.append(row_data)
|
||||
|
||||
for merged_range in sheet.merged_cells.ranges:
|
||||
cell_value = data[merged_range.min_row - 1][merged_range.min_col - 1]
|
||||
for row_index in range(merged_range.min_row, merged_range.max_row + 1):
|
||||
for col_index in range(merged_range.min_col, merged_range.max_col + 1):
|
||||
data[row_index - 1][col_index - 1] = cell_value
|
||||
return data
|
||||
|
||||
def handle(self, file, get_buffer, save_image):
|
||||
|
|
@ -53,13 +65,11 @@ class XlsxSplitHandle(BaseParseTableHandle):
|
|||
paragraphs = []
|
||||
ws = wb[sheetname]
|
||||
data = self.fill_merged_cells(ws, image_dict)
|
||||
if len(data) >= 2:
|
||||
head_list = data[0]
|
||||
for row_index in range(1, len(data)):
|
||||
row_output = "; ".join(
|
||||
[f"{head_list[col_index]}: {data[row_index][col_index]}" for col_index in
|
||||
range(0, len(data[row_index]))])
|
||||
paragraphs.append({'title': '', 'content': row_output})
|
||||
|
||||
for row in data:
|
||||
row_output = "; ".join([f"{key}: {value}" for key, value in row.items()])
|
||||
# print(row_output)
|
||||
paragraphs.append({'title': '', 'content': row_output})
|
||||
|
||||
result.append({'name': sheetname, 'paragraphs': paragraphs})
|
||||
|
||||
|
|
@ -68,6 +78,7 @@ class XlsxSplitHandle(BaseParseTableHandle):
|
|||
return [{'name': file.name, 'paragraphs': []}]
|
||||
return result
|
||||
|
||||
|
||||
def get_content(self, file, save_image):
|
||||
try:
|
||||
# 加载 Excel 文件
|
||||
|
|
@ -83,18 +94,18 @@ class XlsxSplitHandle(BaseParseTableHandle):
|
|||
# 如果未指定 sheet_name,则使用第一个工作表
|
||||
for sheetname in workbook.sheetnames:
|
||||
sheet = workbook[sheetname] if sheetname else workbook.active
|
||||
data = self.fill_merged_cells(sheet, image_dict)
|
||||
if len(data) == 0:
|
||||
rows = self.fill_merged_cells(sheet, image_dict)
|
||||
if len(rows) == 0:
|
||||
continue
|
||||
# 提取表头和内容
|
||||
|
||||
headers = [f"{value}" for value in data[0]]
|
||||
headers = [f"{key}" for key, value in rows[0].items()]
|
||||
|
||||
# 构建 Markdown 表格
|
||||
md_table = '| ' + ' | '.join(headers) + ' |\n'
|
||||
md_table += '| ' + ' | '.join(['---'] * len(headers)) + ' |\n'
|
||||
for row_index in range(1, len(data)):
|
||||
r = [f'{value}' for value in data[row_index]]
|
||||
for row in rows:
|
||||
r = [f'{value}' for key, value in row.items()]
|
||||
md_table += '| ' + ' | '.join(
|
||||
[str(cell).replace('\n', '<br>') if cell is not None else '' for cell in r]) + ' |\n'
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ from common.handle.base_split_handle import BaseSplitHandle
|
|||
|
||||
|
||||
def post_cell(cell_value):
|
||||
return cell_value.replace('\r\n', '<br>').replace('\n', '<br>').replace('|', '|')
|
||||
return cell_value.replace('\n', '<br>').replace('|', '|')
|
||||
|
||||
|
||||
def row_to_md(row):
|
||||
|
|
|
|||
|
|
@ -24,13 +24,12 @@ class GunicornLocalModelService(BaseService):
|
|||
os.environ.setdefault('SERVER_NAME', 'local_model')
|
||||
log_format = '%(h)s %(t)s %(L)ss "%(r)s" %(s)s %(b)s '
|
||||
bind = f'{CONFIG.get("LOCAL_MODEL_HOST")}:{CONFIG.get("LOCAL_MODEL_PORT")}'
|
||||
worker = CONFIG.get("LOCAL_MODEL_HOST_WORKER", 1)
|
||||
cmd = [
|
||||
'gunicorn', 'smartdoc.wsgi:application',
|
||||
'-b', bind,
|
||||
'-k', 'gthread',
|
||||
'--threads', '200',
|
||||
'-w', str(worker),
|
||||
'-w', "1",
|
||||
'--max-requests', '10240',
|
||||
'--max-requests-jitter', '2048',
|
||||
'--access-logformat', log_format,
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ import importlib
|
|||
import io
|
||||
import mimetypes
|
||||
import pickle
|
||||
import random
|
||||
import re
|
||||
import shutil
|
||||
from functools import reduce
|
||||
|
|
@ -298,14 +297,3 @@ def markdown_to_plain_text(md: str) -> str:
|
|||
# 去除首尾空格
|
||||
text = text.strip()
|
||||
return text
|
||||
|
||||
|
||||
SAFE_CHAR_SET = (
|
||||
[chr(i) for i in range(65, 91) if chr(i) not in {'I', 'O'}] + # 大写字母 A-H, J-N, P-Z
|
||||
[chr(i) for i in range(97, 123) if chr(i) not in {'i', 'l', 'o'}] + # 小写字母 a-h, j-n, p-z
|
||||
[str(i) for i in range(10) if str(i) not in {'0', '1', '7'}] # 数字 2-6, 8-9
|
||||
)
|
||||
|
||||
|
||||
def get_random_chars(number=4):
|
||||
return ''.join(random.choices(SAFE_CHAR_SET, k=number))
|
||||
|
|
|
|||
|
|
@ -7,12 +7,13 @@
|
|||
@desc:
|
||||
"""
|
||||
import os
|
||||
import pickle
|
||||
import subprocess
|
||||
import sys
|
||||
import uuid
|
||||
from textwrap import dedent
|
||||
|
||||
from diskcache import Cache
|
||||
|
||||
from smartdoc.const import BASE_DIR
|
||||
from smartdoc.const import PROJECT_DIR
|
||||
|
||||
|
|
@ -36,8 +37,6 @@ class FunctionExecutor:
|
|||
old_mask = os.umask(0o077)
|
||||
try:
|
||||
os.makedirs(self.sandbox_path, 0o700, exist_ok=True)
|
||||
os.makedirs(os.path.join(self.sandbox_path, 'execute'), 0o700, exist_ok=True)
|
||||
os.makedirs(os.path.join(self.sandbox_path, 'result'), 0o700, exist_ok=True)
|
||||
finally:
|
||||
os.umask(old_mask)
|
||||
|
||||
|
|
@ -45,11 +44,10 @@ class FunctionExecutor:
|
|||
_id = str(uuid.uuid1())
|
||||
success = '{"code":200,"msg":"成功","data":exec_result}'
|
||||
err = '{"code":500,"msg":str(e),"data":None}'
|
||||
result_path = f'{self.sandbox_path}/result/{_id}.result'
|
||||
path = r'' + self.sandbox_path + ''
|
||||
_exec_code = f"""
|
||||
try:
|
||||
import os
|
||||
import pickle
|
||||
env = dict(os.environ)
|
||||
for key in list(env.keys()):
|
||||
if key in os.environ and (key.startswith('MAXKB') or key.startswith('POSTGRES') or key.startswith('PG')):
|
||||
|
|
@ -62,11 +60,13 @@ try:
|
|||
for local in locals_v:
|
||||
globals_v[local] = locals_v[local]
|
||||
exec_result=f(**keywords)
|
||||
with open({result_path!a}, 'wb') as file:
|
||||
file.write(pickle.dumps({success}))
|
||||
from diskcache import Cache
|
||||
cache = Cache({path!a})
|
||||
cache.set({_id!a},{success})
|
||||
except Exception as e:
|
||||
with open({result_path!a}, 'wb') as file:
|
||||
file.write(pickle.dumps({err}))
|
||||
from diskcache import Cache
|
||||
cache = Cache({path!a})
|
||||
cache.set({_id!a},{err})
|
||||
"""
|
||||
if self.sandbox:
|
||||
subprocess_result = self._exec_sandbox(_exec_code, _id)
|
||||
|
|
@ -74,18 +74,18 @@ except Exception as e:
|
|||
subprocess_result = self._exec(_exec_code)
|
||||
if subprocess_result.returncode == 1:
|
||||
raise Exception(subprocess_result.stderr)
|
||||
with open(result_path, 'rb') as file:
|
||||
result = pickle.loads(file.read())
|
||||
os.remove(result_path)
|
||||
cache = Cache(self.sandbox_path)
|
||||
result = cache.get(_id)
|
||||
cache.delete(_id)
|
||||
if result.get('code') == 200:
|
||||
return result.get('data')
|
||||
raise Exception(result.get('msg'))
|
||||
|
||||
def _exec_sandbox(self, _code, _id):
|
||||
exec_python_file = f'{self.sandbox_path}/execute/{_id}.py'
|
||||
exec_python_file = f'{self.sandbox_path}/{_id}.py'
|
||||
with open(exec_python_file, 'w') as file:
|
||||
file.write(_code)
|
||||
os.system(f"chown {self.user}:root {exec_python_file}")
|
||||
os.system(f"chown {self.user}:{self.user} {exec_python_file}")
|
||||
kwargs = {'cwd': BASE_DIR}
|
||||
subprocess_result = subprocess.run(
|
||||
['su', '-s', python_directory, '-c', "exec(open('" + exec_python_file + "').read())", self.user],
|
||||
|
|
|
|||
|
|
@ -40,12 +40,15 @@ def generate():
|
|||
def get_key_pair():
|
||||
rsa_value = rsa_cache.get(cache_key)
|
||||
if rsa_value is None:
|
||||
with lock:
|
||||
rsa_value = rsa_cache.get(cache_key)
|
||||
if rsa_value is not None:
|
||||
return rsa_value
|
||||
lock.acquire()
|
||||
rsa_value = rsa_cache.get(cache_key)
|
||||
if rsa_value is not None:
|
||||
return rsa_value
|
||||
try:
|
||||
rsa_value = get_key_pair_by_sql()
|
||||
rsa_cache.set(cache_key, rsa_value)
|
||||
finally:
|
||||
lock.release()
|
||||
return rsa_value
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -23,8 +23,6 @@ from django.db import transaction, models
|
|||
from django.db.models import QuerySet, Count
|
||||
from django.db.models.functions import Substr, Reverse
|
||||
from django.http import HttpResponse
|
||||
from django.utils.translation import get_language
|
||||
from django.utils.translation import gettext_lazy as _, gettext, to_locale
|
||||
from drf_yasg import openapi
|
||||
from openpyxl.cell.cell import ILLEGAL_CHARACTERS_RE
|
||||
from rest_framework import serializers
|
||||
|
|
@ -66,6 +64,8 @@ from embedding.task.embedding import embedding_by_document, delete_embedding_by_
|
|||
embedding_by_document_list
|
||||
from setting.models import Model
|
||||
from smartdoc.conf import PROJECT_DIR
|
||||
from django.utils.translation import gettext_lazy as _, gettext, to_locale
|
||||
from django.utils.translation import get_language
|
||||
|
||||
parse_qa_handle_list = [XlsParseQAHandle(), CsvParseQAHandle(), XlsxParseQAHandle(), ZipParseQAHandle()]
|
||||
parse_table_handle_list = [CsvSplitTableHandle(), XlsSplitTableHandle(), XlsxSplitTableHandle()]
|
||||
|
|
@ -661,8 +661,6 @@ class DocumentSerializers(ApiMixin, serializers.Serializer):
|
|||
cell = worksheet.cell(row=row_idx + 1, column=col_idx + 1)
|
||||
if isinstance(col, str):
|
||||
col = re.sub(ILLEGAL_CHARACTERS_RE, '', col)
|
||||
if col.startswith(('=', '+', '-', '@')):
|
||||
col = '\ufeff' + col
|
||||
cell.value = col
|
||||
# 创建HttpResponse对象返回Excel文件
|
||||
return workbook
|
||||
|
|
|
|||
|
|
@ -28,9 +28,6 @@ mime_types = {"html": "text/html", "htm": "text/html", "shtml": "text/html", "cs
|
|||
"woff2": "font/woff2", "jar": "application/java-archive", "war": "application/java-archive",
|
||||
"ear": "application/java-archive", "json": "application/json", "hqx": "application/mac-binhex40",
|
||||
"doc": "application/msword", "pdf": "application/pdf", "ps": "application/postscript",
|
||||
"docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||
"xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
"pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
||||
"eps": "application/postscript", "ai": "application/postscript", "rtf": "application/rtf",
|
||||
"m3u8": "application/vnd.apple.mpegurl", "kml": "application/vnd.google-earth.kml+xml",
|
||||
"kmz": "application/vnd.google-earth.kmz", "xls": "application/vnd.ms-excel",
|
||||
|
|
@ -90,4 +87,4 @@ class FileSerializer(serializers.Serializer):
|
|||
'Content-Disposition': 'attachment; filename="{}"'.format(
|
||||
file.file_name)})
|
||||
return HttpResponse(file.get_byte(), status=200,
|
||||
headers={'Content-Type': mime_types.get(file_type, 'text/plain')})
|
||||
headers={'Content-Type': mime_types.get(file.file_name.split(".")[-1], 'text/plain')})
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ UPDATE "document"
|
|||
SET "char_length" = ( SELECT CASE WHEN
|
||||
"sum" ( "char_length" ( "content" ) ) IS NULL THEN
|
||||
0 ELSE "sum" ( "char_length" ( "content" ) )
|
||||
END FROM paragraph WHERE "document_id" = %s ),
|
||||
"update_time" = CURRENT_TIMESTAMP
|
||||
END FROM paragraph WHERE "document_id" = %s )
|
||||
WHERE
|
||||
"id" = %s
|
||||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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'))
|
||||
]
|
||||
]
|
||||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -7490,13 +7490,4 @@ msgid "Field: {name} No value set"
|
|||
msgstr ""
|
||||
|
||||
msgid "Generate related"
|
||||
msgstr ""
|
||||
|
||||
msgid "Obtain graphical captcha"
|
||||
msgstr ""
|
||||
|
||||
msgid "Captcha code error or expiration"
|
||||
msgstr ""
|
||||
|
||||
msgid "captcha"
|
||||
msgstr ""
|
||||
|
|
@ -7653,13 +7653,4 @@ msgid "Field: {name} No value set"
|
|||
msgstr "字段: {name} 未设置值"
|
||||
|
||||
msgid "Generate related"
|
||||
msgstr "生成问题"
|
||||
|
||||
msgid "Obtain graphical captcha"
|
||||
msgstr "获取图形验证码"
|
||||
|
||||
msgid "Captcha code error or expiration"
|
||||
msgstr "验证码错误或过期"
|
||||
|
||||
msgid "captcha"
|
||||
msgstr "验证码"
|
||||
msgstr "生成问题"
|
||||
|
|
@ -7663,13 +7663,4 @@ msgid "Field: {name} No value set"
|
|||
msgstr "欄位: {name} 未設定值"
|
||||
|
||||
msgid "Generate related"
|
||||
msgstr "生成問題"
|
||||
|
||||
msgid "Obtain graphical captcha"
|
||||
msgstr "獲取圖形驗證碼"
|
||||
|
||||
msgid "Captcha code error or expiration"
|
||||
msgstr "驗證碼錯誤或過期"
|
||||
|
||||
msgid "captcha"
|
||||
msgstr "驗證碼"
|
||||
msgstr "生成問題"
|
||||
|
|
@ -19,8 +19,6 @@ from setting.models_provider.impl.kimi_model_provider.kimi_model_provider import
|
|||
from setting.models_provider.impl.ollama_model_provider.ollama_model_provider import OllamaModelProvider
|
||||
from setting.models_provider.impl.openai_model_provider.openai_model_provider import OpenAIModelProvider
|
||||
from setting.models_provider.impl.qwen_model_provider.qwen_model_provider import QwenModelProvider
|
||||
from setting.models_provider.impl.regolo_model_provider.regolo_model_provider import \
|
||||
RegoloModelProvider
|
||||
from setting.models_provider.impl.siliconCloud_model_provider.siliconCloud_model_provider import \
|
||||
SiliconCloudModelProvider
|
||||
from setting.models_provider.impl.tencent_cloud_model_provider.tencent_cloud_model_provider import \
|
||||
|
|
@ -57,4 +55,3 @@ class ModelProvideConstants(Enum):
|
|||
aliyun_bai_lian_model_provider = AliyunBaiLianModelProvider()
|
||||
model_anthropic_provider = AnthropicModelProvider()
|
||||
model_siliconCloud_provider = SiliconCloudModelProvider()
|
||||
model_regolo_provider = RegoloModelProvider()
|
||||
|
|
|
|||
|
|
@ -51,23 +51,6 @@ model_info_list = [ModelInfo('gte-rerank',
|
|||
_("Universal text vector is Tongyi Lab's multi-language text unified vector model based on the LLM base. It provides high-level vector services for multiple mainstream languages around the world and helps developers quickly convert text data into high-quality vector data."),
|
||||
ModelTypeConst.EMBEDDING, aliyun_bai_lian_embedding_model_credential,
|
||||
AliyunBaiLianEmbedding),
|
||||
ModelInfo('qwen3-0.6b', '', ModelTypeConst.LLM, aliyun_bai_lian_llm_model_credential,
|
||||
BaiLianChatModel),
|
||||
ModelInfo('qwen3-1.7b', '', ModelTypeConst.LLM, aliyun_bai_lian_llm_model_credential,
|
||||
BaiLianChatModel),
|
||||
ModelInfo('qwen3-4b', '', ModelTypeConst.LLM, aliyun_bai_lian_llm_model_credential,
|
||||
BaiLianChatModel),
|
||||
ModelInfo('qwen3-8b', '', ModelTypeConst.LLM, aliyun_bai_lian_llm_model_credential,
|
||||
BaiLianChatModel),
|
||||
ModelInfo('qwen3-14b', '', ModelTypeConst.LLM, aliyun_bai_lian_llm_model_credential,
|
||||
BaiLianChatModel),
|
||||
ModelInfo('qwen3-32b', '', ModelTypeConst.LLM, aliyun_bai_lian_llm_model_credential,
|
||||
BaiLianChatModel),
|
||||
ModelInfo('qwen3-30b-a3b', '', ModelTypeConst.LLM, aliyun_bai_lian_llm_model_credential,
|
||||
BaiLianChatModel),
|
||||
ModelInfo('qwen3-235b-a22b', '', ModelTypeConst.LLM, aliyun_bai_lian_llm_model_credential,
|
||||
BaiLianChatModel),
|
||||
|
||||
ModelInfo('qwen-turbo', '', ModelTypeConst.LLM, aliyun_bai_lian_llm_model_credential,
|
||||
BaiLianChatModel),
|
||||
ModelInfo('qwen-plus', '', ModelTypeConst.LLM, aliyun_bai_lian_llm_model_credential,
|
||||
|
|
|
|||
|
|
@ -30,29 +30,6 @@ class BaiLianLLMModelParams(BaseForm):
|
|||
precision=0)
|
||||
|
||||
|
||||
class BaiLianLLMStreamModelParams(BaseForm):
|
||||
temperature = forms.SliderField(TooltipLabel(_('Temperature'),
|
||||
_('Higher values make the output more random, while lower values make it more focused and deterministic')),
|
||||
required=True, default_value=0.7,
|
||||
_min=0.1,
|
||||
_max=1.0,
|
||||
_step=0.01,
|
||||
precision=2)
|
||||
|
||||
max_tokens = forms.SliderField(
|
||||
TooltipLabel(_('Output the maximum Tokens'),
|
||||
_('Specify the maximum number of tokens that the model can generate')),
|
||||
required=True, default_value=800,
|
||||
_min=1,
|
||||
_max=100000,
|
||||
_step=1,
|
||||
precision=0)
|
||||
|
||||
stream = forms.SwitchField(label=TooltipLabel(_('Is the answer in streaming mode'),
|
||||
_('Is the answer in streaming mode')),
|
||||
required=True, default_value=True)
|
||||
|
||||
|
||||
class BaiLianLLMModelCredential(BaseForm, BaseModelCredential):
|
||||
|
||||
def is_valid(self, model_type: str, model_name, model_credential: Dict[str, object], model_params, provider,
|
||||
|
|
@ -70,11 +47,7 @@ class BaiLianLLMModelCredential(BaseForm, BaseModelCredential):
|
|||
return False
|
||||
try:
|
||||
model = provider.get_model(model_type, model_name, model_credential, **model_params)
|
||||
if model_params.get('stream'):
|
||||
for res in model.stream([HumanMessage(content=gettext('Hello'))]):
|
||||
pass
|
||||
else:
|
||||
model.invoke([HumanMessage(content=gettext('Hello'))])
|
||||
model.invoke([HumanMessage(content=gettext('Hello'))])
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
if isinstance(e, AppApiException):
|
||||
|
|
@ -95,6 +68,4 @@ class BaiLianLLMModelCredential(BaseForm, BaseModelCredential):
|
|||
api_key = forms.PasswordInputField('API Key', required=True)
|
||||
|
||||
def get_model_params_setting_form(self, model_name):
|
||||
if 'qwen3' in model_name:
|
||||
return BaiLianLLMStreamModelParams()
|
||||
return BaiLianLLMModelParams()
|
||||
|
|
|
|||
|
|
@ -24,11 +24,10 @@ def _convert_delta_to_message_chunk(
|
|||
_dict: Mapping[str, Any], default_class: type[BaseMessageChunk]
|
||||
) -> BaseMessageChunk:
|
||||
id_ = _dict.get("id")
|
||||
reasoning_content = cast(str, _dict.get("reasoning_content") or "")
|
||||
role = cast(str, _dict.get("role"))
|
||||
content = cast(str, _dict.get("content") or "")
|
||||
additional_kwargs: dict = {}
|
||||
if 'reasoning_content' in _dict:
|
||||
additional_kwargs['reasoning_content'] = _dict.get('reasoning_content')
|
||||
additional_kwargs: dict = {'reasoning_content': reasoning_content}
|
||||
if _dict.get("function_call"):
|
||||
function_call = dict(_dict["function_call"])
|
||||
if "name" in function_call and function_call["name"] is None:
|
||||
|
|
@ -133,7 +132,7 @@ class BaseChatOpenAI(ChatOpenAI):
|
|||
)
|
||||
|
||||
usage_metadata: Optional[UsageMetadata] = (
|
||||
_create_usage_metadata(token_usage) if token_usage and token_usage.get("prompt_tokens") else None
|
||||
_create_usage_metadata(token_usage) if token_usage else None
|
||||
)
|
||||
if len(choices) == 0:
|
||||
# logprobs is implicitly None
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ from google.ai.generativelanguage_v1beta.types import (
|
|||
Tool as GoogleTool,
|
||||
)
|
||||
from langchain_core.callbacks import CallbackManagerForLLMRun
|
||||
from langchain_core.messages import BaseMessage, get_buffer_string
|
||||
from langchain_core.messages import BaseMessage
|
||||
from langchain_core.outputs import ChatGenerationChunk
|
||||
from langchain_google_genai import ChatGoogleGenerativeAI
|
||||
from langchain_google_genai._function_utils import _ToolConfigDict, _ToolDict
|
||||
|
|
@ -22,8 +22,6 @@ from langchain_google_genai.chat_models import _chat_with_retry, _response_to_re
|
|||
from langchain_google_genai._common import (
|
||||
SafetySettingDict,
|
||||
)
|
||||
|
||||
from common.config.tokenizer_manage_config import TokenizerManage
|
||||
from setting.models_provider.base_model_provider import MaxKBBaseModel
|
||||
|
||||
|
||||
|
|
@ -48,18 +46,10 @@ class GeminiChatModel(MaxKBBaseModel, ChatGoogleGenerativeAI):
|
|||
return self.__dict__.get('_last_generation_info')
|
||||
|
||||
def get_num_tokens_from_messages(self, messages: List[BaseMessage]) -> int:
|
||||
try:
|
||||
return self.get_last_generation_info().get('input_tokens', 0)
|
||||
except Exception as e:
|
||||
tokenizer = TokenizerManage.get_tokenizer()
|
||||
return sum([len(tokenizer.encode(get_buffer_string([m]))) for m in messages])
|
||||
return self.get_last_generation_info().get('input_tokens', 0)
|
||||
|
||||
def get_num_tokens(self, text: str) -> int:
|
||||
try:
|
||||
return self.get_last_generation_info().get('output_tokens', 0)
|
||||
except Exception as e:
|
||||
tokenizer = TokenizerManage.get_tokenizer()
|
||||
return len(tokenizer.encode(text))
|
||||
return self.get_last_generation_info().get('output_tokens', 0)
|
||||
|
||||
def _stream(
|
||||
self,
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ class OllamaLLMModelParams(BaseForm):
|
|||
_step=0.01,
|
||||
precision=2)
|
||||
|
||||
num_predict = forms.SliderField(
|
||||
max_tokens = forms.SliderField(
|
||||
TooltipLabel(_('Output the maximum Tokens'),
|
||||
_('Specify the maximum number of tokens that the model can generate')),
|
||||
required=True, default_value=1024,
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
# coding=utf-8
|
||||
"""
|
||||
@project: maxkb
|
||||
@Author:虎
|
||||
@file: __init__.py.py
|
||||
@date:2024/3/28 16:25
|
||||
@desc:
|
||||
"""
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
# coding=utf-8
|
||||
"""
|
||||
@project: MaxKB
|
||||
@Author:虎
|
||||
@file: embedding.py
|
||||
@date:2024/7/12 16:45
|
||||
@desc:
|
||||
"""
|
||||
import traceback
|
||||
from typing import Dict
|
||||
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from common import forms
|
||||
from common.exception.app_exception import AppApiException
|
||||
from common.forms import BaseForm
|
||||
from setting.models_provider.base_model_provider import BaseModelCredential, ValidCode
|
||||
|
||||
|
||||
class RegoloEmbeddingCredential(BaseForm, BaseModelCredential):
|
||||
def is_valid(self, model_type: str, model_name, model_credential: Dict[str, object], model_params, provider,
|
||||
raise_exception=True):
|
||||
model_type_list = provider.get_model_type_list()
|
||||
if not any(list(filter(lambda mt: mt.get('value') == model_type, model_type_list))):
|
||||
raise AppApiException(ValidCode.valid_error.value,
|
||||
_('{model_type} Model type is not supported').format(model_type=model_type))
|
||||
|
||||
for key in ['api_key']:
|
||||
if key not in model_credential:
|
||||
if raise_exception:
|
||||
raise AppApiException(ValidCode.valid_error.value, _('{key} is required').format(key=key))
|
||||
else:
|
||||
return False
|
||||
try:
|
||||
model = provider.get_model(model_type, model_name, model_credential)
|
||||
model.embed_query(_('Hello'))
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
if isinstance(e, AppApiException):
|
||||
raise e
|
||||
if raise_exception:
|
||||
raise AppApiException(ValidCode.valid_error.value,
|
||||
_('Verification failed, please check whether the parameters are correct: {error}').format(
|
||||
error=str(e)))
|
||||
else:
|
||||
return False
|
||||
return True
|
||||
|
||||
def encryption_dict(self, model: Dict[str, object]):
|
||||
return {**model, 'api_key': super().encryption(model.get('api_key', ''))}
|
||||
|
||||
api_key = forms.PasswordInputField('API Key', required=True)
|
||||
|
|
@ -1,74 +0,0 @@
|
|||
# coding=utf-8
|
||||
import base64
|
||||
import os
|
||||
import traceback
|
||||
from typing import Dict
|
||||
|
||||
from langchain_core.messages import HumanMessage
|
||||
|
||||
from common import forms
|
||||
from common.exception.app_exception import AppApiException
|
||||
from common.forms import BaseForm, TooltipLabel
|
||||
from setting.models_provider.base_model_provider import BaseModelCredential, ValidCode
|
||||
from django.utils.translation import gettext_lazy as _, gettext
|
||||
|
||||
|
||||
class RegoloImageModelParams(BaseForm):
|
||||
temperature = forms.SliderField(TooltipLabel(_('Temperature'),
|
||||
_('Higher values make the output more random, while lower values make it more focused and deterministic')),
|
||||
required=True, default_value=0.7,
|
||||
_min=0.1,
|
||||
_max=1.0,
|
||||
_step=0.01,
|
||||
precision=2)
|
||||
|
||||
max_tokens = forms.SliderField(
|
||||
TooltipLabel(_('Output the maximum Tokens'),
|
||||
_('Specify the maximum number of tokens that the model can generate')),
|
||||
required=True, default_value=800,
|
||||
_min=1,
|
||||
_max=100000,
|
||||
_step=1,
|
||||
precision=0)
|
||||
|
||||
|
||||
class RegoloImageModelCredential(BaseForm, BaseModelCredential):
|
||||
api_base = forms.TextInputField('API URL', required=True)
|
||||
api_key = forms.PasswordInputField('API Key', required=True)
|
||||
|
||||
def is_valid(self, model_type: str, model_name, model_credential: Dict[str, object], model_params, provider,
|
||||
raise_exception=False):
|
||||
model_type_list = provider.get_model_type_list()
|
||||
if not any(list(filter(lambda mt: mt.get('value') == model_type, model_type_list))):
|
||||
raise AppApiException(ValidCode.valid_error.value,
|
||||
gettext('{model_type} Model type is not supported').format(model_type=model_type))
|
||||
|
||||
for key in ['api_key']:
|
||||
if key not in model_credential:
|
||||
if raise_exception:
|
||||
raise AppApiException(ValidCode.valid_error.value, gettext('{key} is required').format(key=key))
|
||||
else:
|
||||
return False
|
||||
try:
|
||||
model = provider.get_model(model_type, model_name, model_credential, **model_params)
|
||||
res = model.stream([HumanMessage(content=[{"type": "text", "text": gettext('Hello')}])])
|
||||
for chunk in res:
|
||||
print(chunk)
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
if isinstance(e, AppApiException):
|
||||
raise e
|
||||
if raise_exception:
|
||||
raise AppApiException(ValidCode.valid_error.value,
|
||||
gettext(
|
||||
'Verification failed, please check whether the parameters are correct: {error}').format(
|
||||
error=str(e)))
|
||||
else:
|
||||
return False
|
||||
return True
|
||||
|
||||
def encryption_dict(self, model: Dict[str, object]):
|
||||
return {**model, 'api_key': super().encryption(model.get('api_key', ''))}
|
||||
|
||||
def get_model_params_setting_form(self, model_name):
|
||||
return RegoloImageModelParams()
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
# coding=utf-8
|
||||
"""
|
||||
@project: MaxKB
|
||||
@Author:虎
|
||||
@file: llm.py
|
||||
@date:2024/7/11 18:32
|
||||
@desc:
|
||||
"""
|
||||
import traceback
|
||||
from typing import Dict
|
||||
|
||||
from django.utils.translation import gettext_lazy as _, gettext
|
||||
from langchain_core.messages import HumanMessage
|
||||
|
||||
from common import forms
|
||||
from common.exception.app_exception import AppApiException
|
||||
from common.forms import BaseForm, TooltipLabel
|
||||
from setting.models_provider.base_model_provider import BaseModelCredential, ValidCode
|
||||
|
||||
|
||||
class RegoloLLMModelParams(BaseForm):
|
||||
temperature = forms.SliderField(TooltipLabel(_('Temperature'),
|
||||
_('Higher values make the output more random, while lower values make it more focused and deterministic')),
|
||||
required=True, default_value=0.7,
|
||||
_min=0.1,
|
||||
_max=1.0,
|
||||
_step=0.01,
|
||||
precision=2)
|
||||
|
||||
max_tokens = forms.SliderField(
|
||||
TooltipLabel(_('Output the maximum Tokens'),
|
||||
_('Specify the maximum number of tokens that the model can generate')),
|
||||
required=True, default_value=800,
|
||||
_min=1,
|
||||
_max=100000,
|
||||
_step=1,
|
||||
precision=0)
|
||||
|
||||
|
||||
class RegoloLLMModelCredential(BaseForm, BaseModelCredential):
|
||||
|
||||
def is_valid(self, model_type: str, model_name, model_credential: Dict[str, object], model_params, provider,
|
||||
raise_exception=False):
|
||||
model_type_list = provider.get_model_type_list()
|
||||
if not any(list(filter(lambda mt: mt.get('value') == model_type, model_type_list))):
|
||||
raise AppApiException(ValidCode.valid_error.value,
|
||||
gettext('{model_type} Model type is not supported').format(model_type=model_type))
|
||||
|
||||
for key in ['api_key']:
|
||||
if key not in model_credential:
|
||||
if raise_exception:
|
||||
raise AppApiException(ValidCode.valid_error.value, gettext('{key} is required').format(key=key))
|
||||
else:
|
||||
return False
|
||||
try:
|
||||
|
||||
model = provider.get_model(model_type, model_name, model_credential, **model_params)
|
||||
model.invoke([HumanMessage(content=gettext('Hello'))])
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
if isinstance(e, AppApiException):
|
||||
raise e
|
||||
if raise_exception:
|
||||
raise AppApiException(ValidCode.valid_error.value,
|
||||
gettext(
|
||||
'Verification failed, please check whether the parameters are correct: {error}').format(
|
||||
error=str(e)))
|
||||
else:
|
||||
return False
|
||||
return True
|
||||
|
||||
def encryption_dict(self, model: Dict[str, object]):
|
||||
return {**model, 'api_key': super().encryption(model.get('api_key', ''))}
|
||||
|
||||
api_key = forms.PasswordInputField('API Key', required=True)
|
||||
|
||||
def get_model_params_setting_form(self, model_name):
|
||||
return RegoloLLMModelParams()
|
||||
|
|
@ -1,89 +0,0 @@
|
|||
# coding=utf-8
|
||||
import traceback
|
||||
from typing import Dict
|
||||
|
||||
from django.utils.translation import gettext_lazy as _, gettext
|
||||
|
||||
from common import forms
|
||||
from common.exception.app_exception import AppApiException
|
||||
from common.forms import BaseForm, TooltipLabel
|
||||
from setting.models_provider.base_model_provider import BaseModelCredential, ValidCode
|
||||
|
||||
|
||||
class RegoloTTIModelParams(BaseForm):
|
||||
size = forms.SingleSelect(
|
||||
TooltipLabel(_('Image size'),
|
||||
_('The image generation endpoint allows you to create raw images based on text prompts. ')),
|
||||
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(_('Picture quality'), _('''
|
||||
By default, images are produced in standard quality.
|
||||
''')),
|
||||
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(_('Number of pictures'),
|
||||
_('1 as default')),
|
||||
required=True, default_value=1,
|
||||
_min=1,
|
||||
_max=10,
|
||||
_step=1,
|
||||
precision=0)
|
||||
|
||||
|
||||
class RegoloTextToImageModelCredential(BaseForm, BaseModelCredential):
|
||||
api_key = forms.PasswordInputField('API Key', required=True)
|
||||
|
||||
def is_valid(self, model_type: str, model_name, model_credential: Dict[str, object], model_params, provider,
|
||||
raise_exception=False):
|
||||
model_type_list = provider.get_model_type_list()
|
||||
if not any(list(filter(lambda mt: mt.get('value') == model_type, model_type_list))):
|
||||
raise AppApiException(ValidCode.valid_error.value,
|
||||
gettext('{model_type} Model type is not supported').format(model_type=model_type))
|
||||
|
||||
for key in ['api_key']:
|
||||
if key not in model_credential:
|
||||
if raise_exception:
|
||||
raise AppApiException(ValidCode.valid_error.value, gettext('{key} is required').format(key=key))
|
||||
else:
|
||||
return False
|
||||
try:
|
||||
model = provider.get_model(model_type, model_name, model_credential, **model_params)
|
||||
res = model.check_auth()
|
||||
print(res)
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
if isinstance(e, AppApiException):
|
||||
raise e
|
||||
if raise_exception:
|
||||
raise AppApiException(ValidCode.valid_error.value,
|
||||
gettext(
|
||||
'Verification failed, please check whether the parameters are correct: {error}').format(
|
||||
error=str(e)))
|
||||
else:
|
||||
return False
|
||||
return True
|
||||
|
||||
def encryption_dict(self, model: Dict[str, object]):
|
||||
return {**model, 'api_key': super().encryption(model.get('api_key', ''))}
|
||||
|
||||
def get_model_params_setting_form(self, model_name):
|
||||
return RegoloTTIModelParams()
|
||||
|
|
@ -1,64 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
id="Livello_2"
|
||||
data-name="Livello 2"
|
||||
viewBox="0 0 104.4 104.38"
|
||||
version="1.1"
|
||||
sodipodi:docname="Regolo_logo_positive.svg"
|
||||
width="100%" height="100%"
|
||||
inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview13"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:zoom="2.1335227"
|
||||
inkscape:cx="119.05193"
|
||||
inkscape:cy="48.511318"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1025"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g13" />
|
||||
<defs
|
||||
id="defs1">
|
||||
<style
|
||||
id="style1">
|
||||
.cls-1 {
|
||||
fill: #303030;
|
||||
}
|
||||
|
||||
.cls-2 {
|
||||
fill: #59e389;
|
||||
}
|
||||
</style>
|
||||
</defs>
|
||||
<g
|
||||
id="Grafica"
|
||||
transform="translate(0,-40.87)">
|
||||
<g
|
||||
id="g13">
|
||||
<path
|
||||
class="cls-1"
|
||||
d="m 104.39,105.96 v 36.18 c 0,0.32 -0.05,0.62 -0.14,0.91 -0.39,1.27 -1.58,2.2 -2.99,2.2 H 65.08 c -1.73,0 -3.13,-1.41 -3.13,-3.13 V 113.4 c 0,-0.15 0,-0.29 0,-0.44 v -7 c 0,-1.73 1.4,-3.13 3.13,-3.13 h 36.19 c 1.5,0 2.77,1.07 3.06,2.5 0.05,0.21 0.07,0.41 0.07,0.63 z"
|
||||
id="path1" />
|
||||
<path
|
||||
class="cls-1"
|
||||
d="m 104.39,105.96 v 36.18 c 0,0.32 -0.05,0.62 -0.14,0.91 -0.39,1.27 -1.58,2.2 -2.99,2.2 H 65.08 c -1.73,0 -3.13,-1.41 -3.13,-3.13 V 113.4 c 0,-0.15 0,-0.29 0,-0.44 v -7 c 0,-1.73 1.4,-3.13 3.13,-3.13 h 36.19 c 1.5,0 2.77,1.07 3.06,2.5 0.05,0.21 0.07,0.41 0.07,0.63 z"
|
||||
id="path2" />
|
||||
<path
|
||||
class="cls-2"
|
||||
d="M 101.27,40.88 H 65.09 c -1.73,0 -3.13,1.4 -3.13,3.13 v 28.71 c 0,4.71 -1.88,9.23 -5.2,12.56 L 44.42,97.61 c -3.32,3.33 -7.85,5.2 -12.55,5.2 H 18.98 c -2.21,0 -3.99,-1.79 -3.99,-3.99 V 87.29 c 0,-2.21 1.79,-3.99 3.99,-3.99 h 20.34 c 1.41,0 2.59,-0.93 2.99,-2.2 0.09,-0.29 0.14,-0.59 0.14,-0.91 V 44 c 0,-0.22 -0.02,-0.42 -0.07,-0.63 -0.29,-1.43 -1.56,-2.5 -3.06,-2.5 H 3.13 C 1.4,40.87 0,42.27 0,44 v 7 c 0,0.15 0,0.29 0,0.44 v 28.72 c 0,1.72 1.41,3.13 3.13,3.13 h 3.16 c 2.21,0 3.99,1.79 3.99,3.99 v 11.53 c 0,2.21 -1.79,3.99 -3.99,3.99 H 3.15 c -1.73,0 -3.13,1.4 -3.13,3.13 v 36.19 c 0,1.72 1.41,3.13 3.13,3.13 h 36.19 c 1.73,0 3.13,-1.41 3.13,-3.13 V 113.4 c 0,-4.7 1.87,-9.23 5.2,-12.55 L 60,88.51 c 3.33,-3.32 7.85,-5.2 12.56,-5.2 h 28.71 c 1.73,0 3.13,-1.4 3.13,-3.13 V 44 c 0,-1.73 -1.4,-3.13 -3.13,-3.13 z"
|
||||
id="path3" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.8 KiB |
|
|
@ -1,23 +0,0 @@
|
|||
# coding=utf-8
|
||||
"""
|
||||
@project: MaxKB
|
||||
@Author:虎
|
||||
@file: embedding.py
|
||||
@date:2024/7/12 17:44
|
||||
@desc:
|
||||
"""
|
||||
from typing import Dict
|
||||
|
||||
from langchain_community.embeddings import OpenAIEmbeddings
|
||||
|
||||
from setting.models_provider.base_model_provider import MaxKBBaseModel
|
||||
|
||||
|
||||
class RegoloEmbeddingModel(MaxKBBaseModel, OpenAIEmbeddings):
|
||||
@staticmethod
|
||||
def new_instance(model_type, model_name, model_credential: Dict[str, object], **model_kwargs):
|
||||
return RegoloEmbeddingModel(
|
||||
api_key=model_credential.get('api_key'),
|
||||
model=model_name,
|
||||
openai_api_base="https://api.regolo.ai/v1",
|
||||
)
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
from typing import Dict
|
||||
|
||||
from setting.models_provider.base_model_provider import MaxKBBaseModel
|
||||
from setting.models_provider.impl.base_chat_open_ai import BaseChatOpenAI
|
||||
|
||||
|
||||
class RegoloImage(MaxKBBaseModel, BaseChatOpenAI):
|
||||
|
||||
@staticmethod
|
||||
def new_instance(model_type, model_name, model_credential: Dict[str, object], **model_kwargs):
|
||||
optional_params = MaxKBBaseModel.filter_optional_params(model_kwargs)
|
||||
return RegoloImage(
|
||||
model_name=model_name,
|
||||
openai_api_base="https://api.regolo.ai/v1",
|
||||
openai_api_key=model_credential.get('api_key'),
|
||||
streaming=True,
|
||||
stream_usage=True,
|
||||
extra_body=optional_params
|
||||
)
|
||||
|
|
@ -1,38 +0,0 @@
|
|||
# coding=utf-8
|
||||
"""
|
||||
@project: maxkb
|
||||
@Author:虎
|
||||
@file: llm.py
|
||||
@date:2024/4/18 15:28
|
||||
@desc:
|
||||
"""
|
||||
from typing import List, Dict
|
||||
|
||||
from langchain_core.messages import BaseMessage, get_buffer_string
|
||||
from langchain_openai.chat_models import ChatOpenAI
|
||||
|
||||
from common.config.tokenizer_manage_config import TokenizerManage
|
||||
from setting.models_provider.base_model_provider import MaxKBBaseModel
|
||||
from setting.models_provider.impl.base_chat_open_ai import BaseChatOpenAI
|
||||
|
||||
|
||||
def custom_get_token_ids(text: str):
|
||||
tokenizer = TokenizerManage.get_tokenizer()
|
||||
return tokenizer.encode(text)
|
||||
|
||||
|
||||
class RegoloChatModel(MaxKBBaseModel, BaseChatOpenAI):
|
||||
|
||||
@staticmethod
|
||||
def is_cache_model():
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def new_instance(model_type, model_name, model_credential: Dict[str, object], **model_kwargs):
|
||||
optional_params = MaxKBBaseModel.filter_optional_params(model_kwargs)
|
||||
return RegoloChatModel(
|
||||
model=model_name,
|
||||
openai_api_base="https://api.regolo.ai/v1",
|
||||
openai_api_key=model_credential.get('api_key'),
|
||||
extra_body=optional_params
|
||||
)
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
from typing import Dict
|
||||
|
||||
from openai import OpenAI
|
||||
|
||||
from common.config.tokenizer_manage_config import TokenizerManage
|
||||
from setting.models_provider.base_model_provider import MaxKBBaseModel
|
||||
from setting.models_provider.impl.base_tti import BaseTextToImage
|
||||
|
||||
|
||||
def custom_get_token_ids(text: str):
|
||||
tokenizer = TokenizerManage.get_tokenizer()
|
||||
return tokenizer.encode(text)
|
||||
|
||||
|
||||
class RegoloTextToImage(MaxKBBaseModel, BaseTextToImage):
|
||||
api_base: str
|
||||
api_key: str
|
||||
model: str
|
||||
params: dict
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
super().__init__(**kwargs)
|
||||
self.api_key = kwargs.get('api_key')
|
||||
self.api_base = "https://api.regolo.ai/v1"
|
||||
self.model = kwargs.get('model')
|
||||
self.params = kwargs.get('params')
|
||||
|
||||
@staticmethod
|
||||
def new_instance(model_type, model_name, model_credential: Dict[str, object], **model_kwargs):
|
||||
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
|
||||
return RegoloTextToImage(
|
||||
model=model_name,
|
||||
api_base="https://api.regolo.ai/v1",
|
||||
api_key=model_credential.get('api_key'),
|
||||
**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()
|
||||
|
||||
# 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
|
||||
file_urls.append(url)
|
||||
|
||||
return file_urls
|
||||
|
|
@ -1,89 +0,0 @@
|
|||
# coding=utf-8
|
||||
"""
|
||||
@project: maxkb
|
||||
@Author:虎
|
||||
@file: openai_model_provider.py
|
||||
@date:2024/3/28 16:26
|
||||
@desc:
|
||||
"""
|
||||
import os
|
||||
|
||||
from common.util.file_util import get_file_content
|
||||
from setting.models_provider.base_model_provider import IModelProvider, ModelProvideInfo, ModelInfo, \
|
||||
ModelTypeConst, ModelInfoManage
|
||||
from setting.models_provider.impl.regolo_model_provider.credential.embedding import \
|
||||
RegoloEmbeddingCredential
|
||||
from setting.models_provider.impl.regolo_model_provider.credential.llm import RegoloLLMModelCredential
|
||||
from setting.models_provider.impl.regolo_model_provider.credential.tti import \
|
||||
RegoloTextToImageModelCredential
|
||||
from setting.models_provider.impl.regolo_model_provider.model.embedding import RegoloEmbeddingModel
|
||||
from setting.models_provider.impl.regolo_model_provider.model.llm import RegoloChatModel
|
||||
from setting.models_provider.impl.regolo_model_provider.model.tti import RegoloTextToImage
|
||||
from smartdoc.conf import PROJECT_DIR
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
openai_llm_model_credential = RegoloLLMModelCredential()
|
||||
openai_tti_model_credential = RegoloTextToImageModelCredential()
|
||||
model_info_list = [
|
||||
ModelInfo('Phi-4', '', ModelTypeConst.LLM,
|
||||
openai_llm_model_credential, RegoloChatModel
|
||||
),
|
||||
ModelInfo('DeepSeek-R1-Distill-Qwen-32B', '', ModelTypeConst.LLM,
|
||||
openai_llm_model_credential,
|
||||
RegoloChatModel),
|
||||
ModelInfo('maestrale-chat-v0.4-beta', '',
|
||||
ModelTypeConst.LLM, openai_llm_model_credential,
|
||||
RegoloChatModel),
|
||||
ModelInfo('Llama-3.3-70B-Instruct',
|
||||
'',
|
||||
ModelTypeConst.LLM, openai_llm_model_credential,
|
||||
RegoloChatModel),
|
||||
ModelInfo('Llama-3.1-8B-Instruct',
|
||||
'',
|
||||
ModelTypeConst.LLM, openai_llm_model_credential,
|
||||
RegoloChatModel),
|
||||
ModelInfo('DeepSeek-Coder-6.7B-Instruct', '',
|
||||
ModelTypeConst.LLM, openai_llm_model_credential,
|
||||
RegoloChatModel)
|
||||
]
|
||||
open_ai_embedding_credential = RegoloEmbeddingCredential()
|
||||
model_info_embedding_list = [
|
||||
ModelInfo('gte-Qwen2', '',
|
||||
ModelTypeConst.EMBEDDING, open_ai_embedding_credential,
|
||||
RegoloEmbeddingModel),
|
||||
]
|
||||
|
||||
model_info_tti_list = [
|
||||
ModelInfo('FLUX.1-dev', '',
|
||||
ModelTypeConst.TTI, openai_tti_model_credential,
|
||||
RegoloTextToImage),
|
||||
ModelInfo('sdxl-turbo', '',
|
||||
ModelTypeConst.TTI, openai_tti_model_credential,
|
||||
RegoloTextToImage),
|
||||
]
|
||||
model_info_manage = (
|
||||
ModelInfoManage.builder()
|
||||
.append_model_info_list(model_info_list)
|
||||
.append_default_model_info(
|
||||
ModelInfo('gpt-3.5-turbo', _('The latest gpt-3.5-turbo, updated with OpenAI adjustments'), ModelTypeConst.LLM,
|
||||
openai_llm_model_credential, RegoloChatModel
|
||||
))
|
||||
.append_model_info_list(model_info_embedding_list)
|
||||
.append_default_model_info(model_info_embedding_list[0])
|
||||
.append_model_info_list(model_info_tti_list)
|
||||
.append_default_model_info(model_info_tti_list[0])
|
||||
|
||||
.build()
|
||||
)
|
||||
|
||||
|
||||
class RegoloModelProvider(IModelProvider):
|
||||
|
||||
def get_model_info_manage(self):
|
||||
return model_info_manage
|
||||
|
||||
def get_model_provide_info(self):
|
||||
return ModelProvideInfo(provider='model_regolo_provider', name='Regolo', icon=get_file_content(
|
||||
os.path.join(PROJECT_DIR, "apps", "setting", 'models_provider', 'impl', 'regolo_model_provider',
|
||||
'icon',
|
||||
'regolo_icon_svg')))
|
||||
|
|
@ -22,9 +22,6 @@ class XInferenceReranker(MaxKBBaseModel, BaseDocumentCompressor):
|
|||
"""UID of the launched model"""
|
||||
api_key: Optional[str]
|
||||
|
||||
@staticmethod
|
||||
def is_cache_model():
|
||||
return False
|
||||
@staticmethod
|
||||
def new_instance(model_type, model_name, model_credential: Dict[str, object], **model_kwargs):
|
||||
return XInferenceReranker(server_url=model_credential.get('server_url'), model_uid=model_name,
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -93,8 +93,7 @@ class Config(dict):
|
|||
'SANDBOX': False,
|
||||
'LOCAL_MODEL_HOST': '127.0.0.1',
|
||||
'LOCAL_MODEL_PORT': '11636',
|
||||
'LOCAL_MODEL_PROTOCOL': "http",
|
||||
'LOCAL_MODEL_HOST_WORKER': 1
|
||||
'LOCAL_MODEL_PROTOCOL': "http"
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -114,8 +113,7 @@ class Config(dict):
|
|||
"ENGINE": self.get('DB_ENGINE'),
|
||||
"POOL_OPTIONS": {
|
||||
"POOL_SIZE": 20,
|
||||
"MAX_OVERFLOW": int(self.get('DB_MAX_OVERFLOW')),
|
||||
'RECYCLE': 30 * 60
|
||||
"MAX_OVERFLOW": int(self.get('DB_MAX_OVERFLOW'))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -126,10 +126,6 @@ CACHES = {
|
|||
"token_cache": {
|
||||
'BACKEND': 'common.cache.file_cache.FileCache',
|
||||
'LOCATION': os.path.join(PROJECT_DIR, 'data', 'cache', "token_cache") # 文件夹路径
|
||||
},
|
||||
'captcha_cache': {
|
||||
'BACKEND': 'common.cache.file_cache.FileCache',
|
||||
'LOCATION': os.path.join(PROJECT_DIR, 'data', 'cache', "captcha_cache") # 文件夹路径
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,22 +6,18 @@
|
|||
@date:2023/9/5 16:32
|
||||
@desc:
|
||||
"""
|
||||
import base64
|
||||
import datetime
|
||||
import os
|
||||
import random
|
||||
import re
|
||||
import uuid
|
||||
|
||||
from captcha.image import ImageCaptcha
|
||||
from django.conf import settings
|
||||
from django.core import validators, signing, cache
|
||||
from django.core.mail import send_mail
|
||||
from django.core.mail.backends.smtp import EmailBackend
|
||||
from django.db import transaction
|
||||
from django.db.models import Q, QuerySet, Prefetch
|
||||
from django.utils.translation import get_language
|
||||
from django.utils.translation import gettext_lazy as _, to_locale
|
||||
from drf_yasg import openapi
|
||||
from rest_framework import serializers
|
||||
|
||||
|
|
@ -34,7 +30,7 @@ from common.exception.app_exception import AppApiException
|
|||
from common.mixins.api_mixin import ApiMixin
|
||||
from common.models.db_model_manage import DBModelManage
|
||||
from common.response.result import get_api_response
|
||||
from common.util.common import valid_license, get_random_chars
|
||||
from common.util.common import valid_license
|
||||
from common.util.field_message import ErrMessage
|
||||
from common.util.lock import lock
|
||||
from dataset.models import DataSet, Document, Paragraph, Problem, ProblemParagraphMapping
|
||||
|
|
@ -43,29 +39,9 @@ from function_lib.models.function import FunctionLib
|
|||
from setting.models import Team, SystemSetting, SettingType, Model, TeamMember, TeamMemberPermission
|
||||
from smartdoc.conf import PROJECT_DIR
|
||||
from users.models.user import User, password_encrypt, get_user_dynamics_permission
|
||||
|
||||
from django.utils.translation import gettext_lazy as _, gettext, to_locale
|
||||
from django.utils.translation import get_language
|
||||
user_cache = cache.caches['user_cache']
|
||||
captcha_cache = cache.caches['captcha_cache']
|
||||
|
||||
|
||||
class CaptchaSerializer(ApiMixin, serializers.Serializer):
|
||||
@staticmethod
|
||||
def get_response_body_api():
|
||||
return get_api_response(openapi.Schema(
|
||||
type=openapi.TYPE_STRING,
|
||||
title="captcha",
|
||||
default="xxxx",
|
||||
description="captcha"
|
||||
))
|
||||
|
||||
@staticmethod
|
||||
def generate():
|
||||
chars = get_random_chars()
|
||||
image = ImageCaptcha()
|
||||
data = image.generate(chars)
|
||||
captcha = base64.b64encode(data.getbuffer())
|
||||
captcha_cache.set(f"LOGIN:{chars.lower()}", chars, timeout=5 * 60)
|
||||
return 'data:image/png;base64,' + captcha.decode()
|
||||
|
||||
|
||||
class SystemSerializer(ApiMixin, serializers.Serializer):
|
||||
|
|
@ -95,8 +71,6 @@ class LoginSerializer(ApiMixin, serializers.Serializer):
|
|||
|
||||
password = serializers.CharField(required=True, error_messages=ErrMessage.char(_("Password")))
|
||||
|
||||
captcha = serializers.CharField(required=True, error_messages=ErrMessage.char(_("captcha")))
|
||||
|
||||
def is_valid(self, *, raise_exception=False):
|
||||
"""
|
||||
校验参数
|
||||
|
|
@ -104,10 +78,6 @@ class LoginSerializer(ApiMixin, serializers.Serializer):
|
|||
:return: User information
|
||||
"""
|
||||
super().is_valid(raise_exception=True)
|
||||
captcha = self.data.get('captcha')
|
||||
captcha_value = captcha_cache.get(f"LOGIN:{captcha.lower()}")
|
||||
if captcha_value is None:
|
||||
raise AppApiException(1005, _("Captcha code error or expiration"))
|
||||
username = self.data.get("username")
|
||||
password = password_encrypt(self.data.get("password"))
|
||||
user = QuerySet(User).filter(Q(username=username,
|
||||
|
|
@ -139,8 +109,7 @@ class LoginSerializer(ApiMixin, serializers.Serializer):
|
|||
required=['username', 'password'],
|
||||
properties={
|
||||
'username': openapi.Schema(type=openapi.TYPE_STRING, title=_("Username"), description=_("Username")),
|
||||
'password': openapi.Schema(type=openapi.TYPE_STRING, title=_("Password"), description=_("Password")),
|
||||
'captcha': openapi.Schema(type=openapi.TYPE_STRING, title=_("captcha"), description=_("captcha"))
|
||||
'password': openapi.Schema(type=openapi.TYPE_STRING, title=_("Password"), description=_("Password"))
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ app_name = "user"
|
|||
urlpatterns = [
|
||||
path('profile', views.Profile.as_view()),
|
||||
path('user', views.User.as_view(), name="profile"),
|
||||
path('user/captcha', views.CaptchaView.as_view(), name='captcha'),
|
||||
path('user/language', views.SwitchUserLanguageView.as_view(), name='language'),
|
||||
path('user/list', views.User.Query.as_view()),
|
||||
path('user/login', views.Login.as_view(), name='login'),
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ from smartdoc.settings import JWT_AUTH
|
|||
from users.serializers.user_serializers import RegisterSerializer, LoginSerializer, CheckCodeSerializer, \
|
||||
RePasswordSerializer, \
|
||||
SendEmailSerializer, UserProfile, UserSerializer, UserManageSerializer, UserInstanceSerializer, SystemSerializer, \
|
||||
SwitchLanguageSerializer, CaptchaSerializer
|
||||
SwitchLanguageSerializer
|
||||
from users.views.common import get_user_operation_object, get_re_password_details
|
||||
|
||||
user_cache = cache.caches['user_cache']
|
||||
|
|
@ -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},
|
||||
|
|
@ -170,18 +170,6 @@ def _get_details(request):
|
|||
}
|
||||
|
||||
|
||||
class CaptchaView(APIView):
|
||||
|
||||
@action(methods=['GET'], detail=False)
|
||||
@swagger_auto_schema(operation_summary=_("Obtain graphical captcha"),
|
||||
operation_id=_("Obtain graphical captcha"),
|
||||
responses=CaptchaSerializer().get_response_body_api(),
|
||||
security=[],
|
||||
tags=[_("User management")])
|
||||
def get(self, request: Request):
|
||||
return result.success(CaptchaSerializer().generate())
|
||||
|
||||
|
||||
class Login(APIView):
|
||||
|
||||
@action(methods=['POST'], detail=False)
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ RUN python3 -m venv /opt/py3 && \
|
|||
pip install poetry==1.8.5 --break-system-packages && \
|
||||
poetry config virtualenvs.create false && \
|
||||
. /opt/py3/bin/activate && \
|
||||
if [ "$(uname -m)" = "x86_64" ]; then sed -i 's/^torch.*/torch = {version = "2.6.0+cpu", source = "pytorch"}/g' pyproject.toml; fi && \
|
||||
if [ "$(uname -m)" = "x86_64" ]; then sed -i 's/^torch.*/torch = {version = "^2.6.0+cpu", source = "pytorch"}/g' pyproject.toml; fi && \
|
||||
poetry install && \
|
||||
export MAXKB_CONFIG_TYPE=ENV && python3 /opt/maxkb/app/apps/manage.py compilemessages
|
||||
|
||||
|
|
@ -70,8 +70,7 @@ RUN chmod 755 /opt/maxkb/app/installer/run-maxkb.sh && \
|
|||
useradd --no-create-home --home /opt/maxkb/app/sandbox sandbox -g root && \
|
||||
chown -R sandbox:root /opt/maxkb/app/sandbox && \
|
||||
chmod g-x /usr/local/bin/* /usr/bin/* /bin/* /usr/sbin/* /sbin/* /usr/lib/postgresql/15/bin/* && \
|
||||
chmod g+x /usr/local/bin/python* && \
|
||||
find /etc/ -type f ! -path '/etc/resolv.conf' ! -path '/etc/hosts' | xargs chmod g-rx
|
||||
chmod g+x /usr/local/bin/python*
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
|
|
|
|||
102
pyproject.toml
102
pyproject.toml
|
|
@ -9,66 +9,64 @@ package-mode = false
|
|||
[tool.poetry.dependencies]
|
||||
python = ">=3.11,<3.12"
|
||||
django = "4.2.20"
|
||||
djangorestframework = "3.16.0"
|
||||
djangorestframework = "^3.15.2"
|
||||
drf-yasg = "1.21.7"
|
||||
django-filter = "23.2"
|
||||
langchain = "0.3.23"
|
||||
langchain-openai = "0.3.12"
|
||||
langchain-anthropic = "0.3.12"
|
||||
langchain-community = "0.3.21"
|
||||
langchain-deepseek = "0.1.3"
|
||||
langchain-google-genai = "2.1.2"
|
||||
langchain-mcp-adapters = "0.0.11"
|
||||
langchain-huggingface = "0.1.2"
|
||||
langchain-ollama = "0.3.2"
|
||||
langgraph = "0.3.27"
|
||||
mcp = "1.8.0"
|
||||
langchain-openai = "^0.3.0"
|
||||
langchain-anthropic = "^0.3.0"
|
||||
langchain-community = "^0.3.0"
|
||||
langchain-deepseek = "^0.1.0"
|
||||
langchain-google-genai = "^2.0.9"
|
||||
langchain-mcp-adapters = "^0.0.5"
|
||||
langchain-huggingface = "^0.1.2"
|
||||
langchain-ollama = "^0.3.0"
|
||||
langgraph = "^0.3.0"
|
||||
mcp = "^1.4.1"
|
||||
psycopg2-binary = "2.9.10"
|
||||
jieba = "0.42.1"
|
||||
diskcache = "5.6.3"
|
||||
pillow = "10.4.0"
|
||||
filetype = "1.2.0"
|
||||
jieba = "^0.42.1"
|
||||
diskcache = "^5.6.3"
|
||||
pillow = "^10.2.0"
|
||||
filetype = "^1.2.0"
|
||||
torch = "2.6.0"
|
||||
sentence-transformers = "4.0.2"
|
||||
openai = "1.72.0"
|
||||
tiktoken = "0.7.0"
|
||||
qianfan = "0.3.18"
|
||||
pycryptodome = "3.22.0"
|
||||
beautifulsoup4 = "4.13.3"
|
||||
html2text = "2024.2.26"
|
||||
django-ipware = "6.0.5"
|
||||
django-apscheduler = "0.6.2"
|
||||
sentence-transformers = "^4.0.2"
|
||||
openai = "^1.13.3"
|
||||
tiktoken = "^0.7.0"
|
||||
qianfan = "^0.3.6.1"
|
||||
pycryptodome = "^3.19.0"
|
||||
beautifulsoup4 = "^4.12.2"
|
||||
html2text = "^2024.2.26"
|
||||
django-ipware = "^6.0.4"
|
||||
django-apscheduler = "^0.6.2"
|
||||
pymupdf = "1.24.9"
|
||||
pypdf = "4.3.1"
|
||||
rapidocr-onnxruntime = "1.3.24"
|
||||
python-docx = "1.1.2"
|
||||
xlwt = "1.3.0"
|
||||
dashscope = "1.23.1"
|
||||
zhipuai = "2.1.5.20250410"
|
||||
httpx = "0.27.2"
|
||||
httpx-sse = "0.4.0"
|
||||
websockets = "13.1"
|
||||
openpyxl = "3.1.5"
|
||||
xlrd = "2.0.1"
|
||||
gunicorn = "23.0.0"
|
||||
python-docx = "^1.1.0"
|
||||
xlwt = "^1.3.0"
|
||||
dashscope = "^1.17.0"
|
||||
zhipuai = "^2.0.1"
|
||||
httpx = "^0.27.0"
|
||||
httpx-sse = "^0.4.0"
|
||||
websockets = "^13.0"
|
||||
openpyxl = "^3.1.2"
|
||||
xlrd = "^2.0.1"
|
||||
gunicorn = "^23.0.0"
|
||||
python-daemon = "3.0.1"
|
||||
boto3 = "1.37.31"
|
||||
tencentcloud-sdk-python = "3.0.1357"
|
||||
xinference-client = "1.4.1"
|
||||
psutil = "6.1.1"
|
||||
celery = { extras = ["sqlalchemy"], version = "5.5.1" }
|
||||
django-celery-beat = "2.7.0"
|
||||
celery-once = "3.0.1"
|
||||
anthropic = "0.49.0"
|
||||
pylint = "3.3.6"
|
||||
pydub = "0.25.1"
|
||||
cffi = "1.17.1"
|
||||
pysilk = "0.0.1"
|
||||
django-db-connection-pool = "1.2.5"
|
||||
opencv-python-headless = "4.11.0.86"
|
||||
pymysql = "1.1.1"
|
||||
accelerate = "1.6.0"
|
||||
captcha = "0.7.1"
|
||||
boto3 = "^1.34.160"
|
||||
tencentcloud-sdk-python = "^3.0.1209"
|
||||
xinference-client = "^1.3.0"
|
||||
psutil = "^6.0.0"
|
||||
celery = { extras = ["sqlalchemy"], version = "^5.4.0" }
|
||||
django-celery-beat = "^2.6.0"
|
||||
celery-once = "^3.0.1"
|
||||
anthropic = "^0.49.0"
|
||||
pylint = "3.1.0"
|
||||
pydub = "^0.25.1"
|
||||
cffi = "^1.17.1"
|
||||
pysilk = "^0.0.1"
|
||||
django-db-connection-pool = "^1.2.5"
|
||||
opencv-python-headless = "^4.11.0.86"
|
||||
pymysql = "^1.1.1"
|
||||
accelerate = "^1.6.0"
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
build-backend = "poetry.core.masonry.api"
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ const getMemberPermissions: (member_id: String) => Promise<Result<any>> = (membe
|
|||
}
|
||||
|
||||
/**
|
||||
* 修改成员权限
|
||||
* 获取成员权限
|
||||
* @param 参数 member_id
|
||||
* @param 参数 {
|
||||
"team_member_permission_list": [
|
||||
|
|
|
|||
|
|
@ -37,10 +37,6 @@ interface LoginRequest {
|
|||
* 密码
|
||||
*/
|
||||
password: string
|
||||
/**
|
||||
* 验证码
|
||||
*/
|
||||
captcha: string
|
||||
}
|
||||
|
||||
interface RegisterRequest {
|
||||
|
|
|
|||
|
|
@ -27,13 +27,6 @@ const login: (
|
|||
}
|
||||
return post('/user/login', request, undefined, loading)
|
||||
}
|
||||
/**
|
||||
* 获取图形验证码
|
||||
* @returns
|
||||
*/
|
||||
const getCaptcha: () => Promise<Result<string>> = () => {
|
||||
return get('user/captcha')
|
||||
}
|
||||
/**
|
||||
* 登出
|
||||
* @param loading 接口加载器
|
||||
|
|
@ -233,6 +226,5 @@ export default {
|
|||
postLanguage,
|
||||
getDingOauth2Callback,
|
||||
getlarkCallback,
|
||||
getQrSource,
|
||||
getCaptcha
|
||||
getQrSource
|
||||
}
|
||||
|
|
|
|||
|
|
@ -392,17 +392,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
|
||||
// 单次上传文件数量限制
|
||||
|
|
@ -428,6 +418,7 @@ const uploadFile = async (file: any, fileList: any) => {
|
|||
formData.append('file', file.raw, file.name)
|
||||
//
|
||||
const extension = file.name.split('.').pop().toUpperCase() // 获取文件后缀名并转为小写
|
||||
|
||||
if (imageExtensions.includes(extension)) {
|
||||
uploadImageList.value.push(file)
|
||||
} else if (documentExtensions.includes(extension)) {
|
||||
|
|
@ -461,35 +452,45 @@ 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))
|
||||
const f = response.data.filter(
|
||||
(f: any) => f.name.replaceAll(' ', '') === file.name.replaceAll(' ', '')
|
||||
)
|
||||
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))
|
||||
const f = response.data.filter(
|
||||
(f: any) => f.name.replaceAll(' ', '') == file.name.replaceAll(' ', '')
|
||||
)
|
||||
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))
|
||||
const f = response.data.filter(
|
||||
(f: any) => f.name.replaceAll(' ', '') === file.name.replaceAll(' ', '')
|
||||
)
|
||||
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))
|
||||
const f = response.data.filter(
|
||||
(f: any) => f.name.replaceAll(' ', '') === file.name.replaceAll(' ', '')
|
||||
)
|
||||
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))
|
||||
const f = response.data.filter(
|
||||
(f: any) => f.name.replaceAll(' ', '') === file.name.replaceAll(' ', '')
|
||||
)
|
||||
if (f.length > 0) {
|
||||
file.url = f[0].url
|
||||
file.file_id = f[0].file_id
|
||||
|
|
|
|||
|
|
@ -200,7 +200,8 @@ onMounted(() => {})
|
|||
}
|
||||
.media-file-width {
|
||||
:deep(.el-space__item) {
|
||||
width: 49% !important;
|
||||
min-width: 40% !important;
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
.media_2 {
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
ref="userFormRef"
|
||||
></UserForm>
|
||||
</div>
|
||||
<template v-if="!(isUserInput || isAPIInput) || !firsUserInput || type === 'log'">
|
||||
<template v-if="!isUserInput || !firsUserInput || type === 'log'">
|
||||
<el-scrollbar ref="scrollDiv" @scroll="handleScrollTop">
|
||||
<div ref="dialogScrollbar" class="ai-chat__content p-16">
|
||||
<PrologueContent
|
||||
|
|
@ -80,7 +80,7 @@
|
|||
</slot>
|
||||
|
||||
<el-button
|
||||
v-if="isUserInput || isAPIInput"
|
||||
v-if="isUserInput"
|
||||
class="user-input-button mb-8"
|
||||
type="primary"
|
||||
text
|
||||
|
|
@ -153,9 +153,8 @@ const form_data = ref<any>({})
|
|||
const api_form_data = ref<any>({})
|
||||
const userFormRef = ref<InstanceType<typeof UserForm>>()
|
||||
// 用户输入
|
||||
const firsUserInput = ref(false)
|
||||
const firsUserInput = ref(true)
|
||||
const showUserInput = ref(false)
|
||||
|
||||
// 初始表单数据(用于恢复)
|
||||
const initialFormData = ref({})
|
||||
const initialApiFormData = ref({})
|
||||
|
|
@ -165,17 +164,8 @@ const isUserInput = computed(
|
|||
props.applicationDetails.work_flow?.nodes?.filter((v: any) => v.id === 'base-node')[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
|
||||
)
|
||||
const showUserInputContent = computed(() => {
|
||||
return (
|
||||
(((isUserInput.value || isAPIInput.value) && firsUserInput.value) || showUserInput.value) &&
|
||||
props.type !== 'log'
|
||||
)
|
||||
return ((isUserInput.value && firsUserInput.value) || showUserInput.value) && props.type !== 'log'
|
||||
})
|
||||
watch(
|
||||
() => props.chatId,
|
||||
|
|
@ -185,14 +175,10 @@ watch(
|
|||
firsUserInput.value = false
|
||||
} else {
|
||||
chartOpenId.value = ''
|
||||
if (isUserInput.value) {
|
||||
firsUserInput.value = true
|
||||
} else if (props.type == 'debug-ai-chat' && isAPIInput.value) {
|
||||
firsUserInput.value = true
|
||||
}
|
||||
firsUserInput.value = true
|
||||
}
|
||||
},
|
||||
{ deep: true, immediate: true }
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
watch(
|
||||
|
|
@ -252,9 +238,7 @@ function sendMessage(val: string, other_params_data?: any, chat?: chatType): Pro
|
|||
return result
|
||||
}, {})
|
||||
localStorage.setItem(`${accessToken}userForm`, JSON.stringify(newData))
|
||||
|
||||
showUserInput.value = false
|
||||
|
||||
if (!loading.value && props.applicationDetails?.name) {
|
||||
handleDebounceClick(val, other_params_data, chat)
|
||||
return true
|
||||
|
|
@ -262,12 +246,7 @@ function sendMessage(val: string, other_params_data?: any, chat?: chatType): Pro
|
|||
throw 'err: no send'
|
||||
})
|
||||
.catch((e) => {
|
||||
if (isAPIInput.value && props.type !== 'debug-ai-chat') {
|
||||
showUserInput.value = false
|
||||
} else {
|
||||
showUserInput.value = true
|
||||
}
|
||||
|
||||
showUserInput.value = true
|
||||
return false
|
||||
})
|
||||
} else {
|
||||
|
|
@ -529,16 +508,14 @@ function chatMessage(chat?: any, problem?: string, re_chat?: boolean, other_para
|
|||
* @param row
|
||||
*/
|
||||
function getSourceDetail(row: any) {
|
||||
if (row.record_id) {
|
||||
logApi.getRecordDetail(id || props.appId, row.chat_id, row.record_id, loading).then((res) => {
|
||||
const exclude_keys = ['answer_text', 'id', 'answer_text_list']
|
||||
Object.keys(res.data).forEach((key) => {
|
||||
if (!exclude_keys.includes(key)) {
|
||||
row[key] = res.data[key]
|
||||
}
|
||||
})
|
||||
logApi.getRecordDetail(id || props.appId, row.chat_id, row.record_id, loading).then((res) => {
|
||||
const exclude_keys = ['answer_text', 'id', 'answer_text_list']
|
||||
Object.keys(res.data).forEach((key) => {
|
||||
if (!exclude_keys.includes(key)) {
|
||||
row[key] = res.data[key]
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -228,11 +228,6 @@ function getApplication() {
|
|||
}
|
||||
function refresh() {
|
||||
common.saveBreadcrumb(null)
|
||||
if (isDataset.value) {
|
||||
getDataset()
|
||||
} else if (isApplication.value) {
|
||||
getApplication()
|
||||
}
|
||||
}
|
||||
onMounted(() => {
|
||||
if (!breadcrumbData.value) {
|
||||
|
|
|
|||
|
|
@ -223,14 +223,14 @@ export default {
|
|||
},
|
||||
mcpNode: {
|
||||
label: 'MCP Server',
|
||||
text: 'Call MCP Tools through SSE/Streamable HTTP',
|
||||
text: 'Call MCP Tools through SSE',
|
||||
getToolsSuccess: 'Get Tools Successfully',
|
||||
getTool: 'Get Tools',
|
||||
tool: 'Tool',
|
||||
toolParam: 'Tool Params',
|
||||
mcpServerTip: 'Please enter the JSON format of the MCP server config',
|
||||
mcpToolTip: 'Please select a tool',
|
||||
configLabel: 'MCP Server Config (Only supports SSE/Streamable HTTP call method)'
|
||||
configLabel: 'MCP Server Config (Only supports SSE call method)'
|
||||
},
|
||||
imageGenerateNode: {
|
||||
label: 'Image Generation',
|
||||
|
|
|
|||
|
|
@ -28,10 +28,6 @@ export default {
|
|||
requiredMessage: 'Please enter username',
|
||||
lengthMessage: 'Length must be between 6 and 20 words'
|
||||
},
|
||||
captcha: {
|
||||
label: 'captcha',
|
||||
placeholder: 'Please enter the captcha'
|
||||
},
|
||||
nick_name: {
|
||||
label: 'Name',
|
||||
placeholder: 'Please enter name'
|
||||
|
|
|
|||
|
|
@ -224,14 +224,14 @@ export default {
|
|||
},
|
||||
mcpNode: {
|
||||
label: 'MCP 调用',
|
||||
text: '通过SSE/Streamable HTTP方式执行MCP服务中的工具',
|
||||
text: '通过SSE方式执行MCP服务中的工具',
|
||||
getToolsSuccess: '获取工具成功',
|
||||
getTool: '获取工具',
|
||||
tool: '工具',
|
||||
toolParam: '工具参数',
|
||||
mcpServerTip: '请输入JSON格式的MCP服务器配置',
|
||||
mcpToolTip: '请选择工具',
|
||||
configLabel: 'MCP Server Config (仅支持SSE/Streamable HTTP调用方式)'
|
||||
configLabel: 'MCP Server Config (仅支持SSE调用方式)'
|
||||
},
|
||||
imageGenerateNode: {
|
||||
label: '图片生成',
|
||||
|
|
@ -266,7 +266,7 @@ export default {
|
|||
label: '文本转语音',
|
||||
text: '将文本通过语音合成模型转换为音频',
|
||||
tts_model: {
|
||||
label: '语音合成模型'
|
||||
label: '语音识别模型'
|
||||
},
|
||||
content: {
|
||||
label: '选择文本内容'
|
||||
|
|
|
|||
|
|
@ -25,10 +25,6 @@ export default {
|
|||
requiredMessage: '请输入用户名',
|
||||
lengthMessage: '长度在 6 到 20 个字符'
|
||||
},
|
||||
captcha: {
|
||||
label: '验证码',
|
||||
placeholder: '请输入验证码'
|
||||
},
|
||||
nick_name: {
|
||||
label: '姓名',
|
||||
placeholder: '请输入姓名'
|
||||
|
|
@ -37,7 +33,7 @@ export default {
|
|||
label: '邮箱',
|
||||
placeholder: '请输入邮箱',
|
||||
requiredMessage: '请输入邮箱',
|
||||
validatorEmail: '请输入有效邮箱格式!'
|
||||
validatorEmail: '请输入有效邮箱格式!',
|
||||
},
|
||||
phone: {
|
||||
label: '手机号',
|
||||
|
|
@ -52,13 +48,13 @@ export default {
|
|||
new_password: {
|
||||
label: '新密码',
|
||||
placeholder: '请输入新密码',
|
||||
requiredMessage: '请输入新密码'
|
||||
requiredMessage: '请输入新密码',
|
||||
},
|
||||
re_password: {
|
||||
label: '确认密码',
|
||||
placeholder: '请输入确认密码',
|
||||
requiredMessage: '请输入确认密码',
|
||||
validatorMessage: '密码不一致'
|
||||
validatorMessage: '密码不一致',
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -223,14 +223,14 @@ export default {
|
|||
},
|
||||
mcpNode: {
|
||||
label: 'MCP 調用',
|
||||
text: '透過SSE/Streamable HTTP方式執行MCP服務中的工具',
|
||||
text: '透過SSE方式執行MCP服務中的工具',
|
||||
getToolsSuccess: '獲取工具成功',
|
||||
getTool: '獲取工具',
|
||||
tool: '工具',
|
||||
toolParam: '工具變數',
|
||||
mcpServerTip: '請輸入JSON格式的MCP服務器配置',
|
||||
mcpToolTip: '請選擇工具',
|
||||
configLabel: 'MCP Server Config (僅支持SSE/Streamable HTTP調用方式)'
|
||||
configLabel: 'MCP Server Config (僅支持SSE調用方式)'
|
||||
},
|
||||
imageGenerateNode: {
|
||||
label: '圖片生成',
|
||||
|
|
|
|||
|
|
@ -26,10 +26,6 @@ export default {
|
|||
requiredMessage: '請輸入使用者名稱',
|
||||
lengthMessage: '長度須介於 6 到 20 個字元之間'
|
||||
},
|
||||
captcha: {
|
||||
label: '驗證碼',
|
||||
placeholder: '請輸入驗證碼'
|
||||
},
|
||||
nick_name: {
|
||||
label: '姓名',
|
||||
placeholder: '請輸入姓名'
|
||||
|
|
|
|||
|
|
@ -135,8 +135,8 @@ const useUserStore = defineStore({
|
|||
})
|
||||
},
|
||||
|
||||
async login(auth_type: string, username: string, password: string, captcha: string) {
|
||||
return UserApi.login(auth_type, { username, password, captcha }).then((ok) => {
|
||||
async login(auth_type: string, username: string, password: string) {
|
||||
return UserApi.login(auth_type, { username, password }).then((ok) => {
|
||||
this.token = ok.data
|
||||
localStorage.setItem('token', ok.data)
|
||||
return this.profile()
|
||||
|
|
|
|||
|
|
@ -6,8 +6,7 @@
|
|||
padding: 0;
|
||||
margin: 0;
|
||||
font-size: inherit;
|
||||
word-break: break-word;
|
||||
table {
|
||||
table{
|
||||
display: block;
|
||||
}
|
||||
p {
|
||||
|
|
|
|||
|
|
@ -135,4 +135,51 @@ onMounted(() => {
|
|||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
<style lang="scss" scoped>
|
||||
.p-16-24 {
|
||||
padding: 16px 24px;
|
||||
}
|
||||
|
||||
.mb-16 {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.flex-between {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.flex {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.align-center {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.ml-8 {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.mr-8 {
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.ml-12 {
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
||||
.mr-4 {
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.cursor {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 32px; // 设置图标宽度
|
||||
height: 32px; // 设置图标高度
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -242,7 +242,6 @@ const submitHandle = async (formEl: FormInstance | undefined) => {
|
|||
}
|
||||
applicationApi.postApplication(applicationForm.value, loading).then((res) => {
|
||||
MsgSuccess(t('common.createSuccess'))
|
||||
emit('refresh')
|
||||
if (isWorkFlow(applicationForm.value.type)) {
|
||||
router.push({ path: `/application/${res.data.id}/workflow` })
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
>
|
||||
<el-scrollbar max-height="550">
|
||||
<div class="p-16">
|
||||
<el-form label-position="top" ref="paramFormRef" :model="form" v-loading="loading">
|
||||
<el-form label-position="top" ref="paramFormRef" :model="form">
|
||||
<el-form-item :label="$t('views.application.applicationForm.dialog.selectSearchMode')">
|
||||
<el-radio-group
|
||||
v-model="form.dataset_setting.search_mode"
|
||||
|
|
@ -81,7 +81,7 @@
|
|||
<el-input-number
|
||||
v-model="form.dataset_setting.similarity"
|
||||
:min="0"
|
||||
:max="form.dataset_setting.search_mode === 'blend' ? 2 : 1"
|
||||
:max="form.search_mode === 'blend' ? 2 : 1"
|
||||
:precision="3"
|
||||
:step="0.1"
|
||||
:value-on-clear="0"
|
||||
|
|
@ -259,20 +259,20 @@ const isWorkflowType = ref(false)
|
|||
|
||||
watch(dialogVisible, (bool) => {
|
||||
if (!bool) {
|
||||
// form.value = {
|
||||
// dataset_setting: {
|
||||
// search_mode: 'embedding',
|
||||
// top_n: 3,
|
||||
// similarity: 0.6,
|
||||
// max_paragraph_char_number: 5000,
|
||||
// no_references_setting: {
|
||||
// status: 'ai_questioning',
|
||||
// value: '{question}'
|
||||
// }
|
||||
// },
|
||||
// problem_optimization: false,
|
||||
// problem_optimization_prompt: ''
|
||||
// }
|
||||
form.value = {
|
||||
dataset_setting: {
|
||||
search_mode: 'embedding',
|
||||
top_n: 3,
|
||||
similarity: 0.6,
|
||||
max_paragraph_char_number: 5000,
|
||||
no_references_setting: {
|
||||
status: 'ai_questioning',
|
||||
value: '{question}'
|
||||
}
|
||||
},
|
||||
problem_optimization: false,
|
||||
problem_optimization_prompt: ''
|
||||
}
|
||||
noReferencesform.value = {
|
||||
ai_questioning: defaultValue['ai_questioning'],
|
||||
designated_answer: defaultValue['designated_answer']
|
||||
|
|
|
|||
|
|
@ -430,8 +430,14 @@ async function changeState(bool: Boolean, row: any) {
|
|||
row.is_active = false
|
||||
return
|
||||
}
|
||||
const init_params = res.data.init_field_list.reduce((acc: any, item: any) => {
|
||||
acc[item.field] = item.default_value
|
||||
return acc
|
||||
}, {})
|
||||
const obj = {
|
||||
is_active: bool,
|
||||
init_params: init_params,
|
||||
init_field_list: res.data.init_field_list
|
||||
}
|
||||
functionLibApi.putFunctionLib(row.id, obj, changeStateloading).then((res) => {})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<login-layout v-if="!loading" v-loading="loading || sendLoading">
|
||||
<login-layout v-if="!loading" v-loading="loading">
|
||||
<LoginContainer
|
||||
:subTitle="
|
||||
user.themeInfo?.slogan ? user.themeInfo?.slogan : $t('views.system.theme.defaultSlogan')
|
||||
|
|
|
|||
|
|
@ -34,21 +34,6 @@
|
|||
</el-input>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div class="mb-24">
|
||||
<el-form-item prop="captcha">
|
||||
<div class="flex-between w-full">
|
||||
<el-input
|
||||
size="large"
|
||||
class="input-item"
|
||||
v-model="loginForm.captcha"
|
||||
:placeholder="$t('views.user.userForm.form.captcha.placeholder')"
|
||||
>
|
||||
</el-input>
|
||||
|
||||
<img :src="identifyCode" alt="" height="38" class="ml-8 cursor border border-r-4" @click="makeCode" />
|
||||
</div>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-form>
|
||||
|
||||
<el-button size="large" type="primary" class="w-full" @click="login"
|
||||
|
|
@ -122,7 +107,6 @@ import { useRoute, useRouter } from 'vue-router'
|
|||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
import useStore from '@/stores'
|
||||
import authApi from '@/api/auth-setting'
|
||||
import useApi from '@/api/user'
|
||||
import { MsgConfirm, MsgError, MsgSuccess } from '@/utils/message'
|
||||
|
||||
import { t, getBrowserLang } from '@/locales'
|
||||
|
|
@ -136,15 +120,9 @@ const { user } = useStore()
|
|||
const router = useRouter()
|
||||
const loginForm = ref<LoginRequest>({
|
||||
username: '',
|
||||
password: '',
|
||||
captcha: ''
|
||||
password: ''
|
||||
})
|
||||
const identifyCode = ref<string>('')
|
||||
function makeCode() {
|
||||
useApi.getCaptcha().then((res: any) => {
|
||||
identifyCode.value = res.data
|
||||
})
|
||||
}
|
||||
|
||||
const rules = ref<FormRules<LoginRequest>>({
|
||||
username: [
|
||||
{
|
||||
|
|
@ -159,13 +137,6 @@ const rules = ref<FormRules<LoginRequest>>({
|
|||
message: t('views.user.userForm.form.password.requiredMessage'),
|
||||
trigger: 'blur'
|
||||
}
|
||||
],
|
||||
captcha: [
|
||||
{
|
||||
required: true,
|
||||
message: t('views.user.userForm.form.captcha.placeholder'),
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
})
|
||||
const loginFormRef = ref<FormInstance>()
|
||||
|
|
@ -251,8 +222,7 @@ function changeMode(val: string) {
|
|||
showQrCodeTab.value = false
|
||||
loginForm.value = {
|
||||
username: '',
|
||||
password: '',
|
||||
captcha: ''
|
||||
password: ''
|
||||
}
|
||||
redirectAuth(val)
|
||||
loginFormRef.value?.clearValidate()
|
||||
|
|
@ -262,12 +232,7 @@ const login = () => {
|
|||
loginFormRef.value?.validate().then(() => {
|
||||
loading.value = true
|
||||
user
|
||||
.login(
|
||||
loginMode.value,
|
||||
loginForm.value.username,
|
||||
loginForm.value.password,
|
||||
loginForm.value.captcha
|
||||
)
|
||||
.login(loginMode.value, loginForm.value.username, loginForm.value.password)
|
||||
.then(() => {
|
||||
locale.value = localStorage.getItem('MaxKB-locale') || getBrowserLang() || 'en-US'
|
||||
router.push({ name: 'home' })
|
||||
|
|
@ -320,7 +285,6 @@ onBeforeMount(() => {
|
|||
declare const window: any
|
||||
|
||||
onMounted(() => {
|
||||
makeCode()
|
||||
const route = useRoute()
|
||||
const currentUrl = ref(route.fullPath)
|
||||
const params = new URLSearchParams(currentUrl.value.split('?')[1])
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<login-layout v-if="!loading" v-loading="loading || sendLoading">
|
||||
<login-layout v-if="!loading" v-loading="loading">
|
||||
<LoginContainer
|
||||
:subTitle="
|
||||
user.themeInfo?.slogan ? user.themeInfo?.slogan : $t('views.system.theme.defaultSlogan')
|
||||
|
|
|
|||
|
|
@ -214,4 +214,8 @@ onMounted(() => {
|
|||
getList()
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scoped></style>
|
||||
<style lang="scss" scoped>
|
||||
.log-table tr {
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -238,42 +238,49 @@ const update_field = () => {
|
|||
const new_user_input_field_list = cloneDeep(
|
||||
ok.data.work_flow.nodes[0].properties.user_input_field_list
|
||||
)
|
||||
|
||||
const merge_api_input_field_list = (new_api_input_field_list || []).map((item: any) => {
|
||||
const find_field = old_api_input_field_list.find(
|
||||
(old_item: any) => old_item.variable == item.variable
|
||||
)
|
||||
if (find_field) {
|
||||
return {
|
||||
...item,
|
||||
value: find_field.value,
|
||||
label:
|
||||
typeof item.label === 'object' && item.label != null ? item.label.label : item.label
|
||||
const merge_api_input_field_list =
|
||||
new_api_input_field_list ||
|
||||
[].map((item: any) => {
|
||||
const find_field = old_api_input_field_list.find(
|
||||
(old_item: any) => old_item.variable == item.variable
|
||||
)
|
||||
if (find_field) {
|
||||
return {
|
||||
...item,
|
||||
value: find_field.value,
|
||||
label:
|
||||
typeof item.label === 'object' && item.label != null
|
||||
? item.label.label
|
||||
: item.label
|
||||
}
|
||||
} else {
|
||||
return item
|
||||
}
|
||||
} else {
|
||||
return item
|
||||
}
|
||||
})
|
||||
})
|
||||
set(
|
||||
props.nodeModel.properties.node_data,
|
||||
'api_input_field_list',
|
||||
merge_api_input_field_list
|
||||
)
|
||||
const merge_user_input_field_list = (new_user_input_field_list || []).map((item: any) => {
|
||||
const find_field = old_user_input_field_list.find(
|
||||
(old_item: any) => old_item.field == item.field
|
||||
)
|
||||
if (find_field) {
|
||||
return {
|
||||
...item,
|
||||
value: find_field.value,
|
||||
label:
|
||||
typeof item.label === 'object' && item.label != null ? item.label.label : item.label
|
||||
const merge_user_input_field_list =
|
||||
new_user_input_field_list ||
|
||||
[].map((item: any) => {
|
||||
const find_field = old_user_input_field_list.find(
|
||||
(old_item: any) => old_item.field == item.field
|
||||
)
|
||||
if (find_field) {
|
||||
return {
|
||||
...item,
|
||||
value: find_field.value,
|
||||
label:
|
||||
typeof item.label === 'object' && item.label != null
|
||||
? item.label.label
|
||||
: item.label
|
||||
}
|
||||
} else {
|
||||
return item
|
||||
}
|
||||
} else {
|
||||
return item
|
||||
}
|
||||
})
|
||||
})
|
||||
set(
|
||||
props.nodeModel.properties.node_data,
|
||||
'user_input_field_list',
|
||||
|
|
|
|||
|
|
@ -316,7 +316,7 @@ const switchFileUpload = () => {
|
|||
audio: false,
|
||||
video: false,
|
||||
other: false,
|
||||
otherExtensions: ['PPT', 'DOC']
|
||||
otherExtensions: ['ppt', 'doc']
|
||||
}
|
||||
|
||||
if (form_data.value.file_upload_enable) {
|
||||
|
|
|
|||
|
|
@ -4,28 +4,6 @@ class FormNode extends AppNode {
|
|||
constructor(props: any) {
|
||||
super(props, FormNodeVue)
|
||||
}
|
||||
get_node_field_list() {
|
||||
const result = []
|
||||
const fields = this.props.model.properties?.config?.fields || []
|
||||
|
||||
try {
|
||||
this.props.model.properties.node_data.form_field_list.forEach((item: any) => {
|
||||
if (!fields.some((f: any) => f.value === item.field)) {
|
||||
fields.push({
|
||||
value: item.field,
|
||||
label: typeof item.label == 'string' ? item.label : item.label.label
|
||||
})
|
||||
}
|
||||
})
|
||||
} catch (e) {}
|
||||
result.push({
|
||||
value: this.props.model.id,
|
||||
label: this.props.model.properties.stepName,
|
||||
type: this.props.model.type,
|
||||
children: fields
|
||||
})
|
||||
return result
|
||||
}
|
||||
}
|
||||
export default {
|
||||
type: 'form-node',
|
||||
|
|
|
|||
|
|
@ -59,149 +59,135 @@
|
|||
<h5 class="title-decoration-1 mb-8">
|
||||
{{ $t('views.applicationWorkflow.nodes.mcpNode.toolParam') }}
|
||||
</h5>
|
||||
<template v-if="form_data.tool_params[form_data.params_nested]">
|
||||
<div class="p-8-12" v-if="!form_data.mcp_tool">
|
||||
<el-text type="info">{{ $t('common.noData') }}</el-text>
|
||||
</div>
|
||||
<div v-else class="border-r-4 p-8-12 mb-8 layout-bg lighter">
|
||||
<el-form
|
||||
ref="dynamicsFormRef"
|
||||
label-position="top"
|
||||
v-loading="loading"
|
||||
require-asterisk-position="right"
|
||||
:hide-required-asterisk="true"
|
||||
v-if="form_data.mcp_tool"
|
||||
@submit.prevent
|
||||
<div
|
||||
class="border-r-4 p-8-12 mb-8 layout-bg lighter"
|
||||
v-if="form_data.tool_params[form_data.params_nested]"
|
||||
>
|
||||
<el-form
|
||||
ref="dynamicsFormRef"
|
||||
label-position="top"
|
||||
v-loading="loading"
|
||||
require-asterisk-position="right"
|
||||
:hide-required-asterisk="true"
|
||||
v-if="form_data.mcp_tool"
|
||||
@submit.prevent
|
||||
>
|
||||
<el-form-item
|
||||
v-for="item in form_data.tool_form_field" :key="item.field"
|
||||
:required="item.required"
|
||||
>
|
||||
<el-form-item
|
||||
v-for="item in form_data.tool_form_field"
|
||||
:key="item.field"
|
||||
:required="item.required"
|
||||
>
|
||||
<template #label>
|
||||
<div class="flex-between">
|
||||
<div>
|
||||
<TooltipLabel :label="item.label.label" :tooltip="item.label.attrs.tooltip" />
|
||||
<span v-if="item.required" class="danger">*</span>
|
||||
</div>
|
||||
<el-select
|
||||
:teleported="false"
|
||||
v-model="item.source"
|
||||
size="small"
|
||||
style="width: 85px"
|
||||
@change="form_data.tool_params[form_data.params_nested] = {}"
|
||||
>
|
||||
<el-option
|
||||
:label="$t('views.applicationWorkflow.nodes.replyNode.replyContent.reference')"
|
||||
value="referencing"
|
||||
/>
|
||||
<el-option
|
||||
:label="$t('views.applicationWorkflow.nodes.replyNode.replyContent.custom')"
|
||||
value="custom"
|
||||
/>
|
||||
</el-select>
|
||||
<template #label>
|
||||
<div class="flex-between">
|
||||
<div>
|
||||
<TooltipLabel :label="item.label.label" :tooltip="item.label.attrs.tooltip" />
|
||||
<span v-if="item.required" class="danger">*</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-input
|
||||
v-if="item.source === 'custom' && item.input_type === 'TextInput'"
|
||||
v-model="form_data.tool_params[form_data.params_nested][item.label.label]"
|
||||
/>
|
||||
<el-input-number
|
||||
v-else-if="item.source === 'custom' && item.input_type === 'NumberInput'"
|
||||
v-model="form_data.tool_params[form_data.params_nested][item.label.label]"
|
||||
/>
|
||||
<el-switch
|
||||
v-else-if="item.source === 'custom' && item.input_type === 'SwitchInput'"
|
||||
v-model="form_data.tool_params[form_data.params_nested][item.label.label]"
|
||||
/>
|
||||
<el-input
|
||||
v-else-if="item.source === 'custom' && item.input_type === 'JsonInput'"
|
||||
v-model="form_data.tool_params[form_data.params_nested][item.label.label]"
|
||||
type="textarea"
|
||||
/>
|
||||
<NodeCascader
|
||||
v-if="item.source === 'referencing'"
|
||||
ref="nodeCascaderRef2"
|
||||
:nodeModel="nodeModel"
|
||||
class="w-full"
|
||||
:placeholder="$t('views.applicationWorkflow.variable.placeholder')"
|
||||
v-model="form_data.tool_params[form_data.params_nested][item.label.label]"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="p-8-12" v-if="!form_data.mcp_tool">
|
||||
<el-text type="info">{{ $t('common.noData') }}</el-text>
|
||||
</div>
|
||||
<div v-else class="border-r-4 p-8-12 mb-8 layout-bg lighter">
|
||||
<el-form
|
||||
ref="dynamicsFormRef"
|
||||
label-position="top"
|
||||
v-loading="loading"
|
||||
require-asterisk-position="right"
|
||||
:hide-required-asterisk="true"
|
||||
v-if="form_data.mcp_tool"
|
||||
@submit.prevent
|
||||
<el-select :teleported="false" v-model="item.source" size="small"
|
||||
style="width: 85px"
|
||||
@change="form_data.tool_params[form_data.params_nested] = {}">
|
||||
<el-option
|
||||
:label="$t('views.applicationWorkflow.nodes.replyNode.replyContent.reference')"
|
||||
value="referencing"
|
||||
/>
|
||||
<el-option
|
||||
:label="$t('views.applicationWorkflow.nodes.replyNode.replyContent.custom')"
|
||||
value="custom"
|
||||
/>
|
||||
</el-select>
|
||||
</div>
|
||||
</template>
|
||||
<el-input
|
||||
v-if="item.source === 'custom' && item.input_type === 'TextInput'"
|
||||
v-model="form_data.tool_params[form_data.params_nested][item.label.label]"
|
||||
/>
|
||||
<el-input-number
|
||||
v-else-if="item.source === 'custom' && item.input_type === 'NumberInput'"
|
||||
v-model="form_data.tool_params[form_data.params_nested][item.label.label]"
|
||||
/>
|
||||
<el-switch
|
||||
v-else-if="item.source === 'custom' && item.input_type === 'SwitchInput'"
|
||||
v-model="form_data.tool_params[form_data.params_nested][item.label.label]"
|
||||
/>
|
||||
<el-input
|
||||
v-else-if="item.source === 'custom' && item.input_type === 'JsonInput'"
|
||||
v-model="form_data.tool_params[form_data.params_nested][item.label.label]"
|
||||
type="textarea"
|
||||
/>
|
||||
<NodeCascader
|
||||
v-if="item.source === 'referencing'"
|
||||
ref="nodeCascaderRef2"
|
||||
:nodeModel="nodeModel"
|
||||
class="w-full"
|
||||
:placeholder="$t('views.applicationWorkflow.variable.placeholder')"
|
||||
v-model="form_data.tool_params[form_data.params_nested][item.label.label]"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<div
|
||||
v-else
|
||||
class="border-r-4 p-8-12 mb-8 layout-bg lighter"
|
||||
>
|
||||
<el-form
|
||||
ref="dynamicsFormRef"
|
||||
label-position="top"
|
||||
v-loading="loading"
|
||||
require-asterisk-position="right"
|
||||
:hide-required-asterisk="true"
|
||||
v-if="form_data.mcp_tool"
|
||||
@submit.prevent
|
||||
>
|
||||
<el-form-item
|
||||
v-for="item in form_data.tool_form_field" :key="item.field"
|
||||
:required="item.required"
|
||||
>
|
||||
<el-form-item
|
||||
v-for="item in form_data.tool_form_field"
|
||||
:key="item.field"
|
||||
:required="item.required"
|
||||
>
|
||||
<template #label>
|
||||
<div class="flex-between">
|
||||
<div>
|
||||
<TooltipLabel :label="item.label.label" :tooltip="item.label.attrs.tooltip" />
|
||||
<span v-if="item.required" class="danger">*</span>
|
||||
</div>
|
||||
<el-select
|
||||
:teleported="false"
|
||||
v-model="item.source"
|
||||
size="small"
|
||||
style="width: 85px"
|
||||
>
|
||||
<el-option
|
||||
:label="$t('views.applicationWorkflow.nodes.replyNode.replyContent.reference')"
|
||||
value="referencing"
|
||||
/>
|
||||
<el-option
|
||||
:label="$t('views.applicationWorkflow.nodes.replyNode.replyContent.custom')"
|
||||
value="custom"
|
||||
/>
|
||||
</el-select>
|
||||
<template #label>
|
||||
<div class="flex-between">
|
||||
<div>
|
||||
<TooltipLabel :label="item.label.label" :tooltip="item.label.attrs.tooltip" />
|
||||
<span v-if="item.required" class="danger">*</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-input
|
||||
v-if="item.source === 'custom' && item.input_type === 'TextInput'"
|
||||
v-model="form_data.tool_params[item.label.label]"
|
||||
/>
|
||||
<el-input-number
|
||||
v-else-if="item.source === 'custom' && item.input_type === 'NumberInput'"
|
||||
v-model="form_data.tool_params[item.label.label]"
|
||||
/>
|
||||
<el-switch
|
||||
v-else-if="item.source === 'custom' && item.input_type === 'SwitchInput'"
|
||||
v-model="form_data.tool_params[item.label.label]"
|
||||
/>
|
||||
<el-input
|
||||
v-else-if="item.source === 'custom' && item.input_type === 'JsonInput'"
|
||||
v-model="form_data.tool_params[item.label.label]"
|
||||
type="textarea"
|
||||
/>
|
||||
<NodeCascader
|
||||
v-if="item.source === 'referencing'"
|
||||
ref="nodeCascaderRef2"
|
||||
:nodeModel="nodeModel"
|
||||
class="w-full"
|
||||
:placeholder="$t('views.applicationWorkflow.variable.placeholder')"
|
||||
v-model="form_data.tool_params[item.label.label]"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</template>
|
||||
<el-select :teleported="false" v-model="item.source" size="small"
|
||||
style="width: 85px">
|
||||
<el-option
|
||||
:label="$t('views.applicationWorkflow.nodes.replyNode.replyContent.reference')"
|
||||
value="referencing"
|
||||
/>
|
||||
<el-option
|
||||
:label="$t('views.applicationWorkflow.nodes.replyNode.replyContent.custom')"
|
||||
value="custom"
|
||||
/>
|
||||
</el-select>
|
||||
</div>
|
||||
</template>
|
||||
<el-input
|
||||
v-if="item.source === 'custom' && item.input_type === 'TextInput'"
|
||||
v-model="form_data.tool_params[item.label.label]"
|
||||
/>
|
||||
<el-input-number
|
||||
v-else-if="item.source === 'custom' && item.input_type === 'NumberInput'"
|
||||
v-model="form_data.tool_params[item.label.label]"
|
||||
/>
|
||||
<el-switch
|
||||
v-else-if="item.source === 'custom' && item.input_type === 'SwitchInput'"
|
||||
v-model="form_data.tool_params[item.label.label]"
|
||||
/>
|
||||
<el-input
|
||||
v-else-if="item.source === 'custom' && item.input_type === 'JsonInput'"
|
||||
v-model="form_data.tool_params[item.label.label]"
|
||||
type="textarea"
|
||||
/>
|
||||
<NodeCascader
|
||||
v-if="item.source === 'referencing'"
|
||||
ref="nodeCascaderRef2"
|
||||
:nodeModel="nodeModel"
|
||||
class="w-full"
|
||||
:placeholder="$t('views.applicationWorkflow.variable.placeholder')"
|
||||
v-model="form_data.tool_params[item.label.label]"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</NodeContainer>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
|
|
@ -266,10 +252,6 @@ function getTools() {
|
|||
.then((res: any) => {
|
||||
form_data.value.mcp_tools = res.data
|
||||
MsgSuccess(t('views.applicationWorkflow.nodes.mcpNode.getToolsSuccess'))
|
||||
// 修改了json,刷新mcp_server
|
||||
form_data.value.mcp_server = form_data.value.mcp_tools.filter(
|
||||
(item: any) => item.name === form_data.value.mcp_tool
|
||||
)[0].server
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -337,7 +319,7 @@ function changeTool() {
|
|||
} else if (args_schema.properties[item].type === 'object') {
|
||||
input_type = 'JsonInput'
|
||||
}
|
||||
console.log(args_schema.properties[item])
|
||||
console.log(args_schema.properties[item]);
|
||||
form_data.value.tool_form_field.push({
|
||||
field: item,
|
||||
label: {
|
||||
|
|
|
|||
Loading…
Reference in New Issue