mirror of
https://github.com/1Panel-dev/MaxKB.git
synced 2025-12-26 01:33:05 +00:00
Merge branch 'main' into release-1.0
This commit is contained in:
commit
02050a2cca
|
|
@ -48,7 +48,7 @@ jobs:
|
|||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: main
|
||||
ref: ${{ github.ref_name }}
|
||||
- name: Prepare
|
||||
id: prepare
|
||||
run: |
|
||||
|
|
@ -104,7 +104,7 @@ jobs:
|
|||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: main
|
||||
ref: ${{ github.ref_name }}
|
||||
- name: Prepare
|
||||
id: prepare
|
||||
run: |
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
name: Typos Check
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
run:
|
||||
name: Spell Check with Typos
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Actions Repository
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Check spelling
|
||||
uses: crate-ci/typos@master
|
||||
|
|
@ -42,7 +42,7 @@ share/python-wheels/
|
|||
MANIFEST
|
||||
|
||||
# PyInstaller
|
||||
# Usually these files are written by a python script froms a template
|
||||
# Usually these files are written by a python script forms a template
|
||||
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||
*.manifest
|
||||
*.spec
|
||||
|
|
|
|||
10
README.md
10
README.md
|
|
@ -51,7 +51,7 @@ docker run -d --name=maxkb -p 8080:8080 -v ~/.maxkb:/var/lib/postgresql/data 1pa
|
|||
|
||||
- 前端:[Vue.js](https://cn.vuejs.org/)
|
||||
- 后端:[Python / Django](https://www.djangoproject.com/)
|
||||
- Langchain:[Langchain](https://www.langchain.com/)
|
||||
- LangChain:[LangChain](https://www.langchain.com/)
|
||||
- 向量数据库:[PostgreSQL / pgvector](https://www.postgresql.org/)
|
||||
- 大模型:Azure OpenAI、百度千帆大模型、[Ollama](https://github.com/ollama/ollama)
|
||||
|
||||
|
|
@ -59,6 +59,14 @@ docker run -d --name=maxkb -p 8080:8080 -v ~/.maxkb:/var/lib/postgresql/data 1pa
|
|||
|
||||
[](https://star-history.com/#1Panel-dev/MaxKB&Date)
|
||||
|
||||
## 我们的其他开源产品
|
||||
|
||||
- [JumpServer](https://github.com/jumpserver/jumpserver/) - 广受欢迎的开源堡垒机
|
||||
- [DataEase](https://github.com/dataease/dataease/) - 人人可用的开源数据可视化分析工具
|
||||
- [MeterSphere](https://github.com/metersphere/metersphere/) - 一站式开源自动化测试平台
|
||||
- [1Panel](https://github.com/1panel-dev/1panel/) - 现代化、开源的 Linux 服务器运维管理面板
|
||||
- [Halo](https://github.com/halo-dev/halo/) - 强大易用的开源建站工具
|
||||
|
||||
## License
|
||||
|
||||
Copyright (c) 2014-2024 飞致云 FIT2CLOUD, All rights reserved.
|
||||
|
|
|
|||
|
|
@ -196,7 +196,7 @@ class ApplicationSerializer(serializers.Serializer):
|
|||
access_token = serializers.CharField(required=True, error_messages=ErrMessage.char("access_token"))
|
||||
|
||||
def auth(self, request, with_valid=True):
|
||||
token = request.META.get('HTTP_AUTHORIZATION', None)
|
||||
token = request.META.get('HTTP_AUTHORIZATION')
|
||||
token_details = None
|
||||
try:
|
||||
# 校验token
|
||||
|
|
@ -257,7 +257,7 @@ class ApplicationSerializer(serializers.Serializer):
|
|||
application_model = ApplicationSerializer.Create.to_application_model(user_id, application)
|
||||
dataset_id_list = application.get('dataset_id_list', [])
|
||||
application_dataset_mapping_model_list = [
|
||||
ApplicationSerializer.Create.to_application_dateset_mapping(application_model.id, dataset_id) for
|
||||
ApplicationSerializer.Create.to_application_dataset_mapping(application_model.id, dataset_id) for
|
||||
dataset_id in dataset_id_list]
|
||||
# 插入应用
|
||||
application_model.save()
|
||||
|
|
@ -280,7 +280,7 @@ class ApplicationSerializer(serializers.Serializer):
|
|||
)
|
||||
|
||||
@staticmethod
|
||||
def to_application_dateset_mapping(application_id: str, dataset_id: str):
|
||||
def to_application_dataset_mapping(application_id: str, dataset_id: str):
|
||||
return ApplicationDatasetMapping(id=uuid.uuid1(), application_id=application_id, dataset_id=dataset_id)
|
||||
|
||||
class HitTest(serializers.Serializer):
|
||||
|
|
|
|||
|
|
@ -178,12 +178,12 @@ class ChatMessageSerializer(serializers.Serializer):
|
|||
client_id = self.data.get('client_id')
|
||||
client_type = self.data.get('client_type')
|
||||
chat_info = self.is_valid(raise_exception=True)
|
||||
pipline_manage_builder = PipelineManage.builder()
|
||||
pipeline_manage_builder = PipelineManage.builder()
|
||||
# 如果开启了问题优化,则添加上问题优化步骤
|
||||
if chat_info.application.problem_optimization:
|
||||
pipline_manage_builder.append_step(BaseResetProblemStep)
|
||||
pipeline_manage_builder.append_step(BaseResetProblemStep)
|
||||
# 构建流水线管理器
|
||||
pipline_message = (pipline_manage_builder.append_step(BaseSearchDatasetStep)
|
||||
pipeline_message = (pipeline_manage_builder.append_step(BaseSearchDatasetStep)
|
||||
.append_step(BaseGenerateHumanMessageStep)
|
||||
.append_step(BaseChatStep)
|
||||
.build())
|
||||
|
|
@ -198,8 +198,8 @@ class ChatMessageSerializer(serializers.Serializer):
|
|||
params = chat_info.to_pipeline_manage_params(message, get_post_handler(chat_info), exclude_paragraph_id_list,
|
||||
client_id, client_type, stream)
|
||||
# 运行流水线作业
|
||||
pipline_message.run(params)
|
||||
return pipline_message.context['chat_result']
|
||||
pipeline_message.run(params)
|
||||
return pipeline_message.context['chat_result']
|
||||
|
||||
@staticmethod
|
||||
def re_open_chat(chat_id: str):
|
||||
|
|
|
|||
|
|
@ -247,7 +247,7 @@ function initMaxkbStyle(root){
|
|||
#maxkb #maxkb-chat-container{
|
||||
z-index:10000;position: relative;
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--N300, #DEE0E3);
|
||||
border: 1px solid #ffffff;
|
||||
background: linear-gradient(188deg, rgba(235, 241, 255, 0.20) 39.6%, rgba(231, 249, 255, 0.20) 94.3%), #EFF0F1;
|
||||
box-shadow: 0px 4px 8px 0px rgba(31, 35, 41, 0.10);
|
||||
position: fixed;bottom: 20px;right: 45px;overflow: hidden;
|
||||
|
|
|
|||
|
|
@ -47,8 +47,7 @@ class TokenDetails:
|
|||
class TokenAuth(TokenAuthentication):
|
||||
# 重新 authenticate 方法,自定义认证规则
|
||||
def authenticate(self, request):
|
||||
auth = request.META.get('HTTP_AUTHORIZATION', None
|
||||
)
|
||||
auth = request.META.get('HTTP_AUTHORIZATION')
|
||||
# 未认证
|
||||
if auth is None:
|
||||
raise AppAuthenticationFailed(1003, '未登录,请先登录')
|
||||
|
|
|
|||
|
|
@ -215,7 +215,7 @@ class ListenerManagement:
|
|||
|
||||
@staticmethod
|
||||
@poxy
|
||||
def init_embedding_model(ags):
|
||||
def init_embedding_model(ages):
|
||||
EmbeddingModel.get_embedding_model()
|
||||
|
||||
def run(self):
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
"""
|
||||
from typing import Dict
|
||||
|
||||
from common.froms.base_field import BaseExecField, TriggerType
|
||||
from common.forms.base_field import BaseExecField, TriggerType
|
||||
|
||||
|
||||
class ArrayCard(BaseExecField):
|
||||
|
|
@ -6,7 +6,7 @@
|
|||
@date:2023/11/1 16:04
|
||||
@desc:
|
||||
"""
|
||||
from common.froms import BaseField
|
||||
from common.forms import BaseField
|
||||
|
||||
|
||||
class BaseForm:
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
"""
|
||||
from typing import List, Dict
|
||||
|
||||
from common.froms.base_field import BaseExecField, TriggerType
|
||||
from common.forms.base_field import BaseExecField, TriggerType
|
||||
|
||||
|
||||
class MultiSelect(BaseExecField):
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
"""
|
||||
from typing import Dict
|
||||
|
||||
from common.froms.base_field import BaseExecField, TriggerType
|
||||
from common.forms.base_field import BaseExecField, TriggerType
|
||||
|
||||
|
||||
class ObjectCard(BaseExecField):
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
"""
|
||||
from typing import Dict
|
||||
|
||||
from common.froms import BaseField, TriggerType
|
||||
from common.forms import BaseField, TriggerType
|
||||
|
||||
|
||||
class PasswordInputField(BaseField):
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
"""
|
||||
from typing import List, Dict
|
||||
|
||||
from common.froms.base_field import BaseExecField, TriggerType
|
||||
from common.forms.base_field import BaseExecField, TriggerType
|
||||
|
||||
|
||||
class Radio(BaseExecField):
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
"""
|
||||
from typing import List, Dict
|
||||
|
||||
from common.froms.base_field import BaseExecField, TriggerType
|
||||
from common.forms.base_field import BaseExecField, TriggerType
|
||||
|
||||
|
||||
class Radio(BaseExecField):
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
"""
|
||||
from typing import List, Dict
|
||||
|
||||
from common.froms.base_field import BaseExecField, TriggerType
|
||||
from common.forms.base_field import BaseExecField, TriggerType
|
||||
|
||||
|
||||
class Radio(BaseExecField):
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
"""
|
||||
from typing import List, Dict
|
||||
|
||||
from common.froms.base_field import TriggerType, BaseExecField
|
||||
from common.forms.base_field import TriggerType, BaseExecField
|
||||
|
||||
|
||||
class SingleSelect(BaseExecField):
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
"""
|
||||
from typing import Dict
|
||||
|
||||
from common.froms.base_field import BaseExecField, TriggerType
|
||||
from common.forms.base_field import BaseExecField, TriggerType
|
||||
|
||||
|
||||
class TabCard(BaseExecField):
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
"""
|
||||
from typing import Dict
|
||||
|
||||
from common.froms.base_field import TriggerType, BaseExecField
|
||||
from common.forms.base_field import TriggerType, BaseExecField
|
||||
|
||||
|
||||
class TableRadio(BaseExecField):
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
"""
|
||||
from typing import Dict
|
||||
|
||||
from common.froms.base_field import TriggerType, BaseExecField
|
||||
from common.forms.base_field import TriggerType, BaseExecField
|
||||
|
||||
|
||||
class TableRadio(BaseExecField):
|
||||
|
|
@ -8,7 +8,7 @@
|
|||
"""
|
||||
from typing import Dict
|
||||
|
||||
from common.froms.base_field import BaseField, TriggerType
|
||||
from common.forms.base_field import BaseField, TriggerType
|
||||
|
||||
|
||||
class TextInputField(BaseField):
|
||||
|
|
@ -9,8 +9,6 @@
|
|||
|
||||
|
||||
def get_file_content(path):
|
||||
file = open(path, "r",
|
||||
encoding='utf-8')
|
||||
content = file.read()
|
||||
file.close()
|
||||
with open(path, "r", encoding='utf-8') as file:
|
||||
content = file.read()
|
||||
return content
|
||||
|
|
|
|||
|
|
@ -336,6 +336,7 @@ class SplitModel:
|
|||
:return: 解析后数据 {content:段落数据,keywords:[‘段落关键词’],parent_chain:['段落父级链路']}
|
||||
"""
|
||||
text = text.replace('\r', '\n')
|
||||
text = text.replace("\0", '')
|
||||
result_tree = self.parse_to_tree(text, 0)
|
||||
result = result_tree_to_paragraph(result_tree, [], [])
|
||||
return [item for item in [self.post_reset_paragraph(row) for row in result] if
|
||||
|
|
|
|||
|
|
@ -11,7 +11,9 @@ from enum import Enum
|
|||
from setting.models_provider.impl.azure_model_provider.azure_model_provider import AzureModelProvider
|
||||
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.wenxin_model_provider.wenxin_model_provider import WenxinModelProvider
|
||||
from setting.models_provider.impl.kimi_model_provider.kimi_model_provider import KimiModelProvider
|
||||
|
||||
|
||||
class ModelProvideConstants(Enum):
|
||||
|
|
@ -19,3 +21,5 @@ class ModelProvideConstants(Enum):
|
|||
model_wenxin_provider = WenxinModelProvider()
|
||||
model_ollama_provider = OllamaModelProvider()
|
||||
model_openai_provider = OpenAIModelProvider()
|
||||
model_kimi_provider = KimiModelProvider()
|
||||
model_qwen_provider = QwenModelProvider()
|
||||
|
|
|
|||
|
|
@ -12,9 +12,9 @@ from typing import Dict
|
|||
from langchain.schema import HumanMessage
|
||||
from langchain_community.chat_models.azure_openai import AzureChatOpenAI
|
||||
|
||||
from common import froms
|
||||
from common import forms
|
||||
from common.exception.app_exception import AppApiException
|
||||
from common.froms import BaseForm
|
||||
from common.forms import BaseForm
|
||||
from common.util.file_util import get_file_content
|
||||
from setting.models_provider.base_model_provider import IModelProvider, ModelProvideInfo, BaseModelCredential, \
|
||||
ModelInfo, \
|
||||
|
|
@ -51,11 +51,11 @@ class AzureLLMModelCredential(BaseForm, BaseModelCredential):
|
|||
def encryption_dict(self, model: Dict[str, object]):
|
||||
return {**model, 'api_key': super().encryption(model.get('api_key', ''))}
|
||||
|
||||
api_base = froms.TextInputField('API 域名', required=True)
|
||||
api_base = forms.TextInputField('API 域名', required=True)
|
||||
|
||||
api_key = froms.PasswordInputField("API Key", required=True)
|
||||
api_key = forms.PasswordInputField("API Key", required=True)
|
||||
|
||||
deployment_name = froms.TextInputField("部署名", required=True)
|
||||
deployment_name = forms.TextInputField("部署名", required=True)
|
||||
|
||||
|
||||
class DefaultAzureLLMModelCredential(BaseForm, BaseModelCredential):
|
||||
|
|
@ -87,13 +87,13 @@ class DefaultAzureLLMModelCredential(BaseForm, BaseModelCredential):
|
|||
def encryption_dict(self, model: Dict[str, object]):
|
||||
return {**model, 'api_key': super().encryption(model.get('api_key', ''))}
|
||||
|
||||
api_version = froms.TextInputField("api_version", required=True)
|
||||
api_version = forms.TextInputField("api_version", required=True)
|
||||
|
||||
api_base = froms.TextInputField('API 域名', required=True)
|
||||
api_base = forms.TextInputField('API 域名', required=True)
|
||||
|
||||
api_key = froms.PasswordInputField("API Key", required=True)
|
||||
api_key = forms.PasswordInputField("API Key", required=True)
|
||||
|
||||
deployment_name = froms.TextInputField("部署名", required=True)
|
||||
deployment_name = forms.TextInputField("部署名", required=True)
|
||||
|
||||
|
||||
azure_llm_model_credential = AzureLLMModelCredential()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
# coding=utf-8
|
||||
"""
|
||||
@project: maxkb
|
||||
@Author:虎
|
||||
@file: __init__.py.py
|
||||
@date:2023/10/31 17:16
|
||||
@desc:
|
||||
"""
|
||||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 23 KiB |
|
|
@ -0,0 +1,109 @@
|
|||
# coding=utf-8
|
||||
"""
|
||||
@project: maxkb
|
||||
@Author:虎
|
||||
@file: kimi_model_provider.py
|
||||
@date:2024/3/28 16:26
|
||||
@desc:
|
||||
"""
|
||||
import os
|
||||
from typing import Dict
|
||||
|
||||
from langchain.schema import HumanMessage
|
||||
from langchain.chat_models.base import BaseChatModel
|
||||
|
||||
|
||||
from common import forms
|
||||
from common.exception.app_exception import AppApiException
|
||||
from common.forms import BaseForm
|
||||
from common.util.file_util import get_file_content
|
||||
from setting.models_provider.base_model_provider import IModelProvider, ModelProvideInfo, BaseModelCredential, \
|
||||
ModelInfo, \
|
||||
ModelTypeConst, ValidCode
|
||||
from smartdoc.conf import PROJECT_DIR
|
||||
from setting.models_provider.impl.kimi_model_provider.model.kimi_chat_model import KimiChatModel
|
||||
|
||||
|
||||
|
||||
|
||||
class KimiLLMModelCredential(BaseForm, BaseModelCredential):
|
||||
|
||||
def is_valid(self, model_type: str, model_name, model_credential: Dict[str, object], raise_exception=False):
|
||||
model_type_list = KimiModelProvider().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, f'{model_type} 模型类型不支持')
|
||||
|
||||
for key in ['api_base', 'api_key']:
|
||||
if key not in model_credential:
|
||||
if raise_exception:
|
||||
raise AppApiException(ValidCode.valid_error.value, f'{key} 字段为必填字段')
|
||||
else:
|
||||
return False
|
||||
try:
|
||||
# llm_kimi = Moonshot(
|
||||
# model_name=model_name,
|
||||
# base_url=model_credential['api_base'],
|
||||
# moonshot_api_key=model_credential['api_key']
|
||||
# )
|
||||
|
||||
model = KimiModelProvider().get_model(model_type, model_name, model_credential)
|
||||
model.invoke([HumanMessage(content='你好')])
|
||||
except Exception as e:
|
||||
if isinstance(e, AppApiException):
|
||||
raise e
|
||||
if raise_exception:
|
||||
raise AppApiException(ValidCode.valid_error.value, f'校验失败,请检查参数是否正确: {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_base = forms.TextInputField('API 域名', required=True)
|
||||
api_key = forms.PasswordInputField('API Key', required=True)
|
||||
|
||||
|
||||
kimi_llm_model_credential = KimiLLMModelCredential()
|
||||
|
||||
model_dict = {
|
||||
'moonshot-v1-8k': ModelInfo('moonshot-v1-8k', '', ModelTypeConst.LLM, kimi_llm_model_credential,
|
||||
),
|
||||
'moonshot-v1-32k': ModelInfo('moonshot-v1-32k', '', ModelTypeConst.LLM, kimi_llm_model_credential,
|
||||
),
|
||||
'moonshot-v1-128k': ModelInfo('moonshot-v1-128k', '', ModelTypeConst.LLM, kimi_llm_model_credential,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
class KimiModelProvider(IModelProvider):
|
||||
|
||||
def get_dialogue_number(self):
|
||||
return 3
|
||||
|
||||
def get_model(self, model_type, model_name, model_credential: Dict[str, object], **model_kwargs) -> BaseChatModel:
|
||||
kimi_chat_open_ai = KimiChatModel(
|
||||
openai_api_base=model_credential['api_base'],
|
||||
openai_api_key=model_credential['api_key'],
|
||||
model_name=model_name,
|
||||
)
|
||||
return kimi_chat_open_ai
|
||||
|
||||
def get_model_credential(self, model_type, model_name):
|
||||
if model_name in model_dict:
|
||||
return model_dict.get(model_name).model_credential
|
||||
return kimi_llm_model_credential
|
||||
|
||||
def get_model_provide_info(self):
|
||||
return ModelProvideInfo(provider='model_kimi_provider', name='Kimi', icon=get_file_content(
|
||||
os.path.join(PROJECT_DIR, "apps", "setting", 'models_provider', 'impl', 'kimi_model_provider', 'icon',
|
||||
'kimi_icon_svg')))
|
||||
|
||||
def get_model_list(self, model_type: str):
|
||||
if model_type is None:
|
||||
raise AppApiException(500, '模型类型不能为空')
|
||||
return [model_dict.get(key).to_dict() for key in
|
||||
list(filter(lambda key: model_dict.get(key).model_type == model_type, model_dict.keys()))]
|
||||
|
||||
def get_model_type_list(self):
|
||||
return [{'key': "大语言模型", 'value': "LLM"}]
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
# coding=utf-8
|
||||
"""
|
||||
@project: maxkb
|
||||
@Author:虎
|
||||
@file: kimi_chat_model.py
|
||||
@date:2023/11/10 17:45
|
||||
@desc:
|
||||
"""
|
||||
from typing import List
|
||||
|
||||
from langchain_community.chat_models import ChatOpenAI
|
||||
from langchain_core.messages import BaseMessage, get_buffer_string
|
||||
|
||||
|
||||
class TokenizerManage:
|
||||
tokenizer = None
|
||||
|
||||
@staticmethod
|
||||
def get_tokenizer():
|
||||
from transformers import GPT2TokenizerFast
|
||||
if TokenizerManage.tokenizer is None:
|
||||
TokenizerManage.tokenizer = GPT2TokenizerFast.from_pretrained('gpt2',
|
||||
cache_dir="/opt/maxkb/model/tokenizer",
|
||||
resume_download=False,
|
||||
force_download=False)
|
||||
return TokenizerManage.tokenizer
|
||||
|
||||
|
||||
class KimiChatModel(ChatOpenAI):
|
||||
def get_num_tokens_from_messages(self, messages: List[BaseMessage]) -> int:
|
||||
tokenizer = TokenizerManage.get_tokenizer()
|
||||
return sum([len(tokenizer.encode(get_buffer_string([m]))) for m in messages])
|
||||
|
||||
def get_num_tokens(self, text: str) -> int:
|
||||
tokenizer = TokenizerManage.get_tokenizer()
|
||||
return len(tokenizer.encode(text))
|
||||
|
|
@ -14,9 +14,9 @@ from urllib.parse import urlparse, ParseResult
|
|||
import requests
|
||||
from langchain.chat_models.base import BaseChatModel
|
||||
|
||||
from common import froms
|
||||
from common import forms
|
||||
from common.exception.app_exception import AppApiException
|
||||
from common.froms import BaseForm
|
||||
from common.forms import BaseForm
|
||||
from common.util.file_util import get_file_content
|
||||
from setting.models_provider.base_model_provider import IModelProvider, ModelProvideInfo, ModelInfo, ModelTypeConst, \
|
||||
BaseModelCredential, DownModelChunk, DownModelChunkStatus, ValidCode
|
||||
|
|
@ -51,8 +51,8 @@ class OllamaLLMModelCredential(BaseForm, BaseModelCredential):
|
|||
self.api_key = model_info.get('api_key')
|
||||
return self
|
||||
|
||||
api_base = froms.TextInputField('API 域名', required=True)
|
||||
api_key = froms.PasswordInputField('API Key', required=True)
|
||||
api_base = forms.TextInputField('API 域名', required=True)
|
||||
api_key = forms.PasswordInputField('API Key', required=True)
|
||||
|
||||
|
||||
ollama_llm_model_credential = OllamaLLMModelCredential()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,42 @@
|
|||
# coding=utf-8
|
||||
"""
|
||||
@project: maxkb
|
||||
@Author:虎
|
||||
@file: openai_chat_model.py
|
||||
@date:2024/4/18 15:28
|
||||
@desc:
|
||||
"""
|
||||
from typing import List
|
||||
|
||||
from langchain_core.messages import BaseMessage, get_buffer_string
|
||||
from langchain_openai import ChatOpenAI
|
||||
|
||||
|
||||
class TokenizerManage:
|
||||
tokenizer = None
|
||||
|
||||
@staticmethod
|
||||
def get_tokenizer():
|
||||
from transformers import GPT2TokenizerFast
|
||||
if TokenizerManage.tokenizer is None:
|
||||
TokenizerManage.tokenizer = GPT2TokenizerFast.from_pretrained('gpt2',
|
||||
cache_dir="/opt/maxkb/model/tokenizer",
|
||||
resume_download=False,
|
||||
force_download=False)
|
||||
return TokenizerManage.tokenizer
|
||||
|
||||
|
||||
class OpenAIChatModel(ChatOpenAI):
|
||||
def get_num_tokens_from_messages(self, messages: List[BaseMessage]) -> int:
|
||||
try:
|
||||
return super().get_num_tokens_from_messages(messages)
|
||||
except Exception as e:
|
||||
tokenizer = TokenizerManage.get_tokenizer()
|
||||
return sum([len(tokenizer.encode(get_buffer_string([m]))) for m in messages])
|
||||
|
||||
def get_num_tokens(self, text: str) -> int:
|
||||
try:
|
||||
return super().get_num_tokens(text)
|
||||
except Exception as e:
|
||||
tokenizer = TokenizerManage.get_tokenizer()
|
||||
return len(tokenizer.encode(text))
|
||||
|
|
@ -10,15 +10,15 @@ import os
|
|||
from typing import Dict
|
||||
|
||||
from langchain.schema import HumanMessage
|
||||
from langchain_openai import ChatOpenAI
|
||||
|
||||
from common import froms
|
||||
from common import forms
|
||||
from common.exception.app_exception import AppApiException
|
||||
from common.froms import BaseForm
|
||||
from common.forms import BaseForm
|
||||
from common.util.file_util import get_file_content
|
||||
from setting.models_provider.base_model_provider import IModelProvider, ModelProvideInfo, BaseModelCredential, \
|
||||
ModelInfo, \
|
||||
ModelTypeConst, ValidCode
|
||||
from setting.models_provider.impl.openai_model_provider.model.openai_chat_model import OpenAIChatModel
|
||||
from smartdoc.conf import PROJECT_DIR
|
||||
|
||||
|
||||
|
|
@ -50,8 +50,8 @@ class OpenAILLMModelCredential(BaseForm, BaseModelCredential):
|
|||
def encryption_dict(self, model: Dict[str, object]):
|
||||
return {**model, 'api_key': super().encryption(model.get('api_key', ''))}
|
||||
|
||||
api_base = froms.TextInputField('API 域名', required=True)
|
||||
api_key = froms.PasswordInputField('API Key', required=True)
|
||||
api_base = forms.TextInputField('API 域名', required=True)
|
||||
api_key = forms.PasswordInputField('API Key', required=True)
|
||||
|
||||
|
||||
openai_llm_model_credential = OpenAILLMModelCredential()
|
||||
|
|
@ -71,8 +71,9 @@ class OpenAIModelProvider(IModelProvider):
|
|||
def get_dialogue_number(self):
|
||||
return 3
|
||||
|
||||
def get_model(self, model_type, model_name, model_credential: Dict[str, object], **model_kwargs) -> ChatOpenAI:
|
||||
azure_chat_open_ai = ChatOpenAI(
|
||||
def get_model(self, model_type, model_name, model_credential: Dict[str, object], **model_kwargs) -> OpenAIChatModel:
|
||||
azure_chat_open_ai = OpenAIChatModel(
|
||||
model=model_name,
|
||||
openai_api_base=model_credential.get('api_base'),
|
||||
openai_api_key=model_credential.get('api_key')
|
||||
)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
# coding=utf-8
|
||||
"""
|
||||
@project: maxkb
|
||||
@Author:虎
|
||||
@file: __init__.py.py
|
||||
@date:2023/10/31 17:16
|
||||
@desc:
|
||||
"""
|
||||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 14 KiB |
|
|
@ -0,0 +1,92 @@
|
|||
# coding=utf-8
|
||||
"""
|
||||
@project: maxkb
|
||||
@Author:虎
|
||||
@file: qwen_model_provider.py
|
||||
@date:2023/10/31 16:19
|
||||
@desc:
|
||||
"""
|
||||
import os
|
||||
from typing import Dict
|
||||
|
||||
from langchain.schema import HumanMessage
|
||||
from langchain_community.chat_models.tongyi import ChatTongyi
|
||||
|
||||
from common import forms
|
||||
from common.exception.app_exception import AppApiException
|
||||
from common.forms import BaseForm
|
||||
from common.util.file_util import get_file_content
|
||||
from setting.models_provider.base_model_provider import ModelProvideInfo, ModelTypeConst, BaseModelCredential, \
|
||||
ModelInfo, IModelProvider, ValidCode
|
||||
from smartdoc.conf import PROJECT_DIR
|
||||
|
||||
|
||||
class OpenAILLMModelCredential(BaseForm, BaseModelCredential):
|
||||
|
||||
def is_valid(self, model_type: str, model_name, model_credential: Dict[str, object], raise_exception=False):
|
||||
model_type_list = QwenModelProvider().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, f'{model_type} 模型类型不支持')
|
||||
for key in ['api_key']:
|
||||
if key not in model_credential:
|
||||
if raise_exception:
|
||||
raise AppApiException(ValidCode.valid_error.value, f'{key} 字段为必填字段')
|
||||
else:
|
||||
return False
|
||||
try:
|
||||
model = QwenModelProvider().get_model(model_type, model_name, model_credential)
|
||||
model.invoke([HumanMessage(content='你好')])
|
||||
except Exception as e:
|
||||
if isinstance(e, AppApiException):
|
||||
raise e
|
||||
if raise_exception:
|
||||
raise AppApiException(ValidCode.valid_error.value, f'校验失败,请检查参数是否正确: {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)
|
||||
|
||||
|
||||
qwen_model_credential = OpenAILLMModelCredential()
|
||||
|
||||
model_dict = {
|
||||
'qwen-turbo': ModelInfo('qwen-turbo', '', ModelTypeConst.LLM, qwen_model_credential),
|
||||
'qwen-plus': ModelInfo('qwen-plus', '', ModelTypeConst.LLM, qwen_model_credential),
|
||||
'qwen-max': ModelInfo('qwen-max', '', ModelTypeConst.LLM, qwen_model_credential)
|
||||
}
|
||||
|
||||
|
||||
class QwenModelProvider(IModelProvider):
|
||||
|
||||
def get_dialogue_number(self):
|
||||
return 3
|
||||
|
||||
def get_model(self, model_type, model_name, model_credential: Dict[str, object], **model_kwargs) -> ChatTongyi:
|
||||
chat_tong_yi = ChatTongyi(
|
||||
model_name=model_name,
|
||||
dashscope_api_key=model_credential.get('api_key')
|
||||
)
|
||||
return chat_tong_yi
|
||||
|
||||
def get_model_credential(self, model_type, model_name):
|
||||
if model_name in model_dict:
|
||||
return model_dict.get(model_name).model_credential
|
||||
return qwen_model_credential
|
||||
|
||||
def get_model_provide_info(self):
|
||||
return ModelProvideInfo(provider='model_qwen_provider', name='通义千问', icon=get_file_content(
|
||||
os.path.join(PROJECT_DIR, "apps", "setting", 'models_provider', 'impl', 'qwen_model_provider', 'icon',
|
||||
'qwen_icon_svg')))
|
||||
|
||||
def get_model_list(self, model_type: str):
|
||||
if model_type is None:
|
||||
raise AppApiException(500, '模型类型不能为空')
|
||||
return [model_dict.get(key).to_dict() for key in
|
||||
list(filter(lambda key: model_dict.get(key).model_type == model_type, model_dict.keys()))]
|
||||
|
||||
def get_model_type_list(self):
|
||||
return [{'key': "大语言模型", 'value': "LLM"}]
|
||||
|
|
@ -13,9 +13,9 @@ from langchain.schema import HumanMessage
|
|||
from langchain_community.chat_models import QianfanChatEndpoint
|
||||
from qianfan import ChatCompletion
|
||||
|
||||
from common import froms
|
||||
from common import forms
|
||||
from common.exception.app_exception import AppApiException
|
||||
from common.froms import BaseForm
|
||||
from common.forms import BaseForm
|
||||
from common.util.file_util import get_file_content
|
||||
from setting.models_provider.base_model_provider import ModelProvideInfo, ModelTypeConst, BaseModelCredential, \
|
||||
ModelInfo, IModelProvider, ValidCode
|
||||
|
|
@ -55,9 +55,9 @@ class WenxinLLMModelCredential(BaseForm, BaseModelCredential):
|
|||
self.secret_key = model_info.get('secret_key')
|
||||
return self
|
||||
|
||||
api_key = froms.PasswordInputField('API Key', required=True)
|
||||
api_key = forms.PasswordInputField('API Key', required=True)
|
||||
|
||||
secret_key = froms.PasswordInputField("Secret Key", required=True)
|
||||
secret_key = forms.PasswordInputField("Secret Key", required=True)
|
||||
|
||||
|
||||
win_xin_llm_model_credential = WenxinLLMModelCredential()
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@ class ConfigManager:
|
|||
return True
|
||||
|
||||
def load_from_yml(self):
|
||||
for i in ['config_example.yml', 'config.yaml']:
|
||||
for i in ['config_example.yml', 'config.yaml', 'config.yml']:
|
||||
if not os.path.isfile(os.path.join(self.root_path, i)):
|
||||
continue
|
||||
loaded = self.from_yaml(i)
|
||||
|
|
@ -193,11 +193,12 @@ class ConfigManager:
|
|||
if manager.load_from_yml():
|
||||
config = manager.config
|
||||
else:
|
||||
msg = """
|
||||
msg = f"""
|
||||
|
||||
Error: No config file found.
|
||||
|
||||
You can run `cp config_example.yml config_example.yml`, and edit it.
|
||||
You can run `cp config_example.yml {root_path}/config.yml`, and edit it.
|
||||
|
||||
"""
|
||||
raise ImportError(msg)
|
||||
return config
|
||||
|
|
|
|||
|
|
@ -5,13 +5,13 @@ The `urlpatterns` list routes URLs to views. For more information please see:
|
|||
https://docs.djangoproject.com/en/4.2/topics/http/urls/
|
||||
Examples:
|
||||
Function views
|
||||
1. Add an import: froms my_app import views
|
||||
1. Add an import: forms my_app import views
|
||||
2. Add a URL to urlpatterns: path('', views.home, name='home')
|
||||
Class-based views
|
||||
1. Add an import: froms other_app.views import Home
|
||||
1. Add an import: forms other_app.views import Home
|
||||
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
|
||||
Including another URLconf
|
||||
1. Import the include() function: froms django.urls import include, path
|
||||
1. Import the include() function: forms django.urls import include, path
|
||||
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
||||
"""
|
||||
import os
|
||||
|
|
|
|||
|
|
@ -88,8 +88,7 @@ class ResetCurrentUserPasswordView(APIView):
|
|||
data.update(request.data)
|
||||
serializer_obj = RePasswordSerializer(data=data)
|
||||
if serializer_obj.reset_password():
|
||||
token_cache.delete(request.META.get('HTTP_AUTHORIZATION', None
|
||||
))
|
||||
token_cache.delete(request.META.get('HTTP_AUTHORIZATION'))
|
||||
return result.success(True)
|
||||
return result.error("修改密码失败")
|
||||
|
||||
|
|
@ -119,8 +118,7 @@ class Logout(APIView):
|
|||
responses=SendEmailSerializer().get_response_body_api(),
|
||||
tags=['用户'])
|
||||
def post(self, request: Request):
|
||||
token_cache.delete(request.META.get('HTTP_AUTHORIZATION', None
|
||||
))
|
||||
token_cache.delete(request.META.get('HTTP_AUTHORIZATION'))
|
||||
return result.success(True)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
#!/bin/bash
|
||||
|
||||
# Start postgress
|
||||
# Start postgresql
|
||||
docker-entrypoint.sh postgres &
|
||||
sleep 10
|
||||
# Wait postgress
|
||||
# Wait postgresql
|
||||
until pg_isready --host=127.0.0.1; do sleep 1 && echo "waiting for postgres"; done
|
||||
|
||||
# Start MaxKB
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ django-apscheduler = "^0.6.2"
|
|||
pymupdf = "^1.24.0"
|
||||
python-docx = "^1.1.0"
|
||||
xlwt = "^1.3.0"
|
||||
dashscope = "^1.17.0"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core"]
|
||||
|
|
|
|||
|
|
@ -7,64 +7,64 @@ const prefix = '/application'
|
|||
|
||||
/**
|
||||
* API_KEY列表
|
||||
* @param 参数 applicaiton_id
|
||||
* @param 参数 application_id
|
||||
*/
|
||||
const getAPIKey: (applicaiton_id: string, loading?: Ref<boolean>) => Promise<Result<any>> = (
|
||||
applicaiton_id,
|
||||
const getAPIKey: (application_id: string, loading?: Ref<boolean>) => Promise<Result<any>> = (
|
||||
application_id,
|
||||
loading
|
||||
) => {
|
||||
return get(`${prefix}/${applicaiton_id}/api_key`, undefined, loading)
|
||||
return get(`${prefix}/${application_id}/api_key`, undefined, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 新增API_KEY
|
||||
* @param 参数 applicaiton_id
|
||||
* @param 参数 application_id
|
||||
*/
|
||||
const postAPIKey: (applicaiton_id: string, loading?: Ref<boolean>) => Promise<Result<any>> = (
|
||||
applicaiton_id,
|
||||
const postAPIKey: (application_id: string, loading?: Ref<boolean>) => Promise<Result<any>> = (
|
||||
application_id,
|
||||
loading
|
||||
) => {
|
||||
return post(`${prefix}/${applicaiton_id}/api_key`, {}, undefined, loading)
|
||||
return post(`${prefix}/${application_id}/api_key`, {}, undefined, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除API_KEY
|
||||
* @param 参数 applicaiton_id api_key_id
|
||||
* @param 参数 application_id api_key_id
|
||||
*/
|
||||
const delAPIKey: (
|
||||
applicaiton_id: String,
|
||||
application_id: String,
|
||||
api_key_id: String,
|
||||
loading?: Ref<boolean>
|
||||
) => Promise<Result<boolean>> = (applicaiton_id, api_key_id, loading) => {
|
||||
return del(`${prefix}/${applicaiton_id}/api_key/${api_key_id}`, undefined, undefined, loading)
|
||||
) => Promise<Result<boolean>> = (application_id, api_key_id, loading) => {
|
||||
return del(`${prefix}/${application_id}/api_key/${api_key_id}`, undefined, undefined, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改API_KEY
|
||||
* @param 参数 applicaiton_id,api_key_id
|
||||
* @param 参数 application_id,api_key_id
|
||||
* data {
|
||||
* is_active: boolean
|
||||
* }
|
||||
*/
|
||||
const putAPIKey: (
|
||||
applicaiton_id: string,
|
||||
application_id: string,
|
||||
api_key_id: String,
|
||||
data: any,
|
||||
loading?: Ref<boolean>
|
||||
) => Promise<Result<any>> = (applicaiton_id, api_key_id, data, loading) => {
|
||||
return put(`${prefix}/${applicaiton_id}/api_key/${api_key_id}`, data, undefined, loading)
|
||||
) => Promise<Result<any>> = (application_id, api_key_id, data, loading) => {
|
||||
return put(`${prefix}/${application_id}/api_key/${api_key_id}`, data, undefined, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计
|
||||
* @param 参数 applicaiton_id, data
|
||||
* @param 参数 application_id, data
|
||||
*/
|
||||
const getStatistics: (
|
||||
applicaiton_id: string,
|
||||
application_id: string,
|
||||
data: any,
|
||||
loading?: Ref<boolean>
|
||||
) => Promise<Result<any>> = (applicaiton_id, data, loading) => {
|
||||
return get(`${prefix}/${applicaiton_id}/statistics/chat_record_aggregate_trend`, data, loading)
|
||||
) => Promise<Result<any>> = (application_id, data, loading) => {
|
||||
return get(`${prefix}/${application_id}/statistics/chat_record_aggregate_trend`, data, loading)
|
||||
}
|
||||
|
||||
export default {
|
||||
|
|
|
|||
|
|
@ -49,70 +49,70 @@ const postApplication: (
|
|||
|
||||
*/
|
||||
const putApplication: (
|
||||
applicaiton_id: String,
|
||||
application_id: String,
|
||||
data: ApplicationFormType,
|
||||
loading?: Ref<boolean>
|
||||
) => Promise<Result<any>> = (applicaiton_id, data, loading) => {
|
||||
return put(`${prefix}/${applicaiton_id}`, data, undefined, loading)
|
||||
) => Promise<Result<any>> = (application_id, data, loading) => {
|
||||
return put(`${prefix}/${application_id}`, data, undefined, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除应用
|
||||
* @param 参数 applicaiton_id
|
||||
* @param 参数 application_id
|
||||
*/
|
||||
const delApplication: (
|
||||
applicaiton_id: String,
|
||||
application_id: String,
|
||||
loading?: Ref<boolean>
|
||||
) => Promise<Result<boolean>> = (applicaiton_id, loading) => {
|
||||
return del(`${prefix}/${applicaiton_id}`, undefined, {}, loading)
|
||||
) => Promise<Result<boolean>> = (application_id, loading) => {
|
||||
return del(`${prefix}/${application_id}`, undefined, {}, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用详情
|
||||
* @param 参数 applicaiton_id
|
||||
* @param 参数 application_id
|
||||
*/
|
||||
const getApplicationDetail: (
|
||||
applicaiton_id: string,
|
||||
application_id: string,
|
||||
loading?: Ref<boolean>
|
||||
) => Promise<Result<any>> = (applicaiton_id, loading) => {
|
||||
return get(`${prefix}/${applicaiton_id}`, undefined, loading)
|
||||
) => Promise<Result<any>> = (application_id, loading) => {
|
||||
return get(`${prefix}/${application_id}`, undefined, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得当前应用可使用的知识库
|
||||
* @param 参数 applicaiton_id
|
||||
* @param 参数 application_id
|
||||
*/
|
||||
const getApplicationDataset: (
|
||||
applicaiton_id: string,
|
||||
application_id: string,
|
||||
loading?: Ref<boolean>
|
||||
) => Promise<Result<any>> = (applicaiton_id, loading) => {
|
||||
return get(`${prefix}/${applicaiton_id}/list_dataset`, undefined, loading)
|
||||
) => Promise<Result<any>> = (application_id, loading) => {
|
||||
return get(`${prefix}/${application_id}/list_dataset`, undefined, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取AccessToken
|
||||
* @param 参数 applicaiton_id
|
||||
* @param 参数 application_id
|
||||
*/
|
||||
const getAccessToken: (applicaiton_id: string, loading?: Ref<boolean>) => Promise<Result<any>> = (
|
||||
applicaiton_id,
|
||||
const getAccessToken: (application_id: string, loading?: Ref<boolean>) => Promise<Result<any>> = (
|
||||
application_id,
|
||||
loading
|
||||
) => {
|
||||
return get(`${prefix}/${applicaiton_id}/access_token`, undefined, loading)
|
||||
return get(`${prefix}/${application_id}/access_token`, undefined, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改AccessToken
|
||||
* @param 参数 applicaiton_id
|
||||
* @param 参数 application_id
|
||||
* data {
|
||||
* "is_active": true
|
||||
* }
|
||||
*/
|
||||
const putAccessToken: (
|
||||
applicaiton_id: string,
|
||||
application_id: string,
|
||||
data: any,
|
||||
loading?: Ref<boolean>
|
||||
) => Promise<Result<any>> = (applicaiton_id, data, loading) => {
|
||||
return put(`${prefix}/${applicaiton_id}/access_token`, data, undefined, loading)
|
||||
) => Promise<Result<any>> = (application_id, data, loading) => {
|
||||
return put(`${prefix}/${application_id}/access_token`, data, undefined, loading)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -161,8 +161,8 @@ const postChatOpen: (data: ApplicationFormType) => Promise<Result<any>> = (data)
|
|||
]
|
||||
}
|
||||
*/
|
||||
const getChatOpen: (applicaiton_id: String) => Promise<Result<any>> = (applicaiton_id) => {
|
||||
return get(`${prefix}/${applicaiton_id}/chat/open`)
|
||||
const getChatOpen: (application_id: String) => Promise<Result<any>> = (application_id) => {
|
||||
return get(`${prefix}/${application_id}/chat/open`)
|
||||
}
|
||||
/**
|
||||
* 对话
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ const prefix = '/dataset'
|
|||
"name": "string",
|
||||
}
|
||||
*/
|
||||
const getDateset: (
|
||||
const getDataset: (
|
||||
page: pageRequest,
|
||||
param: any,
|
||||
loading?: Ref<boolean>
|
||||
|
|
@ -29,7 +29,7 @@ const getDateset: (
|
|||
* 获取全部知识库
|
||||
* @param 参数
|
||||
*/
|
||||
const getAllDateset: (loading?: Ref<boolean>) => Promise<Result<any[]>> = (loading) => {
|
||||
const getAllDataset: (loading?: Ref<boolean>) => Promise<Result<any[]>> = (loading) => {
|
||||
return get(`${prefix}`, undefined, loading)
|
||||
}
|
||||
|
||||
|
|
@ -37,7 +37,7 @@ const getAllDateset: (loading?: Ref<boolean>) => Promise<Result<any[]>> = (loadi
|
|||
* 删除知识库
|
||||
* @param 参数 dataset_id
|
||||
*/
|
||||
const delDateset: (dataset_id: String, loading?: Ref<boolean>) => Promise<Result<boolean>> = (
|
||||
const delDataset: (dataset_id: String, loading?: Ref<boolean>) => Promise<Result<boolean>> = (
|
||||
dataset_id,
|
||||
loading
|
||||
) => {
|
||||
|
|
@ -69,7 +69,7 @@ const delDateset: (dataset_id: String, loading?: Ref<boolean>) => Promise<Result
|
|||
]
|
||||
}
|
||||
*/
|
||||
const postDateset: (data: datasetData, loading?: Ref<boolean>) => Promise<Result<any>> = (
|
||||
const postDataset: (data: datasetData, loading?: Ref<boolean>) => Promise<Result<any>> = (
|
||||
data,
|
||||
loading
|
||||
) => {
|
||||
|
|
@ -86,7 +86,7 @@ const postDateset: (data: datasetData, loading?: Ref<boolean>) => Promise<Result
|
|||
"selector": "string",
|
||||
}
|
||||
*/
|
||||
const postWebDateset: (data: any, loading?: Ref<boolean>) => Promise<Result<any>> = (
|
||||
const postWebDataset: (data: any, loading?: Ref<boolean>) => Promise<Result<any>> = (
|
||||
data,
|
||||
loading
|
||||
) => {
|
||||
|
|
@ -97,7 +97,7 @@ const postWebDateset: (data: any, loading?: Ref<boolean>) => Promise<Result<any>
|
|||
* 知识库详情
|
||||
* @param 参数 dataset_id
|
||||
*/
|
||||
const getDatesetDetail: (dataset_id: string, loading?: Ref<boolean>) => Promise<Result<any>> = (
|
||||
const getDatasetDetail: (dataset_id: string, loading?: Ref<boolean>) => Promise<Result<any>> = (
|
||||
dataset_id,
|
||||
loading
|
||||
) => {
|
||||
|
|
@ -113,7 +113,7 @@ const getDatesetDetail: (dataset_id: string, loading?: Ref<boolean>) => Promise<
|
|||
"desc": true
|
||||
}
|
||||
*/
|
||||
const putDateset: (dataset_id: string, data: any) => Promise<Result<any>> = (
|
||||
const putDataset: (dataset_id: string, data: any) => Promise<Result<any>> = (
|
||||
dataset_id,
|
||||
data: any
|
||||
) => {
|
||||
|
|
@ -152,7 +152,7 @@ const getDatasetHitTest: (
|
|||
* @param 参数 dataset_id
|
||||
* @query 参数 sync_type // 同步类型->replace:替换同步,complete:完整同步
|
||||
*/
|
||||
const putSyncWebDateset: (
|
||||
const putSyncWebDataset: (
|
||||
dataset_id: string,
|
||||
sync_type: string,
|
||||
loading?: Ref<boolean>
|
||||
|
|
@ -161,14 +161,14 @@ const putSyncWebDateset: (
|
|||
}
|
||||
|
||||
export default {
|
||||
getDateset,
|
||||
getAllDateset,
|
||||
delDateset,
|
||||
postDateset,
|
||||
getDatesetDetail,
|
||||
putDateset,
|
||||
getDataset,
|
||||
getAllDataset,
|
||||
delDataset,
|
||||
postDataset,
|
||||
getDatasetDetail,
|
||||
putDataset,
|
||||
listUsableApplication,
|
||||
getDatasetHitTest,
|
||||
postWebDateset,
|
||||
putSyncWebDateset
|
||||
postWebDataset,
|
||||
putSyncWebDataset
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,19 +51,23 @@ export class ChatRecordManage {
|
|||
this.loading.value = true
|
||||
}
|
||||
this.id = setInterval(() => {
|
||||
const s = this.chat.buffer.shift()
|
||||
if (s !== undefined) {
|
||||
this.chat.answer_text = this.chat.answer_text + s
|
||||
if (this.chat.buffer.length > 20) {
|
||||
this.chat.answer_text =
|
||||
this.chat.answer_text + this.chat.buffer.splice(0, this.chat.buffer.length - 20).join('')
|
||||
} else if (this.is_close) {
|
||||
this.chat.answer_text = this.chat.answer_text + this.chat.buffer.join('')
|
||||
this.chat.write_ed = true
|
||||
this.write_ed = true
|
||||
if (this.loading) {
|
||||
this.loading.value = false
|
||||
}
|
||||
if (this.id) {
|
||||
clearInterval(this.id)
|
||||
}
|
||||
} else {
|
||||
if (this.is_close) {
|
||||
this.chat.write_ed = true
|
||||
this.write_ed = true
|
||||
if (this.loading) {
|
||||
this.loading.value = false
|
||||
}
|
||||
if (this.id) {
|
||||
clearInterval(this.id)
|
||||
}
|
||||
const s = this.chat.buffer.shift()
|
||||
if (s !== undefined) {
|
||||
this.chat.answer_text = this.chat.answer_text + s
|
||||
}
|
||||
}
|
||||
}, this.ms)
|
||||
|
|
|
|||
Binary file not shown.
|
After Width: | Height: | Size: 4.7 KiB |
|
|
@ -14,7 +14,7 @@
|
|||
<template v-for="(item, index) in prologueList" :key="index">
|
||||
<div
|
||||
v-if="item.type === 'question'"
|
||||
@click="quickProblemHandel(item.str)"
|
||||
@click="quickProblemHandle(item.str)"
|
||||
class="problem-button ellipsis-2 mb-8"
|
||||
:class="log ? 'disabled' : 'cursor'"
|
||||
>
|
||||
|
|
@ -62,6 +62,9 @@
|
|||
>
|
||||
抱歉,没有查找到相关内容,请重新描述您的问题或提供更多信息。
|
||||
</el-card>
|
||||
<el-card v-else-if="item.is_stop" shadow="always" class="dialog-card">
|
||||
已停止回答
|
||||
</el-card>
|
||||
<el-card v-else shadow="always" class="dialog-card">
|
||||
回答中 <span class="dotting"></span>
|
||||
</el-card>
|
||||
|
|
@ -144,6 +147,7 @@
|
|||
placeholder="请输入"
|
||||
:rows="1"
|
||||
type="textarea"
|
||||
:maxlength="1024"
|
||||
@keydown.enter="sendChatHandle($event)"
|
||||
/>
|
||||
<div class="operate">
|
||||
|
|
@ -217,7 +221,7 @@ const chartOpenId = ref('')
|
|||
const chatList = ref<any[]>([])
|
||||
|
||||
const isDisabledChart = computed(
|
||||
() => !(inputValue.value && (props.appId || (props.data?.name && props.data?.model_id)))
|
||||
() => !(inputValue.value.trim() && (props.appId || (props.data?.name && props.data?.model_id)))
|
||||
)
|
||||
const isMdArray = (val: string) => val.match(/^-\s.*/m)
|
||||
const prologueList = computed(() => {
|
||||
|
|
@ -266,7 +270,7 @@ function openParagraph(row: any, id?: string) {
|
|||
ParagraphSourceDialogRef.value.open(row, id)
|
||||
}
|
||||
|
||||
function quickProblemHandel(val: string) {
|
||||
function quickProblemHandle(val: string) {
|
||||
if (!props.log && !loading.value) {
|
||||
// inputValue.value = val
|
||||
// nextTick(() => {
|
||||
|
|
@ -286,7 +290,9 @@ function sendChatHandle(event: any) {
|
|||
// 如果没有按下组合键ctrl,则会阻止默认事件
|
||||
event.preventDefault()
|
||||
if (!isDisabledChart.value && !loading.value && !event.isComposing) {
|
||||
chatMessage()
|
||||
if (inputValue.value.trim()) {
|
||||
chatMessage()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 如果同时按下ctrl+回车键,则会换行
|
||||
|
|
@ -423,7 +429,7 @@ function chatMessage(chat?: any, problem?: string, re_chat?: boolean) {
|
|||
if (!chat) {
|
||||
chat = reactive({
|
||||
id: randomId(),
|
||||
problem_text: problem ? problem : inputValue.value,
|
||||
problem_text: problem ? problem : inputValue.value.trim(),
|
||||
answer_text: '',
|
||||
buffer: [],
|
||||
write_ed: false,
|
||||
|
|
@ -432,6 +438,8 @@ function chatMessage(chat?: any, problem?: string, re_chat?: boolean) {
|
|||
vote_status: '-1'
|
||||
})
|
||||
chatList.value.push(chat)
|
||||
ChatManagement.addChatRecord(chat, 50, loading)
|
||||
ChatManagement.write(chat.id)
|
||||
inputValue.value = ''
|
||||
nextTick(() => {
|
||||
// 将滚动条滚动到最下面
|
||||
|
|
@ -469,8 +477,6 @@ function chatMessage(chat?: any, problem?: string, re_chat?: boolean) {
|
|||
// 将滚动条滚动到最下面
|
||||
scrollDiv.value.setScrollTop(getMaxHeight())
|
||||
})
|
||||
ChatManagement.addChatRecord(chat, 50, loading)
|
||||
ChatManagement.write(chat.id)
|
||||
const reader = response.body.getReader()
|
||||
// 处理流数据
|
||||
const write = getWrite(
|
||||
|
|
|
|||
|
|
@ -34,8 +34,8 @@ function initChart() {
|
|||
myChart = echarts.init(document.getElementById(props.id))
|
||||
}
|
||||
const series: any = []
|
||||
if (props.option?.yDatas?.length) {
|
||||
props.option?.yDatas.forEach((item: any, index: number) => {
|
||||
if (props.option?.yData?.length) {
|
||||
props.option?.yData.forEach((item: any, index: number) => {
|
||||
series.push({
|
||||
itemStyle: {
|
||||
color: color[index]
|
||||
|
|
@ -83,7 +83,7 @@ function initChart() {
|
|||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: props.option.xDatas
|
||||
data: props.option.xData
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ defineProps({
|
|||
option: {
|
||||
type: Object,
|
||||
required: true
|
||||
} // { title , xDatas, yDatas, formatStr }
|
||||
} // { title , xData, yData, formatStr }
|
||||
})
|
||||
|
||||
const typeComponentMap = { line } as any
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
<el-button type="primary" @click="submitHandle" :disabled="loading">创建</el-button>
|
||||
<el-button @click="showInput = false" :disabled="loading">取消</el-button>
|
||||
</div>
|
||||
<div v-else @click="quickCreateHandel" class="w-full">
|
||||
<div v-else @click="quickCreateHandle" class="w-full">
|
||||
<el-button type="primary" link class="quich-button">
|
||||
<el-icon><Plus /></el-icon>
|
||||
<span class="ml-4">{{ quickCreatePlaceholder }}</span>
|
||||
|
|
@ -104,7 +104,7 @@ function submitHandle() {
|
|||
}
|
||||
}
|
||||
|
||||
function quickCreateHandel() {
|
||||
function quickCreateHandle() {
|
||||
showInput.value = true
|
||||
nextTick(() => {
|
||||
quickInputRef.value?.focus()
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
:disabled="!(containerWeight > contentWeight)"
|
||||
effect="dark"
|
||||
placement="bottom"
|
||||
popper-class="auto-tooltip-popper"
|
||||
>
|
||||
<div ref="tagLabel" :class="['auto-tooltip', className]" :style="style">
|
||||
<slot></slot>
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
:parent_field="formField.field + '.' + index"
|
||||
></DynamicsForm>
|
||||
<el-tooltip effect="dark" content="删除" placement="top">
|
||||
<el-button text @click.stop="deleteDateset(item)" class="delete-button">
|
||||
<el-button text @click.stop="deleteDataset(item)" class="delete-button">
|
||||
<el-icon><Delete /></el-icon>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
|
|
@ -46,7 +46,7 @@ const props = defineProps<{
|
|||
const render_data = () => {
|
||||
return Promise.resolve(Result.success(props.formField.children as Array<FormField>))
|
||||
}
|
||||
const deleteDateset = (item: any) => {
|
||||
const deleteDataset = (item: any) => {
|
||||
_data.value = _data.value.filter((row) => row !== item)
|
||||
}
|
||||
const emit = defineEmits(['update:modelValue', 'change'])
|
||||
|
|
|
|||
|
|
@ -150,7 +150,7 @@ function changeMenu(id: string) {
|
|||
function getDataset() {
|
||||
loading.value = true
|
||||
dataset
|
||||
.asyncGetAllDateset()
|
||||
.asyncGetAllDataset()
|
||||
.then((res: any) => {
|
||||
list.value = res.data
|
||||
common.saveBreadcrumb(list.value)
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
·
|
||||
<template>
|
||||
<div class="top-bar-container border-b flex-between">
|
||||
<div class="flex-center h-full">
|
||||
|
|
@ -9,7 +10,31 @@
|
|||
</div>
|
||||
<TopMenu></TopMenu>
|
||||
</div>
|
||||
<div class="avatar">
|
||||
<div class="flex-center avatar">
|
||||
<el-tooltip effect="dark" content="项目地址" placement="top">
|
||||
<AppIcon
|
||||
iconName="app-github"
|
||||
class="cursor color-secondary mr-8 ml-8"
|
||||
style="font-size: 20px"
|
||||
@click="toUrl('https://github.com/1Panel-dev/MaxKB')"
|
||||
></AppIcon>
|
||||
</el-tooltip>
|
||||
<el-tooltip effect="dark" content="用户手册" placement="top">
|
||||
<AppIcon
|
||||
iconName="app-reading"
|
||||
class="cursor color-secondary mr-8 ml-8"
|
||||
style="font-size: 20px"
|
||||
@click="toUrl('https://github.com/1Panel-dev/MaxKB/wiki')"
|
||||
></AppIcon>
|
||||
</el-tooltip>
|
||||
<el-tooltip effect="dark" content="论坛求助" placement="top">
|
||||
<AppIcon
|
||||
iconName="app-help"
|
||||
class="cursor color-secondary mr-16 ml-8"
|
||||
style="font-size: 20px"
|
||||
@click="toUrl('https://bbs.fit2cloud.com/c/mk/11')"
|
||||
></AppIcon>
|
||||
</el-tooltip>
|
||||
<Avatar></Avatar>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -20,6 +45,10 @@ import Avatar from './avatar/index.vue'
|
|||
import { useRouter } from 'vue-router'
|
||||
const router = useRouter()
|
||||
const defaultTitle = import.meta.env.VITE_APP_TITLE
|
||||
|
||||
function toUrl(url: string) {
|
||||
window.open(url, '_blank')
|
||||
}
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.top-bar-container {
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ const applicationRouter = {
|
|||
parentPath: '/application/:id',
|
||||
parentName: 'ApplicationDetail'
|
||||
},
|
||||
component: () => import('@/views/applicaiton-overview/index.vue')
|
||||
component: () => import('@/views/application-overview/index.vue')
|
||||
},
|
||||
{
|
||||
path: 'setting',
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ export const routes: Array<RouteRecordRaw> = [
|
|||
component: () => import('@/layout/app-layout/index.vue'),
|
||||
redirect: '/application',
|
||||
children: [
|
||||
// TODO 待处理
|
||||
// {
|
||||
// path: '/first',
|
||||
// name: 'first',
|
||||
|
|
|
|||
|
|
@ -27,10 +27,10 @@ const useDatasetStore = defineStore({
|
|||
saveDocumentsFile(file: UploadUserFile[]) {
|
||||
this.documentsFiles = file
|
||||
},
|
||||
async asyncGetAllDateset(loading?: Ref<boolean>) {
|
||||
async asyncGetAllDataset(loading?: Ref<boolean>) {
|
||||
return new Promise((resolve, reject) => {
|
||||
datasetApi
|
||||
.getAllDateset(loading)
|
||||
.getAllDataset(loading)
|
||||
.then((data) => {
|
||||
resolve(data)
|
||||
})
|
||||
|
|
@ -39,10 +39,10 @@ const useDatasetStore = defineStore({
|
|||
})
|
||||
})
|
||||
},
|
||||
async asyncGetDatesetDetail(id: string, loading?: Ref<boolean>) {
|
||||
async asyncGetDatasetDetail(id: string, loading?: Ref<boolean>) {
|
||||
return new Promise((resolve, reject) => {
|
||||
datasetApi
|
||||
.getDatesetDetail(id, loading)
|
||||
.getDatasetDetail(id, loading)
|
||||
.then((data) => {
|
||||
resolve(data)
|
||||
})
|
||||
|
|
@ -51,10 +51,10 @@ const useDatasetStore = defineStore({
|
|||
})
|
||||
})
|
||||
},
|
||||
async asyncSyncDateset(id: string, sync_type: string, loading?: Ref<boolean>) {
|
||||
async asyncSyncDataset(id: string, sync_type: string, loading?: Ref<boolean>) {
|
||||
return new Promise((resolve, reject) => {
|
||||
datasetApi
|
||||
.putSyncWebDateset(id, sync_type, loading)
|
||||
.putSyncWebDataset(id, sync_type, loading)
|
||||
.then((data) => {
|
||||
resolve(data)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -168,7 +168,7 @@
|
|||
.el-checkbox {
|
||||
height: 23px;
|
||||
}
|
||||
tr.hightlight {
|
||||
tr.highlight {
|
||||
background: var(--el-table-current-row-bg-color);
|
||||
}
|
||||
}
|
||||
|
|
@ -319,3 +319,15 @@
|
|||
padding-left: 12px !important;
|
||||
padding-right: 12px !important;
|
||||
}
|
||||
|
||||
// select下拉框
|
||||
.select-popper {
|
||||
max-width: 300px;
|
||||
.el-select-dropdown__wrap {
|
||||
max-width: 300px;
|
||||
}
|
||||
}
|
||||
|
||||
.auto-tooltip-popper {
|
||||
max-width: 500px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -86,10 +86,10 @@ class Theme {
|
|||
/**
|
||||
* 转换外推数据
|
||||
* @param setting 设置
|
||||
* @param inferDatas 外推数据
|
||||
* @param inferData 外推数据
|
||||
*/
|
||||
mapInferData = (setting: ThemeSetting, inferDatas: Array<InferData>) => {
|
||||
return inferDatas
|
||||
mapInferData = (setting: ThemeSetting, inferData: Array<InferData>) => {
|
||||
return inferData
|
||||
.map((itemData) => {
|
||||
return this.mapInferMainStyle(setting, itemData)
|
||||
})
|
||||
|
|
@ -173,7 +173,7 @@ class Theme {
|
|||
*
|
||||
* @param setting 主题设置
|
||||
* @param keyValue 主题键值对数据
|
||||
* @param inferDatas 外推数据
|
||||
* @param inferData 外推数据
|
||||
* @returns 合并后的键值对数据
|
||||
*/
|
||||
tokeyValueStyle = () => {
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ export function isRightType(name: string) {
|
|||
/*
|
||||
从指定数组中过滤出对应的对象
|
||||
*/
|
||||
export function realatedObject(list: any, val: any, attr: string) {
|
||||
export function relatedObject(list: any, val: any, attr: string) {
|
||||
const filterData: any = list.filter((item: any) => item[attr] === val)?.[0]
|
||||
return filterData || null
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
<el-col class="message-container" :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
|
||||
<div class="title">404</div>
|
||||
<div class="message">很抱歉,无法访问应用!</div>
|
||||
<!-- TODO 暂时不处理 -->
|
||||
<!-- <div class="operate"><el-button type="primary" @click="router.push('/')">返回首页</el-button></div> -->
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
|
|
|||
|
|
@ -76,8 +76,8 @@ const statisticsType = computed(() => [
|
|||
],
|
||||
option: {
|
||||
title: '用户总数',
|
||||
xDatas: getAttrsArray(props.data, 'day'),
|
||||
yDatas: [
|
||||
xData: getAttrsArray(props.data, 'day'),
|
||||
yData: [
|
||||
{
|
||||
name: '用户总数',
|
||||
type: 'line',
|
||||
|
|
@ -102,8 +102,8 @@ const statisticsType = computed(() => [
|
|||
sum: [getSum(getAttrsArray(props.data, 'chat_record_count') || 0)],
|
||||
option: {
|
||||
title: '提问次数',
|
||||
xDatas: getAttrsArray(props.data, 'day'),
|
||||
yDatas: [
|
||||
xData: getAttrsArray(props.data, 'day'),
|
||||
yData: [
|
||||
{
|
||||
type: 'line',
|
||||
data: getAttrsArray(props.data, 'chat_record_count')
|
||||
|
|
@ -120,8 +120,8 @@ const statisticsType = computed(() => [
|
|||
sum: [getSum(getAttrsArray(props.data, 'tokens_num') || 0)],
|
||||
option: {
|
||||
title: 'Tokens 总数',
|
||||
xDatas: getAttrsArray(props.data, 'day'),
|
||||
yDatas: [
|
||||
xData: getAttrsArray(props.data, 'day'),
|
||||
yData: [
|
||||
{
|
||||
type: 'line',
|
||||
data: getAttrsArray(props.data, 'tokens_num')
|
||||
|
|
@ -141,8 +141,8 @@ const statisticsType = computed(() => [
|
|||
],
|
||||
option: {
|
||||
title: '用户满意度',
|
||||
xDatas: getAttrsArray(props.data, 'day'),
|
||||
yDatas: [
|
||||
xData: getAttrsArray(props.data, 'day'),
|
||||
yData: [
|
||||
{
|
||||
name: '赞同',
|
||||
type: 'line',
|
||||
|
|
@ -54,13 +54,13 @@
|
|||
<el-select
|
||||
v-model="applicationForm.model_id"
|
||||
placeholder="请选择 AI 模型"
|
||||
style="width: 100%"
|
||||
class="w-full"
|
||||
popper-class="select-model"
|
||||
>
|
||||
<el-option-group
|
||||
v-for="(value, label) in modelOptions"
|
||||
:key="value"
|
||||
:label="realatedObject(providerOptions, label, 'provider')?.name"
|
||||
:label="relatedObject(providerOptions, label, 'provider')?.name"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in value.filter((v: any) => v.status === 'SUCCESS')"
|
||||
|
|
@ -71,7 +71,7 @@
|
|||
>
|
||||
<div class="flex">
|
||||
<span
|
||||
v-html="realatedObject(providerOptions, label, 'provider')?.icon"
|
||||
v-html="relatedObject(providerOptions, label, 'provider')?.icon"
|
||||
class="model-icon mr-8"
|
||||
></span>
|
||||
<span>{{ item.name }}</span>
|
||||
|
|
@ -91,7 +91,7 @@
|
|||
>
|
||||
<div class="flex">
|
||||
<span
|
||||
v-html="realatedObject(providerOptions, label, 'provider')?.icon"
|
||||
v-html="relatedObject(providerOptions, label, 'provider')?.icon"
|
||||
class="model-icon mr-8"
|
||||
></span>
|
||||
<span>{{ item.name }}</span>
|
||||
|
|
@ -242,7 +242,7 @@
|
|||
<div class="flex-between">
|
||||
<div class="flex align-center">
|
||||
<AppAvatar
|
||||
v-if="realatedObject(datasetList, item, 'id')?.type === '1'"
|
||||
v-if="relatedObject(datasetList, item, 'id')?.type === '1'"
|
||||
class="mr-8 avatar-purple"
|
||||
shape="square"
|
||||
:size="32"
|
||||
|
|
@ -254,7 +254,7 @@
|
|||
<img src="@/assets/icon_document.svg" style="width: 58%" alt="" />
|
||||
</AppAvatar>
|
||||
<div class="ellipsis">
|
||||
{{ realatedObject(datasetList, item, 'id')?.name }}
|
||||
{{ relatedObject(datasetList, item, 'id')?.name }}
|
||||
</div>
|
||||
</div>
|
||||
<el-button text @click="removeDataset(item)">
|
||||
|
|
@ -338,7 +338,7 @@ import applicationApi from '@/api/application'
|
|||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
import type { ApplicationFormType } from '@/api/type/application'
|
||||
import type { Provider } from '@/api/type/model'
|
||||
import { realatedObject } from '@/utils/utils'
|
||||
import { relatedObject } from '@/utils/utils'
|
||||
import { MsgSuccess } from '@/utils/message'
|
||||
import useStore from '@/stores'
|
||||
|
||||
|
|
@ -474,7 +474,7 @@ function getDataset() {
|
|||
datasetList.value = res.data
|
||||
})
|
||||
} else {
|
||||
dataset.asyncGetAllDateset(datasetLoading).then((res: any) => {
|
||||
dataset.asyncGetAllDataset(datasetLoading).then((res: any) => {
|
||||
datasetList.value = res.data?.filter((v: any) => v.user_id === user.userInfo?.id)
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ onMounted(() => {
|
|||
height: var(--app-header-height);
|
||||
line-height: var(--app-header-height);
|
||||
box-sizing: border-box;
|
||||
border-bottom: 1px solid rgba(31, 35, 41, 0.15);
|
||||
border-bottom: 1px solid var(--el-border-color);
|
||||
}
|
||||
&__main {
|
||||
padding-top: calc(var(--app-header-height) + 24px);
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ function submit() {
|
|||
loading.value = false
|
||||
})
|
||||
} else {
|
||||
datasetApi.postDateset(obj, loading).then((res) => {
|
||||
datasetApi.postDataset(obj, loading).then((res) => {
|
||||
successInfo.value = res.data
|
||||
active.value = 2
|
||||
clearStore()
|
||||
|
|
|
|||
|
|
@ -126,7 +126,7 @@ async function submit() {
|
|||
...BaseFormRef.value.form
|
||||
}
|
||||
datasetApi
|
||||
.putDateset(id, obj)
|
||||
.putDataset(id, obj)
|
||||
.then((res) => {
|
||||
MsgSuccess('保存成功')
|
||||
loading.value = false
|
||||
|
|
@ -140,7 +140,7 @@ async function submit() {
|
|||
}
|
||||
|
||||
function getDetail() {
|
||||
dataset.asyncGetDatesetDetail(id, loading).then((res: any) => {
|
||||
dataset.asyncGetDatasetDetail(id, loading).then((res: any) => {
|
||||
detail.value = res.data
|
||||
if (detail.value.type === '1') {
|
||||
form.value = res.data.meta
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ const open = (id: string) => {
|
|||
}
|
||||
|
||||
const submit = () => {
|
||||
dataset.asyncSyncDateset(datasetId.value, method.value, loading).then((res: any) => {
|
||||
dataset.asyncSyncDataset(datasetId.value, method.value, loading).then((res: any) => {
|
||||
emit('refresh', res.data)
|
||||
dialogVisible.value = false
|
||||
})
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
>
|
||||
<el-form-item prop="fileList">
|
||||
<el-upload
|
||||
:webkitdirectory="false"
|
||||
class="w-full"
|
||||
drag
|
||||
multiple
|
||||
|
|
@ -19,18 +20,18 @@
|
|||
accept=".txt, .md, .csv, .log, .docx, .pdf"
|
||||
:limit="50"
|
||||
:on-exceed="onExceed"
|
||||
:on-change="filehandleChange"
|
||||
:on-change="fileHandleChange"
|
||||
@click.prevent="handlePreview(false)"
|
||||
>
|
||||
<img src="@/assets/upload-icon.svg" alt="" />
|
||||
<div class="el-upload__text">
|
||||
<p>
|
||||
将文件拖拽至此区域或
|
||||
<em> 选择文件上传 </em>
|
||||
拖拽文件至此上传或
|
||||
<em class="hover" @click.prevent="handlePreview(false)"> 选择文件 </em>
|
||||
<em class="hover" @click.prevent="handlePreview(true)"> 选择文件夹 </em>
|
||||
</p>
|
||||
<div class="upload__decoration">
|
||||
<p>
|
||||
支持格式:TXT、Markdown、PDF、DOCX,每次最多上传50个文件,每个文件不超过 100MB
|
||||
</p>
|
||||
<p>支持格式:TXT、Markdown、PDF、DOCX,每次最多上传50个文件,每个文件不超过 100MB</p>
|
||||
<p>若使用【高级分段】建议上传前规范文件的分段标识</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -49,7 +50,7 @@
|
|||
<el-text type="info">{{ filesize(item && item?.size) || '0K' }}</el-text>
|
||||
</div>
|
||||
</div>
|
||||
<el-button text @click="deleteFlie(index)">
|
||||
<el-button text @click="deleteFile(index)">
|
||||
<el-icon><Delete /></el-icon>
|
||||
</el-button>
|
||||
</div>
|
||||
|
|
@ -59,7 +60,7 @@
|
|||
</el-row>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onUnmounted, onMounted, computed, watch } from 'vue'
|
||||
import { ref, reactive, onUnmounted, onMounted, computed, watch, nextTick } from 'vue'
|
||||
import type { UploadFile, UploadFiles } from 'element-plus'
|
||||
import { filesize, getImgUrl, isRightType } from '@/utils/utils'
|
||||
import { MsgError } from '@/utils/message'
|
||||
|
|
@ -78,12 +79,12 @@ const FormRef = ref()
|
|||
watch(form.value, (value) => {
|
||||
dataset.saveDocumentsFile(value.fileList)
|
||||
})
|
||||
function deleteFlie(index: number) {
|
||||
function deleteFile(index: number) {
|
||||
form.value.fileList.splice(index, 1)
|
||||
}
|
||||
|
||||
// 上传on-change事件
|
||||
const filehandleChange = (file: any, fileList: UploadFiles) => {
|
||||
const fileHandleChange = (file: any, fileList: UploadFiles) => {
|
||||
//1、判断文件大小是否合法,文件限制不能大于10M
|
||||
const isLimit = file?.size / 1024 / 1024 < 100
|
||||
if (!isLimit) {
|
||||
|
|
@ -101,6 +102,17 @@ const filehandleChange = (file: any, fileList: UploadFiles) => {
|
|||
const onExceed = () => {
|
||||
MsgError('每次最多上传50个文件')
|
||||
}
|
||||
|
||||
const handlePreview = (bool: boolean) => {
|
||||
let inputDom: any = null
|
||||
nextTick(() => {
|
||||
if (document.querySelector('.el-upload__input') != null) {
|
||||
inputDom = document.querySelector('.el-upload__input')
|
||||
inputDom.webkitdirectory = bool
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/*
|
||||
表单校验
|
||||
*/
|
||||
|
|
@ -133,4 +145,9 @@ defineExpose({
|
|||
line-height: 20px;
|
||||
color: var(--el-text-color-secondary);
|
||||
}
|
||||
.el-upload__text {
|
||||
.hover:hover {
|
||||
color: var(--el-color-primary-light-5);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@
|
|||
@click.stop="router.push({ path: `/dataset/${item.id}/setting` })"
|
||||
>设置</el-dropdown-item
|
||||
>
|
||||
<el-dropdown-item icon="Delete" @click.stop="deleteDateset(item)"
|
||||
<el-dropdown-item icon="Delete" @click.stop="deleteDataset(item)"
|
||||
>删除</el-dropdown-item
|
||||
>
|
||||
</el-dropdown-menu>
|
||||
|
|
@ -131,7 +131,7 @@ function searchHandle() {
|
|||
getList()
|
||||
}
|
||||
|
||||
function deleteDateset(row: any) {
|
||||
function deleteDataset(row: any) {
|
||||
MsgConfirm(
|
||||
`是否删除知识库:${row.name} ?`,
|
||||
`此知识库关联 ${row.application_mapping_count} 个应用,删除后无法恢复,请谨慎操作。`,
|
||||
|
|
@ -141,7 +141,7 @@ function deleteDateset(row: any) {
|
|||
}
|
||||
)
|
||||
.then(() => {
|
||||
datasetApi.delDateset(row.id, loading).then(() => {
|
||||
datasetApi.delDataset(row.id, loading).then(() => {
|
||||
const index = datasetList.value.findIndex((v) => v.id === row.id)
|
||||
datasetList.value.splice(index, 1)
|
||||
MsgSuccess('删除成功')
|
||||
|
|
@ -152,7 +152,7 @@ function deleteDateset(row: any) {
|
|||
|
||||
function getList() {
|
||||
datasetApi
|
||||
.getDateset(paginationConfig, searchValue.value && { name: searchValue.value }, loading)
|
||||
.getDataset(paginationConfig, searchValue.value && { name: searchValue.value }, loading)
|
||||
.then((res) => {
|
||||
paginationConfig.total = res.data.total
|
||||
datasetList.value = [...datasetList.value, ...res.data.records]
|
||||
|
|
|
|||
|
|
@ -137,7 +137,7 @@ const onSubmit = async () => {
|
|||
await webFormRef.value.validate((valid: any) => {
|
||||
if (valid) {
|
||||
const obj = { ...BaseFormRef.value.form, ...form.value }
|
||||
datasetApi.postWebDateset(obj, loading).then((res) => {
|
||||
datasetApi.postWebDataset(obj, loading).then((res) => {
|
||||
MsgSuccess('提交成功')
|
||||
dataset.saveBaseInfo(null)
|
||||
dataset.saveWebInfo(null)
|
||||
|
|
|
|||
|
|
@ -392,7 +392,7 @@ function getList(bool?: boolean) {
|
|||
}
|
||||
|
||||
function getDetail() {
|
||||
dataset.asyncGetDatesetDetail(id, loading).then((res: any) => {
|
||||
dataset.asyncGetDatasetDetail(id, loading).then((res: any) => {
|
||||
datasetDetail.value = res.data
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@
|
|||
</div>
|
||||
<el-scrollbar>
|
||||
<div class="hit-test-height">
|
||||
<el-empty v-if="paragraphDetail.length == 0" description="暂无数据" />
|
||||
<el-empty v-if="first" :image="emptyImg" description="命中段落显示在这里" />
|
||||
<el-empty v-else-if="paragraphDetail.length == 0" description="没有命中的分段" />
|
||||
<el-row v-else>
|
||||
<el-col
|
||||
:xs="24"
|
||||
|
|
@ -43,7 +44,8 @@
|
|||
>
|
||||
<template #icon>
|
||||
<AppAvatar class="mr-12 avatar-light" :size="22">
|
||||
{{ index + 1 + '' }}</AppAvatar>
|
||||
{{ index + 1 + '' }}</AppAvatar
|
||||
>
|
||||
</template>
|
||||
<div class="active-button primary">{{ item.similarity?.toFixed(3) }}</div>
|
||||
<template #footer>
|
||||
|
|
@ -145,6 +147,7 @@ import datasetApi from '@/api/dataset'
|
|||
import applicationApi from '@/api/application'
|
||||
import ParagraphDialog from '@/views/paragraph/component/ParagraphDialog.vue'
|
||||
import { arraySort } from '@/utils/utils'
|
||||
import emptyImg from '@/assets/hit-test-empty.png'
|
||||
|
||||
const route = useRoute()
|
||||
const {
|
||||
|
|
@ -161,6 +164,9 @@ const formInline = ref({
|
|||
top_number: 5
|
||||
})
|
||||
|
||||
// 第一次加载
|
||||
const first = ref(true)
|
||||
|
||||
const cloneForm = ref<any>({})
|
||||
|
||||
const popoverVisible = ref(false)
|
||||
|
|
@ -215,12 +221,14 @@ function getHitTestList() {
|
|||
paragraphDetail.value = res.data && arraySort(res.data, 'comprehensive_score', true)
|
||||
questionTitle.value = inputValue.value
|
||||
inputValue.value = ''
|
||||
first.value = false
|
||||
})
|
||||
} else if (isApplication.value) {
|
||||
applicationApi.getApplicationHitTest(id, obj, loading).then((res) => {
|
||||
paragraphDetail.value = res.data && arraySort(res.data, 'comprehensive_score', true)
|
||||
questionTitle.value = inputValue.value
|
||||
inputValue.value = ''
|
||||
first.value = false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<el-drawer v-model="visible" size="60%" @close="closeHandel" class="chat-record-drawer">
|
||||
<el-drawer v-model="visible" size="60%" @close="closeHandle" class="chat-record-drawer">
|
||||
<template #header>
|
||||
<h4>{{ currentAbstract }}</h4>
|
||||
</template>
|
||||
|
|
@ -77,7 +77,7 @@ const paginationConfig = reactive({
|
|||
total: 0
|
||||
})
|
||||
|
||||
function closeHandel() {
|
||||
function closeHandle() {
|
||||
recordList.value = []
|
||||
paginationConfig.total = 0
|
||||
paginationConfig.current_page = 1
|
||||
|
|
|
|||
|
|
@ -270,7 +270,7 @@ function rowClickHandle(row: any) {
|
|||
}
|
||||
|
||||
const setRowClass = ({ row }: any) => {
|
||||
return currentChatId.value === row?.id ? 'hightlight' : ''
|
||||
return currentChatId.value === row?.id ? 'highlight' : ''
|
||||
}
|
||||
|
||||
function deleteLog(row: any) {
|
||||
|
|
|
|||
|
|
@ -25,13 +25,17 @@
|
|||
@change="addProblemHandle"
|
||||
@blur="isAddProblem = false"
|
||||
class="mb-16"
|
||||
popper-class="select-popper"
|
||||
:popper-append-to-body="false"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in problemOptions"
|
||||
:key="item.id"
|
||||
:label="item.content"
|
||||
:value="item.id"
|
||||
/>
|
||||
>
|
||||
{{ item.content }}
|
||||
</el-option>
|
||||
</el-select>
|
||||
<template v-for="(item, index) in problemList" :key="index">
|
||||
<TagEllipsis
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<el-drawer v-model="visible" size="60%" @close="closeHandel">
|
||||
<el-drawer v-model="visible" size="60%" @close="closeHandle">
|
||||
<template #header>
|
||||
<h4>问题详情</h4>
|
||||
</template>
|
||||
|
|
@ -148,7 +148,7 @@ function editName(val: string) {
|
|||
}
|
||||
}
|
||||
|
||||
function closeHandel() {
|
||||
function closeHandle() {
|
||||
paragraphList.value = []
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -157,7 +157,7 @@ function associationClick(item: any) {
|
|||
function searchHandle() {
|
||||
paginationConfig.current_page = 1
|
||||
paragraphList.value = []
|
||||
getParagraphList(currentDocument.value)
|
||||
currentDocument.value && getParagraphList(currentDocument.value)
|
||||
}
|
||||
|
||||
function clickDocumentHandle(item: any) {
|
||||
|
|
|
|||
|
|
@ -304,7 +304,7 @@ function rowClickHandle(row: any) {
|
|||
}
|
||||
|
||||
const setRowClass = ({ row }: any) => {
|
||||
return currentClickId.value === row?.id ? 'hightlight' : ''
|
||||
return currentClickId.value === row?.id ? 'highlight' : ''
|
||||
}
|
||||
|
||||
function handleSizeChange() {
|
||||
|
|
|
|||
|
|
@ -44,9 +44,8 @@
|
|||
<el-select
|
||||
v-loading="model_type_loading"
|
||||
@change="list_base_model($event)"
|
||||
style="width: 100%"
|
||||
v-model="base_form_data.model_type"
|
||||
class="m-2"
|
||||
class="w-full m-2"
|
||||
placeholder="请选择模型类型"
|
||||
>
|
||||
<el-option
|
||||
|
|
@ -61,9 +60,8 @@
|
|||
<el-select
|
||||
@change="getModelForm($event)"
|
||||
v-loading="base_model_loading"
|
||||
style="width: 100%"
|
||||
v-model="base_form_data.model_name"
|
||||
class="m-2"
|
||||
class="w-full m-2"
|
||||
placeholder="请选择基础模型"
|
||||
filterable
|
||||
allow-create
|
||||
|
|
|
|||
|
|
@ -39,9 +39,8 @@
|
|||
<el-select
|
||||
v-loading="model_type_loading"
|
||||
@change="list_base_model($event)"
|
||||
style="width: 100%"
|
||||
v-model="base_form_data.model_type"
|
||||
class="m-2"
|
||||
class="w-full m-2"
|
||||
placeholder="请选择模型类型"
|
||||
>
|
||||
<el-option
|
||||
|
|
@ -56,9 +55,8 @@
|
|||
<el-select
|
||||
@change="getModelForm($event)"
|
||||
v-loading="base_model_loading"
|
||||
style="width: 100%"
|
||||
v-model="base_form_data.model_name"
|
||||
class="m-2"
|
||||
class="w-full m-2"
|
||||
placeholder="请选择基础模型"
|
||||
filterable
|
||||
allow-create
|
||||
|
|
|
|||
|
|
@ -6,6 +6,8 @@
|
|||
"composite": true,
|
||||
"moduleResolution": "node",
|
||||
"baseUrl": ".",
|
||||
"target": "esnext", // 使用ES最新语法
|
||||
"module": "esnext", // 使用ES模块语法
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,6 +11,9 @@
|
|||
"composite": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "node",
|
||||
"types": ["node"]
|
||||
"skipLibCheck": true, // 跳过node依赖包语法检查
|
||||
"types": [
|
||||
"node"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,8 @@
|
|||
"exclude": [],
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"target": "esnext", // 使用ES最新语法
|
||||
"module": "esnext", // 使用ES模块语法
|
||||
"lib": [],
|
||||
"types": ["node", "jsdom"]
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue