├── apps ├── __init__.py ├── users │ ├── __init__.py │ ├── migrations │ │ ├── __init__.py │ │ └── 0002_user_create_time_user_update_time.py │ ├── models │ │ └── __init__.py │ ├── apps.py │ ├── views │ │ └── __init__.py │ └── urls.py ├── application │ ├── __init__.py │ ├── migrations │ │ ├── __init__.py │ │ ├── 0002_chat_client_id.py │ │ ├── 0003_application_icon.py │ │ ├── 0004_applicationaccesstoken_show_source.py │ │ ├── 0007_alter_application_prologue.py │ │ ├── 0005_alter_chat_abstract_alter_chatrecord_answer_text.py │ │ └── 0006_applicationapikey_allow_cross_domain_and_more.py │ ├── models │ │ └── __init__.py │ ├── tests.py │ ├── admin.py │ ├── chat_pipeline │ │ ├── __init__.py │ │ ├── step │ │ │ ├── __init__.py │ │ │ ├── chat_step │ │ │ │ └── __init__.py │ │ │ ├── reset_problem_step │ │ │ │ └── __init__.py │ │ │ ├── search_dataset_step │ │ │ │ └── __init__.py │ │ │ └── generate_human_message_step │ │ │ │ └── __init__.py │ │ └── pipeline_manage.py │ ├── apps.py │ ├── sql │ │ ├── customer_count_trend.sql │ │ ├── customer_count.sql │ │ ├── list_dataset_paragraph_by_paragraph_id.sql │ │ ├── list_application.sql │ │ ├── list_application_dataset.sql │ │ ├── chat_record_count.sql │ │ ├── list_application_chat.sql │ │ └── chat_record_count_trend.sql │ └── views │ │ └── __init__.py ├── dataset │ ├── __init__.py │ ├── migrations │ │ ├── __init__.py │ │ ├── 0006_alter_paragraph_content.py │ │ ├── 0004_document_directly_return_similarity.py │ │ ├── 0003_document_hit_handling_method.py │ │ ├── 0005_alter_dataset_type_child.py │ │ ├── 0007_document_extraction_status.py │ │ └── 0002_image.py │ ├── models │ │ └── __init__.py │ ├── tests.py │ ├── apps.py │ ├── sql │ │ ├── update_document_char_length.sql │ │ ├── list_problem.sql │ │ ├── list_paragraph.sql │ │ ├── list_document.sql │ │ ├── list_dataset_application.sql │ │ └── list_dataset.sql │ ├── views │ │ └── __init__.py │ ├── swagger_api │ │ ├── image_api.py │ │ └── document_api.py │ └── serializers │ │ └── image_serializers.py ├── embedding │ ├── __init__.py │ ├── migrations │ │ └── __init__.py │ ├── models │ │ ├── __init__.py │ │ └── embedding.py │ ├── admin.py │ ├── tests.py │ ├── views.py │ ├── apps.py │ └── sql │ │ ├── hit_test.sql │ │ ├── embedding_search.sql │ │ ├── keywords_search.sql │ │ └── blend_search.sql ├── setting │ ├── __init__.py │ ├── migrations │ │ ├── __init__.py │ │ ├── 0004_alter_model_credential.py │ │ ├── 0005_model_permission_type.py │ │ ├── 0003_model_meta_model_status.py │ │ └── 0002_systemsetting.py │ ├── models_provider │ │ ├── impl │ │ │ ├── zhipu_model_provider │ │ │ │ ├── __init__.py │ │ │ │ └── model │ │ │ │ │ └── zhipu_chat_model.py │ │ │ ├── local_model_provider │ │ │ │ ├── __init__.py │ │ │ │ ├── model │ │ │ │ │ └── embedding.py │ │ │ │ ├── icon │ │ │ │ │ └── local_icon_svg │ │ │ │ └── local_model_provider.py │ │ │ ├── xf_model_provider │ │ │ │ └── __init__.py │ │ │ ├── kimi_model_provider │ │ │ │ ├── __init__.py │ │ │ │ └── model │ │ │ │ │ └── kimi_chat_model.py │ │ │ ├── ollama_model_provider │ │ │ │ └── __init__.py │ │ │ ├── qwen_model_provider │ │ │ │ ├── __init__.py │ │ │ │ └── model │ │ │ │ │ └── qwen_chat_model.py │ │ │ ├── azure_model_provider │ │ │ │ ├── __init__.py │ │ │ │ └── model │ │ │ │ │ └── azure_chat_model.py │ │ │ ├── openai_model_provider │ │ │ │ ├── __init__.py │ │ │ │ └── model │ │ │ │ │ ├── embedding.py │ │ │ │ │ └── openai_chat_model.py │ │ │ ├── wenxin_model_provider │ │ │ │ ├── __init__.py │ │ │ │ └── icon │ │ │ │ │ └── azure_icon_svg │ │ │ ├── deepseek_model_provider │ │ │ │ ├── __init__.py │ │ │ │ └── model │ │ │ │ │ └── deepseek_chat_model.py │ │ │ └── gemini_model_provider │ │ │ │ ├── __init__.py │ │ │ │ ├── model │ │ │ │ └── gemini_chat_model.py │ │ │ │ └── icon │ │ │ │ └── gemini_icon_svg │ │ └── __init__.py │ ├── tests.py │ ├── admin.py │ ├── models │ │ ├── __init__.py │ │ └── system_management.py │ ├── apps.py │ ├── views │ │ └── __init__.py │ ├── sql │ │ ├── get_member_permission.sql │ │ ├── check_member_permission_target_exists.sql │ │ ├── get_user_permission.sql │ │ └── get_member_permission_dataset.sql │ └── urls.py ├── smartdoc │ ├── __init__.py │ ├── settings │ │ └── __init__.py │ ├── const.py │ ├── asgi.py │ └── wsgi.py ├── common │ ├── field │ │ ├── __init__.py │ │ ├── vector_field.py │ │ └── common.py │ ├── __init__.py │ ├── handle │ │ ├── __init__.py │ │ └── base_split_handle.py │ ├── auth │ │ ├── __init__.py │ │ └── handle │ │ │ └── auth_base_handle.py │ ├── job │ │ ├── __init__.py │ │ └── client_access_num_job.py │ ├── util │ │ ├── file_util.py │ │ ├── lock.py │ │ └── common.py │ ├── constants │ │ ├── authentication_type.py │ │ └── exception_code_constants.py │ ├── mixins │ │ ├── app_model_mixin.py │ │ └── api_mixin.py │ ├── forms │ │ ├── base_form.py │ │ ├── __init__.py │ │ ├── password_input.py │ │ ├── text_input_field.py │ │ ├── object_card.py │ │ ├── table_radio.py │ │ ├── tab_card.py │ │ ├── table_checkbox.py │ │ ├── array_object_card.py │ │ ├── radio_field.py │ │ ├── radio_card_field.py │ │ ├── radio_button_field.py │ │ ├── multi_select.py │ │ └── single_select_field.py │ ├── event │ │ ├── common.py │ │ └── __init__.py │ ├── config │ │ ├── swagger_conf.py │ │ └── tokenizer_manage_config.py │ ├── sql │ │ └── list_embedding_text.sql │ ├── middleware │ │ └── static_headers_middleware.py │ └── cache │ │ └── mem_cache.py ├── .idea │ ├── vcs.xml │ ├── inspectionProfiles │ │ ├── profiles_settings.xml │ │ └── Project_Default.xml │ ├── misc.xml │ └── modules.xml └── manage.py ├── .github ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── feature.yml │ └── bug.yml ├── workflows │ └── typos_check.yml └── PULL_REQUEST_TEMPLATE.md ├── .dockerignore ├── Security ├── .idea └── icon.png ├── ui ├── src │ ├── locales │ │ ├── lang │ │ │ ├── en_US │ │ │ │ ├── components │ │ │ │ │ └── index.ts │ │ │ │ ├── views │ │ │ │ │ ├── 404.ts │ │ │ │ │ └── index.ts │ │ │ │ ├── index.ts │ │ │ │ └── layout.ts │ │ │ └── zh_CN │ │ │ │ ├── components │ │ │ │ └── index.ts │ │ │ │ ├── views │ │ │ │ ├── 404.ts │ │ │ │ └── index.ts │ │ │ │ ├── index.ts │ │ │ │ └── layout.ts │ │ └── useLocale.ts │ ├── assets │ │ ├── 404.png │ │ ├── img.png │ │ ├── logo.png │ │ ├── login.jpg │ │ ├── window1.png │ │ ├── window2.png │ │ ├── hit-test-empty.png │ │ ├── home-modified.png │ │ ├── 图片1-modified.png │ │ ├── icon_web.svg │ │ ├── doc-icon.svg │ │ ├── docx-icon.svg │ │ ├── md-icon.svg │ │ ├── upload-icon1.svg │ │ ├── icon_send.svg │ │ ├── txt-icon.svg │ │ ├── user-icon.svg │ │ ├── icon_send_colorful.svg │ │ ├── icon_document.svg │ │ └── upload-icon.svg │ ├── views │ │ ├── 404 │ │ │ └── index.vue │ │ ├── document │ │ │ └── utils.ts │ │ ├── qa_document │ │ │ └── utils.ts │ │ ├── index │ │ │ ├── components │ │ │ │ ├── index.ts │ │ │ │ ├── index-main │ │ │ │ │ └── index.vue │ │ │ │ └── top-bar │ │ │ │ │ └── MenuItem.vue │ │ │ └── index.vue │ │ ├── team │ │ │ └── utils.ts │ │ ├── chat │ │ │ └── index.vue │ │ ├── first │ │ │ └── index.vue │ │ └── template │ │ │ └── component │ │ │ └── SelectProviderDialog.vue │ ├── api │ │ ├── type │ │ │ ├── dataset.ts │ │ │ ├── team.ts │ │ │ └── common.ts │ │ ├── image.ts │ │ ├── provider.ts │ │ └── team.ts │ ├── components │ │ ├── dynamics-form │ │ │ ├── items │ │ │ │ ├── TextInput.vue │ │ │ │ ├── PasswordInput.vue │ │ │ │ ├── table │ │ │ │ │ └── TableColumn.vue │ │ │ │ ├── radio │ │ │ │ │ ├── Radio.vue │ │ │ │ │ └── RadioButton.vue │ │ │ │ └── select │ │ │ │ │ └── MultiSelect.vue │ │ │ ├── FormItemLabel.vue │ │ │ └── index.ts │ │ ├── app-charts │ │ │ └── index.vue │ │ ├── back-button │ │ │ └── index.vue │ │ ├── icons │ │ │ └── AppIcon.vue │ │ ├── tag-ellipsis │ │ │ └── index.vue │ │ ├── layout-container │ │ │ └── index.vue │ │ ├── auto-tooltip │ │ │ └── index.vue │ │ ├── card-add │ │ │ └── index.vue │ │ ├── markdown-editor │ │ │ └── index.vue │ │ ├── login-container │ │ │ └── index.vue │ │ ├── login-layout │ │ │ └── index.vue │ │ ├── app-avatar │ │ │ └── index.vue │ │ ├── infinite-scroll │ │ │ └── index.vue │ │ ├── common-list │ │ │ └── index.vue │ │ └── markdown-renderer │ │ │ └── MdRenderer.vue │ ├── theme │ │ ├── defaultKeyValueData.ts │ │ ├── setting.ts │ │ ├── defaultInferData.ts │ │ └── type.ts │ ├── bus │ │ └── index.ts │ ├── utils │ │ ├── application.ts │ │ ├── clipboard.ts │ │ ├── decimalFormat.ts │ │ ├── permission │ │ │ └── type.ts │ │ ├── common.ts │ │ ├── message.ts │ │ └── time.ts │ ├── layout │ │ ├── components │ │ │ ├── index.ts │ │ │ ├── app-main │ │ │ │ └── index.vue │ │ │ ├── top-bar │ │ │ │ └── top-menu │ │ │ │ │ ├── index.vue │ │ │ │ │ └── MenuItem.vue │ │ │ └── sidebar │ │ │ │ └── SidebarItem.vue │ │ ├── app-layout │ │ │ └── index.vue │ │ └── main-layout │ │ │ └── index.vue │ ├── styles │ │ ├── index.scss │ │ └── md-editor.scss │ ├── App.vue │ ├── directives │ │ ├── index.ts │ │ └── hasPermission.ts │ ├── stores │ │ ├── modules │ │ │ ├── common.ts │ │ │ ├── model.ts │ │ │ ├── paragraph.ts │ │ │ └── log.ts │ │ └── index.ts │ ├── common │ │ └── font │ │ │ └── iconfont.css │ ├── main.ts │ ├── request │ │ └── Result.ts │ └── prompts │ │ └── DocRewritePrompts.ts ├── public │ ├── favicon.ico │ └── chatchat_icon_blue_square_v2.png ├── env │ └── .env ├── .prettierrc.json ├── tsconfig.json ├── tsconfig.vitest.json ├── .gitignore ├── index.html ├── tsconfig.app.json ├── vitest.config.ts ├── tsconfig.node.json ├── env.d.ts ├── .eslintrc.cjs └── vite.config.ts ├── installer ├── init.sql ├── run-maxkb.sh ├── Dockerfile-vector-model ├── Dockerfile-python-pg └── config.yaml ├── SECURITY.md ├── config_example.yml ├── conf └── config_example.yml ├── README.en.md ├── pyproject.toml └── CONTRIBUTING.md /apps/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/users/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/application/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/dataset/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/embedding/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/setting/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/smartdoc/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/common/field/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/users/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .git* 2 | .idea* 3 | -------------------------------------------------------------------------------- /apps/dataset/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/embedding/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/setting/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/application/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /Security: -------------------------------------------------------------------------------- 1 | 安全说明 2 | 如果您发现安全问题,请直接联系我们 3 | 感谢您的支持! 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /apps/setting/models_provider/impl/zhipu_model_provider/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/users/models/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | 3 | from .user import * -------------------------------------------------------------------------------- /.idea/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hduchat/KB-Builder/HEAD/.idea/icon.png -------------------------------------------------------------------------------- /apps/dataset/models/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | 3 | from .data_set import * 4 | -------------------------------------------------------------------------------- /ui/src/locales/lang/en_US/components/index.ts: -------------------------------------------------------------------------------- 1 | 2 | export default { 3 | 4 | }; 5 | -------------------------------------------------------------------------------- /ui/src/locales/lang/zh_CN/components/index.ts: -------------------------------------------------------------------------------- 1 | 2 | export default { 3 | 4 | }; 5 | -------------------------------------------------------------------------------- /apps/embedding/models/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | 3 | from .embedding import * 4 | -------------------------------------------------------------------------------- /apps/setting/models_provider/impl/local_model_provider/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | -------------------------------------------------------------------------------- /apps/application/models/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | 3 | from .application import * 4 | -------------------------------------------------------------------------------- /apps/dataset/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /apps/setting/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /apps/application/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /apps/embedding/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /apps/embedding/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /apps/embedding/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | -------------------------------------------------------------------------------- /apps/setting/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /ui/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hduchat/KB-Builder/HEAD/ui/public/favicon.ico -------------------------------------------------------------------------------- /ui/src/assets/404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hduchat/KB-Builder/HEAD/ui/src/assets/404.png -------------------------------------------------------------------------------- /ui/src/assets/img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hduchat/KB-Builder/HEAD/ui/src/assets/img.png -------------------------------------------------------------------------------- /ui/src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hduchat/KB-Builder/HEAD/ui/src/assets/logo.png -------------------------------------------------------------------------------- /apps/application/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /installer/init.sql: -------------------------------------------------------------------------------- 1 | CREATE DATABASE "maxkb"; 2 | 3 | \c "maxkb"; 4 | 5 | CREATE EXTENSION "vector"; -------------------------------------------------------------------------------- /ui/src/assets/login.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hduchat/KB-Builder/HEAD/ui/src/assets/login.jpg -------------------------------------------------------------------------------- /ui/src/assets/window1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hduchat/KB-Builder/HEAD/ui/src/assets/window1.png -------------------------------------------------------------------------------- /ui/src/assets/window2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hduchat/KB-Builder/HEAD/ui/src/assets/window2.png -------------------------------------------------------------------------------- /ui/src/assets/hit-test-empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hduchat/KB-Builder/HEAD/ui/src/assets/hit-test-empty.png -------------------------------------------------------------------------------- /ui/src/assets/home-modified.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hduchat/KB-Builder/HEAD/ui/src/assets/home-modified.png -------------------------------------------------------------------------------- /ui/src/assets/图片1-modified.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hduchat/KB-Builder/HEAD/ui/src/assets/图片1-modified.png -------------------------------------------------------------------------------- /ui/public/chatchat_icon_blue_square_v2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hduchat/KB-Builder/HEAD/ui/public/chatchat_icon_blue_square_v2.png -------------------------------------------------------------------------------- /ui/src/views/document/utils.ts: -------------------------------------------------------------------------------- 1 | export const hitHandlingMethod: any = { 2 | optimization: '模型优化', 3 | directly_return: '直接回答' 4 | } 5 | -------------------------------------------------------------------------------- /ui/src/views/qa_document/utils.ts: -------------------------------------------------------------------------------- 1 | export const hitHandlingMethod: any = { 2 | optimization: '模型优化', 3 | directly_return: '直接回答' 4 | } 5 | -------------------------------------------------------------------------------- /ui/env/.env: -------------------------------------------------------------------------------- 1 | VITE_APP_NAME=ui 2 | VITE_BASE_PATH=/ui/ 3 | VITE_APP_PORT=3000 4 | VITE_APP_TITLE = 'KB Builder' 5 | VITE_SERVER_PATH=http://127.0.0.1:8088 -------------------------------------------------------------------------------- /ui/src/locales/lang/zh_CN/views/404.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | title: "404", 3 | Message: "无法访问应用", 4 | operate: "返回首页", 5 | }; 6 | -------------------------------------------------------------------------------- /apps/setting/models/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | 3 | from .team_management import * 4 | from .model_management import * 5 | from .system_management import * 6 | -------------------------------------------------------------------------------- /ui/src/locales/lang/en_US/views/404.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | title: "404", 3 | message: "Unable to Access Application", 4 | operate: "Back to Home", 5 | }; 6 | -------------------------------------------------------------------------------- /apps/common/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: smart-doc 4 | @Author:虎 5 | @file: __init__.py 6 | @date:2023/9/14 16:22 7 | @desc: 8 | """ 9 | -------------------------------------------------------------------------------- /apps/common/handle/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: qabot 4 | @Author:虎 5 | @file: __init__.py.py 6 | @date:2023/9/6 10:09 7 | @desc: 8 | """ 9 | -------------------------------------------------------------------------------- /apps/setting/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class SettingConfig(AppConfig): 5 | default_auto_field = 'django.db.models.BigAutoField' 6 | name = 'setting' -------------------------------------------------------------------------------- /ui/src/api/type/dataset.ts: -------------------------------------------------------------------------------- 1 | interface datasetData { 2 | name: String 3 | desc: String 4 | documents?: Array 5 | type?: String 6 | } 7 | 8 | export type { datasetData } 9 | -------------------------------------------------------------------------------- /ui/src/components/dynamics-form/items/TextInput.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /ui/src/locales/lang/en_US/views/index.ts: -------------------------------------------------------------------------------- 1 | import notFound from './404'; 2 | import application from './application'; 3 | export default { 4 | notFound, 5 | application, 6 | }; 7 | -------------------------------------------------------------------------------- /ui/src/locales/lang/zh_CN/views/index.ts: -------------------------------------------------------------------------------- 1 | import notFound from './404'; 2 | import application from './application'; 3 | export default { 4 | notFound, 5 | application, 6 | }; 7 | -------------------------------------------------------------------------------- /apps/application/chat_pipeline/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: maxkb 4 | @Author:虎 5 | @file: __init__.py.py 6 | @date:2024/1/9 17:23 7 | @desc: 8 | """ 9 | -------------------------------------------------------------------------------- /apps/dataset/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class DatasetConfig(AppConfig): 5 | default_auto_field = 'django.db.models.BigAutoField' 6 | name = 'dataset' 7 | -------------------------------------------------------------------------------- /apps/setting/models_provider/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: maxkb 4 | @Author:虎 5 | @file: __init__.py.py 6 | @date:2023/10/31 17:16 7 | @desc: 8 | """ 9 | -------------------------------------------------------------------------------- /apps/users/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class UsersConfig(AppConfig): 5 | default_auto_field = 'django.db.models.BigAutoField' 6 | name = 'users' 7 | 8 | -------------------------------------------------------------------------------- /apps/application/chat_pipeline/step/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: maxkb 4 | @Author:虎 5 | @file: __init__.py.py 6 | @date:2024/1/9 18:23 7 | @desc: 8 | """ 9 | -------------------------------------------------------------------------------- /apps/embedding/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class EmbeddingConfig(AppConfig): 5 | default_auto_field = 'django.db.models.BigAutoField' 6 | name = 'embedding' 7 | -------------------------------------------------------------------------------- /ui/src/theme/defaultKeyValueData.ts: -------------------------------------------------------------------------------- 1 | import type { KeyValueData } from './type' 2 | const keyValueData: KeyValueData = { 3 | '--el-header-padding': '0px' 4 | } 5 | export default keyValueData 6 | -------------------------------------------------------------------------------- /apps/application/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ApplicationConfig(AppConfig): 5 | default_auto_field = 'django.db.models.BigAutoField' 6 | name = 'application' 7 | -------------------------------------------------------------------------------- /apps/dataset/sql/update_document_char_length.sql: -------------------------------------------------------------------------------- 1 | UPDATE "document" 2 | SET "char_length" = ( SELECT "sum" ( "char_length" ( "content" ) ) FROM paragraph WHERE "document_id" = %s ) 3 | WHERE 4 | "id" = %s -------------------------------------------------------------------------------- /apps/application/chat_pipeline/step/chat_step/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: maxkb 4 | @Author:虎 5 | @file: __init__.py.py 6 | @date:2024/1/9 18:23 7 | @desc: 8 | """ 9 | -------------------------------------------------------------------------------- /apps/dataset/sql/list_problem.sql: -------------------------------------------------------------------------------- 1 | SELECT 2 | problem.*, 3 | (SELECT "count"("id") FROM "problem_paragraph_mapping" WHERE problem_id="problem"."id") as "paragraph_count" 4 | FROM 5 | problem problem 6 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # 安全说明 2 | 3 | 如果您发现安全问题,请直接联系我们: 4 | 5 | 感谢您的支持! 6 | 7 | # Security Policy 8 | 9 | All security bugs should be reported to the contact as below: 10 | 11 | Thanks for your support! 12 | -------------------------------------------------------------------------------- /apps/setting/models_provider/impl/xf_model_provider/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: maxkb 4 | @Author:虎 5 | @file: __init__.py.py 6 | @date:2024/04/19 15:55 7 | @desc: 8 | """ -------------------------------------------------------------------------------- /ui/.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/prettierrc", 3 | "semi": false, 4 | "tabWidth": 2, 5 | "singleQuote": true, 6 | "printWidth": 100, 7 | "trailingComma": "none" 8 | } -------------------------------------------------------------------------------- /ui/src/bus/index.ts: -------------------------------------------------------------------------------- 1 | import mitt from "mitt"; 2 | const bus: any = {}; 3 | const emitter = mitt(); 4 | bus.on = emitter.on; 5 | bus.off = emitter.off; 6 | bus.emit = emitter.emit; 7 | 8 | export default bus; 9 | -------------------------------------------------------------------------------- /ui/src/utils/application.ts: -------------------------------------------------------------------------------- 1 | export const defaultIcon = '/ui/favicon.ico' 2 | 3 | // 是否显示字母 / icon 4 | export function isAppIcon(url: string | undefined) { 5 | return url === defaultIcon ? '' : url 6 | } 7 | -------------------------------------------------------------------------------- /apps/application/chat_pipeline/step/reset_problem_step/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: maxkb 4 | @Author:虎 5 | @file: __init__.py.py 6 | @date:2024/1/9 18:23 7 | @desc: 8 | """ 9 | -------------------------------------------------------------------------------- /apps/application/chat_pipeline/step/search_dataset_step/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: maxkb 4 | @Author:虎 5 | @file: __init__.py.py 6 | @date:2024/1/9 18:24 7 | @desc: 8 | """ 9 | -------------------------------------------------------------------------------- /apps/setting/models_provider/impl/kimi_model_provider/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: maxkb 4 | @Author:虎 5 | @file: __init__.py.py 6 | @date:2023/10/31 17:16 7 | @desc: 8 | """ 9 | -------------------------------------------------------------------------------- /apps/setting/models_provider/impl/ollama_model_provider/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: maxkb 4 | @Author:虎 5 | @file: __init__.py.py 6 | @date:2024/3/5 17:20 7 | @desc: 8 | """ 9 | -------------------------------------------------------------------------------- /apps/setting/models_provider/impl/qwen_model_provider/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: maxkb 4 | @Author:虎 5 | @file: __init__.py.py 6 | @date:2023/10/31 17:16 7 | @desc: 8 | """ 9 | -------------------------------------------------------------------------------- /apps/users/views/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: smart-doc 4 | @Author:虎 5 | @file: __init__.py.py 6 | @date:2023/9/14 19:01 7 | @desc: 8 | """ 9 | from .user import * 10 | -------------------------------------------------------------------------------- /ui/src/layout/components/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Sidebar } from './sidebar/index.vue' 2 | export { default as AppMain } from './app-main/index.vue' 3 | export { default as TopBar } from './top-bar/index.vue' 4 | -------------------------------------------------------------------------------- /apps/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /apps/setting/models_provider/impl/azure_model_provider/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: maxkb 4 | @Author:虎 5 | @file: __init__.py.py 6 | @date:2023/10/31 17:16 7 | @desc: 8 | """ 9 | -------------------------------------------------------------------------------- /apps/setting/models_provider/impl/openai_model_provider/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: maxkb 4 | @Author:虎 5 | @file: __init__.py.py 6 | @date:2024/3/28 16:25 7 | @desc: 8 | """ 9 | -------------------------------------------------------------------------------- /apps/setting/models_provider/impl/wenxin_model_provider/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: maxkb 4 | @Author:虎 5 | @file: __init__.py.py 6 | @date:2023/10/31 17:16 7 | @desc: 8 | """ 9 | -------------------------------------------------------------------------------- /ui/src/components/dynamics-form/items/PasswordInput.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /apps/application/chat_pipeline/step/generate_human_message_step/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: maxkb 4 | @Author:虎 5 | @file: __init__.py.py 6 | @date:2024/1/9 18:23 7 | @desc: 8 | """ 9 | -------------------------------------------------------------------------------- /ui/src/views/index/components/index.ts: -------------------------------------------------------------------------------- 1 | export { default as IndexMain } from './index-main/index.vue' 2 | export { default as TopBar } from './top-bar/index.vue' 3 | export { default as BottomBar } from './bottom-bar/index.vue' 4 | -------------------------------------------------------------------------------- /apps/dataset/sql/list_paragraph.sql: -------------------------------------------------------------------------------- 1 | SELECT 2 | (SELECT "name" FROM "document" WHERE "id"=document_id) as document_name, 3 | (SELECT "name" FROM "dataset" WHERE "id"=dataset_id) as dataset_name, 4 | * 5 | FROM 6 | "paragraph" 7 | -------------------------------------------------------------------------------- /apps/dataset/sql/list_document.sql: -------------------------------------------------------------------------------- 1 | SELECT 2 | "document".* , 3 | to_json("document"."meta") as meta, 4 | (SELECT "count"("id") FROM "paragraph" WHERE document_id="document"."id") as "paragraph_count" 5 | FROM 6 | "document" "document" 7 | -------------------------------------------------------------------------------- /apps/setting/models_provider/impl/deepseek_model_provider/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: UTF-8 -*- 3 | """ 4 | @Project :MaxKB 5 | @File :__init__.py.py 6 | @Author :Brian Yang 7 | @Date :5/12/24 7:38 AM 8 | """ 9 | -------------------------------------------------------------------------------- /apps/setting/models_provider/impl/gemini_model_provider/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: UTF-8 -*- 3 | """ 4 | @Project :MaxKB 5 | @File :__init__.py.py 6 | @Author :Brian Yang 7 | @Date :5/13/24 7:40 AM 8 | """ 9 | -------------------------------------------------------------------------------- /apps/.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /apps/smartdoc/settings/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: smart-doc 4 | @Author:虎 5 | @file: __init__.py 6 | @date:2023/9/14 15:45 7 | @desc: 8 | """ 9 | from .base import * 10 | from .logging import * 11 | -------------------------------------------------------------------------------- /apps/application/sql/customer_count_trend.sql: -------------------------------------------------------------------------------- 1 | SELECT 2 | COUNT ( "application_public_access_client"."id" ) AS "customer_added_count", 3 | create_time :: DATE as "day" 4 | FROM 5 | "application_public_access_client" 6 | ${default_sql} 7 | GROUP BY "day" -------------------------------------------------------------------------------- /apps/common/auth/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: smart-doc 4 | @Author:虎 5 | @file: __init__.py 6 | @date:2023/9/14 19:44 7 | @desc: 8 | """ 9 | from .authenticate import * 10 | from .authentication import * 11 | -------------------------------------------------------------------------------- /ui/src/views/team/utils.ts: -------------------------------------------------------------------------------- 1 | export const MANAGE = 'MANAGE' 2 | export const USE = 'USE' 3 | export const DATASET = 'DATASET' 4 | export const APPLICATION = 'APPLICATION' 5 | 6 | export function isManage(type: String) { 7 | return type === 'manage' 8 | } 9 | -------------------------------------------------------------------------------- /apps/application/views/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: maxkb 4 | @Author:虎 5 | @file: __init__.py.py 6 | @date:2023/9/25 17:12 7 | @desc: 8 | """ 9 | from .application_views import * 10 | from .chat_views import * 11 | -------------------------------------------------------------------------------- /ui/src/api/type/team.ts: -------------------------------------------------------------------------------- 1 | interface TeamMember { 2 | id: string 3 | username: string 4 | email: string 5 | team_id: string 6 | /** 7 | * 类型:type:manage 所有者; 8 | */ 9 | type: string 10 | user_id: string 11 | } 12 | 13 | export type { TeamMember } -------------------------------------------------------------------------------- /apps/setting/views/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: maxkb 4 | @Author:虎 5 | @file: __init__.py.py 6 | @date:2023/9/25 17:12 7 | @desc: 8 | """ 9 | from .Team import * 10 | from .model import * 11 | from .system_setting import * 12 | -------------------------------------------------------------------------------- /apps/application/sql/customer_count.sql: -------------------------------------------------------------------------------- 1 | SELECT 2 | ( SUM ( CASE WHEN create_time :: DATE = CURRENT_DATE THEN 1 ELSE 0 END ) ) AS "customer_today_added_count", 3 | COUNT ( "application_public_access_client"."id" ) AS "customer_added_count" 4 | FROM 5 | "application_public_access_client" -------------------------------------------------------------------------------- /apps/common/job/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: maxkb 4 | @Author:虎 5 | @file: __init__.py 6 | @date:2024/3/14 11:54 7 | @desc: 8 | """ 9 | from .client_access_num_job import * 10 | 11 | 12 | def run(): 13 | client_access_num_job.run() 14 | -------------------------------------------------------------------------------- /installer/run-maxkb.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Start postgresql 4 | docker-entrypoint.sh postgres & 5 | sleep 10 6 | # Wait postgresql 7 | until pg_isready --host=127.0.0.1; do sleep 1 && echo "waiting for postgres"; done 8 | 9 | # Start MaxKB 10 | python /opt/maxkb/app/main.py start -------------------------------------------------------------------------------- /ui/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | 3 | "files": [], 4 | 5 | "references": [ 6 | { 7 | "path": "./tsconfig.node.json" 8 | }, 9 | { 10 | "path": "./tsconfig.app.json" 11 | }, 12 | { 13 | "path": "./tsconfig.vitest.json" 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /ui/src/api/type/common.ts: -------------------------------------------------------------------------------- 1 | interface KeyValue { 2 | key: K 3 | value: V 4 | } 5 | interface Dict { 6 | [propName: string]: V 7 | } 8 | 9 | interface pageRequest { 10 | current_page: number 11 | page_size: number 12 | } 13 | 14 | export type { KeyValue, Dict, pageRequest } 15 | -------------------------------------------------------------------------------- /ui/tsconfig.vitest.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.app.json", 3 | "exclude": [], 4 | "compilerOptions": { 5 | "composite": true, 6 | "target": "esnext", // 使用ES最新语法 7 | "module": "esnext", // 使用ES模块语法 8 | "lib": [], 9 | "types": ["node", "jsdom"] 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /apps/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /apps/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /apps/common/field/vector_field.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | 4 | class VectorField(models.Field): 5 | 6 | def db_type(self, connection): 7 | return 'vector' 8 | 9 | 10 | class TsVectorField(models.Field): 11 | def db_type(self, connection): 12 | return 'tsvector' 13 | -------------------------------------------------------------------------------- /ui/src/styles/index.scss: -------------------------------------------------------------------------------- 1 | @import 'element-plus/dist/index.css'; 2 | @import './variables.scss'; 3 | @import './app.scss'; 4 | @import './element-plus.scss'; 5 | @import 'nprogress/nprogress.css'; 6 | @import 'highlight.js/styles/default.css'; 7 | @import 'md-editor-v3/lib/style.css'; 8 | @import './md-editor.scss'; -------------------------------------------------------------------------------- /ui/src/App.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 15 | -------------------------------------------------------------------------------- /ui/src/locales/lang/en_US/index.ts: -------------------------------------------------------------------------------- 1 | import en from 'element-plus/es/locale/lang/en'; 2 | import components from './components'; 3 | import layout from './layout'; 4 | import views from './views'; 5 | 6 | export default { 7 | lang: 'English', 8 | layout, 9 | views, 10 | components, 11 | en, 12 | }; 13 | -------------------------------------------------------------------------------- /ui/src/locales/lang/zh_CN/index.ts: -------------------------------------------------------------------------------- 1 | import zhCn from 'element-plus/es/locale/lang/zh-cn'; 2 | import components from './components'; 3 | import layout from './layout'; 4 | import views from './views'; 5 | 6 | export default { 7 | lang: '简体中文', 8 | layout, 9 | views, 10 | components, 11 | zhCn, 12 | }; 13 | -------------------------------------------------------------------------------- /apps/dataset/views/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: maxkb 4 | @Author:虎 5 | @file: __init__.py 6 | @date:2023/9/21 9:32 7 | @desc: 8 | """ 9 | from .dataset import * 10 | from .document import * 11 | from .paragraph import * 12 | from .problem import * 13 | from .image import * 14 | 15 | 16 | -------------------------------------------------------------------------------- /apps/common/util/file_util.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: maxkb 4 | @Author:虎 5 | @file: file_util.py 6 | @date:2023/9/25 21:06 7 | @desc: 8 | """ 9 | 10 | 11 | def get_file_content(path): 12 | with open(path, "r", encoding='utf-8') as file: 13 | content = file.read() 14 | return content 15 | -------------------------------------------------------------------------------- /ui/src/components/dynamics-form/FormItemLabel.vue: -------------------------------------------------------------------------------- 1 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /ui/src/theme/setting.ts: -------------------------------------------------------------------------------- 1 | import type { ThemeSetting } from "./type"; 2 | const setting: ThemeSetting = { 3 | namespace: "el", 4 | division: "-", 5 | startDivision: "--", 6 | colorInferSetting: { 7 | light: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 8 | dark: [2], 9 | type: "color", 10 | }, 11 | }; 12 | export default setting; 13 | -------------------------------------------------------------------------------- /.github/workflows/typos_check.yml: -------------------------------------------------------------------------------- 1 | name: Typos Check 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | run: 6 | name: Spell Check with Typos 7 | runs-on: ubuntu-latest 8 | steps: 9 | - name: Checkout Actions Repository 10 | uses: actions/checkout@v2 11 | 12 | - name: Check spelling 13 | uses: crate-ci/typos@master 14 | -------------------------------------------------------------------------------- /installer/Dockerfile-vector-model: -------------------------------------------------------------------------------- 1 | FROM python:3.11-slim-bookworm as vector-model 2 | 3 | COPY installer/install_model.py install_model.py 4 | RUN pip3 install --upgrade pip setuptools && \ 5 | pip install pycrawlers && \ 6 | pip install transformers && \ 7 | python3 install_model.py 8 | 9 | FROM scratch 10 | COPY --from=vector-model model /opt/maxkb/app/model -------------------------------------------------------------------------------- /ui/src/api/image.ts: -------------------------------------------------------------------------------- 1 | import { Result } from '@/request/Result' 2 | import { get, post, del, put } from '@/request/index' 3 | 4 | const prefix = '/image' 5 | /** 6 | * 上传图片 7 | * @param 参数 file:file 8 | */ 9 | const postImage: (data: any) => Promise> = (data) => { 10 | return post(`${prefix}`, data) 11 | } 12 | 13 | export default { 14 | postImage 15 | } 16 | -------------------------------------------------------------------------------- /ui/src/utils/clipboard.ts: -------------------------------------------------------------------------------- 1 | import Clipboard from 'vue-clipboard3' 2 | import { MsgSuccess, MsgError } from '@/utils/message' 3 | /* 4 | 复制粘贴 5 | */ 6 | export async function copyClick(info: string) { 7 | const { toClipboard } = Clipboard() 8 | try { 9 | await toClipboard(info) 10 | MsgSuccess('复制成功') 11 | } catch (e) { 12 | console.error(e) 13 | MsgError('复制失败') 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /ui/src/utils/decimalFormat.ts: -------------------------------------------------------------------------------- 1 | function format(decimal?: number, digits?: number): string | undefined { 2 | if (digits == undefined) { 3 | digits = 0; 4 | } 5 | return decimal?.toLocaleString("zh-CN", { 6 | style: "decimal", 7 | minimumFractionDigits: digits, 8 | maximumFractionDigits: digits, 9 | }); 10 | } 11 | 12 | const util = { 13 | format, 14 | }; 15 | 16 | export default util; 17 | -------------------------------------------------------------------------------- /apps/smartdoc/const.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | import os 4 | 5 | from .conf import ConfigManager 6 | 7 | __all__ = ['BASE_DIR', 'PROJECT_DIR', 'VERSION', 'CONFIG'] 8 | 9 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 10 | PROJECT_DIR = os.path.dirname(BASE_DIR) 11 | VERSION = '1.0.0' 12 | CONFIG = ConfigManager.load_user_config(root_path=os.path.abspath('/opt/kb_builder/conf')) 13 | -------------------------------------------------------------------------------- /config_example.yml: -------------------------------------------------------------------------------- 1 | # 邮箱配置 2 | EMAIL_ADDRESS: 3 | EMAIL_USE_TLS: False 4 | EMAIL_USE_SSL: True 5 | EMAIL_HOST: smtp.qq.com 6 | EMAIL_PORT: 465 7 | EMAIL_HOST_USER: 8 | EMAIL_HOST_PASSWORD: 9 | 10 | # 数据库链接信息 11 | DB_NAME: kb_builder 12 | DB_HOST: localhost 13 | DB_PORT: 5433 14 | DB_USER: root 15 | DB_PASSWORD: xxxxxxx 16 | DB_ENGINE: django.db.backends.postgresql_psycopg2 17 | 18 | DEBUG: false 19 | 20 | TIME_ZONE: Asia/Shanghai 21 | -------------------------------------------------------------------------------- /ui/src/theme/defaultInferData.ts: -------------------------------------------------------------------------------- 1 | import type { InferData } from "./type"; 2 | const inferData: Array = [ 3 | { 4 | key: "primary", 5 | value: "#3370FF", 6 | }, 7 | { key: "success", value: "#67c23a" }, 8 | { key: "warning", value: "#e6a23c" }, 9 | { key: "danger", value: "#f56c6c" }, 10 | { key: "error", value: "#F54A45" }, 11 | { key: "info", value: "#909399" }, 12 | ]; 13 | export default inferData; 14 | -------------------------------------------------------------------------------- /apps/common/constants/authentication_type.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: maxkb 4 | @Author:虎 5 | @file: authentication_type.py 6 | @date:2023/11/14 20:03 7 | @desc: 8 | """ 9 | from enum import Enum 10 | 11 | 12 | class AuthenticationType(Enum): 13 | # 普通用户 14 | USER = "USER" 15 | # 公共访问链接 16 | APPLICATION_ACCESS_TOKEN = "APPLICATION_ACCESS_TOKEN" 17 | # key API 18 | API_KEY = "API_KEY" 19 | -------------------------------------------------------------------------------- /ui/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | .DS_Store 12 | dist 13 | dist-ssr 14 | coverage 15 | *.local 16 | 17 | /cypress/videos/ 18 | /cypress/screenshots/ 19 | 20 | # Editor directories and files 21 | .vscode/* 22 | !.vscode/extensions.json 23 | .idea 24 | *.suo 25 | *.ntvs* 26 | *.njsproj 27 | *.sln 28 | *.sw? 29 | -------------------------------------------------------------------------------- /ui/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | %VITE_APP_TITLE% 9 | 10 | 11 |
12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /ui/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@vue/tsconfig/tsconfig.dom.json", 3 | "include": ["env.d.ts", "src/**/*", "src/**/*.vue"], 4 | "exclude": ["src/**/__tests__/*"], 5 | "compilerOptions": { 6 | "composite": true, 7 | "moduleResolution": "node", 8 | "baseUrl": ".", 9 | "target": "esnext", // 使用ES最新语法 10 | "module": "esnext", // 使用ES模块语法 11 | "paths": { 12 | "@/*": ["./src/*"] 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /ui/src/directives/index.ts: -------------------------------------------------------------------------------- 1 | import type { App } from 'vue' 2 | 3 | const directives = import.meta.glob('./*.ts', { eager: true }) 4 | const install = (app: App) => { 5 | Object.keys(directives) 6 | .filter((key: string) => { 7 | return !key.endsWith('index.ts') 8 | }) 9 | .forEach((key: string) => { 10 | const directive: any = directives[key] 11 | app.use(directive.default) 12 | }) 13 | } 14 | export default { install } 15 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | #### What this PR does / why we need it? 2 | 3 | #### Summary of your change 4 | 5 | #### Please indicate you've done the following: 6 | 7 | - [ ] Made sure tests are passing and test coverage is added if needed. 8 | - [ ] Made sure commit message follow the rule of [Conventional Commits specification](https://www.conventionalcommits.org/). 9 | - [ ] Considered the docs impact and opened a new docs issue or PR with docs changes if needed. -------------------------------------------------------------------------------- /ui/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { fileURLToPath } from 'node:url' 2 | import { mergeConfig, defineConfig, configDefaults } from 'vitest/config' 3 | import viteConfig from './vite.config' 4 | 5 | export default mergeConfig( 6 | viteConfig as never, 7 | defineConfig({ 8 | test: { 9 | environment: 'jsdom', 10 | exclude: [...configDefaults.exclude, 'e2e/*'], 11 | root: fileURLToPath(new URL('./', import.meta.url)) 12 | } 13 | }) 14 | ) 15 | -------------------------------------------------------------------------------- /ui/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/node18/tsconfig.json", 3 | "include": [ 4 | "vite.config.*", 5 | "vitest.config.*", 6 | "cypress.config.*", 7 | "nightwatch.conf.*", 8 | "playwright.config.*" 9 | ], 10 | "compilerOptions": { 11 | "composite": true, 12 | "module": "ESNext", 13 | "moduleResolution": "node", 14 | "skipLibCheck": true, // 跳过node依赖包语法检查 15 | "types": [ 16 | "node" 17 | ] 18 | } 19 | } -------------------------------------------------------------------------------- /apps/smartdoc/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for apps project. 3 | 4 | It exposes the ASGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/4.2/howto/deployment/asgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.asgi import get_asgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'smartdoc.settings') 15 | 16 | application = get_asgi_application() 17 | -------------------------------------------------------------------------------- /apps/application/sql/list_dataset_paragraph_by_paragraph_id.sql: -------------------------------------------------------------------------------- 1 | SELECT 2 | paragraph.*, 3 | dataset."name" AS "dataset_name", 4 | "document"."name" AS "document_name", 5 | "document"."hit_handling_method" AS "hit_handling_method", 6 | "document"."directly_return_similarity" as "directly_return_similarity" 7 | FROM 8 | paragraph paragraph 9 | LEFT JOIN dataset dataset ON dataset."id" = paragraph.dataset_id 10 | LEFT JOIN "document" "document" ON "document"."id" =paragraph.document_id -------------------------------------------------------------------------------- /ui/src/api/provider.ts: -------------------------------------------------------------------------------- 1 | import { Result } from '@/request/Result' 2 | import { get, post } from '@/request/index' 3 | import type { Ref } from 'vue' 4 | const trigger: ( 5 | provider: string, 6 | method: string, 7 | request_body: any, 8 | loading?: Ref 9 | ) => Promise | string>> = (provider, method, request_body, loading) => { 10 | return post(`provider/${provider}/${method}`, {}, request_body, loading) 11 | } 12 | export default { trigger, get } 13 | -------------------------------------------------------------------------------- /ui/src/assets/icon_web.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /conf/config_example.yml: -------------------------------------------------------------------------------- 1 | # 邮箱配置 2 | EMAIL_ADDRESS: 3 | EMAIL_USE_TLS: False 4 | EMAIL_USE_SSL: True 5 | EMAIL_HOST: smtp.qq.com 6 | EMAIL_PORT: 465 7 | EMAIL_HOST_USER: 8 | EMAIL_HOST_PASSWORD: 9 | 10 | # 数据库链接信息 11 | DB_NAME: kb_builder 12 | DB_HOST: 192.168.30.28 13 | DB_PORT: 5433 14 | DB_USER: postgres 15 | DB_PASSWORD: 123456 16 | DB_ENGINE: django.db.backends.postgresql_psycopg2 17 | 18 | DEBUG: false 19 | 20 | EMBEDDING_MODEL_NAME: D:\project\m3e-base 21 | 22 | TIME_ZONE: Asia/Shanghai 23 | -------------------------------------------------------------------------------- /apps/common/auth/handle/auth_base_handle.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: qabot 4 | @Author:虎 5 | @file: authenticate.py 6 | @date:2024/3/14 03:02 7 | @desc: 认证处理器 8 | """ 9 | from abc import ABC, abstractmethod 10 | 11 | 12 | class AuthBaseHandle(ABC): 13 | @abstractmethod 14 | def support(self, request, token: str, get_token_details): 15 | pass 16 | 17 | @abstractmethod 18 | def handle(self, request, token: str, get_token_details): 19 | pass 20 | -------------------------------------------------------------------------------- /apps/embedding/sql/hit_test.sql: -------------------------------------------------------------------------------- 1 | SELECT 2 | paragraph_id, 3 | comprehensive_score, 4 | comprehensive_score as similarity 5 | FROM 6 | ( 7 | SELECT DISTINCT ON 8 | ("paragraph_id") ( similarity ),* ,similarity AS comprehensive_score 9 | FROM 10 | ( SELECT *, ( 1 - ( embedding.embedding <=> %s ) ) AS similarity FROM embedding ${embedding_query} ) TEMP 11 | ORDER BY 12 | paragraph_id, 13 | similarity DESC 14 | ) DISTINCT_TEMP 15 | WHERE comprehensive_score>%s 16 | ORDER BY comprehensive_score DESC 17 | LIMIT %s -------------------------------------------------------------------------------- /apps/application/sql/list_application.sql: -------------------------------------------------------------------------------- 1 | SELECT *,to_json(dataset_setting) as dataset_setting,to_json(model_setting) as model_setting FROM ( SELECT * FROM application ${application_custom_sql} UNION 2 | SELECT 3 | * 4 | FROM 5 | application 6 | WHERE 7 | application."id" IN ( SELECT team_member_permission.target FROM team_member team_member LEFT JOIN team_member_permission team_member_permission ON team_member_permission.member_id = team_member."id" ${team_member_permission_custom_sql}) 8 | ) temp_application ${default_sql} -------------------------------------------------------------------------------- /apps/common/mixins/app_model_mixin.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: maxkb 4 | @Author:虎 5 | @file: app_model_mixin.py 6 | @date:2023/9/21 9:41 7 | @desc: 8 | """ 9 | from django.db import models 10 | 11 | 12 | class AppModelMixin(models.Model): 13 | create_time = models.DateTimeField(verbose_name="创建时间", auto_now_add=True) 14 | update_time = models.DateTimeField(verbose_name="修改时间", auto_now=True) 15 | 16 | class Meta: 17 | abstract = True 18 | ordering = ['create_time'] 19 | -------------------------------------------------------------------------------- /apps/embedding/sql/embedding_search.sql: -------------------------------------------------------------------------------- 1 | SELECT 2 | paragraph_id, 3 | comprehensive_score, 4 | comprehensive_score as similarity 5 | FROM 6 | ( 7 | SELECT DISTINCT ON 8 | ("paragraph_id") ( similarity ),* ,similarity AS comprehensive_score 9 | FROM 10 | ( SELECT *, ( 1 - ( embedding.embedding <=> %s ) ) AS similarity FROM embedding ${embedding_query}) TEMP 11 | ORDER BY 12 | paragraph_id, 13 | similarity DESC 14 | ) DISTINCT_TEMP 15 | WHERE comprehensive_score>%s 16 | ORDER BY comprehensive_score DESC 17 | LIMIT %s -------------------------------------------------------------------------------- /apps/application/migrations/0002_chat_client_id.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.1.13 on 2024-03-28 13:59 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('application', '0001_initial'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='chat', 15 | name='client_id', 16 | field=models.UUIDField(default=None, null=True, verbose_name='客户端id'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /apps/common/mixins/api_mixin.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: smart-doc 4 | @Author:虎 5 | @file: api_mixin.py 6 | @date:2023/9/14 17:50 7 | @desc: 8 | """ 9 | from rest_framework import serializers 10 | 11 | 12 | class ApiMixin(serializers.Serializer): 13 | 14 | @staticmethod 15 | def get_request_params_api(): 16 | pass 17 | 18 | @staticmethod 19 | def get_request_body_api(): 20 | pass 21 | 22 | @staticmethod 23 | def get_response_body_api(): 24 | pass 25 | -------------------------------------------------------------------------------- /ui/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | declare module 'element-plus/dist/locale/zh-cn.mjs' 3 | declare module 'markdown-it-task-lists' 4 | declare module 'markdown-it-abbr' 5 | declare module 'markdown-it-anchor' 6 | declare module 'markdown-it-footnote' 7 | declare module 'markdown-it-sub' 8 | declare module 'markdown-it-sup' 9 | declare module 'markdown-it-toc-done-right' 10 | declare module 'katex' 11 | interface ImportMeta { 12 | readonly env: ImportMetaEnv 13 | } 14 | declare type Recordable = Record; 15 | -------------------------------------------------------------------------------- /apps/common/handle/base_split_handle.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: maxkb 4 | @Author:虎 5 | @file: base_split_handle.py 6 | @date:2024/3/27 18:13 7 | @desc: 8 | """ 9 | from abc import ABC, abstractmethod 10 | from typing import List 11 | 12 | 13 | class BaseSplitHandle(ABC): 14 | @abstractmethod 15 | def support(self, file, get_buffer): 16 | pass 17 | 18 | @abstractmethod 19 | def handle(self, file, pattern_list: List, with_filter: bool, limit: int, get_buffer, save_image): 20 | pass 21 | -------------------------------------------------------------------------------- /apps/dataset/migrations/0006_alter_paragraph_content.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.1.13 on 2024-09-21 11:07 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('dataset', '0005_alter_dataset_type_child'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='paragraph', 15 | name='content', 16 | field=models.CharField(max_length=102400, verbose_name='段落内容'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /apps/setting/migrations/0004_alter_model_credential.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.1.13 on 2024-04-28 18:06 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('setting', '0003_model_meta_model_status'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='model', 15 | name='credential', 16 | field=models.CharField(max_length=102400, verbose_name='模型认证信息'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /apps/embedding/sql/keywords_search.sql: -------------------------------------------------------------------------------- 1 | SELECT 2 | paragraph_id, 3 | comprehensive_score, 4 | comprehensive_score as similarity 5 | FROM 6 | ( 7 | SELECT DISTINCT ON 8 | ("paragraph_id") ( similarity ),* ,similarity AS comprehensive_score 9 | FROM 10 | ( SELECT *,ts_rank_cd(embedding.search_vector,websearch_to_tsquery('simple',%s),32) AS similarity FROM embedding ${keywords_query}) TEMP 11 | ORDER BY 12 | paragraph_id, 13 | similarity DESC 14 | ) DISTINCT_TEMP 15 | WHERE comprehensive_score>%s 16 | ORDER BY comprehensive_score DESC 17 | LIMIT %s -------------------------------------------------------------------------------- /apps/application/migrations/0003_application_icon.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.1.13 on 2024-04-23 11:16 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('application', '0002_chat_client_id'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='application', 15 | name='icon', 16 | field=models.CharField(default='/ui/favicon.ico', max_length=256, verbose_name='应用icon'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /apps/common/forms/base_form.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: maxkb 4 | @Author:虎 5 | @file: base_form.py 6 | @date:2023/11/1 16:04 7 | @desc: 8 | """ 9 | from common.forms import BaseField 10 | 11 | 12 | class BaseForm: 13 | def to_form_list(self): 14 | return [{**self.__getattribute__(key).to_dict(), 'field': key} for key in 15 | list(filter(lambda key: isinstance(self.__getattribute__(key), BaseField), 16 | [attr for attr in vars(self.__class__) if not attr.startswith("__")]))] 17 | -------------------------------------------------------------------------------- /ui/src/styles/md-editor.scss: -------------------------------------------------------------------------------- 1 | .md-editor-preview { 2 | padding: 0; 3 | margin: 0; 4 | font-size: inherit; 5 | p { 6 | padding: 0 !important; 7 | } 8 | .md-editor-admonition { 9 | margin: 0; 10 | padding: 0; 11 | } 12 | img { 13 | border: 0 !important; 14 | } 15 | } 16 | 17 | .md-editor-preview-wrapper { 18 | padding: 0; 19 | } 20 | 21 | .md-editor-footer { 22 | height: auto !important; 23 | } 24 | 25 | .ͼ1 .cm-placeholder { 26 | color: var(--app-input-color-placeholder); 27 | font-size: 14px; 28 | font-weight: 400; 29 | } 30 | -------------------------------------------------------------------------------- /ui/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | require('@rushstack/eslint-patch/modern-module-resolution') 3 | 4 | module.exports = { 5 | root: true, 6 | 'extends': [ 7 | 'plugin:vue/vue3-essential', 8 | 'eslint:recommended', 9 | '@vue/eslint-config-typescript', 10 | '@vue/eslint-config-prettier/skip-formatting' 11 | ], 12 | parserOptions: { 13 | ecmaVersion: 'latest' 14 | }, 15 | rules: { 16 | // 添加组件命名忽略规则 17 | "vue/multi-word-component-names": ["error", { 18 | "ignores": ["index", "main"]//需要忽略的组件名 19 | }] 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /apps/application/migrations/0004_applicationaccesstoken_show_source.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.1.13 on 2024-04-25 11:28 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('application', '0003_application_icon'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='applicationaccesstoken', 15 | name='show_source', 16 | field=models.BooleanField(default=False, verbose_name='是否显示知识来源'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /apps/dataset/migrations/0004_document_directly_return_similarity.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.1.13 on 2024-05-08 16:43 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('dataset', '0003_document_hit_handling_method'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='document', 15 | name='directly_return_similarity', 16 | field=models.FloatField(default=0.9, verbose_name='直接回答相似度'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /apps/application/migrations/0007_alter_application_prologue.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.1.13 on 2024-06-27 16:19 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [ 8 | ("application", "0006_applicationapikey_allow_cross_domain_and_more"), 9 | ] 10 | 11 | operations = [ 12 | migrations.AlterField( 13 | model_name="application", 14 | name="prologue", 15 | field=models.CharField(default="", max_length=4096, verbose_name="开场白"), 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /apps/application/sql/list_application_dataset.sql: -------------------------------------------------------------------------------- 1 | SELECT 2 | * 3 | FROM 4 | dataset 5 | WHERE 6 | user_id = %s UNION 7 | SELECT 8 | * 9 | FROM 10 | dataset 11 | WHERE 12 | "id" IN ( 13 | SELECT 14 | team_member_permission.target 15 | FROM 16 | team_member team_member 17 | LEFT JOIN team_member_permission team_member_permission ON team_member_permission.member_id = team_member."id" 18 | WHERE 19 | ( "team_member_permission"."auth_target_type" = 'DATASET' AND "team_member_permission"."operate"::text[] @> ARRAY['USE'] AND team_member.team_id = %s AND team_member.user_id =%s ) 20 | ) -------------------------------------------------------------------------------- /installer/Dockerfile-python-pg: -------------------------------------------------------------------------------- 1 | FROM postgres:15.6-bookworm 2 | 3 | ARG DEPENDENCIES=" \ 4 | curl \ 5 | vim \ 6 | python3.11-mini \ 7 | python3.11-venv \ 8 | postgresql-15-pgvector" 9 | 10 | RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \ 11 | echo "Asia/Shanghai" > /etc/timezone && \ 12 | apt-get update && apt-get install -y --no-install-recommends $DEPENDENCIES && \ 13 | apt-get clean all && \ 14 | rm -rf /var/lib/apt/lists/* -------------------------------------------------------------------------------- /ui/src/components/dynamics-form/items/table/TableColumn.vue: -------------------------------------------------------------------------------- 1 | 4 | 22 | 23 | -------------------------------------------------------------------------------- /apps/dataset/sql/list_dataset_application.sql: -------------------------------------------------------------------------------- 1 | SELECT 2 | * 3 | FROM 4 | application 5 | WHERE 6 | user_id = %s UNION 7 | SELECT 8 | * 9 | FROM 10 | application 11 | WHERE 12 | "id" IN ( 13 | SELECT 14 | team_member_permission.target 15 | FROM 16 | team_member team_member 17 | LEFT JOIN team_member_permission team_member_permission ON team_member_permission.member_id = team_member."id" 18 | WHERE 19 | ( "team_member_permission"."auth_target_type" = 'APPLICATION' AND "team_member_permission"."operate"::text[] @> ARRAY['USE'] AND team_member.team_id = %s AND team_member.user_id =%s ) 20 | ) -------------------------------------------------------------------------------- /apps/setting/migrations/0005_model_permission_type.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.1.13 on 2024-11-04 16:28 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('setting', '0004_alter_model_credential'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='model', 15 | name='permission_type', 16 | field=models.CharField(choices=[('PUBLIC', '公开'), ('PRIVATE', '私有')], default='PRIVATE', max_length=20, verbose_name='权限类型'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /apps/dataset/migrations/0003_document_hit_handling_method.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.1.13 on 2024-04-24 15:36 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('dataset', '0002_image'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='document', 15 | name='hit_handling_method', 16 | field=models.CharField(choices=[('optimization', '模型优化'), ('directly_return', '直接返回')], default='optimization', max_length=20, verbose_name='命中处理方式'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /apps/setting/sql/get_member_permission.sql: -------------------------------------------------------------------------------- 1 | SELECT 2 | app_or_dataset.*, 3 | team_member_permission.member_id, 4 | team_member_permission.operate 5 | FROM 6 | ( 7 | SELECT 8 | "id", 9 | "name", 10 | 'DATASET' AS "type", 11 | user_id 12 | FROM 13 | dataset 14 | WHERE 15 | "user_id" = %s UNION 16 | SELECT 17 | "id", 18 | "name", 19 | 'APPLICATION' AS "type", 20 | user_id 21 | FROM 22 | application 23 | WHERE 24 | "user_id" = %s 25 | ) app_or_dataset 26 | LEFT JOIN ( SELECT * FROM team_member_permission WHERE member_id = %s ) team_member_permission ON team_member_permission.target = app_or_dataset."id" -------------------------------------------------------------------------------- /installer/config.yaml: -------------------------------------------------------------------------------- 1 | # 邮箱配置 2 | EMAIL_ADDRESS: ${EMAIL_ADDRESS} 3 | EMAIL_USE_TLS: ${EMAIL_USE_TLS} 4 | EMAIL_USE_SSL: ${EMAIL_USE_SSL} 5 | EMAIL_HOST: ${EMAIL_HOST} 6 | EMAIL_PORT: ${EMAIL_PORT} 7 | EMAIL_HOST_USER: ${EMAIL_HOST_USER} 8 | EMAIL_HOST_PASSWORD: ${EMAIL_HOST_PASSWORD} 9 | 10 | # 数据库链接信息 11 | DB_NAME: maxkb 12 | DB_HOST: 127.0.0.1 13 | DB_PORT: 5433 14 | DB_USER: root 15 | DB_PASSWORD: Password123@postgres 16 | DB_ENGINE: django.db.backends.postgresql_psycopg2 17 | EMBEDDING_MODEL_PATH: /opt/maxkb/model/embedding 18 | EMBEDDING_MODEL_NAME: /opt/maxkb/model/embedding/shibing624_text2vec-base-chinese 19 | 20 | DEBUG: true -------------------------------------------------------------------------------- /apps/application/sql/chat_record_count.sql: -------------------------------------------------------------------------------- 1 | SELECT SUM 2 | ( CASE WHEN application_chat_record.vote_status = '0' THEN 1 ELSE 0 END ) AS "star_num", 3 | SUM ( CASE WHEN application_chat_record.vote_status = '1' THEN 1 ELSE 0 END ) AS "trample_num", 4 | SUM ( application_chat_record.message_tokens + application_chat_record.answer_tokens ) as "tokens_num", 5 | "count"(DISTINCT application_chat.client_id) customer_num, 6 | "count"(application_chat_record."id") as chat_record_count 7 | FROM 8 | application_chat_record application_chat_record 9 | LEFT JOIN application_chat application_chat ON application_chat."id" = application_chat_record.chat_id -------------------------------------------------------------------------------- /apps/common/forms/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: maxkb 4 | @Author:虎 5 | @file: __init__.py 6 | @date:2023/10/31 17:56 7 | @desc: 8 | """ 9 | from .array_object_card import * 10 | from .base_field import * 11 | from .base_form import * 12 | from .multi_select import * 13 | from .object_card import * 14 | from .password_input import * 15 | from .radio_field import * 16 | from .single_select_field import * 17 | from .tab_card import * 18 | from .table_radio import * 19 | from .text_input_field import * 20 | from .radio_button_field import * 21 | from .table_checkbox import * 22 | from .radio_card_field import * 23 | -------------------------------------------------------------------------------- /apps/common/event/common.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: maxkb 4 | @Author:虎 5 | @file: common.py 6 | @date:2023/11/10 10:41 7 | @desc: 8 | """ 9 | from concurrent.futures import ThreadPoolExecutor 10 | 11 | work_thread_pool = ThreadPoolExecutor(5) 12 | 13 | embedding_thread_pool = ThreadPoolExecutor(3) 14 | 15 | 16 | def poxy(poxy_function): 17 | def inner(args): 18 | work_thread_pool.submit(poxy_function, args) 19 | 20 | return inner 21 | 22 | 23 | def embedding_poxy(poxy_function): 24 | def inner(args): 25 | embedding_thread_pool.submit(poxy_function, args) 26 | 27 | return inner 28 | -------------------------------------------------------------------------------- /apps/common/config/swagger_conf.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: qabot 4 | @Author:虎 5 | @file: swagger_conf.py 6 | @date:2023/9/5 14:01 7 | @desc: 用于swagger 分组 8 | """ 9 | 10 | from drf_yasg.inspectors import SwaggerAutoSchema 11 | 12 | tags_dict = { 13 | 'user': '用户' 14 | } 15 | 16 | 17 | class CustomSwaggerAutoSchema(SwaggerAutoSchema): 18 | def get_tags(self, operation_keys=None): 19 | tags = super().get_tags(operation_keys) 20 | if "api" in tags and operation_keys: 21 | return [tags_dict.get(operation_keys[1]) if operation_keys[1] in tags_dict else operation_keys[1]] 22 | return tags 23 | -------------------------------------------------------------------------------- /ui/src/views/chat/index.vue: -------------------------------------------------------------------------------- 1 | 4 | 23 | 24 | -------------------------------------------------------------------------------- /apps/common/event/__init__.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: maxkb 4 | @Author:虎 5 | @file: __init__.py 6 | @date:2023/11/10 10:43 7 | @desc: 8 | """ 9 | import setting.models 10 | from setting.models import Model 11 | from .listener_manage import * 12 | 13 | 14 | def run(): 15 | listener_manage.ListenerManagement().run() 16 | QuerySet(Document).filter(status=Status.embedding).update(**{'status': Status.error}) 17 | QuerySet(Model).filter(status=setting.models.Status.DOWNLOAD).update(status=setting.models.Status.ERROR, 18 | meta={'message': "下载程序被中断,请重试"}) 19 | -------------------------------------------------------------------------------- /apps/embedding/sql/blend_search.sql: -------------------------------------------------------------------------------- 1 | SELECT 2 | paragraph_id, 3 | comprehensive_score, 4 | comprehensive_score AS similarity 5 | FROM 6 | ( 7 | SELECT DISTINCT ON 8 | ( "paragraph_id" ) ( similarity ),* , 9 | similarity AS comprehensive_score 10 | FROM 11 | ( 12 | SELECT 13 | *, 14 | (( 1 - ( embedding.embedding <=> %s ) )+ts_rank_cd( embedding.search_vector, websearch_to_tsquery('simple', %s ), 32 )) AS similarity 15 | FROM 16 | embedding ${embedding_query} 17 | ) TEMP 18 | ORDER BY 19 | paragraph_id, 20 | similarity DESC 21 | ) DISTINCT_TEMP 22 | WHERE 23 | comprehensive_score >%s 24 | ORDER BY 25 | comprehensive_score DESC 26 | LIMIT %s -------------------------------------------------------------------------------- /apps/dataset/swagger_api/image_api.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: maxkb 4 | @Author:虎 5 | @file: image_api.py 6 | @date:2024/4/23 11:23 7 | @desc: 8 | """ 9 | from drf_yasg import openapi 10 | 11 | from common.mixins.api_mixin import ApiMixin 12 | 13 | 14 | class ImageApi(ApiMixin): 15 | @staticmethod 16 | def get_request_params_api(): 17 | return [openapi.Parameter(name='file', 18 | in_=openapi.IN_FORM, 19 | type=openapi.TYPE_FILE, 20 | required=True, 21 | description='上传图片文件') 22 | ] 23 | -------------------------------------------------------------------------------- /apps/setting/models/system_management.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | 3 | 4 | from django.db import models 5 | 6 | from common.mixins.app_model_mixin import AppModelMixin 7 | 8 | 9 | class SettingType(models.IntegerChoices): 10 | """系统设置类型""" 11 | EMAIL = 0, '邮箱' 12 | 13 | RSA = 1, "私钥秘钥" 14 | 15 | 16 | class SystemSetting(AppModelMixin): 17 | """ 18 | 系统设置 19 | """ 20 | type = models.IntegerField(primary_key=True, verbose_name='设置类型', choices=SettingType.choices, 21 | default=SettingType.EMAIL) 22 | 23 | meta = models.JSONField(verbose_name="配置数据", default=dict) 24 | 25 | class Meta: 26 | db_table = "system_setting" 27 | -------------------------------------------------------------------------------- /apps/application/sql/list_application_chat.sql: -------------------------------------------------------------------------------- 1 | SELECT 2 | * 3 | FROM 4 | application_chat application_chat 5 | LEFT JOIN ( 6 | SELECT COUNT 7 | ( "id" ) AS chat_record_count, 8 | SUM ( CASE WHEN "vote_status" = '0' THEN 1 ELSE 0 END ) AS star_num, 9 | SUM ( CASE WHEN "vote_status" = '1' THEN 1 ELSE 0 END ) AS trample_num, 10 | SUM ( CASE WHEN array_length( application_chat_record.improve_paragraph_id_list, 1 ) IS NULL THEN 0 ELSE array_length( application_chat_record.improve_paragraph_id_list, 1 ) END ) AS mark_sum, 11 | chat_id 12 | FROM 13 | application_chat_record 14 | GROUP BY 15 | application_chat_record.chat_id 16 | ) chat_record_temp ON application_chat."id" = chat_record_temp.chat_id -------------------------------------------------------------------------------- /ui/src/views/first/index.vue: -------------------------------------------------------------------------------- 1 | 16 | 19 | 20 | -------------------------------------------------------------------------------- /apps/dataset/migrations/0005_alter_dataset_type_child.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.1.13 on 2024-06-27 16:19 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [ 8 | ("dataset", "0004_document_directly_return_similarity"), 9 | ] 10 | 11 | operations = [ 12 | migrations.AlterField( 13 | model_name="dataset", 14 | name="type_child", 15 | field=models.CharField( 16 | choices=[("0", "通用类型"), ("1", "web站点类型")], 17 | default="0", 18 | max_length=1, 19 | verbose_name="是否为子知识库", 20 | ), 21 | ), 22 | ] 23 | -------------------------------------------------------------------------------- /apps/dataset/migrations/0007_document_extraction_status.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.1.13 on 2024-12-04 15:27 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [ 8 | ("dataset", "0006_alter_paragraph_content"), 9 | ] 10 | 11 | operations = [ 12 | migrations.AddField( 13 | model_name="document", 14 | name="extraction_status", 15 | field=models.CharField( 16 | choices=[("0", "导入中"), ("1", "已完成"), ("2", "导入失败")], 17 | default="0", 18 | max_length=1, 19 | verbose_name="提取状态", 20 | ), 21 | ), 22 | ] 23 | -------------------------------------------------------------------------------- /apps/smartdoc/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for apps project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/4.2/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'smartdoc.settings') 15 | 16 | application = get_wsgi_application() 17 | 18 | 19 | def post_handler(): 20 | from common import event 21 | from common import job 22 | event.run() 23 | event.ListenerManagement.init_embedding_model_signal.send() 24 | job.run() 25 | 26 | 27 | post_handler() 28 | -------------------------------------------------------------------------------- /ui/src/components/app-charts/index.vue: -------------------------------------------------------------------------------- 1 | 10 | 31 | -------------------------------------------------------------------------------- /apps/application/sql/chat_record_count_trend.sql: -------------------------------------------------------------------------------- 1 | SELECT SUM 2 | ( CASE WHEN application_chat_record.vote_status = '0' THEN 1 ELSE 0 END ) AS "star_num", 3 | SUM ( CASE WHEN application_chat_record.vote_status = '1' THEN 1 ELSE 0 END ) AS "trample_num", 4 | SUM ( application_chat_record.message_tokens + application_chat_record.answer_tokens ) as "tokens_num", 5 | "count"(application_chat_record."id") as chat_record_count, 6 | "count"(DISTINCT application_chat.client_id) customer_num, 7 | application_chat_record.create_time :: DATE as "day" 8 | FROM 9 | application_chat_record application_chat_record 10 | LEFT JOIN application_chat application_chat ON application_chat."id" = application_chat_record.chat_id 11 | ${default_sql} 12 | GROUP BY "day" -------------------------------------------------------------------------------- /ui/src/stores/modules/common.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia' 2 | 3 | export interface commonTypes { 4 | breadcrumb: any 5 | paginationConfig: any | null 6 | search: any 7 | } 8 | 9 | const useCommonStore = defineStore({ 10 | id: 'common', 11 | state: (): commonTypes => ({ 12 | breadcrumb: null, 13 | // 搜索和分页缓存 14 | paginationConfig: {}, 15 | search: {} 16 | }), 17 | actions: { 18 | saveBreadcrumb(data: any) { 19 | this.breadcrumb = data 20 | }, 21 | savePage(val: string, data: any) { 22 | this.paginationConfig[val] = data 23 | }, 24 | saveCondition(val: string, data: any) { 25 | this.search[val] = data 26 | } 27 | } 28 | }) 29 | 30 | export default useCommonStore 31 | -------------------------------------------------------------------------------- /ui/src/directives/hasPermission.ts: -------------------------------------------------------------------------------- 1 | import type { App } from 'vue' 2 | import { hasPermission } from '@/utils/permission' 3 | 4 | const display = async (el: any, binding: any) => { 5 | const has = hasPermission( 6 | binding.value?.permission || binding.value, 7 | binding.value?.compare || 'OR' 8 | ) 9 | if (!has) { 10 | el.style.display = 'none' 11 | } else { 12 | delete el.style.display 13 | } 14 | } 15 | 16 | export default { 17 | install: (app: App) => { 18 | app.directive('hasPermission', { 19 | async created(el: any, binding: any) { 20 | display(el, binding) 21 | }, 22 | async beforeUpdate(el: any, binding: any) { 23 | display(el, binding) 24 | } 25 | }) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /apps/users/migrations/0002_user_create_time_user_update_time.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.1.13 on 2024-03-20 12:27 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('users', '0001_initial'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='user', 15 | name='create_time', 16 | field=models.DateTimeField(auto_now_add=True, null=True, verbose_name='创建时间'), 17 | ), 18 | migrations.AddField( 19 | model_name='user', 20 | name='update_time', 21 | field=models.DateTimeField(auto_now=True, null=True, verbose_name='修改时间'), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /apps/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | 7 | def main(): 8 | """Run administrative tasks.""" 9 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'smartdoc.settings') 10 | try: 11 | from django.core.management import execute_from_command_line 12 | except ImportError as exc: 13 | raise ImportError( 14 | "Couldn't import Django. Are you sure it's installed and " 15 | "available on your PYTHONPATH environment variable? Did you " 16 | "forget to activate a virtual environment?" 17 | ) from exc 18 | execute_from_command_line(sys.argv) 19 | 20 | 21 | if __name__ == '__main__': 22 | main() 23 | -------------------------------------------------------------------------------- /ui/src/utils/permission/type.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 角色对象 3 | */ 4 | export class Role { 5 | role: string 6 | 7 | constructor(role: string) { 8 | this.role = role 9 | } 10 | } 11 | /** 12 | * 权限对象 13 | */ 14 | export class Permission { 15 | permission: string 16 | 17 | constructor(permission: string) { 18 | this.permission = permission 19 | } 20 | } 21 | /** 22 | * 复杂权限对象 23 | */ 24 | export class ComplexPermission { 25 | roleList: Array 26 | 27 | permissionList: Array 28 | 29 | compare: 'OR' | 'AND' 30 | 31 | constructor(roleList: Array, permissionList: Array, compare: 'OR' | 'AND') { 32 | this.roleList = roleList 33 | this.permissionList = permissionList 34 | this.compare = compare 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /apps/common/config/tokenizer_manage_config.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: maxkb 4 | @Author:虎 5 | @file: tokenizer_manage_config.py 6 | @date:2024/4/28 10:17 7 | @desc: 8 | """ 9 | 10 | 11 | class TokenizerManage: 12 | tokenizer = None 13 | 14 | @staticmethod 15 | def get_tokenizer(): 16 | from transformers import GPT2TokenizerFast 17 | if TokenizerManage.tokenizer is None: 18 | TokenizerManage.tokenizer = GPT2TokenizerFast.from_pretrained( 19 | 'gpt2', 20 | cache_dir="/opt/kb_builder/model/tokenizer", 21 | local_files_only=True, 22 | resume_download=False, 23 | force_download=False) 24 | return TokenizerManage.tokenizer 25 | -------------------------------------------------------------------------------- /ui/src/utils/common.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 拆分数组 每n个拆分为一个数组 3 | * @param sourceDataList 资源数据 4 | * @param splitNum 每多少个拆分为一个数组 5 | * @returns 拆分后数组 6 | */ 7 | export function splitArray(sourceDataList: Array, splitNum: number) { 8 | const count = 9 | sourceDataList.length % splitNum == 0 10 | ? sourceDataList.length / splitNum 11 | : sourceDataList.length / splitNum + 1 12 | const arrayList: Array> = [] 13 | for (let i = 0; i < count; i++) { 14 | let index = i * splitNum 15 | const list: Array = [] 16 | let j = 0 17 | while (j < splitNum && index < sourceDataList.length) { 18 | list.push(sourceDataList[index++]) 19 | j++ 20 | } 21 | arrayList.push(list) 22 | } 23 | return arrayList 24 | } 25 | -------------------------------------------------------------------------------- /apps/application/migrations/0005_alter_chat_abstract_alter_chatrecord_answer_text.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.1.13 on 2024-04-29 13:33 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('application', '0004_applicationaccesstoken_show_source'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='chat', 15 | name='abstract', 16 | field=models.CharField(max_length=1024, verbose_name='摘要'), 17 | ), 18 | migrations.AlterField( 19 | model_name='chatrecord', 20 | name='answer_text', 21 | field=models.CharField(max_length=40960, verbose_name='答案'), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /ui/src/layout/components/app-main/index.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 26 | -------------------------------------------------------------------------------- /ui/src/layout/components/top-bar/top-menu/index.vue: -------------------------------------------------------------------------------- 1 | 7 | 24 | 30 | -------------------------------------------------------------------------------- /ui/src/views/index/components/index-main/index.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 26 | -------------------------------------------------------------------------------- /ui/src/assets/doc-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /ui/src/assets/docx-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /apps/setting/models_provider/impl/openai_model_provider/model/embedding.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: MaxKB 4 | @Author:虎 5 | @file: embedding.py 6 | @date:2024/7/12 17:44 7 | @desc: 8 | """ 9 | from typing import Dict 10 | 11 | from langchain_community.embeddings import OpenAIEmbeddings 12 | 13 | from setting.models_provider.base_model_provider import MaxKBBaseModel 14 | 15 | 16 | class OpenAIEmbeddingModel(MaxKBBaseModel, OpenAIEmbeddings): 17 | @staticmethod 18 | def new_instance(model_type, model_name, model_credential: Dict[str, object], **model_kwargs): 19 | return OpenAIEmbeddingModel( 20 | api_key=model_credential.get('api_key'), 21 | model=model_name, 22 | openai_api_base=model_credential.get('api_base'), 23 | ) 24 | -------------------------------------------------------------------------------- /apps/setting/sql/check_member_permission_target_exists.sql: -------------------------------------------------------------------------------- 1 | SELECT 2 | static_temp."target_id"::text 3 | FROM 4 | (SELECT * FROM json_to_recordset( 5 | %s 6 | ) AS x(target_id uuid,type text)) static_temp 7 | LEFT JOIN ( 8 | SELECT 9 | "id", 10 | 'DATASET' AS "type", 11 | user_id, 12 | ARRAY [ 'MANAGE', 13 | 'USE', 14 | 'DELETE' ] AS "operate" 15 | FROM 16 | dataset 17 | WHERE 18 | "user_id" = %s UNION 19 | SELECT 20 | "id", 21 | 'APPLICATION' AS "type", 22 | user_id, 23 | ARRAY [ 'MANAGE', 24 | 'USE', 25 | 'DELETE' ] AS "operate" 26 | FROM 27 | application 28 | WHERE 29 | "user_id" = %s 30 | ) "app_and_dataset_temp" 31 | ON "app_and_dataset_temp"."id" = static_temp."target_id" and app_and_dataset_temp."type"=static_temp."type" 32 | WHERE app_and_dataset_temp.id is NULL ; -------------------------------------------------------------------------------- /apps/common/forms/password_input.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: maxkb 4 | @Author:虎 5 | @file: password_input.py 6 | @date:2023/11/1 14:48 7 | @desc: 8 | """ 9 | from typing import Dict 10 | 11 | from common.forms import BaseField, TriggerType 12 | 13 | 14 | class PasswordInputField(BaseField): 15 | """ 16 | 文本输入框 17 | """ 18 | 19 | def __init__(self, label: str, 20 | required: bool = False, 21 | default_value=None, 22 | relation_show_field_dict: Dict = None, 23 | attrs=None, props_info=None): 24 | super().__init__('PasswordInput', label, required, default_value, relation_show_field_dict, 25 | {}, 26 | TriggerType.OPTION_LIST, attrs, props_info) 27 | -------------------------------------------------------------------------------- /apps/setting/migrations/0003_model_meta_model_status.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.1.13 on 2024-03-22 17:51 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('setting', '0002_systemsetting'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='model', 15 | name='meta', 16 | field=models.JSONField(default=dict, verbose_name='模型元数据,用于存储下载,或者错误信息'), 17 | ), 18 | migrations.AddField( 19 | model_name='model', 20 | name='status', 21 | field=models.CharField(choices=[('SUCCESS', '成功'), ('ERROR', '失败'), ('DOWNLOAD', '下载中')], default='SUCCESS', max_length=20, verbose_name='设置类型'), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /ui/src/layout/app-layout/index.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 15 | 39 | -------------------------------------------------------------------------------- /ui/src/common/font/iconfont.css: -------------------------------------------------------------------------------- 1 | /* 在线链接服务仅供平台体验和调试使用,平台不承诺服务的稳定性,企业客户需下载字体包自行发布使用并做好备份。 */ 2 | @font-face { 3 | font-family: 'iconfont'; /* Project id 4617240 */ 4 | src: 5 | url('https://at.alicdn.com/t/c/font_4617240_m61234eid3c.woff2?t=1736216455924') format('woff2'), 6 | url('https://at.alicdn.com/t/c/font_4617240_m61234eid3c.woff?t=1736216455924') format('woff'), 7 | url('https://at.alicdn.com/t/c/font_4617240_m61234eid3c.ttf?t=1736216455924') format('truetype'); 8 | } 9 | .iconfont { 10 | font-family: 'iconfont' !important; 11 | font-size: 16px; 12 | font-style: normal; 13 | -webkit-font-smoothing: antialiased; 14 | -moz-osx-font-smoothing: grayscale; 15 | } 16 | 17 | /* .plus { */ 18 | /* font-size: 56rpx; */ 19 | /* } */ 20 | 21 | .icon-jijianfasong:before { 22 | content: '\e893'; 23 | } 24 | -------------------------------------------------------------------------------- /apps/common/forms/text_input_field.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: maxkb 4 | @Author:虎 5 | @file: text_input_field.py 6 | @date:2023/10/31 17:58 7 | @desc: 8 | """ 9 | from typing import Dict 10 | 11 | from common.forms.base_field import BaseField, TriggerType 12 | 13 | 14 | class TextInputField(BaseField): 15 | """ 16 | 文本输入框 17 | """ 18 | 19 | def __init__(self, label: str, 20 | required: bool = False, 21 | default_value=None, 22 | relation_show_field_dict: Dict = None, 23 | 24 | attrs=None, props_info=None): 25 | super().__init__('TextInput', label, required, default_value, relation_show_field_dict, 26 | {}, 27 | TriggerType.OPTION_LIST, attrs, props_info) 28 | -------------------------------------------------------------------------------- /ui/src/components/dynamics-form/index.ts: -------------------------------------------------------------------------------- 1 | import type { App } from 'vue' 2 | import type { Dict } from '@/api/type/common' 3 | import DynamicsForm from '@/components/dynamics-form/index.vue' 4 | let components: Dict = import.meta.glob('@/components/dynamics-form/**/**.vue', { 5 | eager: true 6 | }) 7 | components = { 8 | ...components, 9 | ...import.meta.glob('@/components/dynamics-form/**/**/**.vue', { 10 | eager: true 11 | }) 12 | } 13 | 14 | const install = (app: App) => { 15 | Object.keys(components).forEach((key: string) => { 16 | const commentName: string = key 17 | .substring(key.lastIndexOf('/') + 1, key.length) 18 | .replace('.vue', '') 19 | app.component(commentName, components[key].default) 20 | }) 21 | app.component('DynamicsForm', DynamicsForm) 22 | } 23 | export default { install } 24 | -------------------------------------------------------------------------------- /apps/setting/sql/get_user_permission.sql: -------------------------------------------------------------------------------- 1 | SELECT 2 | "id", 3 | 'DATASET' AS "type", 4 | user_id, 5 | ARRAY [ 'MANAGE', 6 | 'USE','DELETE' ] AS "operate" 7 | FROM 8 | dataset 9 | WHERE 10 | "user_id" = %s UNION 11 | SELECT 12 | "id", 13 | 'APPLICATION' AS "type", 14 | user_id, 15 | ARRAY [ 'MANAGE', 16 | 'USE','DELETE' ] AS "operate" 17 | FROM 18 | application 19 | WHERE 20 | "user_id" = %s UNION 21 | SELECT 22 | team_member_permission.target AS "id", 23 | team_member_permission.auth_target_type AS "type", 24 | team_member.user_id AS user_id, 25 | team_member_permission.operate AS "operate" 26 | FROM 27 | team_member team_member 28 | LEFT JOIN team_member_permission team_member_permission ON team_member.ID = team_member_permission.member_id 29 | WHERE 30 | team_member.user_id = %s AND team_member_permission.target IS NOT NULL -------------------------------------------------------------------------------- /ui/src/assets/md-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /ui/src/components/back-button/index.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /ui/src/components/icons/AppIcon.vue: -------------------------------------------------------------------------------- 1 | 16 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /ui/src/main.ts: -------------------------------------------------------------------------------- 1 | import '@/styles/index.scss' 2 | import ElementPlus from 'element-plus' 3 | import * as ElementPlusIcons from '@element-plus/icons-vue' 4 | import zhCn from 'element-plus/dist/locale/zh-cn.mjs' 5 | import { createApp } from 'vue' 6 | import { store } from '@/stores' 7 | import theme from '@/theme' 8 | import directives from '@/directives' 9 | import App from './App.vue' 10 | import router from '@/router' 11 | import Components from '@/components' 12 | import i18n from './locales'; 13 | const app = createApp(App) 14 | app.use(store) 15 | app.use(directives) 16 | 17 | for (const [key, component] of Object.entries(ElementPlusIcons)) { 18 | app.component(key, component) 19 | } 20 | app.use(ElementPlus, { 21 | locale: zhCn 22 | }) 23 | 24 | app.use(theme) 25 | 26 | app.use(router) 27 | app.use(i18n); 28 | app.use(Components) 29 | app.mount('#app') 30 | -------------------------------------------------------------------------------- /ui/src/stores/index.ts: -------------------------------------------------------------------------------- 1 | import { createPinia } from 'pinia' 2 | const store = createPinia() 3 | export { store } 4 | import useCommonStore from './modules/common' 5 | import useUserStore from './modules/user' 6 | import useDatasetStore from './modules/dataset' 7 | import useParagraphStore from './modules/paragraph' 8 | import useModelStore from './modules/model' 9 | import useApplicationStore from './modules/application' 10 | import useDocumentStore from './modules/document' 11 | import useLogStore from './modules/log' 12 | 13 | const useStore = () => ({ 14 | common: useCommonStore(), 15 | user: useUserStore(), 16 | dataset: useDatasetStore(), 17 | paragraph: useParagraphStore(), 18 | model: useModelStore(), 19 | application: useApplicationStore(), 20 | document: useDocumentStore(), 21 | log: useLogStore(), 22 | }) 23 | 24 | export default useStore 25 | -------------------------------------------------------------------------------- /ui/src/request/Result.ts: -------------------------------------------------------------------------------- 1 | export class Result { 2 | message: string; 3 | code: number; 4 | data: T; 5 | length: number; 6 | constructor(message: string, code: number, data: T) { 7 | this.message = message; 8 | this.code = code; 9 | this.data = data; 10 | } 11 | 12 | static success(data: any) { 13 | return new Result("请求成功", 200, data); 14 | } 15 | static error(message: string, code: number) { 16 | return new Result(message, code, null); 17 | } 18 | } 19 | 20 | interface Page { 21 | /** 22 | *分页数据 23 | */ 24 | records: Array; 25 | /** 26 | *当前页 27 | */ 28 | current: number; 29 | /** 30 | * 每页展示size 31 | */ 32 | size: number; 33 | /** 34 | *总数 35 | */ 36 | total: number; 37 | /** 38 | *是否有下一页 39 | */ 40 | hasNext: boolean; 41 | } 42 | export type { Page }; 43 | export default Result; 44 | -------------------------------------------------------------------------------- /ui/src/layout/main-layout/index.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 15 | 36 | -------------------------------------------------------------------------------- /ui/src/locales/useLocale.ts: -------------------------------------------------------------------------------- 1 | import { useLocalStorage } from '@vueuse/core'; 2 | import { computed } from 'vue'; 3 | import { useI18n } from 'vue-i18n'; 4 | 5 | import { i18n, langCode, localeConfigKey } from '@/locales/index'; 6 | 7 | export function useLocale() { 8 | const { locale } = useI18n({ useScope: 'global' }); 9 | function changeLocale(lang: string) { 10 | // 如果切换的语言不在对应语言文件里则默认为简体中文 11 | if (!langCode.includes(lang)) { 12 | lang = 'zh_CN'; 13 | } 14 | 15 | locale.value = lang; 16 | useLocalStorage(localeConfigKey, 'zh_CN').value = lang; 17 | } 18 | 19 | const getComponentsLocale = computed(() => { 20 | return i18n.global.getLocaleMessage(locale.value).componentsLocale; 21 | }); 22 | 23 | return { 24 | changeLocale, 25 | getComponentsLocale, 26 | locale, 27 | }; 28 | } 29 | -------------------------------------------------------------------------------- /apps/setting/sql/get_member_permission_dataset.sql: -------------------------------------------------------------------------------- 1 | SELECT 2 | app_or_dataset.*, 3 | team_member_permission.member_id, 4 | team_member_permission.operate 5 | FROM 6 | ( 7 | SELECT 8 | "id", 9 | "name", 10 | 'DATASET' AS "type", 11 | d.type_child, -- 新增:从 dataset 表中获取 type_child 字段 12 | "child_id", 13 | "app_id", 14 | user_id 15 | FROM 16 | dataset d -- 修改:为 dataset 表添加别名 "d" 17 | WHERE 18 | "user_id" = %s 19 | UNION 20 | SELECT 21 | "id", 22 | "name", 23 | 'APPLICATION' AS "type", 24 | NULL AS type_child, -- 新增:为 application 类型添加 type_child 字段,并赋值为 NULL 25 | NULL AS child_id, 26 | NULL AS app_id, 27 | user_id 28 | FROM 29 | application 30 | WHERE 31 | "user_id" = %s 32 | ) app_or_dataset 33 | LEFT JOIN ( SELECT * FROM team_member_permission WHERE member_id = %s ) team_member_permission ON team_member_permission.target = app_or_dataset."id" -------------------------------------------------------------------------------- /ui/src/assets/upload-icon1.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /apps/setting/models_provider/impl/kimi_model_provider/model/kimi_chat_model.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: maxkb 4 | @Author:虎 5 | @file: kimi_chat_model.py 6 | @date:2023/11/10 17:45 7 | @desc: 8 | """ 9 | from typing import List 10 | 11 | from langchain_community.chat_models import ChatOpenAI 12 | from langchain_core.messages import BaseMessage, get_buffer_string 13 | 14 | from common.config.tokenizer_manage_config import TokenizerManage 15 | 16 | 17 | class KimiChatModel(ChatOpenAI): 18 | def get_num_tokens_from_messages(self, messages: List[BaseMessage]) -> int: 19 | tokenizer = TokenizerManage.get_tokenizer() 20 | return sum([len(tokenizer.encode(get_buffer_string([m]))) for m in messages]) 21 | 22 | def get_num_tokens(self, text: str) -> int: 23 | tokenizer = TokenizerManage.get_tokenizer() 24 | return len(tokenizer.encode(text)) 25 | -------------------------------------------------------------------------------- /apps/setting/models_provider/impl/local_model_provider/model/embedding.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: MaxKB 4 | @Author:虎 5 | @file: embedding.py 6 | @date:2024/7/11 14:06 7 | @desc: 8 | """ 9 | from typing import Dict 10 | 11 | from langchain_huggingface import HuggingFaceEmbeddings 12 | 13 | from setting.models_provider.base_model_provider import MaxKBBaseModel 14 | 15 | 16 | class LocalEmbedding(MaxKBBaseModel, HuggingFaceEmbeddings): 17 | @staticmethod 18 | def new_instance(model_type, model_name, model_credential: Dict[str, object], **model_kwargs): 19 | return LocalEmbedding(model_name=model_name, cache_folder=model_credential.get('cache_folder'), 20 | model_kwargs={'device': model_credential.get('device')}, 21 | encode_kwargs={'normalize_embeddings': True}, 22 | ) 23 | -------------------------------------------------------------------------------- /apps/setting/models_provider/impl/qwen_model_provider/model/qwen_chat_model.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: maxkb 4 | @Author:虎 5 | @file: qwen_chat_model.py 6 | @date:2024/4/28 11:44 7 | @desc: 8 | """ 9 | from typing import List 10 | 11 | from langchain_community.chat_models import ChatTongyi 12 | from langchain_core.messages import BaseMessage, get_buffer_string 13 | 14 | from common.config.tokenizer_manage_config import TokenizerManage 15 | 16 | 17 | class QwenChatModel(ChatTongyi): 18 | def get_num_tokens_from_messages(self, messages: List[BaseMessage]) -> int: 19 | tokenizer = TokenizerManage.get_tokenizer() 20 | return sum([len(tokenizer.encode(get_buffer_string([m]))) for m in messages]) 21 | 22 | def get_num_tokens(self, text: str) -> int: 23 | tokenizer = TokenizerManage.get_tokenizer() 24 | return len(tokenizer.encode(text)) 25 | -------------------------------------------------------------------------------- /apps/setting/models_provider/impl/zhipu_model_provider/model/zhipu_chat_model.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: maxkb 4 | @Author:虎 5 | @file: zhipu_chat_model.py 6 | @date:2024/4/28 11:42 7 | @desc: 8 | """ 9 | from typing import List 10 | 11 | from langchain_community.chat_models import ChatZhipuAI 12 | from langchain_core.messages import BaseMessage, get_buffer_string 13 | 14 | from common.config.tokenizer_manage_config import TokenizerManage 15 | 16 | 17 | class ZhipuChatModel(ChatZhipuAI): 18 | def get_num_tokens_from_messages(self, messages: List[BaseMessage]) -> int: 19 | tokenizer = TokenizerManage.get_tokenizer() 20 | return sum([len(tokenizer.encode(get_buffer_string([m]))) for m in messages]) 21 | 22 | def get_num_tokens(self, text: str) -> int: 23 | tokenizer = TokenizerManage.get_tokenizer() 24 | return len(tokenizer.encode(text)) 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature.yml: -------------------------------------------------------------------------------- 1 | name: 需求建议 2 | description: 提出针对本项目的想法和建议 3 | title: "[FEATURE]" 4 | labels: enhancement 5 | assignees: baixin513 6 | body: 7 | - type: markdown 8 | id: environment 9 | attributes: 10 | value: "## 环境信息" 11 | - type: input 12 | id: version 13 | validations: 14 | required: true 15 | attributes: 16 | label: "KB Builder 版本" 17 | description: "登录 KB Builder Web 控制台,在右上角关于页面查看当前版本。" 18 | - type: markdown 19 | id: details 20 | attributes: 21 | value: "## 详细信息" 22 | - type: textarea 23 | id: description 24 | attributes: 25 | label: "请描述您的需求或者改进建议" 26 | validations: 27 | required: true 28 | - type: textarea 29 | id: solution 30 | attributes: 31 | label: "请描述你建议的实现方案" 32 | - type: textarea 33 | id: additional-information 34 | attributes: 35 | label: "附加信息" 36 | description: "如果你还有其他需要提供的信息,可以在这里填写(可以提供截图、视频等)。" -------------------------------------------------------------------------------- /ui/src/assets/icon_send.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /apps/application/migrations/0006_applicationapikey_allow_cross_domain_and_more.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.1.13 on 2024-05-08 13:57 2 | 3 | import django.contrib.postgres.fields 4 | from django.db import migrations, models 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('application', '0005_alter_chat_abstract_alter_chatrecord_answer_text'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='applicationapikey', 16 | name='allow_cross_domain', 17 | field=models.BooleanField(default=False, verbose_name='是否允许跨域'), 18 | ), 19 | migrations.AddField( 20 | model_name='applicationapikey', 21 | name='cross_domain_list', 22 | field=django.contrib.postgres.fields.ArrayField(base_field=models.CharField(blank=True, max_length=128), default=list, size=None, verbose_name='跨域列表'), 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /apps/setting/migrations/0002_systemsetting.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.1.10 on 2024-03-19 16:51 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | dependencies = [ 8 | ('setting', '0001_initial'), 9 | ] 10 | 11 | operations = [ 12 | migrations.CreateModel( 13 | name='SystemSetting', 14 | fields=[ 15 | ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), 16 | ('update_time', models.DateTimeField(auto_now=True, verbose_name='修改时间')), 17 | ('type', models.IntegerField(choices=[(0, '邮箱'), (1, '私钥秘钥')], default=0, primary_key=True, serialize=False, verbose_name='设置类型')), 18 | ('meta', models.JSONField(default=dict, verbose_name='配置数据')), 19 | ], 20 | options={ 21 | 'db_table': 'system_setting', 22 | }, 23 | ), 24 | ] 25 | -------------------------------------------------------------------------------- /apps/common/sql/list_embedding_text.sql: -------------------------------------------------------------------------------- 1 | SELECT 2 | problem_paragraph_mapping."id" AS "source_id", 3 | paragraph.document_id AS document_id, 4 | paragraph."id" AS paragraph_id, 5 | problem.dataset_id AS dataset_id, 6 | 0 AS source_type, 7 | problem."content" AS "text", 8 | paragraph.is_active AS is_active 9 | FROM 10 | problem problem 11 | LEFT JOIN problem_paragraph_mapping problem_paragraph_mapping ON problem_paragraph_mapping.problem_id=problem."id" 12 | LEFT JOIN paragraph paragraph ON paragraph."id" = problem_paragraph_mapping.paragraph_id 13 | ${problem} 14 | 15 | UNION 16 | SELECT 17 | paragraph."id" AS "source_id", 18 | paragraph.document_id AS document_id, 19 | paragraph."id" AS paragraph_id, 20 | paragraph.dataset_id AS dataset_id, 21 | 1 AS source_type, 22 | concat_ws(' 23 | ',concat_ws(' 24 | ',paragraph.title,paragraph."content"),paragraph.title) AS "text", 25 | paragraph.is_active AS is_active 26 | FROM 27 | paragraph paragraph 28 | 29 | ${paragraph} -------------------------------------------------------------------------------- /apps/setting/models_provider/impl/local_model_provider/icon/local_icon_svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/setting/models_provider/impl/wenxin_model_provider/icon/azure_icon_svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /ui/src/stores/modules/model.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia' 2 | import modelApi from '@/api/model' 3 | import type { modelRequest, Provider } from '@/api/type/model' 4 | const useModelStore = defineStore({ 5 | id: 'model', 6 | state: () => ({}), 7 | actions: { 8 | async asyncGetModel(data?: modelRequest) { 9 | return new Promise((resolve, reject) => { 10 | modelApi 11 | .getModel(data) 12 | .then((res) => { 13 | resolve(res) 14 | }) 15 | .catch((error) => { 16 | reject(error) 17 | }) 18 | }) 19 | }, 20 | async asyncGetProvider() { 21 | return new Promise((resolve, reject) => { 22 | modelApi 23 | .getProvider() 24 | .then((res) => { 25 | resolve(res) 26 | }) 27 | .catch((error) => { 28 | reject(error) 29 | }) 30 | }) 31 | } 32 | } 33 | }) 34 | 35 | export default useModelStore 36 | -------------------------------------------------------------------------------- /apps/dataset/migrations/0002_image.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.1.13 on 2024-04-22 19:31 2 | 3 | from django.db import migrations, models 4 | import uuid 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('dataset', '0001_initial'), 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name='Image', 16 | fields=[ 17 | ('create_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), 18 | ('update_time', models.DateTimeField(auto_now=True, verbose_name='修改时间')), 19 | ('id', models.UUIDField(default=uuid.uuid1, editable=False, primary_key=True, serialize=False, verbose_name='主键id')), 20 | ('image', models.BinaryField(verbose_name='图片数据')), 21 | ('image_name', models.CharField(default='', max_length=256, verbose_name='图片名称')), 22 | ], 23 | options={ 24 | 'db_table': 'image', 25 | }, 26 | ), 27 | ] 28 | -------------------------------------------------------------------------------- /ui/src/components/tag-ellipsis/index.vue: -------------------------------------------------------------------------------- 1 | 8 | 14 | 37 | -------------------------------------------------------------------------------- /ui/src/assets/txt-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ui/src/assets/user-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /apps/.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 25 | -------------------------------------------------------------------------------- /ui/src/components/layout-container/index.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 23 | 24 | 45 | -------------------------------------------------------------------------------- /README.en.md: -------------------------------------------------------------------------------- 1 | # KB-Builder 2 | 3 | #### Description 4 | {**When you're done, you can delete the content in this README and update the file with details for others getting started with your repository**} 5 | 6 | #### Software Architecture 7 | Software architecture description 8 | 9 | #### Installation 10 | 11 | 1. xxxx 12 | 2. xxxx 13 | 3. xxxx 14 | 15 | #### Instructions 16 | 17 | 1. xxxx 18 | 2. xxxx 19 | 3. xxxx 20 | 21 | #### Contribution 22 | 23 | 1. Fork the repository 24 | 2. Create Feat_xxx branch 25 | 3. Commit your code 26 | 4. Create Pull Request 27 | 28 | 29 | #### Gitee Feature 30 | 31 | 1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md 32 | 2. Gitee blog [blog.gitee.com](https://blog.gitee.com) 33 | 3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore) 34 | 4. The most valuable open source project [GVP](https://gitee.com/gvp) 35 | 5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help) 36 | 6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) 37 | -------------------------------------------------------------------------------- /ui/src/prompts/DocRewritePrompts.ts: -------------------------------------------------------------------------------- 1 | export const prompts = [ 2 | { 3 | cueWord: '基于知识工程的文档改写', 4 | prompt: `你将接收到一组技术规格列表,请将这些结构化信息转换为自然语言描述。具体要求如下: 5 | 1. **识别数据主题**:从数据中识别出主要的产品类别和特性(如存储设备、接口类型等),并在自然语言描述中突出显示这些主题。 6 | 2. **转换为自然语言描述**:将列表中的每一项规格转换为完整的句子。确保句子结构清晰,且便于阅读。例如: 7 | - “设备支持4 TB PCIe Gen4x4 NVMe M.2固态硬盘” 可以转换为 “这款设备支持最高4 TB容量的PCIe Gen4x4 NVMe M.2固态硬盘,能够提供大容量和高速传输性能。” 8 | 3. **增加背景和解释**:为每个数据项提供必要的上下文。例如,解释某种技术的优点或适用场景,如“SSD采用TLC技术,适合需要大容量存储的用户”。尝试将数据的应用场景、优缺点或技术特点描述出来。 9 | 4. **灵活表达**:尽量使用不同的表达方式,避免单调和重复。你可以通过改变句子的结构或使用同义词来实现多样化的表达。 10 | 5. **确保逻辑连贯**:确保信息在整个段落中的表达是连贯的,避免单一罗列。将多个相关规格信息合并为一个具有连贯逻辑的段落。 11 | **示例格式**: 12 | 原始数据: 13 | - 4 TB PCIe Gen4x4 NVMe M.2 SSD TLC 14 | - 2 TB PCIe Gen4x4 NVMe SED SSD 15 | 16 | 转换后的描述: 17 | “这款设备支持高达4 TB容量的PCIe Gen4x4 NVMe M.2固态硬盘,采用TLC技术,提供卓越的性能和可靠性。此外,它还支持2 TB的PCIe Gen4x4 NVMe SED固态硬盘,适合对数据安全性有较高要求的用户。”` 18 | }, 19 | { 20 | cueWord: '小红书风格改写', 21 | prompt: `请将以下内容用小红书风格进行改写,该风格的特点是引人入胜的标题、每个段落中包含表情符号。要求语气轻松活泼,加入一些个人感受和生活小窍门,适当使用emoji表情,让内容更具亲和力和趣味性。 22 | 注意保留原文中的图片URL信息。格式为:![](/api/image/...)` 23 | }, 24 | ]; -------------------------------------------------------------------------------- /apps/setting/models_provider/impl/azure_model_provider/model/azure_chat_model.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: maxkb 4 | @Author:虎 5 | @file: azure_chat_model.py 6 | @date:2024/4/28 11:45 7 | @desc: 8 | """ 9 | from typing import List 10 | 11 | from langchain_core.messages import BaseMessage, get_buffer_string 12 | from langchain_openai import AzureChatOpenAI 13 | 14 | from common.config.tokenizer_manage_config import TokenizerManage 15 | 16 | 17 | class AzureChatModel(AzureChatOpenAI): 18 | def get_num_tokens_from_messages(self, messages: List[BaseMessage]) -> int: 19 | try: 20 | return super().get_num_tokens_from_messages(messages) 21 | except Exception as e: 22 | tokenizer = TokenizerManage.get_tokenizer() 23 | return sum([len(tokenizer.encode(get_buffer_string([m]))) for m in messages]) 24 | 25 | def get_num_tokens(self, text: str) -> int: 26 | try: 27 | return super().get_num_tokens(text) 28 | except Exception as e: 29 | tokenizer = TokenizerManage.get_tokenizer() 30 | return len(tokenizer.encode(text)) 31 | -------------------------------------------------------------------------------- /ui/src/views/index/index.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 27 | -------------------------------------------------------------------------------- /apps/common/field/common.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: maxkb 4 | @Author:虎 5 | @file: common.py 6 | @date:2024/1/11 18:44 7 | @desc: 8 | """ 9 | from rest_framework import serializers 10 | 11 | 12 | class InstanceField(serializers.Field): 13 | def __init__(self, model_type, **kwargs): 14 | self.model_type = model_type 15 | super().__init__(**kwargs) 16 | 17 | def to_internal_value(self, data): 18 | if not isinstance(data, self.model_type): 19 | self.fail('message类型错误', value=data) 20 | return data 21 | 22 | def to_representation(self, value): 23 | return value 24 | 25 | 26 | class FunctionField(serializers.Field): 27 | 28 | def to_internal_value(self, data): 29 | if not callable(data): 30 | self.fail('不是一个函數', value=data) 31 | return data 32 | 33 | def to_representation(self, value): 34 | return value 35 | 36 | 37 | class UploadedImageField(serializers.ImageField): 38 | def __init__(self, **kwargs): 39 | super().__init__(**kwargs) 40 | 41 | def to_representation(self, value): 42 | return value 43 | -------------------------------------------------------------------------------- /apps/common/job/client_access_num_job.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: maxkb 4 | @Author:虎 5 | @file: client_access_num_job.py 6 | @date:2024/3/14 11:56 7 | @desc: 8 | """ 9 | import logging 10 | 11 | from apscheduler.schedulers.background import BackgroundScheduler 12 | from django.db.models import QuerySet 13 | from django_apscheduler.jobstores import DjangoJobStore 14 | 15 | from application.models.api_key_model import ApplicationPublicAccessClient 16 | 17 | scheduler = BackgroundScheduler() 18 | scheduler.add_jobstore(DjangoJobStore(), "default") 19 | 20 | 21 | def client_access_num_reset_job(): 22 | logging.getLogger("max_kb").info('开始重置access_num') 23 | QuerySet(ApplicationPublicAccessClient).update(intraday_access_num=0) 24 | logging.getLogger("max_kb").info('结束重置access_num') 25 | 26 | 27 | def run(): 28 | scheduler.start() 29 | access_num_reset = scheduler.get_job(job_id='access_num_reset') 30 | if access_num_reset is not None: 31 | access_num_reset.remove() 32 | scheduler.add_job(client_access_num_reset_job, 'cron', hour='0', minute='0', second='0', 33 | id='access_num_reset') 34 | -------------------------------------------------------------------------------- /apps/dataset/swagger_api/document_api.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: maxkb 4 | @Author:虎 5 | @file: document_api.py 6 | @date:2024/4/28 13:56 7 | @desc: 8 | """ 9 | from drf_yasg import openapi 10 | 11 | from common.mixins.api_mixin import ApiMixin 12 | 13 | 14 | class DocumentApi(ApiMixin): 15 | class BatchEditHitHandlingApi(ApiMixin): 16 | @staticmethod 17 | def get_request_body_api(): 18 | return openapi.Schema( 19 | type=openapi.TYPE_OBJECT, 20 | properties={ 21 | 'id_list': openapi.Schema(type=openapi.TYPE_ARRAY, items=openapi.Schema(type=openapi.TYPE_STRING), 22 | title="主键id列表", 23 | description="主键id列表"), 24 | 'hit_handling_method': openapi.Schema(type=openapi.TYPE_STRING, title="命中处理方式", 25 | description="directly_return|optimization"), 26 | 'directly_return_similarity': openapi.Schema(type=openapi.TYPE_NUMBER, title="直接返回相似度") 27 | } 28 | ) 29 | -------------------------------------------------------------------------------- /ui/src/theme/type.ts: -------------------------------------------------------------------------------- 1 | interface ThemeSetting { 2 | /** 3 | *element-ui Namespace 4 | */ 5 | namespace: string; 6 | /** 7 | * 数据分隔符 8 | */ 9 | division: string; 10 | /** 11 | * 前缀 12 | */ 13 | startDivision: string; 14 | /** 15 | * 颜色外推设置 16 | */ 17 | colorInferSetting: ColorInferSetting; 18 | } 19 | 20 | /** 21 | * 颜色混和设置 22 | */ 23 | interface ColorInferSetting { 24 | /** 25 | * 与白色混 26 | */ 27 | light: Array; 28 | /** 29 | * 与黑色混 30 | */ 31 | dark: Array; 32 | /** 33 | * 类型 34 | */ 35 | type: string; 36 | } 37 | 38 | /** 39 | * 平滑数据 40 | */ 41 | interface KeyValueData { 42 | [propName: string]: string; 43 | } 44 | type UpdateInferData = KeyValueData; 45 | 46 | type UpdateKeyValueData = KeyValueData; 47 | /** 48 | *平滑数据 49 | */ 50 | interface InferData { 51 | /** 52 | * 设置 53 | */ 54 | setting?: ColorInferSetting | any; 55 | /** 56 | * 健 57 | */ 58 | key: string; 59 | /** 60 | * 值 61 | */ 62 | value: string; 63 | } 64 | 65 | export type { 66 | KeyValueData, 67 | InferData, 68 | ThemeSetting, 69 | UpdateInferData, 70 | UpdateKeyValueData, 71 | }; 72 | -------------------------------------------------------------------------------- /ui/src/components/dynamics-form/items/radio/Radio.vue: -------------------------------------------------------------------------------- 1 | 8 | 38 | 39 | -------------------------------------------------------------------------------- /apps/setting/models_provider/impl/gemini_model_provider/model/gemini_chat_model.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: UTF-8 -*- 3 | """ 4 | @Project :MaxKB 5 | @File :gemini_chat_model.py 6 | @Author :Brian Yang 7 | @Date :5/13/24 7:40 AM 8 | """ 9 | from typing import List 10 | 11 | from langchain_core.messages import BaseMessage, get_buffer_string 12 | from langchain_google_genai import ChatGoogleGenerativeAI 13 | 14 | from common.config.tokenizer_manage_config import TokenizerManage 15 | 16 | 17 | class GeminiChatModel(ChatGoogleGenerativeAI): 18 | def get_num_tokens_from_messages(self, messages: List[BaseMessage]) -> int: 19 | try: 20 | return super().get_num_tokens_from_messages(messages) 21 | except Exception as e: 22 | tokenizer = TokenizerManage.get_tokenizer() 23 | return sum([len(tokenizer.encode(get_buffer_string([m]))) for m in messages]) 24 | 25 | def get_num_tokens(self, text: str) -> int: 26 | try: 27 | return super().get_num_tokens(text) 28 | except Exception as e: 29 | tokenizer = TokenizerManage.get_tokenizer() 30 | return len(tokenizer.encode(text)) 31 | -------------------------------------------------------------------------------- /ui/src/components/dynamics-form/items/radio/RadioButton.vue: -------------------------------------------------------------------------------- 1 | 8 | 38 | 39 | -------------------------------------------------------------------------------- /apps/common/forms/object_card.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: maxkb 4 | @Author:虎 5 | @file: object_card.py 6 | @date:2023/10/31 18:02 7 | @desc: 8 | """ 9 | from typing import Dict 10 | 11 | from common.forms.base_field import BaseExecField, TriggerType 12 | 13 | 14 | class ObjectCard(BaseExecField): 15 | """ 16 | 收集对象子表卡片 17 | """ 18 | 19 | def __init__(self, 20 | label: str, 21 | text_field: str, 22 | value_field: str, 23 | provider: str, 24 | method: str, 25 | required: bool = False, 26 | default_value: object = None, 27 | relation_show_field_dict: Dict = None, 28 | relation_trigger_field_dict: Dict = None, 29 | trigger_type: TriggerType = TriggerType.OPTION_LIST, 30 | attrs: Dict[str, object] = None, 31 | props_info: Dict[str, object] = None): 32 | super().__init__("ObjectCard", label, text_field, value_field, provider, method, required, default_value, 33 | relation_show_field_dict, relation_trigger_field_dict, trigger_type, attrs, props_info) 34 | -------------------------------------------------------------------------------- /apps/common/forms/table_radio.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: maxkb 4 | @Author:虎 5 | @file: table_radio.py 6 | @date:2023/10/31 18:01 7 | @desc: 8 | """ 9 | from typing import Dict 10 | 11 | from common.forms.base_field import TriggerType, BaseExecField 12 | 13 | 14 | class TableRadio(BaseExecField): 15 | """ 16 | table 单选 17 | """ 18 | 19 | def __init__(self, 20 | label: str, 21 | text_field: str, 22 | value_field: str, 23 | provider: str, 24 | method: str, 25 | required: bool = False, 26 | default_value: object = None, 27 | relation_show_field_dict: Dict = None, 28 | relation_trigger_field_dict: Dict = None, 29 | trigger_type: TriggerType = TriggerType.OPTION_LIST, 30 | attrs: Dict[str, object] = None, 31 | props_info: Dict[str, object] = None): 32 | super().__init__("TableRadio", label, text_field, value_field, provider, method, required, default_value, 33 | relation_show_field_dict, relation_trigger_field_dict, trigger_type, attrs, props_info) 34 | -------------------------------------------------------------------------------- /apps/common/forms/tab_card.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: maxkb 4 | @Author:虎 5 | @file: tab_card.py 6 | @date:2023/10/31 18:03 7 | @desc: 8 | """ 9 | from typing import Dict 10 | 11 | from common.forms.base_field import BaseExecField, TriggerType 12 | 13 | 14 | class TabCard(BaseExecField): 15 | """ 16 | 收集 Tab类型数据 tab1:{},tab2:{} 17 | """ 18 | 19 | def __init__(self, 20 | label: str, 21 | text_field: str, 22 | value_field: str, 23 | provider: str, 24 | method: str, 25 | required: bool = False, 26 | default_value: object = None, 27 | relation_show_field_dict: Dict = None, 28 | relation_trigger_field_dict: Dict = None, 29 | trigger_type: TriggerType = TriggerType.OPTION_LIST, 30 | attrs: Dict[str, object] = None, 31 | props_info: Dict[str, object] = None): 32 | super().__init__("TabCard", label, text_field, value_field, provider, method, required, default_value, 33 | relation_show_field_dict, relation_trigger_field_dict, trigger_type, attrs, props_info) 34 | -------------------------------------------------------------------------------- /apps/common/forms/table_checkbox.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: maxkb 4 | @Author:虎 5 | @file: table_radio.py 6 | @date:2023/10/31 18:01 7 | @desc: 8 | """ 9 | from typing import Dict 10 | 11 | from common.forms.base_field import TriggerType, BaseExecField 12 | 13 | 14 | class TableRadio(BaseExecField): 15 | """ 16 | table 单选 17 | """ 18 | 19 | def __init__(self, 20 | label: str, 21 | text_field: str, 22 | value_field: str, 23 | provider: str, 24 | method: str, 25 | required: bool = False, 26 | default_value: object = None, 27 | relation_show_field_dict: Dict = None, 28 | relation_trigger_field_dict: Dict = None, 29 | trigger_type: TriggerType = TriggerType.OPTION_LIST, 30 | attrs: Dict[str, object] = None, 31 | props_info: Dict[str, object] = None): 32 | super().__init__("TableCheckbox", label, text_field, value_field, provider, method, required, default_value, 33 | relation_show_field_dict, relation_trigger_field_dict, trigger_type, attrs, props_info) 34 | -------------------------------------------------------------------------------- /ui/src/locales/lang/zh_CN/layout.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | breadcrumb: { 3 | 4 | }, 5 | sidebar: { 6 | 7 | }, 8 | topbar: { 9 | github: "项目地址", 10 | wiki: "产品介绍", 11 | forum: "说明文档", 12 | MenuItem: { 13 | dataset: "问答库", 14 | setting: "系统设置" 15 | }, 16 | avatar: { 17 | resetPassword: "修改密码", 18 | about: "关于", 19 | logout: "退出", 20 | version:"版本号", 21 | apiKey: "API Key 管理", 22 | apiServiceAddress: "API 服务地址", 23 | dialog:{ 24 | newPassword:"新密码", 25 | enterPassword: "请输入修改密码", 26 | confirmPassword: "确认密码", 27 | passwordLength:"密码长度在 6 到 20 个字符", 28 | passwordMismatch:"两次密码输入不一致", 29 | useEmail:"使用邮箱", 30 | enterEmail: "请输入邮箱", 31 | enterVerificationCode: "请输入验证码", 32 | getVerificationCode: "获取验证码", 33 | verificationCodeSentSuccess:"验证码发送成功", 34 | resend:"重新发送", 35 | cancel:"取消", 36 | save:"保存", 37 | } 38 | } 39 | }, 40 | }; 41 | -------------------------------------------------------------------------------- /apps/dataset/sql/list_dataset.sql: -------------------------------------------------------------------------------- 1 | SELECT 2 | *, 3 | to_json(meta) as meta 4 | FROM 5 | ( 6 | SELECT 7 | "temp_dataset".*, 8 | "document_temp"."char_length", 9 | CASE 10 | WHEN 11 | "app_dataset_temp"."count" IS NULL THEN 0 ELSE "app_dataset_temp"."count" END AS application_mapping_count, 12 | "document_temp".document_count FROM ( 13 | SELECT dataset.* 14 | FROM 15 | dataset dataset 16 | ${dataset_custom_sql} 17 | UNION 18 | SELECT 19 | * 20 | FROM 21 | dataset 22 | WHERE 23 | dataset."id" IN ( 24 | SELECT 25 | team_member_permission.target 26 | FROM 27 | team_member team_member 28 | LEFT JOIN team_member_permission team_member_permission ON team_member_permission.member_id = team_member."id" 29 | ${team_member_permission_custom_sql} 30 | ) 31 | ) temp_dataset 32 | LEFT JOIN ( SELECT "count" ( "id" ) AS document_count, "sum" ( "char_length" ) "char_length", dataset_id FROM "document" GROUP BY dataset_id ) "document_temp" ON temp_dataset."id" = "document_temp".dataset_id 33 | LEFT JOIN (SELECT "count"("id"),dataset_id FROM application_dataset_mapping GROUP BY dataset_id) app_dataset_temp ON temp_dataset."id" = "app_dataset_temp".dataset_id 34 | ) temp 35 | ${default_sql} -------------------------------------------------------------------------------- /apps/setting/models_provider/impl/gemini_model_provider/icon/gemini_icon_svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ui/src/components/auto-tooltip/index.vue: -------------------------------------------------------------------------------- 1 | 14 | 33 | 40 | -------------------------------------------------------------------------------- /apps/common/forms/array_object_card.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: maxkb 4 | @Author:虎 5 | @file: array_object_card.py 6 | @date:2023/10/31 18:03 7 | @desc: 8 | """ 9 | from typing import Dict 10 | 11 | from common.forms.base_field import BaseExecField, TriggerType 12 | 13 | 14 | class ArrayCard(BaseExecField): 15 | """ 16 | 收集List[Object] 17 | """ 18 | 19 | def __init__(self, 20 | label: str, 21 | text_field: str, 22 | value_field: str, 23 | provider: str, 24 | method: str, 25 | required: bool = False, 26 | default_value: object = None, 27 | relation_show_field_dict: Dict = None, 28 | relation_trigger_field_dict: Dict = None, 29 | trigger_type: TriggerType = TriggerType.OPTION_LIST, 30 | attrs: Dict[str, object] = None, 31 | props_info: Dict[str, object] = None): 32 | super().__init__("ArrayObjectCard", label, text_field, value_field, provider, method, required, default_value, 33 | relation_show_field_dict, relation_trigger_field_dict, trigger_type, attrs, props_info) 34 | -------------------------------------------------------------------------------- /ui/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { fileURLToPath, URL } from 'node:url' 2 | import type { ProxyOptions } from 'vite' 3 | import { defineConfig, loadEnv } from 'vite' 4 | 5 | import vue from '@vitejs/plugin-vue' 6 | import DefineOptions from 'unplugin-vue-define-options/vite' 7 | 8 | const envDir = './env' 9 | // https://vitejs.dev/config/ 10 | export default defineConfig(({ mode }) => { 11 | const ENV = loadEnv(mode, envDir) 12 | const proxyConf: Record = {} 13 | proxyConf['/api'] = { 14 | //http://192.168.30.238:8088 15 | //http://127.0.0.1:8088 16 | target: ENV.VITE_SERVER_PATH, 17 | changeOrigin: true, 18 | rewrite: (path) => path.replace(ENV.VITE_BASE_PATH, '/') 19 | } 20 | return { 21 | preflight: false, 22 | lintOnSave: false, 23 | base: ENV.VITE_BASE_PATH, 24 | envDir: envDir, 25 | plugins: [vue(), DefineOptions()], 26 | server: { 27 | cors: true, 28 | host: '0.0.0.0', 29 | port: Number(ENV.VITE_APP_PORT), 30 | strictPort: true, 31 | proxy: proxyConf 32 | }, 33 | build: { 34 | outDir: 'dist/ui' 35 | }, 36 | resolve: { 37 | alias: { 38 | '@': fileURLToPath(new URL('./src', import.meta.url)) 39 | } 40 | } 41 | } 42 | }) 43 | -------------------------------------------------------------------------------- /ui/src/components/card-add/index.vue: -------------------------------------------------------------------------------- 1 | 9 | 18 | 49 | -------------------------------------------------------------------------------- /ui/src/utils/message.ts: -------------------------------------------------------------------------------- 1 | import { ElMessageBox, ElMessage } from 'element-plus' 2 | 3 | export const MsgSuccess = (message: string) => { 4 | ElMessage.success({ 5 | message: message, 6 | type: 'success', 7 | showClose: true, 8 | duration: 1500 9 | }) 10 | } 11 | 12 | export const MsgInfo = (message: string) => { 13 | ElMessage.info({ 14 | message: message, 15 | type: 'info', 16 | showClose: true, 17 | duration: 1500 18 | }) 19 | } 20 | 21 | export const MsgWarning = (message: string) => { 22 | ElMessage.warning({ 23 | message: message, 24 | type: 'warning', 25 | showClose: true, 26 | duration: 1500 27 | }) 28 | } 29 | 30 | export const MsgError = (message: string) => { 31 | ElMessage.error({ 32 | message: message, 33 | type: 'error', 34 | showClose: true, 35 | duration: 1500 36 | }) 37 | } 38 | 39 | /** 40 | * 删除知识库 41 | * @param 参数 message: {title, description,type} 42 | */ 43 | 44 | export const MsgConfirm = (title: string, description: string, options?: any) => { 45 | const defaultOptions: Object = { 46 | showCancelButton: true, 47 | confirmButtonText: '确定', 48 | cancelButtonText: '取消', 49 | ...options 50 | } 51 | return ElMessageBox.confirm(description, title, defaultOptions) 52 | } 53 | -------------------------------------------------------------------------------- /apps/setting/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from . import views 4 | 5 | app_name = "team" 6 | urlpatterns = [ 7 | path('team/member', views.TeamMember.as_view(), name="team"), 8 | path('team/member/_batch', views.TeamMember.Batch.as_view()), 9 | path('team/member/', views.TeamMember.Operate.as_view(), name='member'), 10 | path('provider//', views.Provide.Exec.as_view(), name='provide_exec'), 11 | path('provider', views.Provide.as_view(), name='provide'), 12 | path('provider/model_type_list', views.Provide.ModelTypeList.as_view(), name="provider/model_type_list"), 13 | path('provider/model_list', views.Provide.ModelList.as_view(), 14 | name="provider/model_name_list"), 15 | path('provider/model_form', views.Provide.ModelForm.as_view(), 16 | name="provider/model_form"), 17 | path('model', views.Model.as_view(), name='model'), 18 | path('model_info', views.Model_info.as_view(), name='model_info'), 19 | path('model/', views.Model.Operate.as_view(), name='model/operate'), 20 | path('model//meta', views.Model.ModelMeta.as_view(), name='model/operate/meta'), 21 | path('email_setting', views.SystemSetting.Email.as_view(), name='email_setting') 22 | 23 | ] 24 | -------------------------------------------------------------------------------- /ui/src/utils/time.ts: -------------------------------------------------------------------------------- 1 | import moment from 'moment' 2 | 3 | // 当天日期 YYYY-MM-DD 4 | export const nowDate = moment().format('YYYY-MM-DD') 5 | 6 | // 当前时间的前n天 7 | export function beforeDay(n: number | string) { 8 | return moment().subtract(n, 'days').format('YYYY-MM-DD') 9 | } 10 | 11 | const getCheckDate = (timestamp: any) => { 12 | if (!timestamp) return false 13 | const dt = new Date(timestamp) 14 | if (isNaN(dt.getTime())) return false 15 | return dt 16 | } 17 | export const datetimeFormat = (timestamp: any) => { 18 | const dt = getCheckDate(timestamp) 19 | if (!dt) return timestamp 20 | 21 | const y = dt.getFullYear() 22 | const m = (dt.getMonth() + 1 + '').padStart(2, '0') 23 | const d = (dt.getDate() + '').padStart(2, '0') 24 | const hh = (dt.getHours() + '').padStart(2, '0') 25 | const mm = (dt.getMinutes() + '').padStart(2, '0') 26 | const ss = (dt.getSeconds() + '').padStart(2, '0') 27 | 28 | return `${y}-${m}-${d} ${hh}:${mm}:${ss}` 29 | } 30 | 31 | export const dateFormat = (timestamp: any) => { 32 | const dt = getCheckDate(timestamp) 33 | if (!dt) return timestamp 34 | 35 | const y = dt.getFullYear() 36 | const m = (dt.getMonth() + 1 + '').padStart(2, '0') 37 | const d = (dt.getDate() + '').padStart(2, '0') 38 | 39 | return `${y}-${m}-${d}` 40 | } 41 | -------------------------------------------------------------------------------- /ui/src/components/markdown-editor/index.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 53 | -------------------------------------------------------------------------------- /ui/src/components/login-container/index.vue: -------------------------------------------------------------------------------- 1 | 17 | 25 | 51 | -------------------------------------------------------------------------------- /apps/common/util/lock.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: qabot 4 | @Author:虎 5 | @file: lock.py 6 | @date:2023/9/11 11:45 7 | @desc: 8 | """ 9 | from datetime import timedelta 10 | 11 | from django.core.cache import caches 12 | 13 | memory_cache = caches['default'] 14 | 15 | 16 | def try_lock(key: str, timeout=None): 17 | """ 18 | 获取锁 19 | :param key: 获取锁 key 20 | :param timeout 超时时间 21 | :return: 是否获取到锁 22 | """ 23 | return memory_cache.add(key, 'lock', timeout=timedelta(hours=1).total_seconds() if timeout is not None else timeout) 24 | 25 | 26 | def un_lock(key: str): 27 | """ 28 | 解锁 29 | :param key: 解锁 key 30 | :return: 是否解锁成功 31 | """ 32 | return memory_cache.delete(key) 33 | 34 | 35 | def lock(lock_key): 36 | """ 37 | 给一个函数上锁 38 | :param lock_key: 上锁key 字符串|函数 函数返回值为字符串 39 | :return: 装饰器函数 当前装饰器主要限制一个key只能一个线程去调用 相同key只能阻塞等待上一个任务执行完毕 不同key不需要等待 40 | """ 41 | 42 | def inner(func): 43 | def run(*args, **kwargs): 44 | key = lock_key(*args, **kwargs) if callable(lock_key) else lock_key 45 | try: 46 | if try_lock(key=key): 47 | return func(*args, **kwargs) 48 | finally: 49 | un_lock(key=key) 50 | 51 | return run 52 | 53 | return inner 54 | -------------------------------------------------------------------------------- /ui/src/components/login-layout/index.vue: -------------------------------------------------------------------------------- 1 | 16 | 30 | 42 | -------------------------------------------------------------------------------- /ui/src/stores/modules/paragraph.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia' 2 | import paragraphApi from '@/api/paragraph' 3 | import type { Ref } from 'vue' 4 | 5 | const useParagraphStore = defineStore({ 6 | id: 'paragraph', 7 | state: () => ({}), 8 | actions: { 9 | async asyncPutParagraph( 10 | datasetId: string, 11 | documentId: string, 12 | paragraphId: string, 13 | data: any, 14 | loading?: Ref 15 | ) { 16 | return new Promise((resolve, reject) => { 17 | paragraphApi 18 | .putParagraph(datasetId, documentId, paragraphId, data, loading) 19 | .then((data) => { 20 | resolve(data) 21 | }) 22 | .catch((error) => { 23 | reject(error) 24 | }) 25 | }) 26 | }, 27 | 28 | async asyncDelParagraph( 29 | datasetId: string, 30 | documentId: string, 31 | paragraphId: string, 32 | loading?: Ref 33 | ) { 34 | return new Promise((resolve, reject) => { 35 | paragraphApi 36 | .delParagraph(datasetId, documentId, paragraphId, loading) 37 | .then((data) => { 38 | resolve(data) 39 | }) 40 | .catch((error) => { 41 | reject(error) 42 | }) 43 | }) 44 | } 45 | } 46 | }) 47 | 48 | export default useParagraphStore 49 | -------------------------------------------------------------------------------- /apps/common/forms/radio_field.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: maxkb 4 | @Author:虎 5 | @file: radio_field.py 6 | @date:2023/10/31 17:59 7 | @desc: 8 | """ 9 | from typing import List, Dict 10 | 11 | from common.forms.base_field import BaseExecField, TriggerType 12 | 13 | 14 | class Radio(BaseExecField): 15 | """ 16 | 下拉单选 17 | """ 18 | 19 | def __init__(self, 20 | label: str, 21 | text_field: str, 22 | value_field: str, 23 | option_list: List[str:object], 24 | provider: str, 25 | method: str, 26 | required: bool = False, 27 | default_value: object = None, 28 | relation_show_field_dict: Dict = None, 29 | relation_trigger_field_dict: Dict = None, 30 | trigger_type: TriggerType = TriggerType.OPTION_LIST, 31 | attrs: Dict[str, object] = None, 32 | props_info: Dict[str, object] = None): 33 | super().__init__("Radio", label, text_field, value_field, provider, method, required, default_value, 34 | relation_show_field_dict, relation_trigger_field_dict, trigger_type, attrs, props_info) 35 | self.option_list = option_list 36 | 37 | def to_dict(self): 38 | return {**super().to_dict(), 'option_list': self.option_list} 39 | -------------------------------------------------------------------------------- /apps/common/forms/radio_card_field.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: maxkb 4 | @Author:虎 5 | @file: radio_field.py 6 | @date:2023/10/31 17:59 7 | @desc: 8 | """ 9 | from typing import List, Dict 10 | 11 | from common.forms.base_field import BaseExecField, TriggerType 12 | 13 | 14 | class Radio(BaseExecField): 15 | """ 16 | 下拉单选 17 | """ 18 | 19 | def __init__(self, 20 | label: str, 21 | text_field: str, 22 | value_field: str, 23 | option_list: List[str:object], 24 | provider: str, 25 | method: str, 26 | required: bool = False, 27 | default_value: object = None, 28 | relation_show_field_dict: Dict = None, 29 | relation_trigger_field_dict: Dict = None, 30 | trigger_type: TriggerType = TriggerType.OPTION_LIST, 31 | attrs: Dict[str, object] = None, 32 | props_info: Dict[str, object] = None): 33 | super().__init__("RadioCard", label, text_field, value_field, provider, method, required, default_value, 34 | relation_show_field_dict, relation_trigger_field_dict, trigger_type, attrs, props_info) 35 | self.option_list = option_list 36 | 37 | def to_dict(self): 38 | return {**super().to_dict(), 'option_list': self.option_list} 39 | -------------------------------------------------------------------------------- /apps/common/forms/radio_button_field.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: maxkb 4 | @Author:虎 5 | @file: radio_field.py 6 | @date:2023/10/31 17:59 7 | @desc: 8 | """ 9 | from typing import List, Dict 10 | 11 | from common.forms.base_field import BaseExecField, TriggerType 12 | 13 | 14 | class Radio(BaseExecField): 15 | """ 16 | 下拉单选 17 | """ 18 | 19 | def __init__(self, 20 | label: str, 21 | text_field: str, 22 | value_field: str, 23 | option_list: List[str:object], 24 | provider: str, 25 | method: str, 26 | required: bool = False, 27 | default_value: object = None, 28 | relation_show_field_dict: Dict = None, 29 | relation_trigger_field_dict: Dict = None, 30 | trigger_type: TriggerType = TriggerType.OPTION_LIST, 31 | attrs: Dict[str, object] = None, 32 | props_info: Dict[str, object] = None): 33 | super().__init__("RadioButton", label, text_field, value_field, provider, method, required, default_value, 34 | relation_show_field_dict, relation_trigger_field_dict, trigger_type, attrs, props_info) 35 | self.option_list = option_list 36 | 37 | def to_dict(self): 38 | return {**super().to_dict(), 'option_list': self.option_list} 39 | -------------------------------------------------------------------------------- /ui/src/locales/lang/en_US/layout.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | breadcrumb: { 3 | 4 | }, 5 | sidebar: { 6 | 7 | }, 8 | topbar: { 9 | github: "Project address", 10 | wiki: "User manual", 11 | forum: "Forum for help", 12 | MenuItem: { 13 | application: "Application", 14 | dataset: "Knowledge base", 15 | setting: "System settings" 16 | }, 17 | avatar: { 18 | resetPassword: "Change password", 19 | about: "About", 20 | logout: "Logout", 21 | version:"Version", 22 | dialog:{ 23 | newPassword:"New password", 24 | enterPassword: "Please enter new password", 25 | confirmPassword: "Confirm password", 26 | passwordLength:"Password length should be between 6 and 20 characters", 27 | passwordMismatch:"Passwords do not match", 28 | useEmail:"Use email", 29 | enterEmail: "Please enter email", 30 | enterVerificationCode: "Please enter verification code", 31 | getVerificationCode: "Get verification code", 32 | verificationCodeSentSuccess:"Verification code sent successfully", 33 | resend:"Resend", 34 | cancel:"Cancel", 35 | save:"Save", 36 | } 37 | } 38 | }, 39 | }; 40 | -------------------------------------------------------------------------------- /apps/users/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from . import views 4 | 5 | app_name = "user" 6 | urlpatterns = [ 7 | path('profile', views.Profile.as_view()), 8 | path('user', views.User.as_view(), name="profile"), 9 | path('user/list', views.User.Query.as_view()), 10 | path('user/login', views.Login.as_view(), name='login'), 11 | path('user/logout', views.Logout.as_view(), name='logout'), 12 | # path('user/register', views.Register.as_view(), name="register"), 13 | path("user/send_email", views.SendEmail.as_view(), name='send_email'), 14 | path("user/check_code", views.CheckCode.as_view(), name='check_code'), 15 | path("user/re_password", views.RePasswordView.as_view(), name='re_password'), 16 | path("user/current/send_email", views.SendEmailToCurrentUserView.as_view(), name="send_email_current"), 17 | path("user/current/reset_password", views.ResetCurrentUserPasswordView.as_view(), name="reset_password_current"), 18 | path("user_manage", views.UserManage.as_view(), name="user_manage"), 19 | path("user_manage/", views.UserManage.Operate.as_view(), name="user_manage_operate"), 20 | path("user_manage//re_password", views.UserManage.RePassword.as_view(), 21 | name="user_manage_re_password"), 22 | path("user_manage//", views.UserManage.Page.as_view(), 23 | name="user_manage_re_password"), 24 | ] 25 | -------------------------------------------------------------------------------- /apps/common/forms/multi_select.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: maxkb 4 | @Author:虎 5 | @file: multi_select.py 6 | @date:2023/10/31 18:00 7 | @desc: 8 | """ 9 | from typing import List, Dict 10 | 11 | from common.forms.base_field import BaseExecField, TriggerType 12 | 13 | 14 | class MultiSelect(BaseExecField): 15 | """ 16 | 下拉单选 17 | """ 18 | 19 | def __init__(self, 20 | label: str, 21 | text_field: str, 22 | value_field: str, 23 | option_list: List[str:object], 24 | provider: str = None, 25 | method: str = None, 26 | required: bool = False, 27 | default_value: object = None, 28 | relation_show_field_dict: Dict = None, 29 | relation_trigger_field_dict: Dict = None, 30 | trigger_type: TriggerType = TriggerType.OPTION_LIST, 31 | attrs: Dict[str, object] = None, 32 | props_info: Dict[str, object] = None): 33 | super().__init__("MultiSelect", label, text_field, value_field, provider, method, required, default_value, 34 | relation_show_field_dict, relation_trigger_field_dict, trigger_type, attrs, props_info) 35 | self.option_list = option_list 36 | 37 | def to_dict(self): 38 | return {**super().to_dict(), 'option_list': self.option_list} 39 | -------------------------------------------------------------------------------- /apps/common/forms/single_select_field.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: maxkb 4 | @Author:虎 5 | @file: single_select_field.py 6 | @date:2023/10/31 18:00 7 | @desc: 8 | """ 9 | from typing import List, Dict 10 | 11 | from common.forms.base_field import TriggerType, BaseExecField 12 | 13 | 14 | class SingleSelect(BaseExecField): 15 | """ 16 | 下拉单选 17 | """ 18 | 19 | def __init__(self, 20 | label: str, 21 | text_field: str, 22 | value_field: str, 23 | option_list: List[str:object], 24 | provider: str = None, 25 | method: str = None, 26 | required: bool = False, 27 | default_value: object = None, 28 | relation_show_field_dict: Dict = None, 29 | relation_trigger_field_dict: Dict = None, 30 | trigger_type: TriggerType = TriggerType.OPTION_LIST, 31 | attrs: Dict[str, object] = None, 32 | props_info: Dict[str, object] = None): 33 | super().__init__("SingleSelect", label, text_field, value_field, provider, method, required, default_value, 34 | relation_show_field_dict, relation_trigger_field_dict, trigger_type, attrs, props_info) 35 | self.option_list = option_list 36 | 37 | def to_dict(self): 38 | return {**super().to_dict(), 'option_list': self.option_list} 39 | -------------------------------------------------------------------------------- /ui/src/views/index/components/top-bar/MenuItem.vue: -------------------------------------------------------------------------------- 1 | 17 | 32 | 54 | -------------------------------------------------------------------------------- /ui/src/assets/icon_send_colorful.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "kb_builder" 3 | authors = ["sixuanwang "] 4 | version = "0.1.0" 5 | description = "智能知识库" 6 | readme = "README.md" 7 | 8 | [tool.poetry.dependencies] 9 | python = "^3.11" 10 | django = "4.1.13" 11 | djangorestframework = "3.14.0" 12 | drf-yasg = "1.21.7" 13 | django-filter = "23.2" 14 | langchain = "0.2.11" 15 | langchain_community = "0.2.10" 16 | langchain_huggingface = "0.0.3" 17 | psycopg2-binary = "2.9.7" 18 | jieba = "^0.42.1" 19 | diskcache = "^5.6.3" 20 | pillow = "^10.2.0" 21 | filetype = "^1.2.0" 22 | torch = "2.2.2" 23 | sentence-transformers = "^2.2.2" 24 | blinker = "^1.6.3" 25 | openai = "^1.37.1" 26 | tiktoken = "^0.7.0" 27 | qianfan = "^0.3.6.1" 28 | pycryptodome = "^3.19.0" 29 | beautifulsoup4 = "^4.12.2" 30 | html2text = "^2024.2.26" 31 | langchain-openai = "^0.1.20" 32 | django-ipware = "^6.0.4" 33 | django-apscheduler = "^0.6.2" 34 | pymupdf = "1.24.1" 35 | python-docx = "^1.1.0" 36 | xlwt = "^1.3.0" 37 | dashscope = "^1.17.0" 38 | zhipuai = "^2.0.1" 39 | httpx = "^0.27.0" 40 | httpx-sse = "^0.4.0" 41 | websocket-client = "^1.7.0" 42 | langchain-google-genai = "^1.0.4" 43 | opencv-python = "^4.10.0.84" 44 | paddlepaddle = "^2.6.2" 45 | setuptools = "^75.1.0" 46 | paddleocr = "^2.8.1" 47 | 48 | [build-system] 49 | requires = ["poetry-core"] 50 | build-backend = "poetry.core.masonry.api" 51 | 52 | [[tool.poetry.source]] 53 | name = "pytorch" 54 | url = "https://download.pytorch.org/whl/cpu" 55 | priority = "explicit" -------------------------------------------------------------------------------- /ui/src/layout/components/top-bar/top-menu/MenuItem.vue: -------------------------------------------------------------------------------- 1 | 14 | 29 | 61 | -------------------------------------------------------------------------------- /apps/common/middleware/static_headers_middleware.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: maxkb 4 | @Author:虎 5 | @file: static_headers_middleware.py 6 | @date:2024/3/13 18:26 7 | @desc: 8 | """ 9 | from django.db.models import QuerySet 10 | from django.utils.deprecation import MiddlewareMixin 11 | 12 | from application.models.api_key_model import ApplicationAccessToken 13 | 14 | 15 | class StaticHeadersMiddleware(MiddlewareMixin): 16 | def process_response(self, request, response): 17 | if request.path.startswith('/ui/chat/'): 18 | access_token = request.path.replace('/ui/chat/', '') 19 | application_access_token = QuerySet(ApplicationAccessToken).filter(access_token=access_token).first() 20 | if application_access_token is not None: 21 | if application_access_token.white_active: 22 | # 添加自定义的响应头 23 | response[ 24 | 'Content-Security-Policy'] = f'frame-ancestors {" ".join(application_access_token.white_list)}' 25 | response.content = (response.content.decode('utf-8').replace( 26 | '', 27 | f'') 28 | .replace('KB Builder', f'{application_access_token.application.name}').encode( 29 | "utf-8")) 30 | return response 31 | -------------------------------------------------------------------------------- /ui/src/views/404/index.vue: -------------------------------------------------------------------------------- 1 | 12 | 16 | 52 | -------------------------------------------------------------------------------- /ui/src/assets/icon_document.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /ui/src/components/app-avatar/index.vue: -------------------------------------------------------------------------------- 1 | 8 | 58 | 59 | -------------------------------------------------------------------------------- /apps/common/constants/exception_code_constants.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: qabot 4 | @Author:虎 5 | @file: exception_code_constants.py 6 | @date:2023/9/4 14:09 7 | @desc: 异常常量类 8 | """ 9 | from enum import Enum 10 | 11 | from common.exception.app_exception import AppApiException 12 | 13 | 14 | class ExceptionCodeConstantsValue: 15 | def __init__(self, code, message): 16 | self.code = code 17 | self.message = message 18 | 19 | def get_message(self): 20 | return self.message 21 | 22 | def get_code(self): 23 | return self.code 24 | 25 | def to_app_api_exception(self): 26 | return AppApiException(code=self.code, message=self.message) 27 | 28 | 29 | class ExceptionCodeConstants(Enum): 30 | INCORRECT_USERNAME_AND_PASSWORD = ExceptionCodeConstantsValue(1000, "用户名或者密码不正确") 31 | NOT_AUTHENTICATION = ExceptionCodeConstantsValue(1001, "请先登录,并携带用户Token") 32 | EMAIL_SEND_ERROR = ExceptionCodeConstantsValue(1002, "邮件发送失败") 33 | EMAIL_FORMAT_ERROR = ExceptionCodeConstantsValue(1003, "邮箱格式错误") 34 | EMAIL_IS_EXIST = ExceptionCodeConstantsValue(1004, "邮箱已经被注册,请勿重复注册") 35 | EMAIL_IS_NOT_EXIST = ExceptionCodeConstantsValue(1005, "邮箱尚未注册,请先注册") 36 | CODE_ERROR = ExceptionCodeConstantsValue(1005, "验证码不正确,或者验证码过期") 37 | USERNAME_IS_EXIST = ExceptionCodeConstantsValue(1006, "用户名已被使用,请使用其他用户名") 38 | USERNAME_ERROR = ExceptionCodeConstantsValue(1006, "用户名不能为空,并且长度在6-20") 39 | PASSWORD_NOT_EQ_RE_PASSWORD = ExceptionCodeConstantsValue(1007, "密码与确认密码不一致") 40 | -------------------------------------------------------------------------------- /ui/src/layout/components/sidebar/SidebarItem.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 28 | 29 | 54 | -------------------------------------------------------------------------------- /ui/src/assets/upload-icon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | ## Create pull request 4 | PR are always welcome, even if they only contain small fixes like typos or a few lines of code. If there will be a significant effort, please document it as an issue and get a discussion going before starting to work on it. 5 | 6 | Please submit a PR broken down into small changes bit by bit. A PR consisting of a lot of features and code changes may be hard to review. It is recommended to submit PRs in an incremental fashion. 7 | 8 | Note: If you split your pull request to small changes, please make sure any of the changes goes to master will not break anything. Otherwise, it can not be merged until this feature complete. 9 | 10 | ## Report issues 11 | It is a great way to contribute by reporting an issue. Well-written and complete bug reports are always welcome! Please open an issue and follow the template to fill in required information. 12 | 13 | Before opening any issue, please look up the existing issues to avoid submitting a duplication. 14 | If you find a match, you can "subscribe" to it to get notified on updates. If you have additional helpful information about the issue, please leave a comment. 15 | 16 | When reporting issues, always include: 17 | 18 | * Which version you are using. 19 | * Steps to reproduce the issue. 20 | * Snapshots or log files if needed 21 | 22 | Because the issues are open to the public, when submitting files, be sure to remove any sensitive information, e.g. user name, password, IP address, and company name. You can 23 | replace those parts with "REDACTED" or other strings like "****". -------------------------------------------------------------------------------- /ui/src/views/template/component/SelectProviderDialog.vue: -------------------------------------------------------------------------------- 1 | 23 | 48 | 49 | -------------------------------------------------------------------------------- /apps/dataset/serializers/image_serializers.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: maxkb 4 | @Author:虎 5 | @file: image_serializers.py 6 | @date:2024/4/22 16:36 7 | @desc: 8 | """ 9 | import uuid 10 | 11 | from django.db.models import QuerySet 12 | from django.http import HttpResponse 13 | from rest_framework import serializers 14 | 15 | from common.exception.app_exception import NotFound404 16 | from common.field.common import UploadedImageField 17 | from common.util.field_message import ErrMessage 18 | from dataset.models import Image 19 | 20 | 21 | class ImageSerializer(serializers.Serializer): 22 | image = UploadedImageField(required=True, error_messages=ErrMessage.image("图片")) 23 | 24 | def upload(self, with_valid=True): 25 | if with_valid: 26 | self.is_valid(raise_exception=True) 27 | image_id = uuid.uuid1() 28 | image = Image(id=image_id, image=self.data.get('image').read(), image_name=self.data.get('image').name) 29 | image.save() 30 | return f'/api/image/{image_id}' 31 | 32 | class Operate(serializers.Serializer): 33 | id = serializers.UUIDField(required=True) 34 | 35 | def get(self, with_valid=True): 36 | if with_valid: 37 | self.is_valid(raise_exception=True) 38 | image_id = self.data.get('id') 39 | image = QuerySet(Image).filter(id=image_id).first() 40 | if image is None: 41 | raise NotFound404(404, "不存在的图片") 42 | return HttpResponse(image.image, status=200, headers={'Content-Type': 'image/png'}) 43 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug.yml: -------------------------------------------------------------------------------- 1 | name: BUG 提交 2 | description: 提交产品缺陷帮助我们更好的改进 3 | title: "[BUG]" 4 | labels: "类型: 缺陷" 5 | assignees: baixin513 6 | body: 7 | - type: markdown 8 | id: contacts_title 9 | attributes: 10 | value: "## 联系方式" 11 | - type: input 12 | id: contacts 13 | validations: 14 | required: false 15 | attributes: 16 | label: "联系方式" 17 | description: "可以快速联系到您的方式:交流群号及昵称、邮箱等" 18 | - type: markdown 19 | id: environment 20 | attributes: 21 | value: "## 环境信息" 22 | - type: input 23 | id: version 24 | validations: 25 | required: true 26 | attributes: 27 | label: "KB Builder 版本" 28 | description: "登录 KB Builder Web 控制台,在右上角关于页面查看当前版本。" 29 | - type: markdown 30 | id: details 31 | attributes: 32 | value: "## 详细信息" 33 | - type: textarea 34 | id: what-happened 35 | attributes: 36 | label: "问题描述" 37 | description: "简要描述您碰到的问题" 38 | validations: 39 | required: true 40 | - type: textarea 41 | id: how-happened 42 | attributes: 43 | label: "重现步骤" 44 | description: "如果操作可以重现该问题" 45 | validations: 46 | required: true 47 | - type: textarea 48 | id: expect 49 | attributes: 50 | label: "期待的正确结果" 51 | - type: textarea 52 | id: logs 53 | attributes: 54 | label: "相关日志输出" 55 | description: "请复制并粘贴任何相关的日志输出。 这将自动格式化为代码,因此无需反引号。" 56 | render: shell 57 | - type: textarea 58 | id: additional-information 59 | attributes: 60 | label: "附加信息" 61 | description: "如果你还有其他需要提供的信息,可以在这里填写(可以提供截图、视频等)。" 62 | -------------------------------------------------------------------------------- /apps/setting/models_provider/impl/openai_model_provider/model/openai_chat_model.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: maxkb 4 | @Author:虎 5 | @file: openai_chat_model.py 6 | @date:2024/4/18 15:28 7 | @desc: 8 | """ 9 | from typing import List, Dict 10 | 11 | from langchain_core.messages import BaseMessage, get_buffer_string 12 | from langchain_openai import ChatOpenAI 13 | 14 | from common.config.tokenizer_manage_config import TokenizerManage 15 | from setting.models_provider.base_model_provider import MaxKBBaseModel 16 | 17 | 18 | class OpenAIChatModel(MaxKBBaseModel,ChatOpenAI): 19 | @staticmethod 20 | def new_instance(model_type, model_name, model_credential: Dict[str, object], **model_kwargs): 21 | azure_chat_open_ai = OpenAIChatModel( 22 | model=model_name, 23 | openai_api_base=model_credential.get('api_base'), 24 | openai_api_key=model_credential.get('api_key') 25 | ) 26 | return azure_chat_open_ai 27 | def get_num_tokens_from_messages(self, messages: List[BaseMessage]) -> int: 28 | try: 29 | return super().get_num_tokens_from_messages(messages) 30 | except Exception as e: 31 | tokenizer = TokenizerManage.get_tokenizer() 32 | return sum([len(tokenizer.encode(get_buffer_string([m]))) for m in messages]) 33 | 34 | def get_num_tokens(self, text: str) -> int: 35 | try: 36 | return super().get_num_tokens(text) 37 | except Exception as e: 38 | tokenizer = TokenizerManage.get_tokenizer() 39 | return len(tokenizer.encode(text)) 40 | -------------------------------------------------------------------------------- /apps/common/cache/mem_cache.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: maxkb 4 | @Author:虎 5 | @file: mem_cache.py 6 | @date:2024/3/6 11:20 7 | @desc: 8 | """ 9 | from django.core.cache.backends.base import DEFAULT_TIMEOUT 10 | from django.core.cache.backends.locmem import LocMemCache 11 | 12 | 13 | class MemCache(LocMemCache): 14 | def __init__(self, name, params): 15 | super().__init__(name, params) 16 | 17 | def set(self, key, value, timeout=DEFAULT_TIMEOUT, version=None): 18 | key = self.make_and_validate_key(key, version=version) 19 | pickled = value 20 | with self._lock: 21 | self._set(key, pickled, timeout) 22 | 23 | def get(self, key, default=None, version=None): 24 | key = self.make_and_validate_key(key, version=version) 25 | with self._lock: 26 | if self._has_expired(key): 27 | self._delete(key) 28 | return default 29 | pickled = self._cache[key] 30 | self._cache.move_to_end(key, last=False) 31 | return pickled 32 | 33 | def clear_by_application_id(self, application_id): 34 | delete_keys = [] 35 | for key in self._cache.keys(): 36 | value = self._cache.get(key) 37 | if (hasattr(value, 38 | 'application') and value.application is not None and value.application.id is not None and 39 | str( 40 | value.application.id) == application_id): 41 | delete_keys.append(key) 42 | for key in delete_keys: 43 | self._delete(key) 44 | -------------------------------------------------------------------------------- /ui/src/components/infinite-scroll/index.vue: -------------------------------------------------------------------------------- 1 | 14 | 64 | 65 | -------------------------------------------------------------------------------- /apps/setting/models_provider/impl/deepseek_model_provider/model/deepseek_chat_model.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: UTF-8 -*- 3 | """ 4 | @Project :MaxKB 5 | @File :deepseek_chat_model.py 6 | @Author :Brian Yang 7 | @Date :5/12/24 7:44 AM 8 | """ 9 | from typing import List, Dict 10 | 11 | from langchain_core.messages import BaseMessage, get_buffer_string 12 | from langchain_openai import ChatOpenAI 13 | 14 | from common.config.tokenizer_manage_config import TokenizerManage 15 | from setting.models_provider.base_model_provider import MaxKBBaseModel 16 | 17 | 18 | class DeepSeekChatModel(MaxKBBaseModel, ChatOpenAI): 19 | @staticmethod 20 | def new_instance(model_type, model_name, model_credential: Dict[str, object], **model_kwargs): 21 | deepseek_chat = DeepSeekChatModel( 22 | model=model_name, 23 | openai_api_base='https://api.deepseek.com', 24 | openai_api_key=model_credential.get('api_key') 25 | ) 26 | return deepseek_chat 27 | def get_num_tokens_from_messages(self, messages: List[BaseMessage]) -> int: 28 | try: 29 | return super().get_num_tokens_from_messages(messages) 30 | except Exception as e: 31 | tokenizer = TokenizerManage.get_tokenizer() 32 | return sum([len(tokenizer.encode(get_buffer_string([m]))) for m in messages]) 33 | 34 | def get_num_tokens(self, text: str) -> int: 35 | try: 36 | return super().get_num_tokens(text) 37 | except Exception as e: 38 | tokenizer = TokenizerManage.get_tokenizer() 39 | return len(tokenizer.encode(text)) 40 | -------------------------------------------------------------------------------- /apps/application/chat_pipeline/pipeline_manage.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: maxkb 4 | @Author:虎 5 | @file: pipeline_manage.py 6 | @date:2024/1/9 17:40 7 | @desc: 8 | """ 9 | import time 10 | from functools import reduce 11 | from typing import List, Type, Dict 12 | 13 | from application.chat_pipeline.I_base_chat_pipeline import IBaseChatPipelineStep 14 | 15 | 16 | class PipelineManage: 17 | def __init__(self, step_list: List[Type[IBaseChatPipelineStep]]): 18 | # 步骤执行器 19 | self.step_list = [step() for step in step_list] 20 | # 上下文 21 | self.context = {'message_tokens': 0, 'answer_tokens': 0} 22 | 23 | def run(self, context: Dict = None): 24 | self.context['start_time'] = time.time() 25 | if context is not None: 26 | for key, value in context.items(): 27 | self.context[key] = value 28 | for step in self.step_list: 29 | step.run(self) 30 | 31 | def get_details(self): 32 | return reduce(lambda x, y: {**x, **y}, [{item.get('step_type'): item} for item in 33 | filter(lambda r: r is not None, 34 | [row.get_details(self) for row in self.step_list])], {}) 35 | 36 | class builder: 37 | def __init__(self): 38 | self.step_list: List[Type[IBaseChatPipelineStep]] = [] 39 | 40 | def append_step(self, step: Type[IBaseChatPipelineStep]): 41 | self.step_list.append(step) 42 | return self 43 | 44 | def build(self): 45 | return PipelineManage(step_list=self.step_list) 46 | -------------------------------------------------------------------------------- /ui/src/stores/modules/log.ts: -------------------------------------------------------------------------------- 1 | import { defineStore } from 'pinia' 2 | import logApi from '@/api/log' 3 | import { type Ref } from 'vue' 4 | import type { pageRequest } from '@/api/type/common' 5 | 6 | const useLogStore = defineStore({ 7 | id: 'log', 8 | state: () => ({}), 9 | actions: { 10 | async asyncGetChatLog(id: string, page: pageRequest, param: any, loading?: Ref) { 11 | return new Promise((resolve, reject) => { 12 | logApi 13 | .getChatLog(id, page, param, loading) 14 | .then((data) => { 15 | resolve(data) 16 | }) 17 | .catch((error) => { 18 | reject(error) 19 | }) 20 | }) 21 | }, 22 | async asyncChatRecordLog( 23 | id: string, 24 | chatId: string, 25 | page: pageRequest, 26 | loading?: Ref, 27 | order_asc?: boolean 28 | ) { 29 | return new Promise((resolve, reject) => { 30 | logApi 31 | .getChatRecordLog(id, chatId, page, loading, order_asc) 32 | .then((data) => { 33 | resolve(data) 34 | }) 35 | .catch((error) => { 36 | reject(error) 37 | }) 38 | }) 39 | }, 40 | async asyncGetChatLogClient(id: string, page: pageRequest, loading?: Ref) { 41 | return new Promise((resolve, reject) => { 42 | logApi 43 | .getChatLogClient(id, page, loading) 44 | .then((data) => { 45 | resolve(data) 46 | }) 47 | .catch((error) => { 48 | reject(error) 49 | }) 50 | }) 51 | } 52 | } 53 | }) 54 | 55 | export default useLogStore 56 | -------------------------------------------------------------------------------- /ui/src/components/common-list/index.vue: -------------------------------------------------------------------------------- 1 | 16 | 51 | 67 | -------------------------------------------------------------------------------- /ui/src/components/markdown-renderer/MdRenderer.vue: -------------------------------------------------------------------------------- 1 | 11 | 47 | 48 | -------------------------------------------------------------------------------- /ui/src/api/team.ts: -------------------------------------------------------------------------------- 1 | import { Result } from '@/request/Result' 2 | import { get, post, del, put } from '@/request/index' 3 | import type { TeamMember } from '@/api/type/team' 4 | 5 | const prefix = '/team/member' 6 | 7 | /** 8 | * 获取团队成员列表 9 | */ 10 | const getTeamMember: () => Promise> = () => { 11 | return get(`${prefix}`) 12 | } 13 | 14 | /** 15 | * 添加成员 16 | * @param 参数 [] 17 | */ 18 | const postCreatTeamMember: (data: Array) => Promise> = (data) => { 19 | return post(`${prefix}/_batch`, data) 20 | } 21 | 22 | /** 23 | * 删除成员 24 | * @param 参数 member_id 25 | */ 26 | const delTeamMember: (member_id: String) => Promise> = (member_id) => { 27 | return del(`${prefix}/${member_id}`) 28 | } 29 | 30 | /** 31 | * 获取成员权限 32 | * @param 参数 member_id 33 | */ 34 | const getMemberPermissions: (member_id: String) => Promise> = (member_id) => { 35 | return get(`${prefix}/${member_id}`) 36 | } 37 | 38 | /** 39 | * 获取成员权限 40 | * @param 参数 member_id 41 | * @param 参数 { 42 | "team_member_permission_list": [ 43 | { 44 | "target_id": "string", 45 | "type": "string", 46 | "operate": { 47 | "USE": true, 48 | "MANAGE": true 49 | } 50 | } 51 | ] 52 | } 53 | */ 54 | const putMemberPermissions: (member_id: String, body: any) => Promise> = ( 55 | member_id, 56 | body 57 | ) => { 58 | return put(`${prefix}/${member_id}`, body) 59 | } 60 | 61 | export default { 62 | getTeamMember, 63 | postCreatTeamMember, 64 | delTeamMember, 65 | getMemberPermissions, 66 | putMemberPermissions 67 | } 68 | -------------------------------------------------------------------------------- /apps/common/util/common.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: maxkb 4 | @Author:虎 5 | @file: common.py 6 | @date:2023/10/16 16:42 7 | @desc: 8 | """ 9 | import importlib 10 | from functools import reduce 11 | from typing import Dict, List 12 | 13 | 14 | def sub_array(array: List, item_num=10): 15 | result = [] 16 | temp = [] 17 | for item in array: 18 | temp.append(item) 19 | if len(temp) >= item_num: 20 | result.append(temp) 21 | temp = [] 22 | if len(temp) > 0: 23 | result.append(temp) 24 | return result 25 | 26 | 27 | def query_params_to_single_dict(query_params: Dict): 28 | return reduce(lambda x, y: {**x, **y}, list( 29 | filter(lambda item: item is not None, [({key: value} if value is not None and len(value) > 0 else None) for 30 | key, value in 31 | query_params.items()])), {}) 32 | 33 | 34 | def get_exec_method(clazz_: str, method_: str): 35 | """ 36 | 根据 class 和method函数 获取执行函数 37 | :param clazz_: class 字符串 38 | :param method_: 执行函数 39 | :return: 执行函数 40 | """ 41 | clazz_split = clazz_.split('.') 42 | clazz_name = clazz_split[-1] 43 | package = ".".join([clazz_split[index] for index in range(len(clazz_split) - 1)]) 44 | package_model = importlib.import_module(package) 45 | return getattr(getattr(package_model, clazz_name), method_) 46 | 47 | 48 | def post(post_function): 49 | def inner(func): 50 | def run(*args, **kwargs): 51 | result = func(*args, **kwargs) 52 | return post_function(*result) 53 | 54 | return run 55 | 56 | return inner 57 | -------------------------------------------------------------------------------- /apps/setting/models_provider/impl/local_model_provider/local_model_provider.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | """ 3 | @project: maxkb 4 | @Author:虎 5 | @file: zhipu_model_provider.py 6 | @date:2024/04/19 13:5 7 | @desc: 8 | """ 9 | import os 10 | from typing import Dict 11 | 12 | from pydantic import BaseModel 13 | 14 | from common.exception.app_exception import AppApiException 15 | from common.util.file_util import get_file_content 16 | from setting.models_provider.base_model_provider import ModelProvideInfo, ModelTypeConst, ModelInfo, IModelProvider, \ 17 | ModelInfoManage 18 | from setting.models_provider.impl.local_model_provider.credential.embedding import LocalEmbeddingCredential 19 | from setting.models_provider.impl.local_model_provider.model.embedding import LocalEmbedding 20 | from smartdoc.conf import PROJECT_DIR 21 | 22 | embedding_text2vec_base_chinese = ModelInfo('shibing624/text2vec-base-chinese', '', ModelTypeConst.EMBEDDING, 23 | LocalEmbeddingCredential(), LocalEmbedding) 24 | 25 | model_info_manage = (ModelInfoManage.builder().append_model_info(embedding_text2vec_base_chinese) 26 | .append_default_model_info(embedding_text2vec_base_chinese) 27 | .build()) 28 | 29 | 30 | class LocalModelProvider(IModelProvider): 31 | 32 | def get_model_info_manage(self): 33 | return model_info_manage 34 | 35 | def get_model_provide_info(self): 36 | return ModelProvideInfo(provider='model_local_provider', name='本地模型', icon=get_file_content( 37 | os.path.join(PROJECT_DIR, "apps", "setting", 'models_provider', 'impl', 'local_model_provider', 'icon', 38 | 'local_icon_svg'))) 39 | -------------------------------------------------------------------------------- /ui/src/components/dynamics-form/items/select/MultiSelect.vue: -------------------------------------------------------------------------------- 1 | 20 | 65 | 66 | -------------------------------------------------------------------------------- /apps/embedding/models/embedding.py: -------------------------------------------------------------------------------- 1 | # coding=utf-8 2 | 3 | from django.db import models 4 | 5 | from common.field.vector_field import VectorField 6 | from dataset.models.data_set import Document, Paragraph, DataSet 7 | from django.contrib.postgres.search import SearchVectorField 8 | 9 | 10 | class SourceType(models.TextChoices): 11 | """订单类型""" 12 | PROBLEM = 0, '问题' 13 | PARAGRAPH = 1, '段落' 14 | TITLE = 2, '标题' 15 | 16 | 17 | class SearchMode(models.TextChoices): 18 | embedding = 'embedding' 19 | keywords = 'keywords' 20 | blend = 'blend' 21 | 22 | 23 | class Embedding(models.Model): 24 | id = models.CharField(max_length=128, primary_key=True, verbose_name="主键id") 25 | 26 | source_id = models.CharField(max_length=128, verbose_name="资源id") 27 | 28 | source_type = models.CharField(verbose_name='资源类型', max_length=5, choices=SourceType.choices, 29 | default=SourceType.PROBLEM) 30 | 31 | is_active = models.BooleanField(verbose_name="是否可用", max_length=1, default=True) 32 | 33 | dataset = models.ForeignKey(DataSet, on_delete=models.DO_NOTHING, verbose_name="文档关联", db_constraint=False) 34 | 35 | document = models.ForeignKey(Document, on_delete=models.DO_NOTHING, verbose_name="文档关联", db_constraint=False) 36 | 37 | paragraph = models.ForeignKey(Paragraph, on_delete=models.DO_NOTHING, verbose_name="段落关联", db_constraint=False) 38 | 39 | embedding = VectorField(verbose_name="向量") 40 | 41 | search_vector = SearchVectorField(verbose_name="分词", default="") 42 | 43 | meta = models.JSONField(verbose_name="元数据", default=dict) 44 | 45 | class Meta: 46 | db_table = "embedding" 47 | unique_together = ['source_id', 'source_type'] 48 | --------------------------------------------------------------------------------