├── LICENSE.txt ├── backend ├── MANIFEST.in ├── promptmanager │ ├── .gitignore │ ├── .gitkeep │ ├── PromptManager │ │ ├── __init__.py │ │ ├── asgi.py │ │ ├── settings │ │ │ ├── __init__.py │ │ │ ├── base.py │ │ │ ├── dev.py │ │ │ ├── install.py │ │ │ ├── prod.py │ │ │ └── prod_test.py │ │ ├── urls.py │ │ ├── view.py │ │ └── wsgi.py │ ├── __init__.py │ ├── app_app │ │ ├── __init__.py │ │ ├── admin.py │ │ ├── apps.py │ │ ├── migrations │ │ │ └── __init__.py │ │ ├── models.py │ │ ├── sdk_example.md │ │ ├── tests.py │ │ ├── urls.py │ │ └── views.py │ ├── app_chat │ │ ├── __init__.py │ │ ├── admin.py │ │ ├── apps.py │ │ ├── migrations │ │ │ └── __init__.py │ │ ├── models.py │ │ ├── tests.py │ │ ├── urls.py │ │ └── views.py │ ├── app_common │ │ ├── __init__.py │ │ ├── category_type_default.py │ │ ├── collect_status_type.py │ │ ├── constant.py │ │ ├── database_util.py │ │ ├── enum_module_path.py │ │ ├── enum_publish_type.py │ │ ├── enum_source_type.py │ │ ├── file_util.py │ │ ├── http_request_util.py │ │ ├── json_util.py │ │ ├── page_util.py │ │ └── result_maker.py │ ├── app_flow │ │ ├── __init__.py │ │ ├── admin.py │ │ ├── apps.py │ │ ├── migrations │ │ │ └── __init__.py │ │ ├── models.py │ │ ├── tests.py │ │ ├── urls.py │ │ └── views.py │ ├── app_model │ │ ├── __init__.py │ │ ├── admin.py │ │ ├── apps.py │ │ ├── migrations │ │ │ └── __init__.py │ │ ├── models.py │ │ ├── tests.py │ │ ├── urls.py │ │ └── views.py │ ├── app_overview │ │ ├── Prompt Manager Quick Guide.md │ │ ├── __init__.py │ │ ├── admin.py │ │ ├── apps.py │ │ ├── migrations │ │ │ └── __init__.py │ │ ├── models.py │ │ ├── tests.py │ │ ├── urls.py │ │ └── views.py │ ├── app_prompt │ │ ├── __init__.py │ │ ├── admin.py │ │ ├── apps.py │ │ ├── migrations │ │ │ └── __init__.py │ │ ├── models.py │ │ ├── tests.py │ │ ├── urls.py │ │ └── views.py │ ├── db.sql │ ├── exception │ │ ├── README.md │ │ ├── __init__.py │ │ ├── base.py │ │ ├── exception.py │ │ ├── middleware.py │ │ └── views.py │ ├── manage.py │ ├── model │ │ ├── config.json │ │ └── default │ │ │ └── openapi_model.conf │ ├── pmctl │ │ ├── __init__.py │ │ ├── db.sqlite3 │ │ └── main.py │ ├── requirements.txt │ ├── runtime │ │ ├── __init__.py │ │ ├── app.py │ │ ├── chat.py │ │ ├── common_util.py │ │ ├── enumeration │ │ │ ├── enum_flow_status.py │ │ │ └── enum_node_status.py │ │ ├── exception │ │ │ ├── __init__.py │ │ │ ├── app_exception.py │ │ │ ├── base_exception.py │ │ │ ├── flow_exception.py │ │ │ ├── model_exception.py │ │ │ └── template_exception.py │ │ ├── flow.py │ │ ├── model.py │ │ └── template.py │ ├── script │ │ ├── __init__.py │ │ ├── chat_message_prompt_template.py │ │ ├── chat_prompt_template.py │ │ ├── chroma_reader.py │ │ ├── chroma_writer.py │ │ ├── csv_loader.py │ │ ├── dingodb_reader.py │ │ ├── dingodb_writer.py │ │ ├── embeddings │ │ │ ├── huggingface_embeddings.py │ │ │ └── openai_embeddings.py │ │ ├── huggingface_embeddings.py │ │ ├── human_message_prompt_template.py │ │ ├── openai_embeddings.py │ │ ├── prompt_runner.py │ │ ├── prompt_template.py │ │ ├── pydantic_v1 │ │ │ └── pydantic_v1.py │ │ ├── python3_script.py │ │ ├── schema │ │ │ ├── document.py │ │ │ ├── embeddings.py │ │ │ └── vectordb.py │ │ ├── system_message_prompt_template.py │ │ ├── text_loader.py │ │ ├── text_segmentation.py │ │ ├── text_splitter.py │ │ ├── text_truncation.py │ │ └── vectorstores │ │ │ ├── __init__.py │ │ │ └── dingo.py │ ├── template │ │ └── index.html │ ├── testmodule │ │ ├── __init__.py │ │ ├── admin.py │ │ ├── apps.py │ │ ├── migrations │ │ │ └── __init__.py │ │ ├── models.py │ │ ├── testmodule.py │ │ ├── tests.py │ │ └── urls.py │ └── web │ │ ├── favicon.png │ │ ├── umi.css │ │ └── umi.js └── setup.py ├── doc ├── .gitkeep ├── Community navigator.md ├── Prompt Manager Quick Guide.md ├── modules │ ├── AI model.md │ ├── Prompt App.md │ ├── Prompt Chat.md │ ├── Prompt Flow.md │ └── Prompt.md └── scripts │ └── readme.md ├── frontend ├── .editorconfig ├── .gitignore ├── .gitkeep ├── .prettierignore ├── .prettierrc ├── .umirc.ts ├── README.md ├── package.json ├── public │ ├── favicon.png │ └── test.md ├── src │ ├── assets │ │ ├── 404.png │ │ ├── default.svg │ │ ├── empty.png │ │ ├── overview-1.png │ │ ├── overview-2.png │ │ ├── overview-3.png │ │ ├── overview-4.png │ │ └── overview-5.png │ ├── components │ │ ├── editor │ │ │ ├── index.jsx │ │ │ └── index.less │ │ ├── empty │ │ │ ├── index.jsx │ │ │ └── index.less │ │ ├── field-sort │ │ │ ├── index.jsx │ │ │ └── index.less │ │ ├── flow-run │ │ │ ├── index.jsx │ │ │ └── index.less │ │ ├── icon │ │ │ └── index.jsx │ │ ├── publish-prompt │ │ │ ├── index.jsx │ │ │ └── index.less │ │ └── role │ │ │ ├── index.jsx │ │ │ └── index.less │ ├── config │ │ └── index.js │ ├── hooks │ │ └── useInterval.js │ ├── models │ │ └── useGlobalModel.js │ ├── pages │ │ ├── 404.jsx │ │ ├── ai-model │ │ │ ├── components │ │ │ │ ├── add.jsx │ │ │ │ ├── step1.jsx │ │ │ │ └── step2.jsx │ │ │ ├── index.jsx │ │ │ └── index.less │ │ ├── assets │ │ │ ├── GPU.png │ │ │ └── node.png │ │ ├── chat │ │ │ ├── chat-item │ │ │ │ ├── index.jsx │ │ │ │ └── index.less │ │ │ ├── main-footer │ │ │ │ ├── index.jsx │ │ │ │ └── index.less │ │ │ ├── main-right │ │ │ │ ├── index.jsx │ │ │ │ └── index.less │ │ │ ├── main │ │ │ │ ├── index.jsx │ │ │ │ └── index.less │ │ │ ├── utils │ │ │ │ └── index.js │ │ │ └── variable-modal │ │ │ │ ├── index.jsx │ │ │ │ └── index.less │ │ ├── components │ │ │ ├── bread │ │ │ │ └── index.jsx │ │ │ ├── layoutHeader │ │ │ │ ├── index.jsx │ │ │ │ └── index.less │ │ │ ├── menu │ │ │ │ └── index.jsx │ │ │ └── workflow │ │ │ │ ├── component │ │ │ │ ├── bottom-panel │ │ │ │ │ └── index.js │ │ │ │ ├── canvas │ │ │ │ │ ├── bg.jpg │ │ │ │ │ ├── bg1.jpg │ │ │ │ │ ├── canvas.less │ │ │ │ │ ├── canvasContainer.js │ │ │ │ │ ├── canvasContextWrapper.js │ │ │ │ │ ├── canvasNode.js │ │ │ │ │ ├── defaultCanvasNode.js │ │ │ │ │ ├── index.js │ │ │ │ │ └── toolbar │ │ │ │ │ │ ├── defaultCanvasMiniNode.js │ │ │ │ │ │ ├── index.js │ │ │ │ │ │ ├── minMap.js │ │ │ │ │ │ ├── minNode.js │ │ │ │ │ │ ├── miniCanvasWrapper.js │ │ │ │ │ │ ├── miniMapCanvas.js │ │ │ │ │ │ └── miniMapTool.js │ │ │ │ ├── drag-node │ │ │ │ │ ├── dragNode.less │ │ │ │ │ └── index.js │ │ │ │ ├── splitpanel │ │ │ │ │ └── index.js │ │ │ │ └── workflow │ │ │ │ │ ├── index.js │ │ │ │ │ ├── index.less │ │ │ │ │ └── workflow.less │ │ │ │ ├── context │ │ │ │ ├── canvasContext.js │ │ │ │ └── workflowContext.js │ │ │ │ ├── enum │ │ │ │ ├── CanvasEnum.js │ │ │ │ ├── CanvasOperationEnum.js │ │ │ │ └── MinMapEnum.js │ │ │ │ ├── hooks │ │ │ │ └── useUpdateHooks.js │ │ │ │ ├── index.js │ │ │ │ ├── style │ │ │ │ ├── index.js │ │ │ │ ├── index.less │ │ │ │ └── themes │ │ │ │ │ └── default.less │ │ │ │ └── utils │ │ │ │ ├── animation.js │ │ │ │ ├── jsPlumb.js │ │ │ │ ├── util.js │ │ │ │ └── workFLowToolkit.js │ │ ├── flow │ │ │ ├── canvas │ │ │ │ ├── canvasNode.js │ │ │ │ └── index.less │ │ │ ├── components │ │ │ │ ├── dragNode.js │ │ │ │ ├── flowModal.js │ │ │ │ ├── index.less │ │ │ │ ├── leftTree.js │ │ │ │ ├── right-info │ │ │ │ │ ├── configParams.js │ │ │ │ │ ├── index.less │ │ │ │ │ ├── renderItem.js │ │ │ │ │ ├── role1.js │ │ │ │ │ ├── scriptIO.js │ │ │ │ │ ├── scriptModal.js │ │ │ │ │ ├── scriptParams.js │ │ │ │ │ └── vectordb.js │ │ │ │ └── rightInfo.js │ │ │ ├── flowEdit.js │ │ │ ├── flowList.js │ │ │ ├── index.less │ │ │ └── util.js │ │ ├── index.jsx │ │ ├── layout │ │ │ ├── index.jsx │ │ │ └── index.less │ │ ├── overview │ │ │ ├── index.jsx │ │ │ └── index.less │ │ ├── prompt-app │ │ │ ├── edit-modal │ │ │ │ └── index.jsx │ │ │ ├── main │ │ │ │ ├── index.jsx │ │ │ │ └── index.less │ │ │ ├── sdk-modal │ │ │ │ ├── index.jsx │ │ │ │ └── index.less │ │ │ └── variable-modal │ │ │ │ ├── index.jsx │ │ │ │ └── index.less │ │ ├── prompt-guide │ │ │ ├── index.jsx │ │ │ └── index.less │ │ └── prompt-market │ │ │ ├── config-modal │ │ │ ├── index.jsx │ │ │ └── index.less │ │ │ ├── detail-modal │ │ │ ├── index.jsx │ │ │ └── index.less │ │ │ ├── main │ │ │ ├── index.jsx │ │ │ └── index.less │ │ │ ├── new-modal │ │ │ ├── index.jsx │ │ │ └── index.less │ │ │ ├── prompt-card │ │ │ ├── index.jsx │ │ │ └── index.less │ │ │ └── tags-filter │ │ │ ├── index.jsx │ │ │ └── index.less │ ├── services │ │ ├── aiModel.js │ │ ├── chat.js │ │ ├── flow.js │ │ ├── overview.js │ │ ├── promptApp.js │ │ └── promptMarket.js │ ├── styles │ │ ├── common-less │ │ │ ├── index.less │ │ │ ├── mixins │ │ │ │ └── index.less │ │ │ └── theme │ │ │ │ └── default.less │ │ └── index.less │ └── utils │ │ ├── http.js │ │ ├── index.js │ │ ├── request.js │ │ └── variable.js ├── theme.ts ├── tsconfig.json └── typings.d.ts └── readme.md /backend/MANIFEST.in: -------------------------------------------------------------------------------- 1 | recursive-include promptmanager/app_app *.md 2 | recursive-include promptmanager/app_overview *.md 3 | recursive-include promptmanager/command *.sh 4 | recursive-include promptmanager/exception *.md 5 | recursive-include promptmanager/model/default *.conf 6 | recursive-include promptmanager/model *.json 7 | recursive-include promptmanager/pmctl *.sqlite3 8 | recursive-include promptmanager/runtime/enumeration *.py 9 | recursive-include promptmanager/script *.py 10 | recursive-include promptmanager/template *.html 11 | recursive-include promptmanager/web *.png 12 | recursive-include promptmanager/web *.css 13 | recursive-include promptmanager/web *.js 14 | recursive-include promptmanager/web *.svg 15 | recursive-include promptmanager/web *.md 16 | recursive-include promptmanager *.sql 17 | recursive-include promptmanager *.txt 18 | recursive-include promptmanager *.py -------------------------------------------------------------------------------- /backend/promptmanager/.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | **/idea/ 3 | /xx/template/ 4 | /xx/web/ 5 | **/__pycache__/ 6 | /xx/flow/ 7 | ./prompt.log 8 | 9 | -------------------------------------------------------------------------------- /backend/promptmanager/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataCanvasIO/LMPM/5c5b7db32c3d82ec224f88a959d39040c5c3a68b/backend/promptmanager/.gitkeep -------------------------------------------------------------------------------- /backend/promptmanager/PromptManager/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataCanvasIO/LMPM/5c5b7db32c3d82ec224f88a959d39040c5c3a68b/backend/promptmanager/PromptManager/__init__.py -------------------------------------------------------------------------------- /backend/promptmanager/PromptManager/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for PromptManager 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", "PromptManager.settings") 15 | 16 | application = get_asgi_application() 17 | -------------------------------------------------------------------------------- /backend/promptmanager/PromptManager/settings/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataCanvasIO/LMPM/5c5b7db32c3d82ec224f88a959d39040c5c3a68b/backend/promptmanager/PromptManager/settings/__init__.py -------------------------------------------------------------------------------- /backend/promptmanager/PromptManager/settings/dev.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for PromptManager project. 3 | 4 | Generated by 'django-admin startproject' using Django 4.2.4. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/4.2/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/4.2/ref/settings/ 11 | """ 12 | import os.path 13 | from pathlib import Path 14 | from .base import * 15 | 16 | # Database 17 | # https://docs.djangoproject.com/en/4.2/ref/settings/#databases 18 | 19 | DATABASES = { 20 | "default": { 21 | "ENGINE": "django.db.backends.sqlite3", 22 | "NAME": BASE_DIR / "db.sqlite3", 23 | } 24 | } 25 | 26 | LOGGING = { 27 | 'version': 1, 28 | 'disable_existing_loggers': False, 29 | 'handlers': { 30 | 'file': { 31 | 'level': 'DEBUG', 32 | 'class': 'logging.FileHandler', 33 | 'filename': os.path.join(BASE_DIR, "prompt.log"), 34 | # 'filename': '/var/log/promptmanager/backend/prompt.log', 35 | }, 36 | }, 37 | 'loggers': { 38 | # 'django': { 39 | # 'handlers': ['file'], 40 | # 'level': 'DEBUG', 41 | # 'propagate': True, 42 | # }, 43 | 'pm_log':{ 44 | 'handlers': ['file'], 45 | 'level': 'DEBUG', 46 | 'propagate': True, 47 | } 48 | }, 49 | } 50 | -------------------------------------------------------------------------------- /backend/promptmanager/PromptManager/settings/install.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for PromptManager project. 3 | 4 | Generated by 'django-admin startproject' using Django 4.2.4. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/4.2/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/4.2/ref/settings/ 11 | """ 12 | import os.path 13 | from pathlib import Path 14 | from .base import * 15 | 16 | # Database 17 | # https://docs.djangoproject.com/en/4.2/ref/settings/#databases 18 | 19 | 20 | db_path = BASE_DIR / "pmctl/db.sqlite3" if os.getenv('SQLITE_DB_PATH') == None else os.getenv('SQLITE_DB_PATH') 21 | 22 | DATABASES = { 23 | "default": { 24 | "ENGINE": "django.db.backends.sqlite3", 25 | "NAME": db_path, 26 | } 27 | } 28 | 29 | LOGGING = { 30 | 'version': 1, 31 | 'disable_existing_loggers': False, 32 | 'handlers': { 33 | 'file': { 34 | 'level': 'DEBUG', 35 | 'class': 'logging.FileHandler', 36 | 'filename': os.path.join(BASE_DIR, "prompt.log"), 37 | # 'filename': '/var/log/promptmanager/backend/prompt.log', 38 | }, 39 | }, 40 | 'loggers': { 41 | # 'django': { 42 | # 'handlers': ['file'], 43 | # 'level': 'DEBUG', 44 | # 'propagate': True, 45 | # }, 46 | 'pm_log':{ 47 | 'handlers': ['file'], 48 | 'level': 'DEBUG', 49 | 'propagate': True, 50 | } 51 | }, 52 | } 53 | -------------------------------------------------------------------------------- /backend/promptmanager/PromptManager/settings/prod.py: -------------------------------------------------------------------------------- 1 | from .base import * 2 | 3 | # Database 4 | # https://docs.djangoproject.com/en/4.2/ref/settings/#databases 5 | 6 | DATABASES = { 7 | "default": { 8 | "ENGINE": "django.db.backends.sqlite3", 9 | "NAME": "/data/db/db.sqlite3", 10 | } 11 | } 12 | 13 | LOGGING = { 14 | 'version': 1, 15 | 'disable_existing_loggers': False, 16 | 'handlers': { 17 | 'file': { 18 | 'level': 'INFO', 19 | 'class': 'logging.FileHandler', 20 | # 'filename': os.path.join(BASE_DIR, "prompt.log"), 21 | 'filename': '/var/log/promptmanager/backend/prompt.log', 22 | }, 23 | }, 24 | 'loggers': { 25 | 'django': { 26 | 'handlers': ['file'], 27 | 'level': 'INFO', 28 | 'propagate': True, 29 | }, 30 | 'pm_log':{ 31 | 'handlers': ['file'], 32 | 'level': 'DEBUG', 33 | 'propagate': True, 34 | } 35 | }, 36 | } 37 | 38 | MODEL_PROXY = 'http://172.20.10.248:1080' 39 | -------------------------------------------------------------------------------- /backend/promptmanager/PromptManager/settings/prod_test.py: -------------------------------------------------------------------------------- 1 | from .base import * 2 | 3 | # Database 4 | # https://docs.djangoproject.com/en/4.2/ref/settings/#databases 5 | 6 | DATABASES = { 7 | "default": { 8 | "ENGINE": "django.db.backends.sqlite3", 9 | "NAME": "/data/test/db/db.sqlite3", 10 | } 11 | } 12 | 13 | LOGGING = { 14 | 'version': 1, 15 | 'disable_existing_loggers': False, 16 | 'handlers': { 17 | 'file': { 18 | 'level': 'DEBUG', 19 | 'class': 'logging.FileHandler', 20 | # 'filename': os.path.join(BASE_DIR, "prompt.log"), 21 | 'filename': '/var/log/promptmanager/test/backend/prompt.log', 22 | }, 23 | }, 24 | 'loggers': { 25 | 'django': { 26 | 'handlers': ['file'], 27 | 'level': 'DEBUG', 28 | 'propagate': True, 29 | }, 30 | 'pm_log':{ 31 | 'handlers': ['file'], 32 | 'level': 'DEBUG', 33 | 'propagate': True, 34 | } 35 | }, 36 | } 37 | 38 | MODEL_PROXY = 'http://172.20.10.248:1080' -------------------------------------------------------------------------------- /backend/promptmanager/PromptManager/urls.py: -------------------------------------------------------------------------------- 1 | """ 2 | URL configuration for PromptManager project. 3 | 4 | The `urlpatterns` list routes URLs to views. For more information please see: 5 | https://docs.djangoproject.com/en/4.2/topics/http/urls/ 6 | Examples: 7 | Function views 8 | 1. Add an import: from my_app import views 9 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 10 | Class-based views 11 | 1. Add an import: from other_app.views import Home 12 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 13 | Including another URLconf 14 | 1. Import the include() function: from django.urls import include, path 15 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 16 | """ 17 | 18 | from django.urls import path, include, re_path 19 | from django.views import static 20 | 21 | from promptmanager.PromptManager.settings import base 22 | from promptmanager.PromptManager import view 23 | 24 | urlpatterns = [ 25 | # path('admin/', admin.site.urls), 26 | path('test/', include("testmodule.urls")), 27 | path('api/chat/', include("app_chat.urls")), 28 | path('api/flow/', include("app_flow.urls")), 29 | path('api/model/', include("app_model.urls")), 30 | path('api/overview/', include("app_overview.urls")), 31 | path('api/app/', include("app_app.urls")), 32 | path('api/prompt/', include("app_prompt.urls")), 33 | path('api/chat/', include("app_chat.urls")), 34 | 35 | re_path(r'^web/(?P.*)$', static.serve, {'document_root': base.STATICFILES_DIRS}), 36 | re_path(r'^', view.IndexView.as_view(), name="index"), 37 | ] 38 | -------------------------------------------------------------------------------- /backend/promptmanager/PromptManager/view.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | from django.views import View 3 | 4 | 5 | class IndexView(View): 6 | def get(self, request): 7 | return render(request, "index.html") 8 | 9 | -------------------------------------------------------------------------------- /backend/promptmanager/PromptManager/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for PromptManager 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", "PromptManager.settings") 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /backend/promptmanager/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataCanvasIO/LMPM/5c5b7db32c3d82ec224f88a959d39040c5c3a68b/backend/promptmanager/__init__.py -------------------------------------------------------------------------------- /backend/promptmanager/app_app/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataCanvasIO/LMPM/5c5b7db32c3d82ec224f88a959d39040c5c3a68b/backend/promptmanager/app_app/__init__.py -------------------------------------------------------------------------------- /backend/promptmanager/app_app/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /backend/promptmanager/app_app/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class AppAppConfig(AppConfig): 5 | default_auto_field = 'django.db.models.BigAutoField' 6 | name = 'promptmanager.app_app' 7 | -------------------------------------------------------------------------------- /backend/promptmanager/app_app/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataCanvasIO/LMPM/5c5b7db32c3d82ec224f88a959d39040c5c3a68b/backend/promptmanager/app_app/migrations/__init__.py -------------------------------------------------------------------------------- /backend/promptmanager/app_app/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | class App(models.Model): 5 | id = models.TextField(primary_key=True,max_length=50) 6 | name = models.TextField(max_length=255) 7 | description = models.TextField() 8 | flow_id = models.TextField(max_length=50) 9 | input_info = models.TextField() 10 | source = models.TextField(max_length=50) 11 | user_id = models.TextField(max_length=50) 12 | create_time = models.IntegerField() 13 | update_time = models.IntegerField() 14 | 15 | class Meta: 16 | db_table = 'app' 17 | -------------------------------------------------------------------------------- /backend/promptmanager/app_app/sdk_example.md: -------------------------------------------------------------------------------- 1 | ```python 2 | from pathlib import Path 3 | 4 | from promptmanager.runtime.app import PMApp 5 | from promptmanager.runtime.flow import PMFlow 6 | 7 | # example1 8 | pmFlow = PMFlow.load(Path(__file__).resolve().parent / "flow/fc815003-cd5a-44b6-9593-0dce13f1f138/text_pm.pmflow"); 9 | pmApp = PMApp.publish_from_flow(pmFlow, "https://promptmanager.zetyun.cn") 10 | variables = [{"variable": "titletitletitletitle", "type": "text", "defaultValue": "", "value": ""}, {"variable": "num1num1num1num1num1num1num1num1", "type": "text", "defaultValue": "", "value": ""}] 11 | pmApp.run_by_pm_flow(variables=variables) 12 | pmApp.show_result() 13 | 14 | # example2 15 | variables = [{"variable": "titletitletitletitle", "type": "text", "defaultValue": ""}, {"variable": "num1num1num1num1num1num1num1num1", "type": "text", "defaultValue": ""}] 16 | url = "https://promptmanager.zetyun.cn/api/app/27a3909c-fe7b-442c-b05c-344630ebea03/run" 17 | PMApp.run_by_app_url(url, variables) 18 | ``` -------------------------------------------------------------------------------- /backend/promptmanager/app_app/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /backend/promptmanager/app_app/urls.py: -------------------------------------------------------------------------------- 1 | """ 2 | URL configuration for PromptManager project. 3 | 4 | The `urlpatterns` list routes URLs to views. For more information please see: 5 | https://docs.djangoproject.com/en/4.2/topics/http/urls/ 6 | Examples: 7 | Function views 8 | 1. Add an import: from my_app import views 9 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 10 | Class-based views 11 | 1. Add an import: from other_app.views import Home 12 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 13 | Including another URLconf 14 | 1. Import the include() function: from django.urls import include, path 15 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 16 | """ 17 | from django.urls import path 18 | 19 | from promptmanager.app_app import views 20 | 21 | urlpatterns = [ 22 | path("list", views.get_app_list, name="list"), 23 | path("delete", views.delete_app, name="delete"), 24 | path("update", views.update_app, name="update"), 25 | path("name/check", views.check_name, name="checkName"), 26 | path("sdk/demo/get", views.get_sdk_demo, name="getSdkDemo"), 27 | path("sdk/export", views.export_sdk, name="exportSdk"), 28 | path("from/flow/publish", views.publish_from_flow, name="publishFlow"), 29 | path("/run", views.run_app, name="runApp"), 30 | path("/upload", views.upload_file, name="uploadFile"), 31 | ] 32 | -------------------------------------------------------------------------------- /backend/promptmanager/app_chat/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataCanvasIO/LMPM/5c5b7db32c3d82ec224f88a959d39040c5c3a68b/backend/promptmanager/app_chat/__init__.py -------------------------------------------------------------------------------- /backend/promptmanager/app_chat/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /backend/promptmanager/app_chat/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ChatConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "promptmanager.app_chat" 7 | -------------------------------------------------------------------------------- /backend/promptmanager/app_chat/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataCanvasIO/LMPM/5c5b7db32c3d82ec224f88a959d39040c5c3a68b/backend/promptmanager/app_chat/migrations/__init__.py -------------------------------------------------------------------------------- /backend/promptmanager/app_chat/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /backend/promptmanager/app_chat/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /backend/promptmanager/app_chat/urls.py: -------------------------------------------------------------------------------- 1 | """ 2 | URL configuration for PromptManager project. 3 | 4 | The `urlpatterns` list routes URLs to views. For more information please see: 5 | https://docs.djangoproject.com/en/4.2/topics/http/urls/ 6 | Examples: 7 | Function views 8 | 1. Add an import: from my_app import views 9 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 10 | Class-based views 11 | 1. Add an import: from other_app.views import Home 12 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 13 | Including another URLconf 14 | 1. Import the include() function: from django.urls import include, path 15 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 16 | """ 17 | from django.urls import path 18 | 19 | from promptmanager.app_chat import views 20 | 21 | urlpatterns = [ 22 | path("model/configuration", views.model_configuration, name="model_configuration"), 23 | path("model/sync/completions", views.sync_chat_completions, name="sync_chat_completions"), 24 | path("model/upload/file", views.upload_file, name="upload_file"), 25 | path("model/async/completions", views.async_chat_completions, name="async_chat_completions"), 26 | ] 27 | -------------------------------------------------------------------------------- /backend/promptmanager/app_common/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataCanvasIO/LMPM/5c5b7db32c3d82ec224f88a959d39040c5c3a68b/backend/promptmanager/app_common/__init__.py -------------------------------------------------------------------------------- /backend/promptmanager/app_common/category_type_default.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | 4 | class CategoryTypeDefault(Enum): 5 | SCENE = '00000000-0000-0000-0000-000000000001' 6 | ROLE = '00000000-0000-0000-0000-000000000002' 7 | 8 | @staticmethod 9 | def getDefaultIdByType(name): 10 | for category in CategoryTypeDefault: 11 | if name == category.name: 12 | return category.value 13 | return None 14 | -------------------------------------------------------------------------------- /backend/promptmanager/app_common/collect_status_type.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | 4 | class CollectStatus(Enum): 5 | COLLECTED = 'collected' 6 | UNCOLLECTED = 'uncollected' 7 | -------------------------------------------------------------------------------- /backend/promptmanager/app_common/constant.py: -------------------------------------------------------------------------------- 1 | class Constant: 2 | 3 | DEFAULT_USER_ID = '00000000-1111-0000-000a-000000000001' 4 | 5 | DEFAULT_DEFINE_PROMPT_ID = '00000000-0000-0000-aaaa-000000000001' 6 | 7 | DEFINE_PROMPT_SCENE_ID = '00000000-0000-0000-0000-000000000000' 8 | 9 | PROMPT_SCENE_ID = '00000000-0000-0000-aaaa-000000000000' 10 | SCRIPT_SCENE_ID = '00000000-0000-0000-bbbb-000000000000' 11 | VECTORDB_SCENE_ID = '00000000-0000-0000-cccc-000000000000' 12 | AGENT_SCENE_ID = '00000000-0000-0000-dddd-000000000000' 13 | LOADER_SCENE_ID = '00000000-0000-0000-eeee-000000000000' 14 | EMBEDDING_SCENE_ID = '00000000-0000-0000-ffff-000000000000' 15 | # CHAINS_SCENE_ID = '00000000-0000-0000-gggg-000000000000' 16 | 17 | NONE_PROMPT_ROLE_ID = '00000000-0000-0000-0000-000000000002' 18 | 19 | -------------------------------------------------------------------------------- /backend/promptmanager/app_common/database_util.py: -------------------------------------------------------------------------------- 1 | from django.db import connection 2 | 3 | 4 | class DatabaseUtil: 5 | @staticmethod 6 | def query(query_sql, params=None, return_dict=False): 7 | cursor = connection.cursor() 8 | if params: 9 | cursor.execute(query_sql, params) 10 | else: 11 | cursor.execute(query_sql) 12 | connection.commit() 13 | if return_dict: 14 | return dict_fetchall(cursor) 15 | else: 16 | return cursor.fetchall() 17 | 18 | 19 | def dict_fetchall(cursor): 20 | "Return all rows from a cursor as a dict" 21 | columns = [col[0] for col in cursor.description] 22 | return [ 23 | dict(zip(columns, row)) 24 | for row in cursor.fetchall() 25 | ] 26 | -------------------------------------------------------------------------------- /backend/promptmanager/app_common/enum_module_path.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | from promptmanager.PromptManager.settings.base import BASE_DIR 4 | 5 | 6 | class ModulePath(Enum): 7 | PYTHON3_SCRIPT = {'module_id': '00000000-0000-0000-1111-000000000001', 8 | 'script_path': BASE_DIR / "script/python3_script.py"} 9 | 10 | TEXT_SEGMENTATION = {'module_id': '00000000-0000-0000-bbbb-000000000001', 11 | 'script_path': BASE_DIR / "script/text_segmentation.py"} 12 | 13 | TEXT_TRUNCATION = {'module_id': '00000000-0000-0000-bbbb-000000000002', 14 | 'script_path': BASE_DIR / "script/text_truncation.py"} 15 | 16 | CHROMA_WRITER = {'module_id': '00000000-0000-0000-cccc-000000000001', 17 | 'script_path': BASE_DIR / "script/chroma_writer.py"} 18 | 19 | CHROMA_READER = {'module_id': '00000000-0000-0000-cccc-000000000002', 20 | 'script_path': BASE_DIR / "script/chroma_reader.py"} 21 | 22 | DINGO_WRITER = {'module_id': '00000000-0000-0000-cccc-000000000003', 23 | 'script_path': BASE_DIR / "script/dingodb_writer.py"} 24 | 25 | DINGO_READER = {'module_id': '00000000-0000-0000-cccc-000000000004', 26 | 'script_path': BASE_DIR / "script/dingodb_reader.py"} 27 | 28 | CSV_LOADER = {'module_id': '00000000-0000-0000-eeee-000000000001', 29 | 'script_path': BASE_DIR / "script/csv_loader.py"} 30 | 31 | TEXT_LOADER = {'module_id': '00000000-0000-0000-eeee-000000000002', 32 | 'script_path': BASE_DIR / "script/text_loader.py"} 33 | 34 | OPENAI_EMBEDDINGS = {'module_id': '00000000-0000-0000-ffff-000000000001', 35 | 'script_path': BASE_DIR / "script/openai_embeddings.py"} 36 | 37 | HUGGINGFACE_EMBEDDINGS = {'module_id': '00000000-0000-0000-ffff-000000000002', 38 | 'script_path': BASE_DIR / "script/huggingface_embeddings.py"} 39 | 40 | PROMPT_TEMPLATE = {'module_id': '00000000-0000-0000-aaaa-000000000005', 41 | 'script_path': BASE_DIR / "script/prompt_template.py"} 42 | 43 | CHAT_MESSAGE_PROMPT_TEMPLATE = {'module_id': '00000000-0000-0000-aaaa-000000000002', 44 | 'script_path': BASE_DIR / "script/chat_message_prompt_template.py"} 45 | 46 | CHAT_PROMPT_TEMPLATE = {'module_id': '00000000-0000-0000-aaaa-000000000003', 47 | 'script_path': BASE_DIR / "script/chat_prompt_template.py"} 48 | 49 | HUMAN_MESSAGE_PROMPT_TEMPLATE = {'module_id': '00000000-0000-0000-aaaa-000000000004', 50 | 'script_path': BASE_DIR / "script/human_message_prompt_template.py"} 51 | 52 | SYSTEM_MESSAGE_PROMPT_TEMPLATE = {'module_id': '00000000-0000-0000-aaaa-000000000006', 53 | 'script_path': BASE_DIR / "script/system_message_prompt_template.py"} 54 | 55 | 56 | @staticmethod 57 | def get_module_path_by_id(module_id: str) -> str: 58 | for module in ModulePath: 59 | if module.value['module_id'] == module_id: 60 | return str(module.value['script_path']) 61 | 62 | from promptmanager.exception import exception 63 | raise exception.FLOW_MODULE_ID_NOT_SUPPORT 64 | -------------------------------------------------------------------------------- /backend/promptmanager/app_common/enum_publish_type.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | 4 | class PublishType(Enum): 5 | ADD = 'add' 6 | UPDATE = 'update' 7 | -------------------------------------------------------------------------------- /backend/promptmanager/app_common/enum_source_type.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | 4 | class SourceType(Enum): 5 | SYSTEM = 'system' 6 | USER = 'user' 7 | -------------------------------------------------------------------------------- /backend/promptmanager/app_common/file_util.py: -------------------------------------------------------------------------------- 1 | import os 2 | import shutil 3 | import zipfile 4 | import platform 5 | import codecs 6 | 7 | 8 | class FileUtil: 9 | 10 | @staticmethod 11 | def copy_folder(src, target): 12 | if not os.path.exists(src): 13 | return 14 | 15 | os.makedirs(target, exist_ok=True) 16 | 17 | for item in os.listdir(src): 18 | src_path = os.path.join(src, item) 19 | target_path = os.path.join(target, item) 20 | 21 | if os.path.isdir(src_path): 22 | FileUtil.copy_folder(src_path, target_path) 23 | else: 24 | shutil.copy2(src_path, target_path) 25 | 26 | @staticmethod 27 | def make_parent_dir(src_dir_path, target_path): 28 | system = platform.system() 29 | if system == 'Windows': 30 | slash = '\\' 31 | else: 32 | slash = '/' 33 | src_path_name = src_dir_path[src_dir_path.rindex(slash): len(src_dir_path)] 34 | target_path += src_path_name 35 | if not os.path.exists(target_path): 36 | os.makedirs(target_path) 37 | return target_path 38 | 39 | @staticmethod 40 | def zip_folder(folder_path, zip_path): 41 | with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf: 42 | for root, dirs, files in os.walk(folder_path): 43 | for file in files: 44 | file_path = os.path.join(root, file) 45 | zipf.write(file_path, os.path.relpath(file_path, folder_path)) 46 | 47 | 48 | @staticmethod 49 | def generate_file(data, file_name): 50 | file = codecs.open(file_name, 'w', encoding='utf-8') 51 | data_str = str(data) 52 | file.write(data_str) 53 | file.close() -------------------------------------------------------------------------------- /backend/promptmanager/app_common/http_request_util.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | 4 | class HttpRequestUtil: 5 | @staticmethod 6 | def get_http_request_body(request): 7 | request_body = request.body 8 | params = json.loads(request_body.decode()) 9 | return params 10 | -------------------------------------------------------------------------------- /backend/promptmanager/app_common/json_util.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | 4 | class JsonUtil: 5 | 6 | @staticmethod 7 | def object_to_dict(obj): 8 | json_str = json.dumps(obj, ensure_ascii=False, default=lambda obj: obj.__dict__) 9 | dict = json.loads(json_str) 10 | return dict 11 | 12 | @staticmethod 13 | def object_to_json(obj): 14 | json_str = json.dumps(obj, ensure_ascii=False, default=lambda obj: obj.__dict__) 15 | return json_str 16 | 17 | @staticmethod 18 | def json_to_dict(json_str): 19 | dict = json.loads(json_str) 20 | return dict 21 | 22 | @staticmethod 23 | def is_json(text): 24 | try: 25 | json.loads(text) 26 | return True 27 | except ValueError: 28 | return False 29 | -------------------------------------------------------------------------------- /backend/promptmanager/app_common/page_util.py: -------------------------------------------------------------------------------- 1 | from django.core.paginator import Paginator 2 | 3 | 4 | class PageUtil: 5 | 6 | @staticmethod 7 | def get_page(page_index, page_num, result): 8 | p = Paginator(result.values(), page_num) 9 | page_data = p.page(page_index) 10 | 11 | page_result = { 12 | 'count': result.count(), 13 | 'rows': list(page_data) 14 | } 15 | 16 | return page_result 17 | 18 | 19 | class PageInfo(object): 20 | def __init__(self, count: int = None, rows: list = None): 21 | self.rows = rows 22 | self.count = count 23 | 24 | 25 | 26 | 27 | 28 | class PageParam(object): 29 | def __init__(self, page_index: int = 1, page_num: int = 10): 30 | self.page_index = page_index 31 | self.page_num = page_num 32 | 33 | def get_offset(self): 34 | page_index = self.page_index 35 | if not page_index or page_index < 1: 36 | page_index = 1 37 | 38 | page_num = self.page_num 39 | if not page_num or page_num <= 0: 40 | page_num = 10 41 | 42 | return (page_index - 1) * page_num 43 | 44 | def get_limit(self): 45 | page_num = self.page_num 46 | if not page_num or page_num <= 0: 47 | page_num = 10 48 | 49 | return page_num 50 | 51 | def get_offset_and_limit(self): 52 | return self.get_offset(), self.get_limit() 53 | -------------------------------------------------------------------------------- /backend/promptmanager/app_common/result_maker.py: -------------------------------------------------------------------------------- 1 | from django.http import JsonResponse 2 | 3 | 4 | class ResultMaker: 5 | 6 | @staticmethod 7 | def success(data): 8 | # if isinstance(data, object): 9 | # data = JsonUtil.object_to_dict(data) 10 | 11 | result = { 12 | "code": 0, 13 | "data": data 14 | } 15 | return JsonResponse(result, json_dumps_params={'ensure_ascii': False}) 16 | 17 | @staticmethod 18 | def fail(code=1, msg=None, data=None): 19 | result = { 20 | "code": code, 21 | "data": { 22 | "message": msg, 23 | "data": data 24 | } 25 | } 26 | return JsonResponse(result, json_dumps_params={'ensure_ascii': False}) 27 | -------------------------------------------------------------------------------- /backend/promptmanager/app_flow/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataCanvasIO/LMPM/5c5b7db32c3d82ec224f88a959d39040c5c3a68b/backend/promptmanager/app_flow/__init__.py -------------------------------------------------------------------------------- /backend/promptmanager/app_flow/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /backend/promptmanager/app_flow/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class FlowConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "promptmanager.app_flow" 7 | -------------------------------------------------------------------------------- /backend/promptmanager/app_flow/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataCanvasIO/LMPM/5c5b7db32c3d82ec224f88a959d39040c5c3a68b/backend/promptmanager/app_flow/migrations/__init__.py -------------------------------------------------------------------------------- /backend/promptmanager/app_flow/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | 4 | # Create your models here. 5 | class Flow(models.Model): 6 | id = models.TextField(max_length=50, primary_key=True) 7 | name = models.TextField(max_length=255) 8 | description = models.TextField() 9 | config = models.TextField() 10 | model_ids = models.TextField() 11 | params = models.TextField() 12 | source = models.TextField(max_length=50) 13 | prompt_count = models.IntegerField() 14 | create_time = models.IntegerField() 15 | update_time = models.IntegerField() 16 | user_id = models.TextField() 17 | 18 | 19 | class Meta: 20 | db_table = 'flow' 21 | 22 | 23 | class Module(models.Model): 24 | id = models.TextField(max_length=50, primary_key=True) 25 | name = models.TextField(max_length=255) 26 | description = models.TextField() 27 | source = models.TextField(max_length=50) 28 | type = models.TextField(max_length=50) 29 | group = models.TextField(max_length=50) 30 | params = models.TextField() 31 | inputs = models.TextField() 32 | outputs = models.TextField() 33 | create_time = models.IntegerField() 34 | update_time = models.IntegerField() 35 | user_id = models.TextField() 36 | 37 | class Meta: 38 | db_table = 'module' 39 | -------------------------------------------------------------------------------- /backend/promptmanager/app_flow/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /backend/promptmanager/app_flow/urls.py: -------------------------------------------------------------------------------- 1 | """ 2 | URL configuration for PromptManager project. 3 | 4 | The `urlpatterns` list routes URLs to views. For more information please see: 5 | https://docs.djangoproject.com/en/4.2/topics/http/urls/ 6 | Examples: 7 | Function views 8 | 1. Add an import: from my_app import views 9 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 10 | Class-based views 11 | 1. Add an import: from other_app.views import Home 12 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 13 | Including another URLconf 14 | 1. Import the include() function: from django.urls import include, path 15 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 16 | """ 17 | from django.urls import path 18 | 19 | from promptmanager.app_flow import views 20 | 21 | urlpatterns = [ 22 | path("name/check", views.check_flow_name, name="checkName"), 23 | path("add", views.add_flow, name="add"), 24 | path("edit", views.edit_flow, name="edit"), 25 | path("list", views.get_flow_list, name="list"), 26 | path("copy", views.copy_flow, name="copy"), 27 | path("delete", views.delete_flow, name="delete"), 28 | path("publish/status", views.get_flow_publish_status, name="getFlowPublishStatus"), 29 | path("query", views.query, name="query"), 30 | 31 | path("module/tree", views.get_module_tree, name="getModuleTree"), 32 | path("node/create", views.create_flow_node, name="createFlowNode"), 33 | 34 | path("pmflow/get", views.get_pm_flow, name="getPMFlow"), 35 | path("pmflow/save", views.save_pm_flow, name="savePMFlow"), 36 | path("pmflow/publish", views.publish_pm_flow, name="publishPMFlow"), 37 | path("pmflow/variables", views.get_pm_flow_variables, name="getPMFLowVariables"), 38 | path("pmflow/inputNode/check", views.check_input_node, name="checkInputNode"), 39 | path("pmflow/run", views.run_pm_flow, name="runPMFLow"), 40 | path("pmflow/run/status", views.get_pm_flow_run_status, name="getPMFlowRunStatus"), 41 | 42 | path("node/script/save", views.save_script_file, name="saveScriptFile"), 43 | path("node/script/get", views.get_script_content, name="getScriptContent"), 44 | 45 | path("app/delete", views.delete_flow_and_app, name="deleteFlowAndApp"), 46 | 47 | ] 48 | -------------------------------------------------------------------------------- /backend/promptmanager/app_model/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataCanvasIO/LMPM/5c5b7db32c3d82ec224f88a959d39040c5c3a68b/backend/promptmanager/app_model/__init__.py -------------------------------------------------------------------------------- /backend/promptmanager/app_model/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /backend/promptmanager/app_model/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ModelConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "promptmanager.app_model" 7 | -------------------------------------------------------------------------------- /backend/promptmanager/app_model/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataCanvasIO/LMPM/5c5b7db32c3d82ec224f88a959d39040c5c3a68b/backend/promptmanager/app_model/migrations/__init__.py -------------------------------------------------------------------------------- /backend/promptmanager/app_model/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | class Model(models.Model): 5 | id = models.TextField(primary_key=True,max_length=50) 6 | name = models.TextField(max_length=255) 7 | description = models.TextField() 8 | config = models.TextField() 9 | params = models.TextField() 10 | source = models.TextField(max_length=50) 11 | enable_stream = models.BooleanField() 12 | is_default = models.BooleanField() 13 | user_id = models.TextField(max_length=50) 14 | create_time = models.IntegerField() 15 | update_time = models.IntegerField() 16 | 17 | class Meta: 18 | db_table = 'model' -------------------------------------------------------------------------------- /backend/promptmanager/app_model/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /backend/promptmanager/app_model/urls.py: -------------------------------------------------------------------------------- 1 | """ 2 | URL configuration for PromptManager project. 3 | 4 | The `urlpatterns` list routes URLs to views. For more information please see: 5 | https://docs.djangoproject.com/en/4.2/topics/http/urls/ 6 | Examples: 7 | Function views 8 | 1. Add an import: from my_app import views 9 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 10 | Class-based views 11 | 1. Add an import: from other_app.views import Home 12 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 13 | Including another URLconf 14 | 1. Import the include() function: from django.urls import include, path 15 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 16 | """ 17 | from django.urls import path 18 | 19 | from promptmanager.app_model import views 20 | 21 | urlpatterns = [ 22 | path("config/get", views.get_model_config, name="getConfig"), 23 | path("params/parse", views.parse_model_params, name="parseParam"), 24 | path("save", views.save_model, name="save"), 25 | path("list", views.get_model_list, name="list"), 26 | path("update", views.update_model, name="update"), 27 | path("default", views.default_model, name="default"), 28 | path("delete", views.delete_model, name="delete"), 29 | path("name/check", views.check_name, name="checkName") 30 | ] 31 | -------------------------------------------------------------------------------- /backend/promptmanager/app_overview/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataCanvasIO/LMPM/5c5b7db32c3d82ec224f88a959d39040c5c3a68b/backend/promptmanager/app_overview/__init__.py -------------------------------------------------------------------------------- /backend/promptmanager/app_overview/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /backend/promptmanager/app_overview/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class OverviewConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "promptmanager.app_overview" 7 | -------------------------------------------------------------------------------- /backend/promptmanager/app_overview/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataCanvasIO/LMPM/5c5b7db32c3d82ec224f88a959d39040c5c3a68b/backend/promptmanager/app_overview/migrations/__init__.py -------------------------------------------------------------------------------- /backend/promptmanager/app_overview/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /backend/promptmanager/app_overview/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /backend/promptmanager/app_overview/urls.py: -------------------------------------------------------------------------------- 1 | """ 2 | URL configuration for PromptManager project. 3 | 4 | The `urlpatterns` list routes URLs to views. For more information please see: 5 | https://docs.djangoproject.com/en/4.2/topics/http/urls/ 6 | Examples: 7 | Function views 8 | 1. Add an import: from my_app import views 9 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 10 | Class-based views 11 | 1. Add an import: from other_app.views import Home 12 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 13 | Including another URLconf 14 | 1. Import the include() function: from django.urls import include, path 15 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 16 | """ 17 | from django.urls import path 18 | 19 | from promptmanager.app_overview import views 20 | 21 | urlpatterns = [ 22 | path("component", views.component, name="component"), 23 | path("quickguide", views.qucikguide, name="qucikguide"), 24 | ] 25 | -------------------------------------------------------------------------------- /backend/promptmanager/app_overview/views.py: -------------------------------------------------------------------------------- 1 | from django.http import FileResponse 2 | 3 | from promptmanager.app_common.database_util import DatabaseUtil 4 | from promptmanager.app_common.result_maker import ResultMaker 5 | 6 | from promptmanager.exception import exception 7 | from pathlib import Path 8 | 9 | 10 | def component(request): 11 | if request.method != 'GET': 12 | raise exception.REQUEST_TYPE_NOT_SUPPORT() 13 | 14 | prompt_count = DatabaseUtil.query(query_sql='select count(*) from prompt') 15 | model_count = DatabaseUtil.query(query_sql='select count(*) from model') 16 | flow_count = DatabaseUtil.query(query_sql='select count(*) from flow') 17 | app_count = DatabaseUtil.query(query_sql='select count(*) from app') 18 | 19 | result = { 20 | 'prompt': prompt_count[0][0], 21 | 'model': model_count[0][0], 22 | 'flow': flow_count[0][0], 23 | 'app': app_count[0][0] 24 | } 25 | 26 | return ResultMaker.success(result) 27 | 28 | 29 | def qucikguide(request): 30 | path = Path(__file__).resolve().parent / 'Prompt Manager Quick Guide.md' 31 | return FileResponse(open(path, 'rb')) -------------------------------------------------------------------------------- /backend/promptmanager/app_prompt/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataCanvasIO/LMPM/5c5b7db32c3d82ec224f88a959d39040c5c3a68b/backend/promptmanager/app_prompt/__init__.py -------------------------------------------------------------------------------- /backend/promptmanager/app_prompt/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /backend/promptmanager/app_prompt/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class PromptConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "promptmanager.app_prompt" 7 | -------------------------------------------------------------------------------- /backend/promptmanager/app_prompt/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataCanvasIO/LMPM/5c5b7db32c3d82ec224f88a959d39040c5c3a68b/backend/promptmanager/app_prompt/migrations/__init__.py -------------------------------------------------------------------------------- /backend/promptmanager/app_prompt/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | class Category(models.Model): 5 | id = models.TextField(max_length=50, primary_key=True) 6 | name = models.TextField(max_length=255) 7 | source = models.TextField(max_length=50) 8 | role_prompt = models.TextField(max_length=50) 9 | type = models.TextField(max_length=30) 10 | create_time = models.IntegerField() 11 | update_time = models.IntegerField() 12 | order_id = models.IntegerField() 13 | user_id = models.TextField() 14 | 15 | class Meta: 16 | db_table = 'class' 17 | 18 | class Prompt(models.Model): 19 | id = models.TextField(max_length=50, primary_key=True) 20 | name = models.TextField(max_length=255) 21 | note = models.TextField(max_length=50, blank=True) 22 | prompt = models.TextField(max_length=50, blank=True) 23 | source = models.TextField(max_length=50) 24 | role_id = models.TextField(max_length=50) 25 | scene_id = models.TextField(max_length=50) 26 | labels_ids = models.TextField() 27 | variables = models.TextField() 28 | collecte_status = models.TextField(max_length=30) 29 | create_time = models.IntegerField() 30 | update_time = models.IntegerField() 31 | user_id = models.TextField() 32 | score = models.FloatField() 33 | 34 | class Meta: 35 | db_table = 'prompt' 36 | -------------------------------------------------------------------------------- /backend/promptmanager/app_prompt/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /backend/promptmanager/app_prompt/urls.py: -------------------------------------------------------------------------------- 1 | """ 2 | URL configuration for PromptManager project. 3 | 4 | The `urlpatterns` list routes URLs to views. For more information please see: 5 | https://docs.djangoproject.com/en/4.2/topics/http/urls/ 6 | Examples: 7 | Function views 8 | 1. Add an import: from my_app import views 9 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 10 | Class-based views 11 | 1. Add an import: from other_app.views import Home 12 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 13 | Including another URLconf 14 | 1. Import the include() function: from django.urls import include, path 15 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 16 | """ 17 | from django.urls import path 18 | 19 | from promptmanager.app_prompt import views 20 | 21 | urlpatterns = [ 22 | path("category/add", views.category_add, name="add"), 23 | path("category/list", views.category_list, name="list"), 24 | path("category/update", views.category_update, name="update"), 25 | path("category/delete/validate", views.category_delete_validate, name="deleteValidate"), 26 | path("category/delete", views.category_delete, name="delete"), 27 | path("category/move/order", views.category_move_order, name="move"), 28 | path("category/batch/operate", views.category_batch_operate, name="batch_operate"), 29 | path("page", views.prompt_page, name="prompt_page"), 30 | path("add", views.prompt_add, name="prompt_add"), 31 | path("name/check", views.prompt_name_validate, name="prompt_name_validate"), 32 | path("update", views.prompt_update, name="prompt_update"), 33 | path("delete", views.prompt_delete, name="prompt_delete"), 34 | path("detail", views.prompt_detail, name="prompt_detail"), 35 | path("variables/parse", views.variables_parse, name="variables_parse"), 36 | path("scenegroup/list", views.scenegroup_list, name="scenegroup_list"), 37 | path("list", views.prompt_list, name="prompt_list"), 38 | path("category/name/check", views.category_name_validate, name="category_name_validate"), 39 | ] 40 | -------------------------------------------------------------------------------- /backend/promptmanager/exception/README.md: -------------------------------------------------------------------------------- 1 | ## 简介 2 | 3 | django-exceptionbox 是一个 Django 的异常处理工具包。 4 | 5 | 将 HTTP 状态码封装成 Python 异常 Base 类,使用 raise 抛出异常,通过 Django 中间件,统一处理异常,打印日志。 6 | 7 | 通过继承 base.py 中的异常类,可以实现对异常的封装。 8 | 9 | 需要注意的是,返回的错误码是类名,是一个短语。这样处理的目的是为了前后端更易于理解和使用。 10 | 11 | 下载包之后,需要重命名为 exceptionbox。 12 | 13 | ## 配置 14 | 15 | 添加中间件 'exceptionbox.middleware.ExceptionBoxMiddleware' 到 settings中 16 | 17 | ```python 18 | MIDDLEWARE_CLASSES = ( 19 | 'exception.middleware.ExceptionBoxMiddleware', 20 | ... 21 | ) 22 | ``` 23 | 24 | 中间件的位置没有特殊要求。 25 | 26 | ## 使用 27 | 28 | ### 第一步 29 | 30 | 在 `exceptionbox/error.py` 文件,继承 `base.py` 中的异常类,实现业务逻辑相关的异常类。 31 | 32 | 例如: 33 | 34 | ```python 35 | class ERROR_LOGIN_FRONT_PAY_NOT_MONEY(base.PreconditionFailed412): 36 | message = "没有足够余额" 37 | ``` 38 | 39 | ### 第二步 40 | 41 | 在 `views.py` 文件中,抛出异常。 42 | 43 | ```python 44 | 45 | from promptmanager import exception 46 | 47 | 48 | def my_view(request): 49 | raise exception.ERROR_LOGIN_FRONT_PAY_NOT_MONEY() 50 | ``` 51 | 52 | 接口返回 53 | 54 | status_code = 412 55 | 56 | ```json 57 | { 58 | "message": "没有足够余额", 59 | "code": "ERROR_LOGIN_FRONT_PAY_NOT_MONEY", 60 | "data": null, 61 | "result": false 62 | } 63 | ``` 64 | -------------------------------------------------------------------------------- /backend/promptmanager/exception/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from .exception import * 3 | -------------------------------------------------------------------------------- /backend/promptmanager/exception/base.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from abc import ABCMeta 3 | 4 | 5 | class BaseReturn(Exception): 6 | __metaclass__ = ABCMeta 7 | 8 | 9 | class OK200(BaseReturn): 10 | status_code = 200 11 | 12 | class OK500(BaseReturn): 13 | status_code = 500 14 | -------------------------------------------------------------------------------- /backend/promptmanager/exception/middleware.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import json 3 | import logging 4 | import traceback 5 | 6 | from django.http import JsonResponse 7 | 8 | from .base import BaseReturn 9 | from ..runtime.exception.base_exception import RuntimeException 10 | 11 | logger = logging.getLogger('pm_log') 12 | 13 | 14 | class ExceptionMiddleware(object): 15 | 16 | def __init__(self, get_response): 17 | self.get_response = get_response 18 | 19 | def __call__(self, request): 20 | return self.get_response(request) 21 | 22 | def process_exception(self, request, exception): 23 | if not issubclass(exception.__class__, BaseReturn) and not issubclass(exception.__class__, RuntimeException): 24 | return None 25 | ret_json = { 26 | 'code': getattr(exception, 'code', ''), 27 | 'data': { 28 | 'message': getattr(exception, 'message', ''), 29 | 'stackTrace': exception.__class__.__name__ 30 | }, 31 | } 32 | response = JsonResponse(ret_json) 33 | response.status_code = getattr(exception, 'status_code', 500) 34 | 35 | # if exception is base_exception(runtime exception),sert status_code 200 36 | if issubclass(exception.__class__, RuntimeException): 37 | response.status_code = 200 38 | 39 | # _logger = logger.error if response.status_code >= 500 else logger.warning 40 | _logger = logger.error 41 | _logger('status_code->{status_code}, error_code->{code}, url->{url}, ' 42 | 'method->{method}, param->{param}, ' 43 | 'traceback->{traceback}'.format( 44 | status_code=response.status_code, code=ret_json['code'], url=request.path, 45 | method=request.method, param=json.dumps(getattr(request, request.method, {})), 46 | traceback=traceback.format_exc() 47 | )) 48 | return response 49 | -------------------------------------------------------------------------------- /backend/promptmanager/exception/views.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import inspect 3 | 4 | from django.http import JsonResponse 5 | 6 | from . import base, exception 7 | 8 | 9 | def get_all_error_code(request): 10 | ret_list = [(item[1].status_code, item[1].__name__, item[1].message) 11 | for item in inspect.getmembers(exception) 12 | if inspect.isclass(item[1]) and item not in inspect.getmembers(base)] 13 | return JsonResponse({'http_status_code - error_code - message': sorted(ret_list, key=lambda x: x[0])}) 14 | -------------------------------------------------------------------------------- /backend/promptmanager/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", "PromptManager.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 | -------------------------------------------------------------------------------- /backend/promptmanager/model/config.json: -------------------------------------------------------------------------------- 1 | {"config": "{\n \"protocol\": \"http\",\n \"method\": \"POST\",\n \"url\": \"https://api.openai.com/v1/chat/completions\",\n \"header\": {\n \"ContentType\": \"application/json\",\n \"Authorization\": \"Bearer ${OPENAI_API_KEY}\"\n },\n \"modelRole\": {\n \"user\": \"user\",\n \"system\": \"system\",\n \"assistant\": \"assistant\"\n },\n \"requestBody\": {\n \"model\": \"${model}\",\n \"messages\": ${message},\n \"temperature\": ${temperature},\n \"stream\": ${stream}\n },\n \"responseBody\": {\n \"id\": \"chatcmpl-7lZq4UwSCrkvyOTUcyReAMXpAydSQ\",\n \"object\": \"chat.completion\",\n \"created\": \"1691573536\",\n \"model\": \"gpt-3.5-turbo-0613\",\n \"choices\": ${result},\n \"usage\": {\n \"prompt_tokens\": 36,\n \"completion_tokens\": 104,\n \"total_tokens\": 140\n }\n },\n \"responseErrorBody\": {\n \"error\": {\n \"message\": \"${errorMessage}\",\n \"type\": \"invalid_request_error\",\n \"param\": null,\n \"code\": null\n }\n },\n \"responseStreamBody\": {\n \"id\": \"chatcmpl-7lZq4UwSCrkvyOTUcyReAMXpAydSQ\",\n \"object\": \"chat.completion\",\n \"created\": \"1691573536\",\n \"model\": \"gpt-3.5-turbo-0613\",\n \"choices\": ${stream_result}\n }\n}", 2 | "params": [ 3 | { 4 | "name": "OPENAI_API_KEY", 5 | "type": "Password", 6 | "defaultValue": "" 7 | }, 8 | { 9 | "name": "model", 10 | "type": "Select", 11 | "defaultValue": "gpt-3.5-turbo-0613;gpt-3.5-turbo;gpt-3.5-turbo-16k-0613;gpt-3.5-turbo-16k;gpt-4-0613;gpt-4-32k-0613;gpt-4;gpt-4-32k" 12 | }, 13 | { 14 | "name": "message", 15 | "type": "Jsonarray", 16 | "defaultValue": [{"role": "${role}", "content": "${content}"}] 17 | }, 18 | { 19 | "name": "temperature", 20 | "type": "Double", 21 | "defaultValue": 0.7 22 | }, 23 | { 24 | "name": "stream", 25 | "type": "Boolean", 26 | "defaultValue": false 27 | }, 28 | { 29 | "name": "result", 30 | "type": "Jsonarray", 31 | "defaultValue": [{"index": 0, "message": {"role": "assistant", "content": "${result_content}"}, "finish_reason": "stop"}] 32 | }, 33 | { 34 | "name": "errorMessage", 35 | "type": "String", 36 | "defaultValue": null 37 | }, 38 | { 39 | "name": "stream_result", 40 | "type": "Jsonarray", 41 | "defaultValue": [ 42 | { 43 | "index": 0, 44 | "delta": { 45 | "role": "assistant", 46 | "content": "${stream_content}" 47 | }, 48 | "finish_reason": null 49 | } 50 | ] 51 | } 52 | ] 53 | } 54 | -------------------------------------------------------------------------------- /backend/promptmanager/model/default/openapi_model.conf: -------------------------------------------------------------------------------- 1 | {"config": "{\n \"protocol\": \"http\",\n \"method\": \"POST\",\n \"url\": \"https://api.openai.com/v1/chat/completions\",\n \"header\": {\n \"ContentType\": \"application/json\",\n \"Authorization\": \"Bearer ${OPENAI_API_KEY}\"\n },\n \"modelRole\": {\n \"user\": \"user\",\n \"system\": \"system\",\n \"assistant\": \"assistant\"\n },\n \"requestBody\": {\n \"model\": \"${model}\",\n \"messages\": ${message},\n \"temperature\": ${temperature},\n \"stream\": ${stream}\n },\n \"responseBody\": {\n \"id\": \"chatcmpl-7lZq4UwSCrkvyOTUcyReAMXpAydSQ\",\n \"object\": \"chat.completion\",\n \"created\": \"1691573536\",\n \"model\": \"gpt-3.5-turbo-0613\",\n \"choices\": ${result},\n \"usage\": {\n \"prompt_tokens\": 36,\n \"completion_tokens\": 104,\n \"total_tokens\": 140\n }\n },\n \"responseErrorBody\": {\n \"error\": {\n \"message\": \"${errorMessage}\",\n \"type\": \"invalid_request_error\",\n \"param\": null,\n \"code\": null\n }\n },\n \"responseStreamBody\": {\n \"id\": \"chatcmpl-7lZq4UwSCrkvyOTUcyReAMXpAydSQ\",\n \"object\": \"chat.completion\",\n \"created\": \"1691573536\",\n \"model\": \"gpt-3.5-turbo-0613\",\n \"choices\": ${stream_result}\n }\n}", 2 | "params": [ 3 | { 4 | "name": "OPENAI_API_KEY", 5 | "type": "Password", 6 | "defaultValue": "" 7 | }, 8 | { 9 | "name": "model", 10 | "type": "Select", 11 | "defaultValue": "gpt-3.5-turbo-0613;gpt-3.5-turbo;gpt-3.5-turbo-16k-0613;gpt-3.5-turbo-16k;gpt-4-0613;gpt-4-32k-0613;gpt-4;gpt-4-32k" 12 | }, 13 | { 14 | "name": "message", 15 | "type": "Jsonarray", 16 | "defaultValue": [{"role": "${role}", "content": "${content}"}] 17 | }, 18 | { 19 | "name": "temperature", 20 | "type": "Double", 21 | "defaultValue": 0.7 22 | }, 23 | { 24 | "name": "stream", 25 | "type": "Boolean", 26 | "defaultValue": false 27 | }, 28 | { 29 | "name": "result", 30 | "type": "Jsonarray", 31 | "defaultValue": [{"index": 0, "message": {"role": "assistant", "content": "${result_content}"}, "finish_reason": "stop"}] 32 | }, 33 | { 34 | "name": "errorMessage", 35 | "type": "String", 36 | "defaultValue": null 37 | }, 38 | { 39 | "name": "stream_result", 40 | "type": "Jsonarray", 41 | "defaultValue": [ 42 | { 43 | "index": 0, 44 | "delta": { 45 | "role": "assistant", 46 | "content": "${stream_content}" 47 | }, 48 | "finish_reason": null 49 | } 50 | ] 51 | } 52 | ] 53 | } 54 | -------------------------------------------------------------------------------- /backend/promptmanager/pmctl/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataCanvasIO/LMPM/5c5b7db32c3d82ec224f88a959d39040c5c3a68b/backend/promptmanager/pmctl/__init__.py -------------------------------------------------------------------------------- /backend/promptmanager/pmctl/db.sqlite3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataCanvasIO/LMPM/5c5b7db32c3d82ec224f88a959d39040c5c3a68b/backend/promptmanager/pmctl/db.sqlite3 -------------------------------------------------------------------------------- /backend/promptmanager/pmctl/main.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import subprocess 3 | import psutil 4 | import operator 5 | import platform 6 | import os 7 | import socket 8 | 9 | from pathlib import Path 10 | 11 | 12 | def main(): 13 | parser = argparse.ArgumentParser(prog='pmctl') 14 | 15 | parser.add_argument( 16 | "-v", "--version", action="version", 17 | version=f"{parser.prog} version 1.0.0" 18 | ) 19 | 20 | subparsers = parser.add_subparsers(dest='sub_command', help='sub-command help') 21 | 22 | service_parser = subparsers.add_parser('service') 23 | service_parser.add_argument('action', choices=['start', 'stop'], help='action to perform on the service') 24 | service_parser.add_argument('-ip', type=str, help='service ip') 25 | service_parser.add_argument('-port', type=int, help='serivce port') 26 | service_parser.add_argument('-proxy', type=str, help='model proxy') 27 | service_parser.add_argument('-db', type=str, help='sqlite db path') 28 | 29 | args = parser.parse_args() 30 | 31 | if getattr(args, "sub_command") == 'service': 32 | if args.action == 'start': 33 | port = 9999 34 | if args.port: 35 | port = args.port 36 | print('service port: ' + str(port)) 37 | 38 | ip = socket.gethostbyname(socket.gethostname()) 39 | if args.ip: 40 | ip = args.ip 41 | print('service ip: ' + ip) 42 | 43 | ip_port = ip + ':' + str(port) 44 | runserver_url = 'http://' + ip_port 45 | os.putenv('RUNSERVER_URL', runserver_url) 46 | print('runserver_url: ' + runserver_url) 47 | 48 | proxy = args.proxy 49 | if proxy: 50 | os.putenv('MODEL_PROXY', proxy) 51 | print('proxy: ' + proxy) 52 | 53 | if args.db: 54 | db_path = args.db 55 | print('sqlite db path: ' + db_path) 56 | os.putenv('SQLITE_DB_PATH', db_path) 57 | 58 | manage_path = str(Path(__file__).resolve().parent.parent / 'manage.py') 59 | print('manege_path: ' + manage_path) 60 | subprocess.run(['python', f"{manage_path}", 'runserver', f"{ip_port}", '--settings=PromptManager.settings.install']) 61 | elif args.action == 'stop': 62 | pids = get_runserver_pids() 63 | if not pids: 64 | print('service PID not found!') 65 | else: 66 | for pid in pids: 67 | print('PID ' + str(pid) + ' will be killed!') 68 | os.kill(pid, 9) 69 | print('service stop done') 70 | 71 | 72 | def get_runserver_pids(): 73 | pids = [] 74 | system = platform.system() 75 | if system == 'Windows': 76 | name = 'promptmanager\manage.py' 77 | else: 78 | name = 'promptmanager/manage.py' 79 | for process in psutil.process_iter(['pid', 'cmdline']): 80 | cmdline = process.info['cmdline'] 81 | if cmdline: 82 | for line in cmdline: 83 | if operator.contains(line, name): 84 | pids.append(process.pid) 85 | return pids 86 | 87 | 88 | if __name__ == "__main__": 89 | main() 90 | -------------------------------------------------------------------------------- /backend/promptmanager/requirements.txt: -------------------------------------------------------------------------------- 1 | asgiref==3.7.2 2 | Django==4.2.4 3 | pip==23.2.1 4 | pytz==2023.3 5 | setuptools==68.0.0 6 | sqlparse==0.4.4 7 | typing_extensions==4.7.1 8 | tzdata==2023.3 9 | wheel==0.38.4 10 | Requests==2.31.0 11 | pandas==2.1.0 12 | python-docx==0.8.11 13 | openpyxl==3.1.2 14 | xlrd==2.0.1 15 | psutil==5.9.0 16 | numpy==1.26.0 17 | tenacity==8.2.2 -------------------------------------------------------------------------------- /backend/promptmanager/runtime/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataCanvasIO/LMPM/5c5b7db32c3d82ec224f88a959d39040c5c3a68b/backend/promptmanager/runtime/__init__.py -------------------------------------------------------------------------------- /backend/promptmanager/runtime/app.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import json 3 | 4 | from promptmanager.runtime.flow import PMFlow 5 | from promptmanager.runtime.common_util import PMCommonUtil 6 | from promptmanager.runtime.exception import app_exception 7 | 8 | publish_flow_url = '/api/app/from/flow/publish' 9 | delete_flow_url = '/api/flow/app/delete' 10 | 11 | 12 | class PMApp: 13 | def __init__(self, pm_flow:PMFlow=None, base_url=None, app_id=None): 14 | self.pm_flow = pm_flow 15 | self.base_url = base_url 16 | self._app_id = app_id 17 | 18 | @staticmethod 19 | def publish_from_flow(pm_flow:PMFlow, base_url, name=None): 20 | # http://127.0.0.1:8888 21 | app_publish_url = base_url + publish_flow_url 22 | data = {'pm_flow': PMCommonUtil.object_to_json(pm_flow), 'name': name} 23 | req = requests.post(url=app_publish_url, json=data) 24 | if req.status_code != 200: 25 | raise app_exception.PUBLISH_FROM_FLOW_ERROR(10001, req.text) 26 | else: 27 | response = json.loads(req.text) 28 | if 'code' in response and response['code'] != 0: 29 | raise app_exception.PUBLISH_FROM_FLOW_ERROR(10001, response['data']['message']) 30 | return PMApp(pm_flow, base_url) 31 | 32 | def run_by_pm_flow(self, variables, run_async=False): 33 | pm_flow = self.pm_flow 34 | try: 35 | pm_flow.run(variables=variables, run_async=run_async) 36 | except Exception as e: 37 | flow_id = pm_flow.id 38 | data = {'id': flow_id} 39 | requests.delete(url=self.base_url + delete_flow_url, json=data) 40 | raise e 41 | 42 | 43 | def show_result(self): 44 | pm_flow = self.pm_flow 45 | return pm_flow.get_result() 46 | 47 | @staticmethod 48 | def run_by_app_url(url, variables): 49 | if isinstance(variables, dict): 50 | variables = PMCommonUtil.convert_dict_to_list(variables) 51 | data = {'variables': variables} 52 | #http://127.0.0.1:8888/api/app/518cd781-6ca4-401b-7202-4bb2c90ced4c/run 53 | req = requests.post(url=url, json=data) 54 | if req.status_code != 200: 55 | raise app_exception.RUN_APP_ERROR(10002, req.text) 56 | else: 57 | response = json.loads(req.text) 58 | if 'code' in response and response['code'] != 0: 59 | raise app_exception.RUN_APP_ERROR(10002, response['data']['message']) 60 | return response['data'] -------------------------------------------------------------------------------- /backend/promptmanager/runtime/chat.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from typing import Generator 3 | 4 | from promptmanager.runtime.exception import template_exception 5 | from promptmanager.runtime.model import PMLLM 6 | from promptmanager.runtime.template import PMChatPromptTemplate 7 | 8 | #设置日志控制台输出 9 | logging.basicConfig(level="INFO", format="%(asctime)s - %(levelname)s: %(message)s", encoding="utf-8") 10 | logger = logging.getLogger() 11 | 12 | class PMChat(object): 13 | def __init__(self, template: PMChatPromptTemplate = None, pm_model: PMLLM = None): 14 | self.template = template 15 | self.pm_model = pm_model 16 | 17 | def run(self, variables: str, params=None): 18 | template = self.template 19 | pm_model = self.pm_model 20 | messages = template.messages(variables, True) 21 | logger.info("this chat message info: %s" % messages) 22 | if params: 23 | logger.info("this chat params info: %s" % params) 24 | else: 25 | logger.info("this chat params info: {}".format({})) 26 | 27 | #调用模型的request方法 28 | if not params: 29 | result = pm_model.request_result_by_message(messages) 30 | if isinstance(result, Generator): 31 | for line in result: 32 | logger.info("%s" % line) 33 | else: 34 | logger.info("this chat result_content info: {}".format(result)) 35 | return result 36 | else: 37 | if isinstance(params, dict) or isinstance(params, list): 38 | result = pm_model.request_result_by_message(messages, params) 39 | if isinstance(result, Generator): 40 | for line in result: 41 | logger.info("%s" % line) 42 | else: 43 | logger.info("this chat result_content info: {}".format(result)) 44 | return result 45 | else: 46 | raise template_exception.ILLEGAL_PARAMS_TYPE() 47 | 48 | -------------------------------------------------------------------------------- /backend/promptmanager/runtime/enumeration/enum_flow_status.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | 4 | class PMFlowStatus(Enum): 5 | RUNNING = 'running' 6 | SUCCESS = 'success' 7 | FAILED = 'failed' 8 | -------------------------------------------------------------------------------- /backend/promptmanager/runtime/enumeration/enum_node_status.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | from promptmanager.runtime.enumeration.enum_flow_status import PMFlowStatus 4 | 5 | 6 | class PMNodeStatus(Enum): 7 | QUEUED = 'queued' 8 | RUNNING = 'running' 9 | SUCCESS = 'success' 10 | FAILED = 'failed' 11 | -------------------------------------------------------------------------------- /backend/promptmanager/runtime/exception/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataCanvasIO/LMPM/5c5b7db32c3d82ec224f88a959d39040c5c3a68b/backend/promptmanager/runtime/exception/__init__.py -------------------------------------------------------------------------------- /backend/promptmanager/runtime/exception/app_exception.py: -------------------------------------------------------------------------------- 1 | from promptmanager.runtime.exception.base_exception import RuntimeException 2 | 3 | 4 | class PUBLISH_FROM_FLOW_ERROR(RuntimeException): 5 | def __init__(self, code, message): 6 | self.code = code 7 | self.message = message 8 | 9 | 10 | class RUN_APP_ERROR(RuntimeException): 11 | def __init__(self, code, message): 12 | self.code = code 13 | self.message = message 14 | -------------------------------------------------------------------------------- /backend/promptmanager/runtime/exception/base_exception.py: -------------------------------------------------------------------------------- 1 | class RuntimeException(Exception): 2 | code = 00000 3 | message = u"runtime exception" 4 | -------------------------------------------------------------------------------- /backend/promptmanager/runtime/exception/flow_exception.py: -------------------------------------------------------------------------------- 1 | from promptmanager.runtime.exception.base_exception import RuntimeException 2 | 3 | 4 | class ILLEGAL_EDGE_INFO(RuntimeException): 5 | code = 10001 6 | message = u"edge info is illegal!!!" 7 | 8 | 9 | class FLOW_INPUT_NOT_EXIST(RuntimeException): 10 | code = 10002 11 | message = u"flow input is not exists!!!" 12 | 13 | 14 | class FLOW_TARGET_NODE_INPUT_NOT_MATCH(RuntimeException): 15 | code = 10003 16 | message = u"flow target node input not match!!!" 17 | 18 | 19 | class FLOW_RUN_VARIABLES_ILLEGAL(RuntimeException): 20 | code = 10004 21 | message = u"run flow variables illegal" 22 | 23 | 24 | class Flow_NODE_NOT_EXIST(RuntimeException): 25 | code = 10005 26 | message = u"flow node not exists" 27 | 28 | 29 | class FLOW_RUN_EXCEPTION(RuntimeException): 30 | code = 10006 31 | message = u"flow run exception" 32 | 33 | 34 | class NODE_PARAMS_ILLEGAL(RuntimeException): 35 | def __init__(self, code=10007, param_name: str = None): 36 | self.code = code 37 | self.message = "node param illegal:" + param_name 38 | 39 | 40 | class FLOW_INPUT_NODE_CONNCTS_NO_EDGE(RuntimeException): 41 | code = 10008 42 | message = u"The input node is not connected." 43 | 44 | 45 | class DINGO_QUERY_ERROR(RuntimeException): 46 | def __init__(self, code=10009, message: str = None): 47 | self.code = code 48 | self.message = message 49 | -------------------------------------------------------------------------------- /backend/promptmanager/runtime/exception/model_exception.py: -------------------------------------------------------------------------------- 1 | from promptmanager.runtime.exception.base_exception import RuntimeException 2 | 3 | 4 | class UNSUPPORTED_REQUEST_METHOD(RuntimeException): 5 | code = 10001 6 | message = u"unsupported request method" 7 | 8 | class UNSUPPORTED_PARAMS_TYPE(RuntimeException): 9 | code = 10002 10 | message = u"unsupportes params type" 11 | 12 | class DEFAULT_VALUE_IS_REQUIRED(RuntimeException): 13 | code = 10003 14 | message = u"default value is required" 15 | 16 | class MESSAGE_PARAM_UNSUPPORTED_CUSTOM(RuntimeException): 17 | code = 10004 18 | message = u"message param unsupported custom" 19 | 20 | class RESULT_PARAM_UNSUPPORTED_CUSTOM(RuntimeException): 21 | code = 10005 22 | message = u"result param unsupported custom" 23 | 24 | class REQUEST_ERROR(RuntimeException): 25 | def __init__(self, code, message): 26 | self.code = code 27 | self.message = message 28 | 29 | class PARAM_FORMAT_ERROR(RuntimeException): 30 | def __init__(self, code, message): 31 | self.code = code 32 | self.message = message 33 | 34 | class PROXY_NOT_CONFIG(RuntimeException): 35 | code = 10008 36 | message = u"proxy not config" 37 | -------------------------------------------------------------------------------- /backend/promptmanager/runtime/exception/template_exception.py: -------------------------------------------------------------------------------- 1 | from promptmanager.runtime.exception.base_exception import RuntimeException 2 | 3 | 4 | class ILLEGAL_MESSAGE_VARIABLES(RuntimeException): 5 | code = 20001 6 | message = u"message variables type is illegal,the variables should be dict[list]!" 7 | 8 | class ILLEGAL_TEMPLATE_LIST_TYPE(RuntimeException): 9 | code = 20002 10 | message = u"template list property type is illegal,the template list property type should be PMPromptTemplate[list]!" 11 | 12 | class ILLEGAL_PARAMS_TYPE(RuntimeException): 13 | code = 20003 14 | message = u"params type is illegal,the params type should be dict or dict[list]!" 15 | -------------------------------------------------------------------------------- /backend/promptmanager/script/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataCanvasIO/LMPM/5c5b7db32c3d82ec224f88a959d39040c5c3a68b/backend/promptmanager/script/__init__.py -------------------------------------------------------------------------------- /backend/promptmanager/script/chat_message_prompt_template.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from promptmanager.runtime.flow import PMNodeOutput 4 | from promptmanager.script.prompt_template import PromptTemplate 5 | 6 | logger = logging.getLogger('pm_log') 7 | 8 | class ChatMessagePromptTemplate(PromptTemplate): 9 | """Chat message prompt template.""" 10 | 11 | def run(params: dict = None, inputs: dict = None, outputs=None) -> PMNodeOutput: 12 | logger.info("Welcome to use Chat Message Prompt Template!") 13 | logger.info("This is params info:") 14 | logger.info(params) 15 | 16 | logger.info("To get the input of inputs value:") 17 | logger.info(inputs) 18 | 19 | logger.info("To get the prompt from params:") 20 | prompt_variables = [input for input in inputs.values()] 21 | template_content = params['prompt'] 22 | logger.info(prompt_variables) 23 | 24 | logger.info("To get the AI model params from config and params:") 25 | if 'message' in params['model']: 26 | del params['model']['message'] 27 | if 'result' in params['model']: 28 | del params['model']['result'] 29 | model_params = [model_param for model_param in params['model'].values()] 30 | model_conf = params['model']['model_config']['value'] 31 | model_param_define = params['model']['model_param_define']['value'] 32 | logger.info(model_conf) 33 | logger.info(model_params) 34 | 35 | logger.info("To get the rolePrompt from params:") 36 | role_prompt = params['role']['rolePrompt']['value'] 37 | logger.info(role_prompt) 38 | logger.info("To get the messageRole from params:") 39 | role_name = params['script']['message_role']['value'] 40 | logger.info(role_name) 41 | 42 | 43 | logger.info("To call AI model:") 44 | promptTemplate = PromptTemplate(template_content, role_name, role_prompt, prompt_variables, model_conf, 45 | model_param_define, model_params) 46 | result = promptTemplate.exec() 47 | 48 | logger.info(result) 49 | 50 | output = PMNodeOutput() 51 | for output_name in outputs.keys(): 52 | output.add_output(output_name, result) 53 | 54 | return output -------------------------------------------------------------------------------- /backend/promptmanager/script/huggingface_embeddings.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from promptmanager.runtime.common_util import PMCommonUtil 4 | from promptmanager.runtime.flow import PMNodeOutput 5 | from promptmanager.script.embeddings.huggingface_embeddings import PMHuggingFaceEmbeddings 6 | 7 | logger = logging.getLogger('pm_log') 8 | 9 | 10 | def run(params: dict, inputs: dict, outputs: dict) -> PMNodeOutput: 11 | logger.info("Welcome to Use HuggingFace Embeddings!") 12 | 13 | logger.info("This is params info:") 14 | logger.info(params) 15 | logger.info("This is inputs info:") 16 | logger.info(inputs) 17 | logger.info("This is outputs info:") 18 | logger.info(outputs) 19 | 20 | model_name = params['script']['model_name']['value'] 21 | if not model_name: 22 | from promptmanager.script.embeddings.huggingface_embeddings import DEFAULT_MODEL_NAME 23 | model_name = DEFAULT_MODEL_NAME 24 | 25 | model_kwargs = params['script']['model_kwargs']['value'] 26 | if model_kwargs: 27 | model_kwargs = PMCommonUtil.json_to_dict(model_kwargs) 28 | 29 | embeddings = PMHuggingFaceEmbeddings(model_name=model_name, model_kwargs=model_kwargs) 30 | results = embeddings 31 | 32 | output = PMNodeOutput() 33 | for output_name in outputs.keys(): 34 | output.add_output(output_name, results) 35 | 36 | return output 37 | -------------------------------------------------------------------------------- /backend/promptmanager/script/human_message_prompt_template.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from promptmanager.runtime.flow import PMNodeOutput 4 | from promptmanager.script.prompt_template import PromptTemplate 5 | 6 | logger = logging.getLogger('pm_log') 7 | class HumanMessagePromptTemplate(PromptTemplate): 8 | """Human message prompt template.""" 9 | 10 | def run(params: dict = None, inputs: dict = None, outputs=None) -> PMNodeOutput: 11 | logger.info("Welcome to use Human Message Prompt Template!") 12 | logger.info("This is params info:") 13 | logger.info(params) 14 | 15 | logger.info("To get the input of inputs value:") 16 | logger.info(inputs) 17 | 18 | logger.info("To get the prompt from params:") 19 | prompt_variables = [input for input in inputs.values()] 20 | template_content = params['prompt'] 21 | logger.info(prompt_variables) 22 | 23 | logger.info("To get the AI model params from config and params:") 24 | if 'message' in params['model']: 25 | del params['model']['message'] 26 | if 'result' in params['model']: 27 | del params['model']['result'] 28 | model_params = [model_param for model_param in params['model'].values()] 29 | model_conf = params['model']['model_config']['value'] 30 | model_param_define = params['model']['model_param_define']['value'] 31 | logger.info(model_conf) 32 | logger.info(model_params) 33 | 34 | logger.info("To get the rolePrompt from params:") 35 | role_prompt = params['role']['rolePrompt']['value'] 36 | logger.info(role_prompt) 37 | 38 | logger.info("To call AI model:") 39 | humanPromptTemplate = HumanMessagePromptTemplate(template_content, 'user', role_prompt, prompt_variables, model_conf, model_param_define, model_params) 40 | result = humanPromptTemplate.exec() 41 | 42 | logger.info(result) 43 | 44 | output = PMNodeOutput() 45 | for output_name in outputs.keys(): 46 | output.add_output(output_name, result) 47 | 48 | return output -------------------------------------------------------------------------------- /backend/promptmanager/script/openai_embeddings.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | 4 | from promptmanager.runtime.flow import PMNodeOutput 5 | from promptmanager.script.embeddings.openai_embeddings import PMOpenAIEmbeddings 6 | 7 | logger = logging.getLogger('pm_log') 8 | 9 | 10 | def run(params: dict, inputs: dict, outputs: dict) -> PMNodeOutput: 11 | logger.info("Welcome to Use OpenAI Embeddings!") 12 | 13 | logger.info("This is params info:") 14 | logger.info(params) 15 | logger.info("This is inputs info:") 16 | logger.info(inputs) 17 | logger.info("This is outputs info:") 18 | logger.info(outputs) 19 | 20 | openai_key = params['script']['openai_key']['value'] 21 | 22 | os.environ["OPENAI_API_KEY"] = openai_key 23 | model_proxy = os.getenv('MODEL_PROXY') if os.getenv('MODEL_PROXY') is not None else None 24 | if model_proxy: 25 | os.environ["OPENAI_PROXY"] = model_proxy 26 | 27 | embeddings = PMOpenAIEmbeddings() 28 | results = embeddings 29 | 30 | output = PMNodeOutput() 31 | for output_name in outputs.keys(): 32 | output.add_output(output_name, results) 33 | 34 | return output 35 | -------------------------------------------------------------------------------- /backend/promptmanager/script/prompt_runner.py: -------------------------------------------------------------------------------- 1 | from promptmanager.runtime.flow import PMNodeOutput 2 | import logging 3 | from promptmanager.runtime.model import PMCustomLLM 4 | from promptmanager.runtime.template import PMPromptTemplate 5 | 6 | 7 | class PMPromptRunner: 8 | def __init__(self, template_content: str, role_prompt: str = None, prompt_variables: list[dict] = None, 9 | model_conf: str = None, model_param_define: dict = None, model_params: list[dict] = None): 10 | self.template_content = template_content 11 | self.role_prompt = role_prompt 12 | self.prompt_variables = prompt_variables 13 | self.model_conf = model_conf 14 | self.model_param_define = model_param_define 15 | self.model_params = model_params 16 | 17 | def exec(self) -> str: 18 | customLLM = PMCustomLLM.load_from_config(self.model_conf, self.model_param_define) 19 | prompt = PMPromptTemplate(role="user", template_content=self.template_content, role_prompt=self.role_prompt) 20 | result = customLLM.request_result_by_message(prompt.message(self.prompt_variables,True), self.model_params) 21 | return result 22 | 23 | 24 | def run(params: dict, inputs: dict, outputs: dict) -> PMNodeOutput: 25 | logger = logging.getLogger('pm_log') 26 | logger.info("Welcome to Large Model Prompt Manager World!") 27 | # runtime =PMRuntime.get_current_runtime() 28 | logger.info("This is params info:") 29 | logger.info(params) 30 | 31 | logger.info("To get the input of inputs value:") 32 | logger.info(inputs) 33 | 34 | logger.info("To get the prompt from params:") 35 | prompt_variables = [input for input in inputs.values()] 36 | template_content = params['prompt'] 37 | logger.info(prompt_variables) 38 | 39 | logger.info("To get the AI model params from config and params:") 40 | if 'message' in params['model']: 41 | del params['model']['message'] 42 | if 'result' in params['model']: 43 | del params['model']['result'] 44 | model_params = [model_param for model_param in params['model'].values()] 45 | model_conf = params['model']['model_config']['value'] 46 | model_param_define = params['model']['model_param_define']['value'] 47 | logger.info(model_conf) 48 | logger.info(model_params) 49 | 50 | logger.info("To get the rolePrompt from params:") 51 | role_prompt = params['role']['rolePrompt']['value'] 52 | logger.info(role_prompt) 53 | 54 | logger.info("To call AI model:") 55 | runner = PMPromptRunner(template_content, role_prompt, prompt_variables, model_conf, model_param_define, 56 | model_params) 57 | result = runner.exec() 58 | 59 | logger.info(result) 60 | 61 | output = PMNodeOutput() 62 | for output_name in outputs.keys(): 63 | output.add_output(output_name, result) 64 | 65 | return output 66 | -------------------------------------------------------------------------------- /backend/promptmanager/script/prompt_template.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from promptmanager.runtime.flow import PMNodeOutput 4 | from promptmanager.runtime.model import PMCustomLLM 5 | from promptmanager.runtime.template import PMPromptTemplate 6 | 7 | logger = logging.getLogger('pm_log') 8 | 9 | class PromptTemplate(PMPromptTemplate): 10 | 11 | def __init__(self, template_content: str, role: str = 'user', role_prompt: str = None, prompt_variables: list[dict] = None, 12 | model_conf: str = None, model_param_define: dict = None, model_params: list[dict] = None): 13 | self.template_content = template_content 14 | self.role_prompt = role_prompt 15 | self.prompt_variables = prompt_variables 16 | self.model_conf = model_conf 17 | self.model_param_define = model_param_define 18 | self.model_params = model_params 19 | self.role = role 20 | 21 | def exec(self) -> str: 22 | customLLM = PMCustomLLM.load_from_config(self.model_conf, self.model_param_define) 23 | prompt = PMPromptTemplate(role=self.role, template_content=self.template_content, role_prompt=self.role_prompt) 24 | result = customLLM.request_result_by_message(prompt.message(self.prompt_variables, True), self.model_params) 25 | return result 26 | 27 | def run(params: dict = None, inputs: dict = None, outputs=None) -> PMNodeOutput: 28 | logger.info("Welcome to use Prompt Template!") 29 | logger.info("This is params info:") 30 | logger.info(params) 31 | 32 | logger.info("To get the input of inputs value:") 33 | logger.info(inputs) 34 | 35 | logger.info("To get the prompt from params:") 36 | prompt_variables = [input for input in inputs.values()] 37 | template_content = params['prompt'] 38 | logger.info(prompt_variables) 39 | 40 | logger.info("To get the AI model params from config and params:") 41 | if 'message' in params['model']: 42 | del params['model']['message'] 43 | if 'result' in params['model']: 44 | del params['model']['result'] 45 | model_params = [model_param for model_param in params['model'].values()] 46 | model_conf = params['model']['model_config']['value'] 47 | model_param_define = params['model']['model_param_define']['value'] 48 | logger.info(model_conf) 49 | logger.info(model_params) 50 | 51 | logger.info("To get the rolePrompt from params:") 52 | role_prompt = params['role']['rolePrompt']['value'] 53 | logger.info("To get the messageRole from params:") 54 | role_name = params['script']['message_role']['value'] 55 | logger.info(role_name) 56 | 57 | logger.info("To call AI model:") 58 | promptTemplate = PromptTemplate(template_content, role_name, role_prompt, prompt_variables, model_conf, model_param_define, model_params) 59 | result = promptTemplate.exec() 60 | 61 | logger.info(result) 62 | 63 | output = PMNodeOutput() 64 | for output_name in outputs.keys(): 65 | output.add_output(output_name, result) 66 | 67 | return output 68 | -------------------------------------------------------------------------------- /backend/promptmanager/script/pydantic_v1/pydantic_v1.py: -------------------------------------------------------------------------------- 1 | from importlib import metadata 2 | 3 | ## Create namespaces for pydantic v1 and v2. 4 | # This code must stay at the top of the file before other modules may 5 | # attempt to import pydantic since it adds pydantic_v1 and pydantic_v2 to sys.modules. 6 | # 7 | # This hack is done for the following reasons: 8 | # * Langchain will attempt to remain compatible with both pydantic v1 and v2 since 9 | # both dependencies and dependents may be stuck on either version of v1 or v2. 10 | # * Creating namespaces for pydantic v1 and v2 should allow us to write code that 11 | # unambiguously uses either v1 or v2 API. 12 | # * This change is easier to roll out and roll back. 13 | 14 | try: 15 | from pydantic.v1 import * # noqa: F403 16 | except ImportError: 17 | from pydantic import * # noqa: F403 18 | 19 | 20 | try: 21 | _PYDANTIC_MAJOR_VERSION: int = int(metadata.version("pydantic").split(".")[0]) 22 | except metadata.PackageNotFoundError: 23 | _PYDANTIC_MAJOR_VERSION = 0 -------------------------------------------------------------------------------- /backend/promptmanager/script/python3_script.py: -------------------------------------------------------------------------------- 1 | from promptmanager.runtime.flow import PMNodeOutput 2 | import logging 3 | 4 | logger = logging.getLogger('pm_log') 5 | 6 | 7 | class PMCustomScript: 8 | def __init__(self): 9 | pass 10 | 11 | def exec(self, inputs: dict = None): 12 | logger.info("To get the input of inputs value") 13 | 14 | input_value = inputs['input']['value'] 15 | logger.info(input_value) 16 | 17 | logger.info("write your script here") 18 | 19 | result = input_value 20 | return result 21 | 22 | 23 | def run(params: dict = None, inputs: dict = None, outputs=None) -> PMNodeOutput: 24 | logger.info("Welcome to Large Model Prompt Manager World!") 25 | logger.info("This is params info:") 26 | logger.info(params) 27 | logger.info("This is inputs info:") 28 | logger.info(inputs) 29 | logger.info("This is outputs info:") 30 | logger.info(outputs) 31 | 32 | custom_script = PMCustomScript() 33 | text = custom_script.exec(inputs) 34 | 35 | output = PMNodeOutput() 36 | for output_name in outputs.keys(): 37 | output.add_output(output_name, text) 38 | 39 | return output 40 | -------------------------------------------------------------------------------- /backend/promptmanager/script/schema/embeddings.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | from abc import ABC, abstractmethod 3 | from typing import List 4 | 5 | 6 | class PMEmbeddings(ABC): 7 | """Interface for embedding models.""" 8 | 9 | @abstractmethod 10 | def embed_documents(self, texts: List[str]) -> List[List[float]]: 11 | """Embed search docs.""" 12 | 13 | @abstractmethod 14 | def embed_query(self, text: str) -> List[float]: 15 | """Embed query text.""" 16 | 17 | async def aembed_documents(self, texts: List[str]) -> List[List[float]]: 18 | """Asynchronous Embed search docs.""" 19 | return await asyncio.get_running_loop().run_in_executor( 20 | None, self.embed_documents, texts 21 | ) 22 | 23 | async def aembed_query(self, text: str) -> List[float]: 24 | """Asynchronous Embed query text.""" 25 | return await asyncio.get_running_loop().run_in_executor( 26 | None, self.embed_query, text 27 | ) 28 | -------------------------------------------------------------------------------- /backend/promptmanager/script/system_message_prompt_template.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from promptmanager.runtime.flow import PMNodeOutput 4 | from promptmanager.script.prompt_template import PromptTemplate 5 | 6 | logger = logging.getLogger('pm_log') 7 | class SystemMessagePromptTemplate(PromptTemplate): 8 | """System message prompt template.""" 9 | 10 | def run(params: dict = None, inputs: dict = None, outputs=None) -> PMNodeOutput: 11 | logger.info("Welcome to use System Message Prompt Template!") 12 | logger.info("This is params info:") 13 | logger.info(params) 14 | 15 | logger.info("To get the input of inputs value:") 16 | logger.info(inputs) 17 | 18 | logger.info("To get the prompt from params:") 19 | prompt_variables = [input for input in inputs.values()] 20 | template_content = params['prompt'] 21 | logger.info(prompt_variables) 22 | 23 | logger.info("To get the AI model params from config and params:") 24 | if 'message' in params['model']: 25 | del params['model']['message'] 26 | if 'result' in params['model']: 27 | del params['model']['result'] 28 | model_params = [model_param for model_param in params['model'].values()] 29 | model_conf = params['model']['model_config']['value'] 30 | model_param_define = params['model']['model_param_define']['value'] 31 | logger.info(model_conf) 32 | logger.info(model_params) 33 | 34 | logger.info("To get the rolePrompt from params:") 35 | role_prompt = params['role']['rolePrompt']['value'] 36 | logger.info(role_prompt) 37 | 38 | logger.info("To call AI model:") 39 | systemPromptTemplate = SystemMessagePromptTemplate(template_content, 'system', role_prompt, prompt_variables, model_conf, model_param_define, model_params) 40 | result = systemPromptTemplate.exec() 41 | 42 | logger.info(result) 43 | 44 | output = PMNodeOutput() 45 | for output_name in outputs.keys(): 46 | output.add_output(output_name, result) 47 | 48 | return output -------------------------------------------------------------------------------- /backend/promptmanager/script/text_loader.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from typing import List, Optional 3 | 4 | from promptmanager.runtime.flow import PMNodeOutput 5 | from promptmanager.script.schema.document import PMDocument 6 | 7 | logger = logging.getLogger("pm_log") 8 | 9 | 10 | class TextLoader(): 11 | """Load text file. 12 | 13 | 14 | Args: 15 | file_path: Path to the file to load. 16 | 17 | encoding: File encoding to use. If `None`, the file will be loaded 18 | with the default system encoding. 19 | 20 | autodetect_encoding: Whether to try to autodetect the file encoding 21 | if the specified encoding fails. 22 | """ 23 | 24 | def __init__( 25 | self, 26 | file_path: str, 27 | encoding: Optional[str] = None, 28 | autodetect_encoding: bool = True, 29 | ): 30 | """Initialize with file path.""" 31 | self.file_path = file_path 32 | self.encoding = encoding 33 | self.autodetect_encoding = autodetect_encoding 34 | 35 | def exec(self) -> List[PMDocument]: 36 | """Load from file path.""" 37 | encodings = ['utf-8', 'gbk', 'utf-16', 'ANSI'] 38 | text = "" 39 | try: 40 | with open(self.file_path, encoding=self.encoding) as f: 41 | text = f.read() 42 | except UnicodeDecodeError: 43 | for encoding in encodings: 44 | logger.debug(f"Trying encoding: {encoding}") 45 | try: 46 | with open(self.file_path, encoding=encoding) as f: 47 | text = f.read() 48 | break 49 | except UnicodeDecodeError: 50 | continue 51 | if not text or len(text) == 0: 52 | raise UnicodeDecodeError 53 | except Exception as e: 54 | raise RuntimeError(f"Error loading {self.file_path}") from e 55 | 56 | metadata = {"source": self.file_path} 57 | return [PMDocument(page_content=text, metadata=metadata)] 58 | 59 | def run(params: dict = None, inputs: dict = None, outputs=None) -> PMNodeOutput: 60 | logger.info("Welcome to Use Text Loader!") 61 | logger.info("This is params info:") 62 | logger.info(params) 63 | logger.info("This is inputs info:") 64 | logger.info(inputs) 65 | logger.info("This is outputs info:") 66 | logger.info(outputs) 67 | 68 | text_path = inputs['text_path']['value'] 69 | if text_path is not None and text_path != '': 70 | file_path = text_path 71 | else: 72 | file_path = params['script']['text_path']['value'] 73 | 74 | text_loader = TextLoader(file_path) 75 | result = text_loader.exec() 76 | 77 | output = PMNodeOutput() 78 | for output_name in outputs.keys(): 79 | output.add_output(output_name, result) 80 | return output 81 | 82 | if __name__ == '__main__': 83 | text_path = 'D:/downloads/text.txt' 84 | text_loader = TextLoader(text_path) 85 | result = text_loader.exec() 86 | for r in result: 87 | print(r.page_content) 88 | -------------------------------------------------------------------------------- /backend/promptmanager/script/text_truncation.py: -------------------------------------------------------------------------------- 1 | from promptmanager.runtime.flow import PMNodeOutput 2 | import logging 3 | 4 | 5 | class TextTruncation: 6 | def __int__(self): 7 | pass 8 | 9 | def exec(self, max_length, text: str) -> str: 10 | max_length = int(max_length) 11 | split_len = max_length if max_length <= len(text) else len(text) 12 | return text[0:split_len:] 13 | 14 | 15 | def run(params: dict = None, inputs: dict = None, outputs=None) -> PMNodeOutput: 16 | logger = logging.getLogger('pm_log') 17 | logger.info("This is params info:") 18 | logger.info(params) 19 | logger.info("This is inputs info:") 20 | logger.info(inputs) 21 | logger.info("This is outputs info:") 22 | logger.info(outputs) 23 | 24 | logger.info("To run text truncation:") 25 | 26 | max_length = params['script']['max_length']['value'] 27 | text = inputs['input']['value'] 28 | 29 | text_truncation = TextTruncation() 30 | result = text_truncation.exec(max_length, text) 31 | 32 | logger.info(result) 33 | 34 | output = PMNodeOutput() 35 | for output_name in outputs.keys(): 36 | output.add_output(output_name, result) 37 | 38 | return output 39 | -------------------------------------------------------------------------------- /backend/promptmanager/script/vectorstores/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | from typing import Any 3 | 4 | from promptmanager.script.schema.vectordb import PMVectorDB 5 | 6 | 7 | def _import_dingo() -> Any: 8 | from promptmanager.script.vectorstores.dingo import Dingo 9 | 10 | return Dingo 11 | 12 | 13 | def __getattr__(name: str) -> Any: 14 | if name == "Dingo": 15 | return _import_dingo() 16 | else: 17 | raise AttributeError(f"Could not find: {name}") 18 | 19 | 20 | __all__ = [ 21 | "Dingo", 22 | "PMVectorDB" 23 | ] 24 | -------------------------------------------------------------------------------- /backend/promptmanager/template/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | 14 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /backend/promptmanager/testmodule/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataCanvasIO/LMPM/5c5b7db32c3d82ec224f88a959d39040c5c3a68b/backend/promptmanager/testmodule/__init__.py -------------------------------------------------------------------------------- /backend/promptmanager/testmodule/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /backend/promptmanager/testmodule/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class TestmoduleConfig(AppConfig): 5 | default_auto_field = "django.db.models.BigAutoField" 6 | name = "promptmanager.testmodule" 7 | -------------------------------------------------------------------------------- /backend/promptmanager/testmodule/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataCanvasIO/LMPM/5c5b7db32c3d82ec224f88a959d39040c5c3a68b/backend/promptmanager/testmodule/migrations/__init__.py -------------------------------------------------------------------------------- /backend/promptmanager/testmodule/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | 4 | # Create your models here. 5 | class UserInfo(models.Model): 6 | id = models.IntegerField(primary_key=True) 7 | username = models.CharField(max_length=30) 8 | password = models.CharField(max_length=30) 9 | create_time = models.DateField(default=None) 10 | last_update_time = models.DateField(default=None) 11 | 12 | def __str__(self): 13 | return str(self.id) + "_" + self.username + "_" + self.password 14 | 15 | 16 | class TestCity(models.Model): 17 | id = models.IntegerField 18 | name = models.CharField(max_length=20) 19 | 20 | class Meta: 21 | db_table = 'testcity' 22 | -------------------------------------------------------------------------------- /backend/promptmanager/testmodule/testmodule.py: -------------------------------------------------------------------------------- 1 | from django.http import JsonResponse 2 | 3 | 4 | from promptmanager.app_common.database_util import DatabaseUtil 5 | from promptmanager.app_common.result_maker import ResultMaker 6 | from promptmanager.testmodule.models import UserInfo 7 | from django.core.paginator import Paginator 8 | import datetime 9 | from promptmanager.exception import exception 10 | 11 | 12 | # Create your views here. 13 | 14 | def hello(request): 15 | data = {} 16 | data['code'] = 0 17 | data['message'] = "hello" 18 | return JsonResponse(data, json_dumps_params={'ensure_ascii': False}) 19 | 20 | 21 | def users(request): 22 | page_index = request.GET.get('pageIndex', 1) 23 | page_size = request.GET.get('pageNum', 15) 24 | 25 | users_rs = UserInfo.objects.all().order_by("id") 26 | p = Paginator(users_rs.values(), page_size) 27 | users_page = p.page(page_index) 28 | 29 | result = { 30 | 'count': users_rs.count(), 31 | 'rows': list(users_page) 32 | } 33 | 34 | return ResultMaker.success(result) 35 | 36 | 37 | def add(request): 38 | for i in range(0, 10): 39 | id = i + 1 40 | user = UserInfo(id=id, username="test" + str(i), password="123456", create_time=datetime.datetime.today(), 41 | last_update_time=datetime.datetime.now()) 42 | user.save() 43 | print("插入数据成功") 44 | print(user) 45 | data = {} 46 | data['code'] = 0 47 | data['message'] = "插入数据成功" 48 | return JsonResponse(data, json_dumps_params={'ensure_ascii': False}) 49 | 50 | 51 | def cities(request): 52 | # cities = TestCity.objects.all() 53 | total_count = DatabaseUtil.query(query_sql='select count(*) as count from "testcity"') 54 | cities = DatabaseUtil.query(query_sql='select * from "testcity" where id < %s and name = %s', 55 | params=[3, '北京']) 56 | 57 | data = {} 58 | data['code'] = 0 59 | data['data'] = { 60 | 'count': total_count[0], 61 | 'rows': cities 62 | } 63 | return JsonResponse(data, json_dumps_params={'ensure_ascii': False}) 64 | 65 | 66 | def error(request): 67 | raise exception.FLOW_NOT_FOUND() 68 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /backend/promptmanager/testmodule/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /backend/promptmanager/testmodule/urls.py: -------------------------------------------------------------------------------- 1 | """ 2 | URL configuration for PromptManager project. 3 | 4 | The `urlpatterns` list routes URLs to views. For more information please see: 5 | https://docs.djangoproject.com/en/4.2/topics/http/urls/ 6 | Examples: 7 | Function views 8 | 1. Add an import: from my_app import views 9 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 10 | Class-based views 11 | 1. Add an import: from other_app.views import Home 12 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 13 | Including another URLconf 14 | 1. Import the include() function: from django.urls import include, path 15 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 16 | """ 17 | from django.urls import path 18 | 19 | from promptmanager.testmodule import testmodule 20 | 21 | urlpatterns = [ 22 | path("hello", testmodule.hello, name="hello"), 23 | path("users", testmodule.users, name="users"), 24 | path("add", testmodule.add, name="add"), 25 | path("cities", testmodule.cities, name="cities"), 26 | path("error", testmodule.error, name="error"), 27 | 28 | ] 29 | -------------------------------------------------------------------------------- /backend/promptmanager/web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataCanvasIO/LMPM/5c5b7db32c3d82ec224f88a959d39040c5c3a68b/backend/promptmanager/web/favicon.png -------------------------------------------------------------------------------- /backend/setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | 3 | from pkg_resources import parse_requirements 4 | from pathlib import Path 5 | 6 | import codecs 7 | 8 | requirements_path = str(Path(__file__).resolve().parent / 'promptmanager/requirements.txt') 9 | with open(requirements_path, encoding="utf-8") as fp: 10 | install_requires = [str(requirement) for requirement in parse_requirements(fp)] 11 | 12 | 13 | def read_quickguide(): 14 | quickguide_path = str(Path(__file__).resolve().parent / 'promptmanager/app_overview/Prompt Manager Quick Guide.md') 15 | with codecs.open(quickguide_path, 'r', encoding='utf-8') as f: 16 | return f.read() 17 | 18 | 19 | setup( 20 | name='PromptManager', 21 | version='1.0.0', 22 | author='aps', 23 | author_email='aps@zetyun.com', 24 | description='', 25 | long_description=read_quickguide(), 26 | long_description_content_type="text/markdown", 27 | 28 | url='', 29 | packages=find_packages(), 30 | include_package_data=True, 31 | install_requires=install_requires, 32 | classifiers=[ 33 | "Programming Language :: Python :: 3", 34 | "License :: OSI Approved :: MIT License", 35 | "Operating System :: OS Independent", 36 | ], 37 | python_requires='>=3.9', 38 | entry_points={ 39 | 'console_scripts': ['pmctl=promptmanager.pmctl.main:main'], 40 | } 41 | 42 | ) 43 | -------------------------------------------------------------------------------- /doc/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataCanvasIO/LMPM/5c5b7db32c3d82ec224f88a959d39040c5c3a68b/doc/.gitkeep -------------------------------------------------------------------------------- /doc/Community navigator.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataCanvasIO/LMPM/5c5b7db32c3d82ec224f88a959d39040c5c3a68b/doc/Community navigator.md -------------------------------------------------------------------------------- /doc/modules/Prompt App.md: -------------------------------------------------------------------------------- 1 | # Prompt App 2 | 3 | Prompt Application is a service for prompt flow ; We can publish a prompt flow to a prompt App,then we can run the flow on server at anywhere by http api; 4 | 5 | 6 | 7 | ## Publish App 8 | 9 | wen can publish a prompt flow to App 10 | 11 | ```python 12 | from promptmanager.runtime.app import PMApp 13 | from promptmanager.runtime.flow import PMflow 14 | 15 | pmFlow = PMflow.load(save_path="/opt/data/text_pm.pmflow"); 16 | # name is not required, it can define flow and app name 17 | pmApp = PMApp.publish_from_flow(pmFlow, base_url="http://127.0.0.1:8888", name='test') 18 | variables = {"title":"Black hole traversal","number":500} 19 | result = pmApp.run(variables) 20 | ``` 21 | 22 | ## Run APP 23 | 24 | We can run the app like this 25 | 26 | ```python 27 | from promptmanager.runtime.app import PMApp 28 | 29 | variables = {"title":"Black hole traversal","number":500} 30 | url = "http://127.0.0.1:8888/api/app/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/run" 31 | result = PMApp.run(variables, url) 32 | 33 | ``` 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /doc/modules/Prompt Chat.md: -------------------------------------------------------------------------------- 1 | # Prompt Chat 2 | 3 | We can have a conversation with the LLM through prompt template; 4 | 5 | 6 | 7 | ## PMChatPromptTemplate 8 | 9 | ```python 10 | from promptmanager.runtime.template import PMPromptTemplate 11 | from promptmanager.runtime.template import PMChatPromptTemplate 12 | chat_prompt_template = PMChatPromptTemplate( 13 | [ 14 | PMPromptTemplate("user","Tell me a ${adjective} joke about ${content}.","i am role_prompt2"), 15 | PMPromptTemplate("system","Your name is ${name}.","i am role_prompt1") 16 | ] 17 | ) 18 | 19 | 20 | variables={ 21 | "adjective":"funny", 22 | "content":"chickens", 23 | "name":"Bob" 24 | } 25 | chat_prompt_template.messages(variables) 26 | ``` 27 | 28 | 29 | 30 | ## without Context 31 | 32 | Every interaction with the large language model is independent of the context 33 | 34 | ```python 35 | from promptmanager.runtime.chat import PMChat 36 | from promptmanager.runtime.template import PMPromptTemplate 37 | from promptmanager.runtime.template import PMChatPromptTemplate 38 | from promptmanager.runtime.model import PMLLM 39 | 40 | role = 'user' 41 | role_prompt = 'I am a user' 42 | content = 'Please Write A science fiction the topic is:${topic[text]} ' 43 | 44 | pmPromptTemplate = PMPromptTemplate(role_prompt="i am a user", role='user', template_content=content) 45 | variables = pmPromptTemplate.show_variables_info() 46 | var3 = [pmPromptTemplate] 47 | pmchatPrompt = PMChatPromptTemplate(var3) 48 | messages = [{ 49 | "name": "topic", 50 | "type": "text", 51 | "defaultValue": "", 52 | "value": "Alien Cat" 53 | } 54 | ] 55 | message = { 56 | 'topic': 'Alien Cat' 57 | } 58 | 59 | from promptmanager.runtime.model import PMOpenAIPMLLM 60 | api_key = "xxxxxx-xxxxxxxxxxxxxxxxxxxx" 61 | params = {'OPENAI_API_KEY': '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', 'model': 'gpt-3.5-turbo', 'stream': False} 62 | pmllm = PMOpenAIPMLLM.load_from_openai_key(api_key) 63 | pmchat = PMChat(pmchatPrompt, pmllm) 64 | result = pmchat.run(message) 65 | result2 = pmchat.run(messages, params) 66 | ``` 67 | 68 | ## 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /doc/modules/Prompt.md: -------------------------------------------------------------------------------- 1 | # Prompt 2 | 3 | The new way of programming models is through prompts. A **prompt** refers to the input to the model. This input is often constructed from multiple components. Prompt Manager provides web application ,and several python SDK functions to make constructing and working with prompts easy. 4 | 5 | ## Prompt Scene 6 | 7 | In web UI prompt template can by Classification A Scene in order to Classification and retrieval; 8 | 9 | ## Prompt Role 10 | 11 | Prompt Role is tell the large language model to be acted a role 12 | 13 | ```python 14 | from promptmanager.runtime.template import PMPromptTemplate 15 | role_prompt = "i am role_prompt" 16 | prompt_template = PMPromptTemplate("user","Tell me a ${adjective} joke about ${content}.",role_prompt) 17 | 18 | ``` 19 | 20 | ## Prompt Label 21 | 22 | In web UI prompt template can by tag by many different label in order to Classification and retrieval; 23 | 24 | ## Prompt Variable 25 | 26 | We can define A variable in the prompt template like this: 27 | 28 | ``` 29 | ${[]:} 30 | ``` 31 | 32 | Variable type [vartype] is not required and default value is [text] ,And the default value of the variable is not required ; 33 | 34 | For example: 35 | 36 | ```tex 37 | ${title} 38 | 39 | ${title [text]} 40 | 41 | ${title [text]} 42 | 43 | ${title: xxxx} 44 | 45 | ``` 46 | 47 | Prompt Manger Support file of vartype: 48 | 49 | ```tex 50 | ${file [file]: science fiction novel} 51 | 52 | There are two types of [vartype]: text/file; The default is text; 53 | 54 | ${title [file]:/xxxxx/xxxxxjife/xx.csv} 55 | ``` 56 | 57 | 58 | 59 | ## Prompt Template 60 | 61 | ```python 62 | from promptmanager.runtime.template import PMPromptTemplate 63 | role_prompt = "i am role_prompt" 64 | prompt_template = PMPromptTemplate("user","Tell me a ${adjective} joke about ${content}.",role_prompt) 65 | 66 | variables={ 67 | "adjective":"funny", 68 | "content":"chickens" 69 | } 70 | 71 | prompt_template.message(variables) 72 | ``` 73 | 74 | You can get variable info 75 | 76 | ```python 77 | prompt_template.show_variables_info() 78 | ``` 79 | 80 | -------------------------------------------------------------------------------- /doc/scripts/readme.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataCanvasIO/LMPM/5c5b7db32c3d82ec224f88a959d39040c5c3a68b/doc/scripts/readme.md -------------------------------------------------------------------------------- /frontend/.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | 15 | [Makefile] 16 | indent_style = tab 17 | -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | TODO.md 2 | .DS_Store 3 | /node_modules 4 | /packages/*/LICENSE 5 | .rtp2_cache 6 | .idea/* 7 | *.tsx/*.js 8 | *.tsx/*.js.map 9 | yarn-error.log 10 | yarn.lock 11 | .history/ 12 | package-lock.json 13 | .umi 14 | .umi-production 15 | docs-dist 16 | todo-done.md 17 | dist 18 | 19 | -------------------------------------------------------------------------------- /frontend/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataCanvasIO/LMPM/5c5b7db32c3d82ec224f88a959d39040c5c3a68b/frontend/.gitkeep -------------------------------------------------------------------------------- /frontend/.prettierignore: -------------------------------------------------------------------------------- 1 | **/*.md 2 | **/*.svg 3 | **/*.ejs 4 | **/*.html 5 | package.json 6 | .umi 7 | .umi-production 8 | .umi-test 9 | -------------------------------------------------------------------------------- /frontend/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "all", 4 | "printWidth": 80, 5 | "overrides": [ 6 | { 7 | "files": ".prettierrc", 8 | "options": { "parser": "json" } 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /frontend/.umirc.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'umi'; 2 | import theme from './theme' 3 | const api = 'http://172.20.52.122:8888/'; 4 | const path = require('path'); 5 | export default defineConfig({ 6 | publicPath: '/web/', 7 | title: 'Prompt Manager', 8 | favicon: './favicon.png', 9 | nodeModulesTransform: { 10 | type: 'none', 11 | }, 12 | theme: { 13 | ...theme 14 | }, 15 | locale: { 16 | default: 'en-US' 17 | }, 18 | antd: {}, 19 | routes: [ 20 | { 21 | path: '/', 22 | component: '@/pages/layout', 23 | routes: [ 24 | { path: '/', component: '@/pages/index', exact: true, name: 'home', parentKey: 0, key: 'home', isHide: true, }, 25 | { path: '/Overview', component: '@/pages/overview', name: 'Overview', parentKey: 0, key: 'Overview' }, 26 | { path: '/Prompt-Market', component: '@/pages/prompt-market/main', name: 'Prompt Market', parentKey: 0, key: 'Prompt Market' }, 27 | { path: '/AI-Model', component: '@/pages/ai-model', name: 'AI Model', parentKey: 0, key: 'AI Model' }, 28 | { path: '/Prompt-Engineering', component: '@/pages/chat/main', name: 'Prompt Engineering', parentKey: 0, key: 'Prompt Engineering' }, 29 | { path: '/chat', component: '@/pages/chat/main', name: 'Chat', parentKey: 'Prompt Engineering', key: 'promptChat', ic: 'chat' }, 30 | { path: '/flowEdit/:id', component: '@/pages/flow/flowEdit', name: 'flowEdit', parentKey: 'Prompt Engineering', isHide: true }, 31 | { path: '/flowList', component: '@/pages/flow/flowList', name: 'Flow', parentKey: 'Prompt Engineering', key: 'flowList', ic: 'flow' }, 32 | { path: '/Prompt-App', component: '@/pages/prompt-app/main', name: 'Prompt App', parentKey: 0, key: 'Prompt App' }, 33 | { path: '/Prompt-Guide', component: '@/pages/prompt-guide', name: 'Prompt Guide', parentKey: 0, key: 'Prompt Guide', isHide: true, }, 34 | { component: '@/pages/404', name: 'Not Found', parentKey: 0, key: 'Not Found', isHide: true, }, 35 | ] 36 | }, 37 | ], 38 | alias: { 39 | '@': path.resolve(__dirname, './src'), 40 | utils: path.resolve(__dirname, './src/utils'), 41 | services: path.resolve(__dirname, './src/services'), 42 | components: path.resolve(__dirname, './src/components'), 43 | config: path.resolve(__dirname, './src/config'), 44 | }, 45 | fastRefresh: {}, 46 | proxy: { 47 | '/api': { 48 | target: api, 49 | changeOrigin: true, 50 | pathRewrite: { '^api': '/api' }, 51 | }, 52 | }, 53 | }); 54 | -------------------------------------------------------------------------------- /frontend/README.md: -------------------------------------------------------------------------------- 1 | # LMS 2 | 3 | 4 | 大模型前端页面 5 | 6 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "start": "umi dev", 5 | "build": "umi build", 6 | "postinstall": "umi generate tmp", 7 | "prettier": "prettier --write '**/*.{js,jsx,tsx,ts,less,md,json}'", 8 | "test": "umi-test", 9 | "test:coverage": "umi-test --coverage", 10 | "cz": "git add . && git-cz" 11 | }, 12 | "gitHooks": { 13 | "pre-commit": "lint-staged" 14 | }, 15 | "browserslist": [ 16 | "last 2 versions" 17 | ], 18 | "lint-staged": { 19 | "*.{js,jsx,less,md,json}": [ 20 | "prettier --write" 21 | ], 22 | "*.ts?(x)": [ 23 | "prettier --parser=typescript --write" 24 | ] 25 | }, 26 | "husky": { 27 | "hooks": { 28 | "pre-commit": "lint-staged", 29 | "commit-msg": "commitlint -E HUSKY_GIT_PARAMS" 30 | } 31 | }, 32 | "dependencies": { 33 | "@ant-design/pro-layout": "^6.5.0", 34 | "@microsoft/fetch-event-source": "^2.0.1", 35 | "array-move": "^4.0.0", 36 | "classnames": "^2.2.6", 37 | "immer": "^8.0.0", 38 | "jsplumb": "^2.15.0", 39 | "konva": "7.0.4", 40 | "marked": "3.0.4", 41 | "pubsub-js": "^1.9.0", 42 | "qs": "^6.9.4", 43 | "rc-tween-one": "^2.7.3", 44 | "react": "17.x", 45 | "react-ace": "10.x", 46 | "react-dnd": "^11.1.3", 47 | "react-dnd-html5-backend": "^11.1.3", 48 | "react-dom": "17.x", 49 | "react-konva": "16.13.0-6", 50 | "react-markdown": "^8.0.7", 51 | "react-sortable-hoc": "^2.0.0", 52 | "react-split-pane": "^0.1.92", 53 | "react-syntax-highlighter": "^15.5.0", 54 | "rehype-raw": "6", 55 | "remark-gfm": "3", 56 | "umi": "^3.5.35", 57 | "umi-request": "1.3.4", 58 | "use-immer": "^0.5.1", 59 | "uuid": "^8.3.0" 60 | }, 61 | "devDependencies": { 62 | "@types/react": "^17.0.0", 63 | "@types/react-dom": "^17.0.0", 64 | "@umijs/preset-react": "1.x", 65 | "@umijs/test": "^3.5.35", 66 | "commitizen": "^3.0.4", 67 | "cz-conventional-changelog": "^3.3.0", 68 | "husky": "^8.0.3", 69 | "lint-staged": "^10.0.7", 70 | "prettier": "^2.2.0", 71 | "typescript": "^4.1.2", 72 | "yorkie": "^2.0.0" 73 | }, 74 | "config": { 75 | "commitizen": { 76 | "path": "cz-conventional-changelog" 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /frontend/public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataCanvasIO/LMPM/5c5b7db32c3d82ec224f88a959d39040c5c3a68b/frontend/public/favicon.png -------------------------------------------------------------------------------- /frontend/src/assets/404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataCanvasIO/LMPM/5c5b7db32c3d82ec224f88a959d39040c5c3a68b/frontend/src/assets/404.png -------------------------------------------------------------------------------- /frontend/src/assets/default.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 编组 4 4 | 5 | 6 | 7 | 8 | 9 | Default 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /frontend/src/assets/empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataCanvasIO/LMPM/5c5b7db32c3d82ec224f88a959d39040c5c3a68b/frontend/src/assets/empty.png -------------------------------------------------------------------------------- /frontend/src/assets/overview-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataCanvasIO/LMPM/5c5b7db32c3d82ec224f88a959d39040c5c3a68b/frontend/src/assets/overview-1.png -------------------------------------------------------------------------------- /frontend/src/assets/overview-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataCanvasIO/LMPM/5c5b7db32c3d82ec224f88a959d39040c5c3a68b/frontend/src/assets/overview-2.png -------------------------------------------------------------------------------- /frontend/src/assets/overview-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataCanvasIO/LMPM/5c5b7db32c3d82ec224f88a959d39040c5c3a68b/frontend/src/assets/overview-3.png -------------------------------------------------------------------------------- /frontend/src/assets/overview-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataCanvasIO/LMPM/5c5b7db32c3d82ec224f88a959d39040c5c3a68b/frontend/src/assets/overview-4.png -------------------------------------------------------------------------------- /frontend/src/assets/overview-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataCanvasIO/LMPM/5c5b7db32c3d82ec224f88a959d39040c5c3a68b/frontend/src/assets/overview-5.png -------------------------------------------------------------------------------- /frontend/src/components/editor/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, useRef } from 'react' 2 | import AceEditor from 'react-ace' 3 | import "ace-builds/src-noconflict/mode-json" 4 | import "ace-builds/src-noconflict/theme-github" 5 | import "ace-builds/src-noconflict/mode-python" 6 | import styles from './index.less' 7 | 8 | const Editor = props => { 9 | const ref = useRef(null) 10 | const { className, value, theme, onChange, ...otherProps } = props 11 | // const [EdValue, setEdValue] = useState(value) 12 | 13 | const onChangeHandle = (value) => { 14 | // setEdValue(value) 15 | onChange && onChange(value) 16 | } 17 | 18 | // useEffect(() => { 19 | // setEdValue(value) 20 | // }, [value]) 21 | 22 | return ( 23 | { 39 | setTimeout(() => { 40 | const session = editor.getSession(); 41 | const undoManager = session.getUndoManager(); 42 | undoManager.reset(); 43 | session.setUndoManager(undoManager); 44 | }, 200) 45 | }} 46 | /> 47 | ) 48 | } 49 | 50 | export default Editor 51 | -------------------------------------------------------------------------------- /frontend/src/components/editor/index.less: -------------------------------------------------------------------------------- 1 | .ace_editor { 2 | border-radius: 4px; 3 | border: 1px solid @background15; 4 | } 5 | -------------------------------------------------------------------------------- /frontend/src/components/empty/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import emptyImg from '@/assets/empty.png'; 3 | import styles from './index.less' 4 | 5 | const Empty = (props) => { 6 | return ( 7 |
8 | Empty 9 |
No Data
10 |
11 | ) 12 | } 13 | 14 | export default Empty 15 | -------------------------------------------------------------------------------- /frontend/src/components/empty/index.less: -------------------------------------------------------------------------------- 1 | @import '../../styles/index'; 2 | .empty { 3 | width: 100%; 4 | height: 100%; 5 | .center(); 6 | flex-direction: column; 7 | .font { 8 | font-size: 20px; 9 | margin-top: 20px; 10 | color: rgba(16,38,58,0.65); 11 | } 12 | } -------------------------------------------------------------------------------- /frontend/src/components/field-sort/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react' 2 | import { CaretUpOutlined, CaretDownOutlined } from '@ant-design/icons' 3 | import { useImmer } from 'use-immer' 4 | import styles from './index.less' 5 | 6 | const FieldSort = (props) => { 7 | const { field, defaultValue, value, onChange } = props 8 | const [state, setState] = useImmer({ sort: 'desc' }) 9 | 10 | useEffect(() => { 11 | if(['desc', 'asc'].includes(defaultValue)) { 12 | setState(draft => { 13 | draft.sort = defaultValue 14 | }) 15 | } 16 | }, []) 17 | 18 | useEffect(() => { 19 | if(['desc', 'asc'].includes(value)) { 20 | setState(draft => { 21 | draft.sort = value 22 | }) 23 | } 24 | }, [value]) 25 | 26 | const handleChange = () => { 27 | const newSort = state.sort === 'desc' ? 'asc' : 'desc' 28 | onChange(newSort) 29 | setState(draft => { 30 | draft.sort = newSort 31 | }) 32 | } 33 | 34 | return ( 35 |
36 |
{field}
37 |
38 | 39 | 40 |
41 |
42 | ) 43 | } 44 | 45 | export default FieldSort 46 | -------------------------------------------------------------------------------- /frontend/src/components/field-sort/index.less: -------------------------------------------------------------------------------- 1 | .fieldSort { 2 | font-weight: 400; 3 | color: @text65; 4 | display: flex; 5 | cursor: pointer; 6 | line-height: 32px; 7 | height: 32px; 8 | .sortTitle { 9 | padding: 0 8px; 10 | } 11 | .sortIcon { 12 | display: inline-flex; 13 | flex-direction: column; 14 | align-items: center; 15 | color: #bfbfbf; 16 | font-size: 9px; 17 | margin-top: 8px; 18 | :global { 19 | .anticon { 20 | height: 8px; 21 | font-size: 12px; 22 | } 23 | } 24 | .iconActive { 25 | color: @primary-color; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /frontend/src/components/icon/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { createFromIconfontCN } from '@ant-design/icons' 3 | 4 | export const IconFont = createFromIconfontCN({ 5 | scriptUrl: [ 6 | '//at.alicdn.com/t/c/font_4216659_m2d9oq9kw2s.js', 7 | ], 8 | }) 9 | 10 | -------------------------------------------------------------------------------- /frontend/src/components/publish-prompt/index.less: -------------------------------------------------------------------------------- 1 | .multSelect { 2 | :global{ 3 | .ant-select-selection-item { 4 | background: @color15; 5 | border-radius: 13px; 6 | color: @primary-color; 7 | margin-right: 8px; 8 | } 9 | .ant-select-selection-item-content { 10 | margin-right: 8px; 11 | } 12 | } 13 | } 14 | .formTableTit { 15 | label { 16 | position: relative; 17 | top: 10px; 18 | } 19 | :global { 20 | .ant-empty-normal { 21 | margin: 9px 0; 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /frontend/src/components/role/index.less: -------------------------------------------------------------------------------- 1 | .stepTwo { 2 | padding: 12px 0; 3 | .back { 4 | color: @primary-color; 5 | cursor: pointer; 6 | padding: 0 12px 7 | } 8 | :global { 9 | .ant-form-item { 10 | margin-bottom: 12px; 11 | } 12 | .ant-divider { 13 | border-top: 1px solid @text30; 14 | } 15 | } 16 | } 17 | .stepOne { 18 | .add { 19 | color: @primary-color; 20 | cursor: pointer; 21 | text-align: center; 22 | padding: 12px 0; 23 | border-top: 1px solid @text30; 24 | } 25 | :global { 26 | .ant-select-item { 27 | min-height: 28px; 28 | line-height: 28px; 29 | } 30 | } 31 | } 32 | .hideLabel { 33 | label { 34 | display: none; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /frontend/src/config/index.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | export const MAXVARIABLES = 10; -------------------------------------------------------------------------------- /frontend/src/hooks/useInterval.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef } from 'react'; 2 | 3 | const useInterval = (callback, delay) => { 4 | const savedCallback = useRef(); 5 | 6 | useEffect(() => { 7 | savedCallback.current = callback; 8 | }, [callback]); 9 | 10 | useEffect(() => { 11 | const handler = (...args) => savedCallback.current(...args); 12 | 13 | if (delay !== null) { 14 | const id = setInterval(handler, delay); 15 | return () => clearInterval(id); 16 | } 17 | }, [delay]); 18 | }; 19 | 20 | export default useInterval; 21 | -------------------------------------------------------------------------------- /frontend/src/models/useGlobalModel.js: -------------------------------------------------------------------------------- 1 | import { useImmer } from 'use-immer' 2 | 3 | export default () => { 4 | const [globalState, setGlobalState] = useImmer({ 5 | edgeName: '加载中' 6 | }) 7 | 8 | return { 9 | globalState, 10 | setGlobalState, 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /frontend/src/pages/404.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import image404 from '@/assets/404.png'; 3 | 4 | export default () => { 5 | return ( 6 |
16 | 404 17 |
18 | ); 19 | }; 20 | -------------------------------------------------------------------------------- /frontend/src/pages/assets/GPU.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataCanvasIO/LMPM/5c5b7db32c3d82ec224f88a959d39040c5c3a68b/frontend/src/pages/assets/GPU.png -------------------------------------------------------------------------------- /frontend/src/pages/assets/node.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataCanvasIO/LMPM/5c5b7db32c3d82ec224f88a959d39040c5c3a68b/frontend/src/pages/assets/node.png -------------------------------------------------------------------------------- /frontend/src/pages/chat/main-right/index.less: -------------------------------------------------------------------------------- 1 | .main { 2 | width: 100%; 3 | height: 100%; 4 | overflow: auto; 5 | padding: 24px; 6 | :global { 7 | .ant-form-item-label { 8 | label { 9 | font-weight: 400; 10 | color: @background65; 11 | } 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /frontend/src/pages/chat/utils/index.js: -------------------------------------------------------------------------------- 1 | export function getContextList(newTalkList, selectVal) { 2 | const temp = [...Array.from(newTalkList)].reverse() 3 | 4 | if (temp[0].type === 'New') { 5 | const current = temp[1] 6 | return [{ 7 | template_content: current.origin, 8 | role: current.promptList[0] ? current.promptList[0].role_name : '', 9 | role_prompt: current.promptList[0] ? current.promptList[0].role_prompt : '', 10 | prompt_variables: current.variables 11 | }] 12 | } else { 13 | let res = [] 14 | for (let i = 0; i < temp.length; i++) { 15 | const current = temp[i] 16 | if (current.type === 'User' && !current.error) { 17 | res.push({ 18 | template_content: current.origin, 19 | role: current.promptList[0] ? current.promptList[0].role_name : '', 20 | role_prompt: current.promptList[0] ? current.promptList[0].role_prompt : '', 21 | prompt_variables: current.variables 22 | }) 23 | if (selectVal === 'Without-context') break 24 | } else if (current.type === 'Assistant' && !current.error && Object.prototype.toString.call(current.text) === "[object String]") { 25 | res.push({ 26 | template_content: current.text, 27 | role: current.role, 28 | }) 29 | } else if (current.type === 'New') { 30 | break 31 | } 32 | } 33 | return res.reverse() 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /frontend/src/pages/chat/variable-modal/index.less: -------------------------------------------------------------------------------- 1 | .modalMain { 2 | :global { 3 | .ant-upload { 4 | width: 100%; 5 | } 6 | .ant-btn-default { 7 | width: 100%; 8 | color: @primary-color; 9 | border-radius: 4px; 10 | border: 1px solid @background15; 11 | } 12 | .anticon-delete,.anticon { 13 | color: @primary-color; 14 | } 15 | .ant-upload-list-item-name { 16 | font-weight: 400; 17 | color: @background85; 18 | } 19 | .ant-upload-list-item-info { 20 | background: #F9F7F8!important; 21 | border-radius: 4px; 22 | padding-left: 4px; 23 | } 24 | .ant-upload-list-item-card-actions-btn { 25 | opacity: 1!important; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /frontend/src/pages/components/bread/index.jsx: -------------------------------------------------------------------------------- 1 | import { React } from 'react' 2 | import { useLocation } from 'umi' 3 | import { useImmer } from 'use-immer' 4 | const Bread = () => { 5 | const location = useLocation() 6 | const [state, setState] = useImmer({ 7 | breadcrumbItems: [] 8 | }) 9 | 10 | useEffect(() => { 11 | const pathSnippets = location.pathname.split('/').filter((i) => i) 12 | const breadItems = generateBreadcrumbItems(pathSnippets, routes[0].routes) 13 | setState((prevState) => { 14 | prevState.breadcrumbItems = breadItems 15 | }) 16 | }, [location.pathname]) 17 | 18 | const generateBreadcrumbItems = (pathSnippets, routes) => { 19 | const breadRes = [] 20 | pathSnippets.reduce((total, item) => { 21 | total = total.concat(item) 22 | const url = `/${total.join('/')}` 23 | const route = routes.filter((ite) => (ite.path === `/${total.join('/')}`)) 24 | if(route.length > 0) { 25 | breadRes.push({ url, name: route[0].name }) 26 | } 27 | return total 28 | }, []) 29 | return breadRes 30 | } 31 | 32 | const generateBreadcrumb = () => { 33 | return state.breadcrumbItems.map((item, index) => ( 34 | 35 | {index === state.breadcrumbItems.length - 1 ? ( 36 |
{item.name}
37 | ) : ( 38 | {item.name} 39 | )} 40 |
41 | )) 42 | } 43 | 44 | return ( 45 | 46 | {generateBreadcrumb()} 47 | 48 | ) 49 | } 50 | 51 | export default Bread 52 | -------------------------------------------------------------------------------- /frontend/src/pages/components/layoutHeader/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Row, Col } from 'antd' 3 | import styles from './index.less' 4 | 5 | const BasicHeader = (props) => { 6 | const { title, children } = props 7 | 8 | return ( 9 | 10 | {title} 11 | {children} 12 | 13 | ) 14 | } 15 | 16 | export default BasicHeader -------------------------------------------------------------------------------- /frontend/src/pages/components/layoutHeader/index.less: -------------------------------------------------------------------------------- 1 | .basicHeader { 2 | width: 100%; 3 | height: 80px; 4 | background-color: #fafafa; 5 | } 6 | -------------------------------------------------------------------------------- /frontend/src/pages/components/menu/index.jsx: -------------------------------------------------------------------------------- 1 | import { Menu } from 'antd' 2 | import { useMemo } from 'react' 3 | import { transverse } from '@/utils' 4 | 5 | const PromptMenu = (props) => { 6 | const { menuRoutes } = props 7 | 8 | const menuTree = transverse(menuRoutes, 0) 9 | 10 | const activeKey = useMemo(() => { 11 | const routeRes = menuRoutes.filter((item) => (item.path === location.pathname)) 12 | return routeRes.length > 0 ? routeRes[0].key : '' 13 | }, [location.pathname]) 14 | 15 | return ( 16 | <> 17 | 18 | 19 | ) 20 | } 21 | 22 | export default PromptMenu -------------------------------------------------------------------------------- /frontend/src/pages/components/workflow/component/bottom-panel/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | const BottomPanel = ({ children }) => { 4 | return ( 5 |
6 | {children} 7 |
8 | ) 9 | } 10 | 11 | export default BottomPanel 12 | -------------------------------------------------------------------------------- /frontend/src/pages/components/workflow/component/canvas/bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataCanvasIO/LMPM/5c5b7db32c3d82ec224f88a959d39040c5c3a68b/frontend/src/pages/components/workflow/component/canvas/bg.jpg -------------------------------------------------------------------------------- /frontend/src/pages/components/workflow/component/canvas/bg1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DataCanvasIO/LMPM/5c5b7db32c3d82ec224f88a959d39040c5c3a68b/frontend/src/pages/components/workflow/component/canvas/bg1.jpg -------------------------------------------------------------------------------- /frontend/src/pages/components/workflow/component/canvas/canvas.less: -------------------------------------------------------------------------------- 1 | 2 | @import '../../style/index.less'; 3 | 4 | .pm-canvas { 5 | overflow: hidden; 6 | position: relative; 7 | display: flex; 8 | width: 100%; 9 | height: 100%; 10 | flex-direction: column; 11 | .pm-canvas-container { 12 | overflow: visible; 13 | position: relative; 14 | display: flex; 15 | width: 50px; 16 | height: 50px; 17 | } 18 | .pm-canvas-wrapper { 19 | background: url(./bg.jpg) repeat; 20 | width: 100%; 21 | height: 100%; 22 | cursor: grab; 23 | overflow: hidden; 24 | } 25 | .pm-canvas-drop { 26 | background: #f9f7f8; 27 | width: 100%; 28 | height: 100%; 29 | } 30 | .pm-canvas-node { 31 | position: absolute; 32 | z-index: 1; 33 | cursor: move; 34 | } 35 | .pm-canvas-toolbar { 36 | width: 173px; 37 | height: 26px; 38 | background: #FFFEFE; 39 | box-shadow: 0px 0px 3px 0px rgba(216,216,216,0.5); 40 | border-radius: 6px; 41 | display: flex; 42 | align-items: center; 43 | padding: 0 12px; 44 | z-index: 1; 45 | &.isAbsolute { 46 | position: absolute; 47 | right: 15px; 48 | top: 15px; 49 | box-shadow: none; 50 | } 51 | .zoom { 52 | display: flex; 53 | align-items: center; 54 | } 55 | .ant-divider { 56 | top: 1px; 57 | } 58 | .ant-switch { 59 | position: relative; 60 | top: -1px; 61 | } 62 | } 63 | } 64 | .subMenuCustom { 65 | max-height: 200px; 66 | overflow: auto; 67 | } 68 | -------------------------------------------------------------------------------- /frontend/src/pages/components/workflow/component/canvas/canvasContextWrapper.js: -------------------------------------------------------------------------------- 1 | import React, { FC, memo, useContext, useMemo } from 'react'; 2 | import CanvasContext from '../../context/canvasContext'; 3 | 4 | const CanvasContextWrapper = (Component) => { 5 | return memo((props) => { 6 | const context = useContext(CanvasContext); 7 | return 8 | }) 9 | } 10 | 11 | export default CanvasContextWrapper; 12 | -------------------------------------------------------------------------------- /frontend/src/pages/components/workflow/component/canvas/canvasNode.js: -------------------------------------------------------------------------------- 1 | import React, { FC, useEffect, useRef, memo } from 'react' 2 | import WorkFLowToolkit from '../../utils/workFLowToolkit' 3 | import CanvasContextWrapper from './canvasContextWrapper' 4 | import { Dropdown, Menu, Divider } from 'antd' 5 | import classNames from 'classnames' 6 | import './canvas.less' 7 | 8 | const MyDivider = () => 9 | 10 | const WrapperNode = (Component) => { 11 | const Node = memo(({ children, onClick, contextMenuList, nodeHeight, nodeWidth, style, className, container, startDrag, onVisibleChange, ...otherProps }) => { 12 | const { left, top, io, id } = otherProps 13 | const classNameStr = classNames('pm-canvas-node', className) 14 | const currStartDrag = useRef(null) 15 | // currStartDrag.current = startDrag 16 | useEffect(() => { 17 | let instance = WorkFLowToolkit.getJsPlumbInstance(container) 18 | const { inputs, outputs } = io || { inputs: [], outputs: [] } 19 | instance.nodeInit(id, inputs, outputs, currStartDrag.current) 20 | return () => {} 21 | }, []) 22 | const handleWheelChange = (e) => { 23 | e.stopPropagation() 24 | } 25 | 26 | // const getNodesMenu = () => { 27 | // return ( 28 | // 29 | // {contextMenuList.map(({ type, name, onClick, subMenus }) => { 30 | // if (type === 'menu') { 31 | // if (subMenus && subMenus.length > 0) { 32 | // return ( 33 | // 34 | // {subMenus.map(item => { 35 | // return ( 36 | // onClick(otherProps, item)}> 37 | // {item.name} 38 | // 39 | // ) 40 | // })} 41 | // 42 | // ) 43 | // } 44 | // return onClick(otherProps)}>{name} 45 | // } 46 | // return 47 | // })} 48 | // 49 | // ) 50 | // } 51 | 52 | // const currVisibleChange = (visible) => { 53 | // onVisibleChange && onVisibleChange(visible, otherProps) 54 | // } 55 | 56 | return ( 57 | // 58 |
{ 63 | e.stopPropagation() 64 | e.nativeEvent.stopImmediatePropagation() 65 | onClick(otherProps) 66 | }} 67 | > 68 | 69 |
70 | //
71 | ) 72 | }) 73 | 74 | Node.defaultProps = { 75 | contextMenuList: [], 76 | onClick: () => { }, 77 | startDrag: () => {return true}, 78 | } 79 | 80 | return CanvasContextWrapper(Node) 81 | } 82 | 83 | export default WrapperNode 84 | -------------------------------------------------------------------------------- /frontend/src/pages/components/workflow/component/canvas/defaultCanvasNode.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import CanvasNode from './canvasNode'; 3 | 4 | 5 | const DefaultCanvasNode = props => { 6 | return ( 7 | <> 8 | {props.name}{props.status} 9 | 10 | ); 11 | } 12 | 13 | 14 | export default CanvasNode(DefaultCanvasNode); 15 | -------------------------------------------------------------------------------- /frontend/src/pages/components/workflow/component/canvas/toolbar/defaultCanvasMiniNode.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import MiniCanvasNode from './minNode' 3 | 4 | const DefaultCanvasMiniNode = props => { 5 | return ( 6 | <> 7 | {props.name} 8 | 9 | ) 10 | } 11 | 12 | export default MiniCanvasNode(DefaultCanvasMiniNode) 13 | -------------------------------------------------------------------------------- /frontend/src/pages/components/workflow/component/canvas/toolbar/minNode.js: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react'; 2 | import CanvasContextWrapper from './miniCanvasWrapper'; 3 | 4 | const WrapperNode = (Component) => { 5 | 6 | const MinNode = (props) => { 7 | const {nodeWidth, nodeHeight, left, top, id, ...otherProps} = props; 8 | // const nodeLeft = left ? (left - nodeWidth / 2) : 0; 9 | // const nodeTop = top ? (top - nodeHeight / 2) : 0; 10 | const nodeLeft = left || 0 ; 11 | const nodeTop = top || 0; 12 | return ( 13 |
18 | 19 |
20 | ); 21 | } 22 | 23 | return CanvasContextWrapper(MinNode); 24 | } 25 | 26 | export default WrapperNode; 27 | -------------------------------------------------------------------------------- /frontend/src/pages/components/workflow/component/canvas/toolbar/miniCanvasWrapper.js: -------------------------------------------------------------------------------- 1 | import React, { memo, useContext, useMemo } from 'react'; 2 | 3 | const miniCanvasWrapper = (Component) => { 4 | return memo((props) => { 5 | return 6 | }) 7 | } 8 | 9 | export default miniCanvasWrapper; 10 | -------------------------------------------------------------------------------- /frontend/src/pages/components/workflow/component/canvas/toolbar/miniMapCanvas.js: -------------------------------------------------------------------------------- 1 | import React, { FC, useEffect, useRef } from 'react' 2 | import CanvasNode from '../canvasNode' 3 | import { Layer, Rect, Stage, Text } from 'react-konva' 4 | import konva from 'konva' 5 | 6 | const MinMapCanvas = ({ nodes, nodeWidth, nodeHeight, scale, offsetX, offsetY, layerRef, canvasMapConfig }) => { 7 | const stageRef = useRef() 8 | 9 | useEffect(() => { 10 | // console.log(stageRef.current.canvas.toDataURL()) // 11 | }, []) 12 | 13 | return ( 14 | 15 | 16 | {nodes.map((item) => { 17 | const nodeLeft = item.left * scale + offsetX 18 | const nodeTop = item.top * scale + offsetY 19 | const getCanvasNodeStyleByType = canvasMapConfig.styleConfig 20 | let color = "#1976D2" 21 | if (getCanvasNodeStyleByType) { 22 | const { color: propColor } = getCanvasNodeStyleByType(item.moduleType) 23 | color = propColor 24 | } 25 | let name = item.name 26 | if (name.length > 12) { 27 | name = item.name.substring(0, 11) 28 | } 29 | return ( 30 | <> 31 | 41 | 53 | 54 | ) 55 | })} 56 | 57 | 58 | ) 59 | } 60 | 61 | 62 | export default MinMapCanvas 63 | -------------------------------------------------------------------------------- /frontend/src/pages/components/workflow/component/canvas/toolbar/miniMapTool.js: -------------------------------------------------------------------------------- 1 | import React, { FC, useEffect } from 'react'; 2 | import { Toolbar } from '../'; 3 | import { Icon, Divider, Tooltip, Switch } from 'antd'; 4 | import { PlusOutlined , MinusOutlined } from '@ant-design/icons'; 5 | import classNames from 'classnames'; 6 | import '../canvas.less'; 7 | 8 | 9 | const MiniMapTool = ({ className, minMap, zoom, onZoom, zoomScale, changeMinMapVisible, }) => { 10 | 11 | 12 | const handleNavigator = () => { 13 | // minMap.onClick && minMap.onClick(); 14 | changeMinMapVisible() 15 | } 16 | 17 | // useEffect(() => { 18 | // const storageData = localStorage.getItem(`workflow-status-${workflowId}`) 19 | // if (storageData) { 20 | // const initStorageData = JSON.parse(storageData); 21 | // setToolbar((draft) => { 22 | // draft.commentVisible = initStorageData.commentVisible; 23 | // draft.resourceVisible = initStorageData.resourceVisible; 24 | // }) 25 | // } 26 | // }, []) 27 | 28 | const toolbarOnMouseDown = (e) => { 29 | e.stopPropagation(); 30 | } 31 | 32 | 33 | const classNameStr = classNames('pm-canvas-mimimaptool', className); 34 | return ( 35 |
36 | <> 37 | {zoom.show && <> 38 | {/* */} 39 |
40 | onZoom(false)} /> 41 | {`${parseInt(zoomScale * 100)}%`} 42 | onZoom(true)}/> 43 |
} 44 | {minMap.show && <> 45 | 46 | 47 | 48 | } 49 | 50 |
51 | ); 52 | } 53 | MiniMapTool.defaultProps = { 54 | minMap: { 55 | show: true, 56 | onClick: () => { }, 57 | }, 58 | zoom: { 59 | show: true, 60 | onClick: () => { }, 61 | }, 62 | onZoom: (operation) => {}, 63 | zoomScale: 1, 64 | } 65 | 66 | export default MiniMapTool; 67 | -------------------------------------------------------------------------------- /frontend/src/pages/components/workflow/component/drag-node/dragNode.less: -------------------------------------------------------------------------------- 1 | .pm-drag-node { 2 | width: 100px; 3 | height: 40px; 4 | border: 1px solid #ccc; 5 | } 6 | -------------------------------------------------------------------------------- /frontend/src/pages/components/workflow/component/drag-node/index.js: -------------------------------------------------------------------------------- 1 | import React, { FC, memo } from 'react' 2 | import classNames from 'classnames' 3 | import { useDrag, DragSourceMonitor } from 'react-dnd' 4 | import './dragNode.less' 5 | 6 | const WrapperNode = (Component, params) => { 7 | const DragNode = memo((props) => { 8 | const {style, className, dragData} = props 9 | const classNameStr = classNames(className) 10 | const [{ isDragging }, drag] = useDrag({ 11 | item: { ...dragData, nodeType: dragData.type, type: 'node', }, 12 | // end: (item: { name: string } | undefined, monitor: DragSourceMonitor) => { 13 | // const dropResult = monitor.getDropResult() 14 | // if (item && dropResult) { 15 | // // alert(`You dropped ${item.name} into ${dropResult.name}!`) 16 | // } 17 | // }, 18 | collect: (monitor) => ({ 19 | isDragging: monitor.isDragging(), 20 | }), 21 | }) 22 | let canDrag = true 23 | if (params && params.canDrag && params.canDrag(dragData) === false) { 24 | canDrag = false 25 | } 26 | return ( 27 |
28 | 29 |
30 | ) 31 | }) 32 | 33 | DragNode.defaultProps = { 34 | dragData: {}, 35 | } 36 | 37 | return DragNode 38 | } 39 | 40 | export default WrapperNode 41 | -------------------------------------------------------------------------------- /frontend/src/pages/components/workflow/component/splitpanel/index.js: -------------------------------------------------------------------------------- 1 | import React,{FC, memo, useState} from 'react'; 2 | import SplitPane from 'react-split-pane' 3 | // export interface SplitProps{ 4 | // children:React.ReactChild, 5 | // panelSize: string | number; 6 | // } 7 | const SplitPanel = ({children, panelSize = '70%', ...otherProps})=>{ 8 | return ( 9 | 10 | {children} 11 | 12 | ) 13 | } 14 | 15 | export default memo(SplitPanel); 16 | -------------------------------------------------------------------------------- /frontend/src/pages/components/workflow/component/workflow/index.less: -------------------------------------------------------------------------------- 1 | .Resizer { 2 | background: @background100; 3 | opacity: 0.15; 4 | z-index: 1; 5 | -moz-box-sizing: border-box; 6 | -webkit-box-sizing: border-box; 7 | box-sizing: border-box; 8 | -moz-background-clip: padding; 9 | -webkit-background-clip: padding; 10 | background-clip: padding-box; 11 | } 12 | 13 | .Resizer:hover { 14 | -webkit-transition: all 2s ease; 15 | transition: all 2s ease; 16 | } 17 | 18 | .Resizer.horizontal { 19 | height: 11px; 20 | margin: -5px 0; 21 | border-top: 5px solid @white0; 22 | border-bottom: 5px solid @white0; 23 | cursor: row-resize; 24 | width: 100%; 25 | } 26 | 27 | .Resizer.horizontal:hover { 28 | border-top: 5px solid @black50; 29 | border-bottom: 5px solid @black50; 30 | } 31 | 32 | .Resizer.vertical { 33 | width: 11px; 34 | margin: 0 -5px; 35 | border-left: 5px solid @white0; 36 | border-right: 5px solid @white0; 37 | cursor: col-resize; 38 | } 39 | 40 | .Resizer.vertical:hover { 41 | border-left: 5px solid @black50; 42 | border-right: 5px solid @black50; 43 | } 44 | .Resizer.disabled { 45 | cursor: not-allowed; 46 | } 47 | .Resizer.disabled:hover { 48 | border-color: transparent; 49 | } 50 | .toggle{ 51 | position: absolute; 52 | top:50%; 53 | left:0; 54 | } 55 | 56 | @keyframes ring { 57 | from { 58 | stroke-dashoffset: 50; 59 | } 60 | to { 61 | stroke-dashoffset: 0; 62 | } 63 | } 64 | 65 | .jtk-connector path { 66 | animation-name: ring; // donghua 67 | animation-duration: 2s; 68 | animation-timing-function: linear; 69 | animation-iteration-count: infinite; 70 | stroke-dasharray:5; 71 | } 72 | 73 | .jtk-connector.active path { 74 | // animation-name: ring; 75 | color: @primary-color; 76 | } 77 | 78 | .jtk-dragging path { 79 | animation-name: null; 80 | // animation-duration: 2s; 81 | // animation-timing-function: linear; 82 | // animation-iteration-count: infinite; 83 | // stroke-dasharray:5; 84 | // color: @primary-color !important; 85 | stroke: @primary-color !important; 86 | stroke-width: 2; 87 | } 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /frontend/src/pages/components/workflow/context/canvasContext.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | 4 | const CanvasContext = React.createContext({ 5 | nodes: [], 6 | addNode: ()=> {}, 7 | delNode: ()=> {}, 8 | copyNode: () => {}, 9 | container: null, 10 | }); 11 | 12 | 13 | export default CanvasContext; 14 | -------------------------------------------------------------------------------- /frontend/src/pages/components/workflow/context/workflowContext.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | 4 | const WorkflowContext = React.createContext({ 5 | animationAttr: { 6 | pausedLeft: true, 7 | reverseLeft: false, 8 | pausedRight: true, 9 | reverseRight: false, 10 | isLeft: false, 11 | }, 12 | }); 13 | 14 | 15 | export default WorkflowContext; 16 | -------------------------------------------------------------------------------- /frontend/src/pages/components/workflow/enum/CanvasEnum.js: -------------------------------------------------------------------------------- 1 | const CanvasEnum = { 2 | Add: '1', 3 | Remove: '2', 4 | Copy: '3', 5 | Operation: '4' 6 | } 7 | 8 | export default CanvasEnum; 9 | -------------------------------------------------------------------------------- /frontend/src/pages/components/workflow/enum/CanvasOperationEnum.js: -------------------------------------------------------------------------------- 1 | const CanvasOperationEnum = { 2 | AddNode: '1', 3 | RemoveNode: '2', 4 | CopyNode: '3', 5 | AddConnection: '4', 6 | RemoveConnection: '5', 7 | DragNode: '6', 8 | DragCanvas: '7', 9 | Zoom: '8', 10 | SwitchComment: '9', 11 | SwitchMinMap: '10', 12 | SwitchResource: '11', 13 | AddComment: '12', 14 | } 15 | 16 | export default CanvasOperationEnum; 17 | -------------------------------------------------------------------------------- /frontend/src/pages/components/workflow/enum/MinMapEnum.js: -------------------------------------------------------------------------------- 1 | const MinMapEnum = { 2 | Render: 'render', 3 | MoveCenter: 'movecenter' 4 | } 5 | 6 | export default MinMapEnum; 7 | -------------------------------------------------------------------------------- /frontend/src/pages/components/workflow/hooks/useUpdateHooks.js: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef } from 'react'; 2 | 3 | const useUpdateEffect = (effect, deps) => { 4 | const isMounted = useRef(false); 5 | 6 | useEffect(() => { 7 | if (!isMounted.current) { 8 | isMounted.current = true; 9 | } else { 10 | return effect(); 11 | } 12 | }, deps); 13 | }; 14 | 15 | export default useUpdateEffect; 16 | -------------------------------------------------------------------------------- /frontend/src/pages/components/workflow/index.js: -------------------------------------------------------------------------------- 1 | import Workflow from './component/workflow'; 2 | import Canvas, {INode, IEdge} from './component/canvas/index'; 3 | import CanvasNode from './component/canvas/canvasNode'; 4 | import MiniCanvasNode from './component/canvas/toolbar/minNode'; 5 | import DragNode from './component/drag-node'; 6 | import SplitPanel from './component/splitpanel'; 7 | import BottomPanel from './component/bottom-panel'; 8 | import CanvasOperationEnum from './enum/CanvasOperationEnum'; 9 | 10 | const ItemPanel = Workflow.ItemPanel; 11 | const ContainerPanel = Workflow.ContainerPanel; 12 | export { 13 | ItemPanel, 14 | ContainerPanel, 15 | Canvas, 16 | CanvasNode, 17 | MiniCanvasNode, 18 | DragNode, 19 | INode, 20 | IEdge, 21 | SplitPanel, 22 | BottomPanel, 23 | CanvasOperationEnum 24 | }; 25 | 26 | export default Workflow; 27 | -------------------------------------------------------------------------------- /frontend/src/pages/components/workflow/style/index.js: -------------------------------------------------------------------------------- 1 | import './index.less'; 2 | -------------------------------------------------------------------------------- /frontend/src/pages/components/workflow/style/index.less: -------------------------------------------------------------------------------- 1 | @import './themes/default.less'; 2 | // @import "~zet-less/dist/index"; 3 | @primary-color: #7340C8; 4 | -------------------------------------------------------------------------------- /frontend/src/pages/components/workflow/style/themes/default.less: -------------------------------------------------------------------------------- 1 | /* 定义默认主题less */ 2 | 3 | // base 4 | @font-size-sm: 12px; 5 | @font-size-base: 14px; 6 | @font-size-lg: @font-size-base + 2px; 7 | 8 | 9 | 10 | // paddings 11 | @padding-lg: 24px; 12 | @padding-md: 16px; 13 | @padding-sm: 12px; 14 | @padding-xs: 8px; 15 | 16 | 17 | // margins 18 | @margin-lg: 24px; 19 | @margin-md: 16px; 20 | @margin-sm: 12px; 21 | @margin-xs: 8px; 22 | -------------------------------------------------------------------------------- /frontend/src/pages/components/workflow/utils/animation.js: -------------------------------------------------------------------------------- 1 | const defaultAnimation = { 2 | repeat: 0, 3 | yoyo: true, 4 | duration: 500, 5 | }; 6 | export const animation = { ...defaultAnimation, width: '0px', overflow: 'hidden' }; 7 | export const animationCenterRight = { ...defaultAnimation, marginRight: '0px' }; 8 | export const animationCenterLeft = { ...defaultAnimation, marginLeft: '0px' }; 9 | -------------------------------------------------------------------------------- /frontend/src/pages/components/workflow/utils/util.js: -------------------------------------------------------------------------------- 1 | import PubSub from 'pubsub-js'; 2 | import CanvasOperationEnum from '../enum/CanvasOperationEnum' 3 | import CanvasEnum from '../enum/CanvasEnum' 4 | 5 | export const triggerCanvasOperation = (type, data) => { 6 | PubSub.publish(CanvasEnum.Operation, { type , data }); 7 | } 8 | 9 | triggerCanvasOperation.CanvasOperationEnum = CanvasOperationEnum; 10 | -------------------------------------------------------------------------------- /frontend/src/pages/components/workflow/utils/workFLowToolkit.js: -------------------------------------------------------------------------------- 1 | import JsPlumb from './jsPlumb'; 2 | 3 | 4 | class JsPlumbFactory { 5 | 6 | instanceMap; 7 | 8 | constructor() { 9 | this.instanceMap = {}; 10 | } 11 | 12 | getJsPlumbInstance(container) { 13 | // debugger; 14 | if (this.instanceMap[container]) { 15 | return this.instanceMap[container]; 16 | } 17 | 18 | if (!container) { 19 | throw Error('not found jsplumb instance container'); 20 | } else { 21 | const length = Object.keys(this.instanceMap).length; 22 | if (length === 0) { 23 | throw Error('not found jsplumb instance instanceMap'); 24 | } 25 | } 26 | console.warn(this.instanceMap, container); 27 | throw Error('not found jsplumb instance'); 28 | } 29 | 30 | createJsPlumbInstance(container, readonly = false) { 31 | if (this.instanceMap[container]) { 32 | return this.instanceMap[container]; 33 | } 34 | let jsPlumbInstance = new JsPlumb(readonly); 35 | jsPlumbInstance.init(container); 36 | // jsPlumbInstance. 37 | this.instanceMap[container] = jsPlumbInstance; 38 | return jsPlumbInstance; 39 | } 40 | 41 | removeJsPlumbInstance(container) { 42 | if (this.instanceMap[container]) { 43 | delete this.instanceMap[container] ; 44 | } 45 | } 46 | 47 | removeAllJsPlumbInstance() { 48 | this.instanceMap = {}; 49 | } 50 | 51 | getInstanceMap() { 52 | return this.instanceMap; 53 | } 54 | } 55 | 56 | export default new JsPlumbFactory(); 57 | -------------------------------------------------------------------------------- /frontend/src/pages/flow/canvas/canvasNode.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import styles from './index.less' 3 | import { IconFont } from 'components/icon' 4 | import { Tooltip, Divider } from 'antd' 5 | import { Typography } from 'antd' 6 | import { CanvasNode } from '../../components/workflow' 7 | 8 | const MyCanvasNode = (props) => { 9 | const { name, moduleName, id, module_type, currNode, copyNode, delNode, publishNode, node, readonly } = props 10 | 11 | const renderIcon = (type) => { 12 | if (type === 'input') return 13 | if (type === 'output') return 14 | if (type === 'prompt') return 15 | if (type === 'script') return 16 | if (type === 'vectordb') return 17 | return 18 | } 19 | 20 | const renderMenu = () => { 21 | const copy = 22 | copyNode(node)} /> 23 | 24 | const publish = 25 | publishNode(node)} /> 26 | 27 | const del = 28 | delNode(node)} /> 29 | 30 | const divider = 31 | if (['prompt', 'script_prompt'].includes(module_type)) { 32 | return
{[copy, divider, publish, divider, del]}
33 | } else if (!['input', 'output'].includes(module_type)) { 34 | return
{[del]}
35 | } 36 | return null 37 | } 38 | 39 | return ( 40 |
41 | {renderIcon(module_type)} 42 | {name || moduleName} 43 | {id === currNode && !readonly && renderMenu()} 44 |
45 | ) 46 | } 47 | 48 | export default CanvasNode(MyCanvasNode) 49 | -------------------------------------------------------------------------------- /frontend/src/pages/flow/canvas/index.less: -------------------------------------------------------------------------------- 1 | @import '../../../styles/index'; 2 | .canvasNode { 3 | width: 198px; 4 | height: 36px; 5 | border-radius: 8px; 6 | display: flex; 7 | align-items: center; 8 | box-sizing: content-box!important; 9 | font-size: 12px; 10 | background: @white100; 11 | border: 1px solid @text30; 12 | padding: 0 12px; 13 | position: relative; 14 | .icon { 15 | margin-right: 4px; 16 | font-size: 16px; 17 | position: relative; 18 | top: -2px; 19 | } 20 | &:hover { 21 | border: 1px solid @primary-color; 22 | } 23 | } 24 | .currNode { 25 | border: 1px solid @primary-color; 26 | } 27 | .nodeMenu { 28 | width: 100px; 29 | height: 26px; 30 | background: #FFFEFE; 31 | box-shadow: 0px 0px 3px 0px rgba(216,216,216,0.5); 32 | border-radius: 6px; 33 | position: absolute; 34 | left: 62px; 35 | top: -36px; 36 | padding: 0 12px; 37 | display: flex; 38 | align-items: center; 39 | justify-content: space-around; 40 | cursor: default; 41 | > span { 42 | cursor: pointer; 43 | color: @primary-color 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /frontend/src/pages/flow/components/dragNode.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useRef, useState } from 'react' 2 | import { Typography, Tooltip } from 'antd' 3 | import { DragNode } from '../../components/workflow' 4 | import { IconFont } from 'components/icon' 5 | import styles from './index.less' 6 | 7 | const DragItem = (props) => { 8 | const { text, data, dragData} = props 9 | const renderTitle = () => { 10 | return
11 |
{data.name}
12 |
Prompt:
13 |
{data.prompt}
14 |
Role:
15 |
{data.role_name}
16 |
17 | } 18 | return ( 19 | 20 |
21 | {text} 22 | 23 |
24 |
25 | 26 | ) 27 | } 28 | 29 | const getNodeCanDrag = (data) => { 30 | return !data.readonly 31 | } 32 | 33 | export default DragNode(DragItem, { canDrag: getNodeCanDrag }) 34 | -------------------------------------------------------------------------------- /frontend/src/pages/flow/components/right-info/index.less: -------------------------------------------------------------------------------- 1 | @import '../../../../styles/index'; 2 | .scriptIO{ 3 | margin-top: 10px; 4 | .scriptIOTitle { 5 | display: flex; 6 | justify-content: space-between; 7 | .scriptIOText { 8 | font-weight: 500; 9 | color: #0F0F0F; 10 | line-height: 20px; 11 | } 12 | } 13 | .setting { 14 | color: @primary-color; 15 | } 16 | 17 | .ioList { 18 | width: 100%; 19 | margin-top: 16px; 20 | .ioItem { 21 | width: 100%; 22 | display: flex; 23 | align-items: center; 24 | &:last-child { 25 | .name, .type { 26 | border-bottom: 1px solid @text15; 27 | // color: rgba(16,38,58,0.65); 28 | } 29 | } 30 | } 31 | .name, .type { 32 | height: 32px; 33 | width: 50%; 34 | border: 1px solid @text15; 35 | border-bottom: none; 36 | line-height: 32px; 37 | text-align: center; 38 | } 39 | .type { 40 | border-left: none; 41 | } 42 | } 43 | } 44 | 45 | .configParams { 46 | .rightItemTitle { 47 | font-weight: 500; 48 | color: #0F0F0F; 49 | line-height: 20px; 50 | margin-bottom: 16px; 51 | } 52 | .formItemTitle { 53 | font-weight: 400; 54 | color: rgba(16,38,58,0.65); 55 | line-height: 20px; 56 | margin-bottom: 4px; 57 | } 58 | :global { 59 | .ant-upload { 60 | width: 100%; 61 | } 62 | .ant-btn-default { 63 | width: 100%; 64 | color: @primary-color; 65 | border-radius: 4px; 66 | border: 1px solid @background15; 67 | } 68 | .anticon-delete,.anticon { 69 | color: @primary-color; 70 | } 71 | .ant-upload-list-item-name { 72 | font-weight: 400; 73 | color: @background85; 74 | } 75 | .ant-upload-list-item-info { 76 | background: #F9F7F8!important; 77 | border-radius: 4px; 78 | padding-left: 4px; 79 | } 80 | .ant-upload-list-item-card-actions-btn { 81 | opacity: 1!important; 82 | } 83 | } 84 | } 85 | 86 | .ioNew { 87 | text-align: right; 88 | margin-bottom: 24px; 89 | :global { 90 | .ant-btn { 91 | border-radius: 8px; 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /frontend/src/pages/flow/components/right-info/renderItem.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Form, Input, Button, Select, Upload, message } from 'antd' 3 | import { UploadOutlined } from '@ant-design/icons' 4 | import { IconFont } from 'components/icon' 5 | 6 | 7 | const booleanOptions = [ 8 | { 9 | value: true, 10 | label: 'True' 11 | }, 12 | { 13 | value: false, 14 | label: 'False' 15 | }, 16 | ] 17 | 18 | const uploadProps = { 19 | accept: '.json,.xml,.md,.xls,.xlsx,.tsv,.csv,.txt,.doc,.docx', 20 | action: '/api/chat/model/upload/file', 21 | maxCount: 1, 22 | progress: { 23 | strokeColor: { 24 | '0%': '#7340C8', 25 | '100%': '#7340C8', 26 | }, 27 | showInfo: false, 28 | strokeWidth: 2, 29 | }, 30 | showUploadList: { 31 | showRemoveIcon: true, 32 | removeIcon: , 33 | }, 34 | iconRender: () => , 35 | onChange(info) { 36 | if (info.file.status !== 'uploading') { 37 | console.log(info.file, info.fileList); 38 | } 39 | if (info.file.status === 'done') { 40 | message.success(`${info.file.name} file uploaded successfully`); 41 | } else if (info.file.status === 'error') { 42 | message.error(`${info.file.name} file upload failed.`); 43 | } 44 | }, 45 | onRemove(info) { 46 | console.log('removeinfo', info) 47 | } 48 | }; 49 | 50 | const renderItem = (item) => { 51 | const type = item.type?.toLowerCase() 52 | if (['select'].includes(type)) { 53 | return 58 | } else if (['password'].includes(type)) { 59 | return 60 | } else if (['boolean'].includes(type)) { 61 | return 68 | } 69 | } 70 | 71 | export default renderItem 72 | -------------------------------------------------------------------------------- /frontend/src/pages/flow/components/right-info/scriptModal.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useRef, useState } from 'react' 2 | import { useImmer } from 'use-immer' 3 | import Editor from 'components/editor' 4 | import { Modal } from 'antd' 5 | import { useParams } from 'umi' 6 | import { getScript, saveScript } from 'services/flow' 7 | import styles from './index.less' 8 | 9 | let time = null 10 | 11 | const ScriptModal = (props) => { 12 | const { id: flowId } = useParams() 13 | const { handleModalVisible, rightInfo , changeNodeParams } = props 14 | const [state, setState] = useImmer({ 15 | value: '', 16 | }) 17 | 18 | const queryScript = () => { 19 | const p = { 20 | flow_id: flowId, 21 | node_id: rightInfo.id, 22 | } 23 | getScript(p).then(res => { 24 | if (res) { 25 | setState(d => { 26 | d.value = res.script_content 27 | }) 28 | } 29 | }) 30 | } 31 | 32 | const handleOk = () => { 33 | const p = { 34 | flow_id: flowId, 35 | node_id: rightInfo.id, 36 | script_content: state.value 37 | } 38 | saveScript(p).then(res => { 39 | if (res) { 40 | handleModalVisible(false) 41 | setState(d => { 42 | d.value = res 43 | }) 44 | const sc = JSON.parse(JSON.stringify(rightInfo.params.script)) 45 | if (sc[0]) { 46 | sc[0].value = res.script_path 47 | changeNodeParams({ 48 | ...rightInfo, 49 | params: { 50 | ...rightInfo.params, 51 | script: sc 52 | } 53 | }) 54 | } 55 | } 56 | }) 57 | } 58 | 59 | const handleEditorChange = (v) => { 60 | setState(d => { 61 | d.value = v 62 | }) 63 | } 64 | 65 | useEffect(() => { 66 | queryScript() 67 | }, []) 68 | 69 | return ( 70 | handleModalVisible(false)} 75 | maskClosable={false} 76 | destroyOnClose 77 | width={960} 78 | cancelText="Cancel" 79 | okText="OK" 80 | > 81 |
82 | 90 |
91 |
92 | ) 93 | } 94 | 95 | export default ScriptModal 96 | -------------------------------------------------------------------------------- /frontend/src/pages/flow/components/right-info/scriptParams.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useRef, useState } from 'react' 2 | import { useImmer } from 'use-immer' 3 | import { Form, Input, Button, Select, Typography } from 'antd' 4 | import RenderFromItem from './renderItem' 5 | import styles from './index.less' 6 | 7 | let time = null 8 | 9 | const ScriptParams = (props) => { 10 | const [form] = Form.useForm() 11 | const { rightInfo, changeNodeParams, readonly } = props 12 | const setFormValue = () => { 13 | const values = {} 14 | rightInfo.params.script.forEach(i => { 15 | const type = i.type?.toLowerCase() 16 | if (type === 'select') { 17 | values[i.name] = i.value ?? i.defaultValue.split(';')[0] 18 | // ['json', 'jsonarray'].includes(item.type) ? : 19 | } else if (['json', 'jsonarray'].includes(type)) { 20 | values[i.name] = JSON.stringify(i.value ?? i.defaultValue, null, 2) 21 | } else { 22 | values[i.name] = i.value ?? i.defaultValue 23 | } 24 | }) 25 | form.setFieldsValue(values) 26 | } 27 | 28 | const handleValueChange = (values) => { 29 | const keys = Object.keys(values) 30 | if (time) { 31 | clearTimeout(time) 32 | time = null 33 | } 34 | 35 | time = setTimeout(() => { 36 | const index = rightInfo.params.script.findIndex(f => { 37 | return f.name === keys[0] 38 | }) 39 | const newSc = JSON.parse(JSON.stringify(rightInfo.params.script)) 40 | newSc[index].value = values[keys[0]] 41 | changeNodeParams({ 42 | ...rightInfo, 43 | params: { 44 | ...rightInfo.params, 45 | script: newSc 46 | } 47 | }) 48 | }, 500) 49 | } 50 | 51 | useEffect(() => { 52 | if (rightInfo?.params?.script) { 53 | setFormValue() 54 | } 55 | }, [JSON.stringify(rightInfo)]) 56 | 57 | return ( 58 |
59 |
64 | {rightInfo?.params?.script?.slice(1).map(item => ( 65 | 72 | {/* */} 73 | {/* */} 74 | {RenderFromItem(item)} 75 | 76 | ))} 77 |
78 |
79 | ) 80 | } 81 | 82 | export default ScriptParams 83 | -------------------------------------------------------------------------------- /frontend/src/pages/flow/util.js: -------------------------------------------------------------------------------- 1 | export const setName = (name, arr) => { 2 | const ar = [] 3 | for (let i = 0; i < arr.length; i++) { 4 | if (arr[i].name.indexOf(name) === name || arr[i].name.split('-')[0] === name) { 5 | if (arr[i].name.indexOf('-', name.length) !== -1) { 6 | const num = Number(arr[i].name.split('-')[arr[i].name.split('-').length - 1]) 7 | if (num) { 8 | ar.push(num) 9 | } 10 | } else { 11 | ar.push(0) 12 | } 13 | } 14 | } 15 | ar.sort((x, y) => { return y - x }) 16 | console.log(ar) 17 | return ar[0] || ar[0] === 0 ? `${name}-${ar[0] + 1}` : name 18 | } 19 | 20 | export function handleNodeData (data) { 21 | const { node , ...otherProps } = data 22 | return otherProps 23 | } -------------------------------------------------------------------------------- /frontend/src/pages/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Redirect } from 'umi' 3 | 4 | const IndexPage = (props) => { 5 | return 6 | } 7 | 8 | export default IndexPage 9 | -------------------------------------------------------------------------------- /frontend/src/pages/layout/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Link, useLocation } from 'umi' 3 | import { Layout } from 'antd' 4 | import Menu from '@/pages/components/menu' 5 | import { IconFont } from 'components/icon' 6 | import styles from './index.less' 7 | 8 | const { Header, Content } = Layout 9 | 10 | const BasicLayout = (props) => { 11 | const { routes } = props 12 | const location = useLocation(); 13 | 14 | return ( 15 | 16 |
17 | LMPM 18 |
19 | 20 |
21 |
22 | 23 |
24 | 25 |
26 | 27 |
28 |
29 | 30 |
31 | {props.children} 32 |
33 |
34 |
35 | ) 36 | } 37 | 38 | export default BasicLayout 39 | -------------------------------------------------------------------------------- /frontend/src/pages/layout/index.less: -------------------------------------------------------------------------------- 1 | .container { 2 | width: 100%; 3 | height: calc(100% - 64px); 4 | background-color: #f9f7f8; 5 | display: flex; 6 | flex-direction: column; 7 | overflow: hidden; 8 | .bread { 9 | width: 100%; 10 | line-height: 40px; 11 | } 12 | .content { 13 | flex: 1; 14 | width: 100%; 15 | min-width: 1400px; 16 | height: 100%; 17 | overflow: auto; 18 | } 19 | } 20 | .head { 21 | padding: 0 64px; 22 | font-size: 16px; 23 | font-weight: 500; 24 | color: #0F0F0F; 25 | display: flex; 26 | height: 64px; 27 | background: @white100; 28 | box-shadow: 0px 2px 12px 0px rgba(206,206,206,0.5); 29 | position: relative; 30 | z-index: 1; 31 | .homeIcon { 32 | cursor: pointer; 33 | margin-right: 55px; 34 | color: @primary-color; 35 | font-size: 20px; 36 | font-weight: 600; 37 | } 38 | .menuItems { 39 | font-size: 16px; 40 | font-weight: 500; 41 | color: #0F0F0F; 42 | flex: 1; 43 | :global { 44 | .ant-menu-horizontal { 45 | height: 40px; 46 | margin-top: 12px; 47 | line-height: 40px; 48 | border-bottom: none; 49 | .ant-menu-item, .ant-menu-submenu { 50 | border-radius: 20px; 51 | } 52 | .ant-menu-item-selected { 53 | line-height: 40px; 54 | background: @color15; 55 | color: @primary-color; 56 | border-radius: 20px; 57 | a { 58 | color: @primary-color; 59 | &:hover { 60 | color: @primary-color; 61 | } 62 | } 63 | &::after { 64 | border: none; 65 | } 66 | } 67 | .ant-menu-submenu { 68 | a { 69 | color: @black85; 70 | } 71 | } 72 | .ant-menu-item{ 73 | margin: 0 10px; 74 | &::after { 75 | visibility: hidden; 76 | } 77 | a { 78 | &:hover { 79 | color: @primary-color; 80 | } 81 | } 82 | } 83 | .ant-menu-submenu { 84 | padding-left: 7px; 85 | .ant-menu-submenu-title { 86 | .ant-menu-title-content { 87 | margin-left: 13px; 88 | } 89 | } 90 | &::after { 91 | visibility: hidden; 92 | left: 12px; 93 | } 94 | } 95 | .ant-menu-item-active, .ant-menu-submenu-active, .ant-menu-submenu-selected { 96 | line-height: 40px; 97 | background: @color15; 98 | color: @primary-color; 99 | border-radius: 20px; 100 | } 101 | } 102 | } 103 | } 104 | .guideContain { 105 | padding-top: 11px; 106 | .questionContain { 107 | width: 40px; 108 | height: 40px; 109 | text-align: center; 110 | line-height: 40px; 111 | border-radius: 20px; 112 | color: #0F0F0F; 113 | &.active { 114 | color: @primary-color; 115 | background: rgba(115,64,200,0.15); 116 | } 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /frontend/src/pages/prompt-app/edit-modal/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react' 2 | import { Modal, Form, Input } from 'antd' 3 | import { asyncName } from 'utils' 4 | import { updateAppName } from 'services/promptApp' 5 | 6 | const formLayout = { 7 | labelCol:{ span: 8 }, 8 | wrapperCol: { span: 12 } 9 | } 10 | 11 | const EditModal = (props) => { 12 | const [form] = Form.useForm() 13 | const { id, name, closeModal, editOpen } = props 14 | 15 | useEffect(() => { 16 | form.setFieldsValue({ name }) 17 | }, [name]) 18 | 19 | const onOk = () => { 20 | form.validateFields().then(values => { 21 | if (values.name !== name) { 22 | updateAppName({ id, name: values.name }).then(() => { 23 | closeModal('update') 24 | }) 25 | } else { 26 | closeModal('cancel') 27 | } 28 | }) 29 | } 30 | 31 | return ( 32 | closeModal('cancel')} 37 | maskClosable={false} 38 | destroyOnClose 39 | width={640} 40 | cancelText="Cancel" 41 | okText="OK" 42 | > 43 |
44 | 52 | 53 | 54 |
55 |
56 | ) 57 | } 58 | 59 | export default EditModal 60 | -------------------------------------------------------------------------------- /frontend/src/pages/prompt-app/sdk-modal/index.less: -------------------------------------------------------------------------------- 1 | .empty { 2 | margin-top: 24px; 3 | } 4 | -------------------------------------------------------------------------------- /frontend/src/pages/prompt-app/variable-modal/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Modal, Table, Button } from 'antd' 3 | import styles from './index.less' 4 | 5 | const VariableModal = (props) => { 6 | const { activeApp, variableOpen, closeModal } = props 7 | 8 | const columns = [ 9 | { 10 | title: 'Variable', 11 | dataIndex: 'variable', 12 | width: 200, 13 | }, 14 | { 15 | title: 'Type', 16 | dataIndex: 'type', 17 | width: 120, 18 | }, 19 | { 20 | title: 'Default value', 21 | dataIndex: 'defaultValue', 22 | render: _ => <>{_ || '--'} 23 | }, 24 | ] 25 | 26 | return ( 27 | Cancel} 38 | > 39 |
40 | 48 | 49 | 50 | ) 51 | } 52 | 53 | export default VariableModal 54 | -------------------------------------------------------------------------------- /frontend/src/pages/prompt-app/variable-modal/index.less: -------------------------------------------------------------------------------- 1 | .mainTable { 2 | padding: 0 10px; 3 | :global { 4 | .ant-table-header { 5 | .ant-table-cell { 6 | background: @background04; 7 | font-weight: 500; 8 | color: @background85; 9 | &::before { 10 | background-color: transparent!important; 11 | } 12 | } 13 | } 14 | .ant-table-body { 15 | overflow: auto!important; 16 | .ant-table-cell { 17 | font-weight: 400; 18 | color: @background65; 19 | } 20 | } 21 | 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /frontend/src/pages/prompt-guide/index.jsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from "react"; 2 | import ReactMarkdown from 'react-markdown'; 3 | import remarkGfm from "remark-gfm"; 4 | import rehypeRaw from 'rehype-raw' 5 | import { message } from 'antd'; 6 | import copy from 'copy-to-clipboard' 7 | import { overviewQuickguide } from '@/services/aiModel' 8 | import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter' 9 | import { gruvboxLight } from 'react-syntax-highlighter/dist/esm/styles/prism' 10 | import { IconFont } from 'components/icon'; 11 | import styles from './index.less'; 12 | 13 | const PromptGuide = () => { 14 | const [markdown, setMarkdown] = useState(''); 15 | 16 | useEffect(async () => { 17 | const res = await overviewQuickguide(); 18 | if(res) { 19 | setMarkdown(res); 20 | } 21 | }, []); 22 | 23 | const copyCode = (children) => { 24 | message.success('Copy successful!') 25 | copy(children[0].props.children[0]) 26 | } 27 | 28 | const Pre = ({ children }) => { 29 | return ( 30 |
31 | 32 | {children} 33 |
34 | ) 35 | } 36 | 37 | return ( 38 |
39 |
40 | 56 | ) : ( 57 | 58 | {children} 59 | 60 | ) 61 | } 62 | }} 63 | > 64 |
65 |
66 | ) 67 | } 68 | 69 | export default PromptGuide 70 | -------------------------------------------------------------------------------- /frontend/src/pages/prompt-guide/index.less: -------------------------------------------------------------------------------- 1 | .markdownBody { 2 | background-color: @white100; 3 | .markdownContent { 4 | width: calc(100% - 240px); 5 | margin: 0 auto; 6 | padding: 30px 0; 7 | .blogPre { 8 | position: relative; 9 | padding-right: 40px; 10 | background: #F9F7F8 !important; 11 | .copyIcon { 12 | position: absolute; 13 | z-index: 10; 14 | right: 16px; 15 | top: 16px; 16 | } 17 | div { 18 | background-color: #F9F7F8 !important; 19 | } 20 | :global { 21 | code { 22 | font-family: 'monospace' !important; 23 | } 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /frontend/src/pages/prompt-market/config-modal/index.less: -------------------------------------------------------------------------------- 1 | .configMian { 2 | width: 100%; 3 | height: 100%; 4 | .mainTop { 5 | display: flex; 6 | justify-content: space-between; 7 | margin-bottom: 24px; 8 | .mainTopSearch { 9 | :global { 10 | .ant-input-search { 11 | border-radius: 8px; 12 | border: 1px solid @text15; 13 | } 14 | .ant-input-affix-wrapper,.ant-input-group-addon,.ant-btn { 15 | border: none; 16 | background: transparent; 17 | box-shadow: none; 18 | color: @text15!important; 19 | } 20 | } 21 | input { 22 | caret-color: @primary-color; 23 | background: transparent; 24 | } 25 | } 26 | .mainTopNew { 27 | :global { 28 | .ant-btn { 29 | border-radius: 8px; 30 | } 31 | } 32 | } 33 | } 34 | :global { 35 | .ant-table-expanded-row-fixed { 36 | width: 862px!important; 37 | } 38 | .editable-row .ant-form-item-explain { 39 | position: absolute; 40 | top: 100%; 41 | font-size: 12px; 42 | } 43 | .ant-table-cell-row-hover { 44 | background: @color15!important; 45 | } 46 | } 47 | } 48 | .mainBottom { 49 | :global { 50 | .ant-input { 51 | border: 1px solid @primary-color; 52 | } 53 | .ant-form-item-explain { 54 | position: absolute; 55 | top: 26px; 56 | font-size: 12px; 57 | } 58 | .ant-table-header { 59 | th { 60 | background: @background04; 61 | font-weight: 500; 62 | color: @background85; 63 | } 64 | } 65 | .ant-table-body { 66 | overflow: auto!important; 67 | } 68 | .ant-table-cell { 69 | font-weight: 400; 70 | color: @background65; 71 | .ant-typography { 72 | color: @background65; 73 | } 74 | .ant-typography-disabled { 75 | color: @black25; 76 | } 77 | &::before { 78 | background-color: #fafafa!important; 79 | } 80 | } 81 | } 82 | } 83 | .dragIcon { 84 | cursor: grab; 85 | color: #999; 86 | } 87 | .dragDisabled { 88 | cursor: not-allowed; 89 | color: @black25; 90 | } 91 | :global { 92 | .row-dragging { 93 | z-index: 1000; 94 | background: @white100; 95 | border: 1px solid #f0f0f0; 96 | line-height: 22px; 97 | background: @color15; 98 | .ant-table-cell { 99 | padding: 8px; 100 | white-space: nowrap; 101 | overflow: hidden; 102 | text-overflow: ellipsis; 103 | word-wrap: break-word; 104 | .ant-typography { 105 | color: @background65; 106 | } 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /frontend/src/pages/prompt-market/detail-modal/index.less: -------------------------------------------------------------------------------- 1 | .modalMain { 2 | padding: 0 36px; 3 | } 4 | .promptDivider { 5 | font-size: 18px!important; 6 | font-weight: 600!important; 7 | color: @text65!important; 8 | .promptDividerIcon { 9 | margin-left: 8px; 10 | color: @primary-color; 11 | cursor: pointer; 12 | font-size: 12px; 13 | position: relative; 14 | bottom: 2px; 15 | } 16 | &::after { 17 | border-color: @background15!important; 18 | } 19 | } 20 | .promptText { 21 | :global { 22 | .ant-descriptions-item-content { 23 | font-weight: 400; 24 | color: @background65; 25 | word-break: break-all; 26 | white-space: pre-wrap; 27 | } 28 | } 29 | } 30 | .promptDesc { 31 | margin-bottom: 24px; 32 | padding: 6px; 33 | } 34 | .variablesDesc { 35 | margin-bottom: 24px; 36 | :global { 37 | .ant-table-body { 38 | .ant-table-cell { 39 | font-weight: 400; 40 | color: @background65; 41 | } 42 | } 43 | .ant-table-header { 44 | .ant-table-cell { 45 | background: @background04; 46 | font-weight: 500; 47 | color: @background85; 48 | &::before { 49 | background-color: transparent!important; 50 | } 51 | } 52 | } 53 | } 54 | } 55 | .infoDesc { 56 | padding: 6px; 57 | :global { 58 | .ant-descriptions-item-label { 59 | font-weight: 400; 60 | color: @background65; 61 | } 62 | .ant-descriptions-item-content { 63 | white-space: nowrap; 64 | overflow: hidden; 65 | text-overflow: ellipsis; 66 | display: block; 67 | font-weight: 400; 68 | color: @background45; 69 | } 70 | .ant-typography { 71 | font-weight: 400; 72 | color: @background45; 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /frontend/src/pages/prompt-market/main/index.less: -------------------------------------------------------------------------------- 1 | .promptMarket { 2 | width: 100%; 3 | height: 100%; 4 | display: flex; 5 | flex-direction: column; 6 | .promptMarketTop { 7 | padding: 26px 64px 0 64px; 8 | background: @white100; 9 | position: relative; 10 | } 11 | .topBar { 12 | margin-bottom: 20px; 13 | .topBarIcon { 14 | font-size: 24px; 15 | margin-right: 8px; 16 | } 17 | .topBarTitle { 18 | font-size: 16px; 19 | font-weight: 600; 20 | color: #0F0F0F; 21 | line-height: 22px; 22 | position: relative; 23 | top: -4px; 24 | } 25 | } 26 | .promptMarketBottom { 27 | flex: 1; 28 | display: flex; 29 | flex-direction: column; 30 | overflow: hidden; 31 | } 32 | .bottomMain { 33 | overflow: auto; 34 | padding: 24px 52px; 35 | height: 100%; 36 | display: flex; 37 | flex-direction: column; 38 | .bottomOperate { 39 | margin-bottom: 12px; 40 | position: relative; 41 | border-radius: 5px 5px 0 0; 42 | .operateLeft { 43 | display: flex; 44 | padding-left: 4px; 45 | .operateLeftSearch { 46 | margin-left: 16px; 47 | :global { 48 | .ant-input-search { 49 | border-radius: 8px; 50 | border: 1px solid @text15; 51 | } 52 | .ant-input-affix-wrapper { 53 | height: 38px; 54 | width: 303px; 55 | vertical-align: middle; 56 | background: transparent; 57 | border-radius: 8px; 58 | border-color: @text15; 59 | } 60 | .anticon-search { 61 | color: @text15; 62 | } 63 | } 64 | input { 65 | caret-color: @primary-color; 66 | background: transparent; 67 | } 68 | } 69 | } 70 | .operateRight { 71 | position: absolute; 72 | top: 0; 73 | right: 0; 74 | margin-right: 12px; 75 | :global { 76 | .ant-btn { 77 | border-radius: 8px; 78 | height: 38px; 79 | } 80 | } 81 | } 82 | } 83 | .bottomContent { 84 | flex: 1 1 auto; 85 | margin-bottom: 12px; 86 | position: relative; 87 | :global { 88 | .ant-col { 89 | padding: 12px; 90 | } 91 | .ant-empty { 92 | position: absolute; 93 | top: calc(50% - 65px); 94 | left: calc(50% - 64px); 95 | } 96 | .ant-empty-description { 97 | font-size: 16px; 98 | color: @text30; 99 | font-weight: 400; 100 | } 101 | } 102 | } 103 | .bottomPagination { 104 | height: 24px; 105 | padding: 0 12px; 106 | :global { 107 | .ant-pagination { 108 | float: right; 109 | } 110 | } 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /frontend/src/pages/prompt-market/new-modal/index.less: -------------------------------------------------------------------------------- 1 | .varRow { 2 | height: 32px; 3 | line-height: 32px; 4 | margin-bottom: 24px; 5 | .varCol { 6 | text-align: right; 7 | color: @black85; 8 | &:after { 9 | content: ':'; 10 | position: relative; 11 | top: -0.5px; 12 | margin: 0 8px 0 2px; 13 | } 14 | } 15 | .varInput { 16 | border: none; 17 | background-color: transparent!important; 18 | color: @background65!important; 19 | white-space: nowrap; 20 | overflow: hidden; 21 | text-overflow: ellipsis!important; 22 | cursor: text; 23 | } 24 | :global { 25 | .ant-typography { 26 | color: @background65; 27 | font-weight: 400; 28 | } 29 | } 30 | } 31 | .multSelect { 32 | :global{ 33 | .ant-select-selection-item { 34 | background: @color15; 35 | border-radius: 13px; 36 | color: @primary-color; 37 | margin-right: 8px; 38 | } 39 | .ant-select-selection-item-content { 40 | margin-right: 8px; 41 | } 42 | } 43 | .formTableTit { 44 | label { 45 | position: relative; 46 | top: 10px; 47 | } 48 | } 49 | } 50 | 51 | -------------------------------------------------------------------------------- /frontend/src/pages/prompt-market/tags-filter/index.less: -------------------------------------------------------------------------------- 1 | .topFilter { 2 | height: auto; 3 | margin: 4px 0; 4 | .foldIcon { 5 | position: absolute; 6 | top: 32px; 7 | right: 65px; 8 | color: @primary-color; 9 | cursor: pointer; 10 | font-size: 16px; 11 | transform: rotateX(0deg); 12 | transition: transform 0.3s; 13 | } 14 | .iconRot180 { 15 | transform: rotateX(180deg); 16 | } 17 | .filterItem { 18 | display: flex; 19 | margin-bottom: 16px; 20 | .filterItemTitle { 21 | height: 32px; 22 | width: 57px; 23 | text-align: right; 24 | font-weight: 500; 25 | color: @text65; 26 | line-height: 32px; 27 | } 28 | .filterItemContent { 29 | height: 30px; 30 | overflow: hidden; 31 | text-overflow: ellipsis; 32 | max-width: calc(100% - 169px); 33 | } 34 | .filterItemContentFold { 35 | height: auto; 36 | white-space: normal; 37 | } 38 | .filterItemFold { 39 | width: 80px; 40 | text-align: center; 41 | position: relative; 42 | top: 2px; 43 | .iconRot { 44 | transform: rotateX(0deg); 45 | transition: transform 0.3s; 46 | height: 14px; 47 | } 48 | .iconRot180 { 49 | transform: rotateX(180deg); 50 | } 51 | button { 52 | color: @primary-color; 53 | } 54 | } 55 | .filterItemAdd { 56 | font-size: 16px; 57 | color: @primary-color; 58 | margin-left: 16px; 59 | margin-top: 6px; 60 | cursor: pointer; 61 | } 62 | :global { 63 | .ant-tag { 64 | margin: 4px 16px 6px 16px; 65 | padding: 0px 16px; 66 | height: 24px; 67 | line-height: 24px; 68 | font-weight: 400; 69 | color: @text65; 70 | border-radius: 13px; 71 | cursor: pointer; 72 | border: 1px solid @color15; 73 | background: @white100; 74 | box-sizing: content-box; 75 | &:hover { 76 | background: @color05; 77 | } 78 | } 79 | } 80 | .itemTagActive { 81 | background: @color15; 82 | color: @primary-color; 83 | } 84 | } 85 | } 86 | .topFilterHidden { 87 | height: 0; 88 | margin: 0; 89 | overflow: hidden; 90 | } 91 | -------------------------------------------------------------------------------- /frontend/src/services/aiModel.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | export async function getModelList(params) { 4 | return request.get('/model/list', params) 5 | } 6 | 7 | export async function getModelConfig(params) { 8 | return request.get('/model/config/get', params) 9 | } 10 | 11 | export async function getParamsParse(params) { 12 | return request.post('/model/params/parse', params) 13 | } 14 | 15 | export async function saveModel(params) { 16 | return request.post('/model/save', params, { needCode: true }) 17 | } 18 | 19 | export async function updateModel(params) { 20 | return request.post('/model/update', params, { needCode: true }) 21 | } 22 | 23 | export async function deleteModel(params) { 24 | return request.post('/model/delete', params) 25 | } 26 | 27 | export async function defaultModel(params) { 28 | return request.post('/model/default', params); 29 | } 30 | export async function overviewQuickguide(params) { 31 | return request.get('/overview/quickguide', params, {needCode: true}); 32 | } 33 | export async function modelNameCheck(params) { 34 | return request.post('/model/name/check', params); 35 | } -------------------------------------------------------------------------------- /frontend/src/services/chat.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | // 获取右侧model列表 4 | export async function getModelList(params) { 5 | return request.get('/chat/model/configuration', params) 6 | } 7 | 8 | // 获取底侧prompt分组列表 9 | export async function getPromptGroup(params) { 10 | return request.get('/prompt/scenegroup/list', params) 11 | } 12 | 13 | // 获取底侧所有prompt列表 14 | export async function getPromptAll(params) { 15 | return request.get('/prompt/list', params) 16 | } 17 | 18 | // chat对话同步发送接口 19 | export async function syncSendMsg(params) { 20 | return request.post('/chat/model/sync/completions', params) 21 | } 22 | -------------------------------------------------------------------------------- /frontend/src/services/flow.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | export async function getEdgeList(params) { 4 | return request.get('/aps/edge/bsox/list', { params }) 5 | } 6 | 7 | // flowList 8 | export async function getFlowList(params) { 9 | return request.get('/flow/list', params) 10 | } 11 | 12 | // flowadd 13 | export async function createFlow(params) { 14 | return request.post('/flow/add', params) 15 | } 16 | 17 | // flowcopy 18 | export async function copyFlow(params) { 19 | return request.post('/flow/copy', params) 20 | } 21 | 22 | // delete 23 | export async function deleteFlow(params) { 24 | return request.delete('/flow/delete', params) 25 | } 26 | 27 | // module tree 28 | export async function getModuleTree(params) { 29 | return request.get('/flow/module/tree', params) 30 | } 31 | 32 | // create node 33 | export async function createNode(params) { 34 | return request.post('/flow/node/create', params) 35 | } 36 | 37 | // get flow 38 | export async function getFlow(params) { 39 | return request.get('/flow/pmflow/get', params) 40 | } 41 | 42 | // save flow 43 | export async function saveFlow(params) { 44 | return request.post('/flow/pmflow/save', params) 45 | } 46 | 47 | // get script 48 | export async function getScript(params) { 49 | return request.get('/flow/node/script/get', params) 50 | } 51 | 52 | // save script 53 | export async function saveScript(params) { 54 | return request.post('/flow/node/script/save', params) 55 | } 56 | 57 | // publish flow 58 | export async function publishFlow(params) { 59 | return request.post('/flow/pmflow/publish', params) 60 | } 61 | 62 | // applist 63 | export async function getAppList(params) { 64 | return request.get('/app/list', params) 65 | } 66 | 67 | // variablelist 68 | export async function getVariablesList(params) { 69 | return request.get('/flow/pmflow/variables', params) 70 | } 71 | 72 | // flow run 73 | export async function runFlow(params) { 74 | return request.post('/flow/pmflow/run', params) 75 | } 76 | 77 | // flow status 78 | export async function getFlowStatus(params) { 79 | return request.get('/flow/pmflow/run/status', params) 80 | } 81 | 82 | // publish status 83 | export async function getFlowPublishStatus(params) { 84 | return request.get('/flow/publish/status', params) 85 | } 86 | 87 | // publish status 88 | export async function editFlowInfo(params) { 89 | return request.post('/flow/edit', params) 90 | } 91 | 92 | // check input 93 | export async function checkInput(params) { 94 | return request.get('/flow/pmflow/inputNode/check', params) 95 | } -------------------------------------------------------------------------------- /frontend/src/services/overview.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | export async function getOverviewComponent() { 4 | return request.get('/overview/component') 5 | } 6 | 7 | export async function getPromptApp(params) { 8 | return request.get('/app/list', params) 9 | } -------------------------------------------------------------------------------- /frontend/src/services/promptApp.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | // 获取app列表 4 | export async function getAppList(params) { 5 | return request.get('/app/list', params) 6 | } 7 | 8 | // 删除app 9 | export async function deleteApp(params) { 10 | return request.post('/app/delete', params) 11 | } 12 | 13 | // 更新app名称 14 | export async function updateAppName(params) { 15 | return request.post('/app/update', params) 16 | } 17 | 18 | // 获取SDK 19 | export async function getSDK(id) { 20 | return request.get('/app/sdk/demo/get', { id }, { needCode: true }); 21 | } 22 | 23 | // 导出SDK 24 | export async function exportSDK(params) { 25 | return request.post('/api/app/sdk/export', params, { needCode: true, responseType: 'blob' }); 26 | } 27 | -------------------------------------------------------------------------------- /frontend/src/services/promptMarket.js: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | 3 | // 获取scene、role、label列表 4 | export async function getCategoryList(params) { 5 | return request.get('/prompt/category/list', params) 6 | } 7 | 8 | // 批量更新scene、role、label 9 | export async function postCategoryList(params) { 10 | return request.post('/prompt/category/batch/operate', params) 11 | } 12 | 13 | // 添加scene、role、label 14 | export async function addCategory(params) { 15 | return request.post('/prompt/category/add', params) 16 | } 17 | 18 | // 获取prompt列表 19 | export async function getPromptList(params) { 20 | return request.get('/prompt/page', params) 21 | } 22 | 23 | // 删除prompt 24 | export async function deletePrompt(params) { 25 | return request.delete('/prompt/delete', params) 26 | } 27 | 28 | // 新增prompt 29 | export async function addPrompt(params) { 30 | return request.post('/prompt/add', params) 31 | } 32 | 33 | // 编辑prompt 34 | export async function updatePrompt(params) { 35 | return request.put('/prompt/update', params) 36 | } 37 | 38 | // 删除(角色,场景,标签)校验 39 | export async function validatePrompt(params) { 40 | return request.get('/prompt/category/delete/validate', params) 41 | } 42 | -------------------------------------------------------------------------------- /frontend/src/styles/common-less/index.less: -------------------------------------------------------------------------------- 1 | @import '~antd/lib/style/themes/default.less'; 2 | @import './mixins/index.less'; 3 | @import './theme/default.less'; -------------------------------------------------------------------------------- /frontend/src/styles/common-less/mixins/index.less: -------------------------------------------------------------------------------- 1 | @import '../theme/default.less'; 2 | 3 | ::-webkit-scrollbar { 4 | width: 8px; 5 | height: 8px; 6 | } 7 | 8 | ::-webkit-scrollbar-track { 9 | background-color:transparent; 10 | -webkit-border-radius: 2em; 11 | -moz-border-radius: 2em; 12 | border-radius:2em; 13 | } 14 | ::-webkit-scrollbar-thumb { 15 | background-color: #D6D6D6; 16 | -webkit-border-radius: 2em; 17 | -moz-border-radius: 2em; 18 | border-radius:2em; 19 | } 20 | 21 | .center() { 22 | display: flex; 23 | align-items: center; 24 | justify-content: center; 25 | } 26 | 27 | // flex布局,可通过传入的参数设置元素排列方向,水平方向对齐方式 垂直方向对齐方式 28 | 29 | .flex(@direction: row, @just: center, @align: center) { 30 | display: flex; 31 | flex-direction: @direction; 32 | justify-content: @just; 33 | align-items: @align; 34 | } 35 | 36 | .clearfix() { 37 | zoom: 1; 38 | 39 | &:before, 40 | &:after { 41 | content: ''; 42 | display: table; 43 | } 44 | 45 | &:after { 46 | clear: both; 47 | } 48 | } 49 | 50 | .size(@width; @height) { 51 | width: @width; 52 | height: @height; 53 | } 54 | 55 | .box(@position: absolute, @top: 0px, @left: 0px, @bottom: 0px, @right: 0px) { 56 | position: @position; 57 | top: @top; 58 | left: @left; 59 | bottom: @bottom; 60 | right: @right; 61 | 62 | } 63 | 64 | .tips_style(@width) { 65 | border-radius: @border-radius-base; 66 | width: @width; 67 | height: @height-lg; 68 | line-height: @height-lg; 69 | padding: 0 @padding-sm; 70 | margin-bottom: @margin-xs; 71 | } 72 | 73 | 74 | 75 | //方便于table内或者list内容中需要根据宽度而不是字数来隐藏,width可以自定义 76 | .nameEllipsis(@width) { 77 | display: inline-block; 78 | width: @width; 79 | overflow: hidden; 80 | vertical-align: bottom; 81 | text-overflow: ellipsis; 82 | white-space: nowrap; 83 | } 84 | 85 | // Placeholder text 86 | .placeholder(@color: @input-placeholder-color) { 87 | 88 | &::-moz-placeholder { 89 | opacity: 1; 90 | } 91 | 92 | &::placeholder { 93 | color: @color; 94 | } 95 | 96 | &:placeholder-shown { 97 | text-overflow: ellipsis; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /frontend/src/styles/index.less: -------------------------------------------------------------------------------- 1 | @import './common-less/index'; 2 | 3 | @bg-color: #f0f6ff; 4 | 5 | :global{ 6 | .mlpm-table { 7 | .ant-table-container table > thead > tr:first-child th:first-child { 8 | padding-left: 50px; 9 | } 10 | .ant-table-container table > tbody > tr td:first-child { 11 | padding-left: 50px; 12 | .ant-table-expanded-row-fixed { 13 | width: 100% !important; 14 | } 15 | } 16 | } 17 | } 18 | 19 | :global { 20 | .ant-tooltip-inner { 21 | white-space: pre-wrap; 22 | word-break: break-all; 23 | } 24 | .ant-tooltip { 25 | max-width: 500px; 26 | } 27 | textarea { 28 | word-break: break-all; 29 | } 30 | .ant-table-thead > tr > th { 31 | background: rgba(16, 38, 58, 0.04); 32 | color: rgba(16, 38, 58, 0.85); 33 | } 34 | .ant-table-body { 35 | overflow: auto !important; 36 | } 37 | .ant-select-item-option-selected:not(.ant-select-item-option-disabled) { 38 | color: @primary-color; 39 | } 40 | .ant-select-item-option-active:not(.ant-select-item-option-disabled) { 41 | color: @primary-color; 42 | } 43 | .ant-table-thead > tr > th:not(:last-child):not(.ant-table-selection-column):not(.ant-table-row-expand-icon-cell):not([colspan])::before { 44 | width: 0; 45 | } 46 | .ant-table-tbody > tr > td , .ant-table-tbody > tr > td .ant-typography { 47 | color: rgba(16, 38, 58, 0.65); 48 | // font-size: 12px; 49 | } 50 | .ant-modal-body { 51 | max-height: 60vh; 52 | overflow: auto; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /frontend/src/utils/request.js: -------------------------------------------------------------------------------- 1 | import { extend } from 'umi-request' 2 | import { notification } from 'antd' 3 | import Http from './http' 4 | 5 | const codeMessage = { 6 | 200: 'The server successfully returned the requested data.', 7 | 404: 'The request was made for a non-existent record and the server did not take any action.', 8 | 500: 'The server encountered an error. Please check the server.', 9 | 502: 'Gateway error.', 10 | 503: 'Service unavailable, server temporarily overloaded or under maintenance.', 11 | 504: 'gateway timeout.', 12 | } 13 | 14 | Http.config = { 15 | prefix: '/api', 16 | // // HTTP错误状态码 17 | errorCallback: (status, url) => { 18 | const errorText = codeMessage[status] 19 | notification.error({ 20 | message: `request error ${status}: ${url}`, 21 | description: errorText, 22 | }) 23 | }, 24 | // 无状态返回 25 | noResponseCallback: () => { 26 | notification.error({ 27 | description: 'Your network has encountered an exception and cannot connect to the server!', 28 | message: 'network anomaly', 29 | }) 30 | }, 31 | // getHeaders: () => { 32 | // return {} 33 | // }, 34 | } 35 | 36 | export default Http -------------------------------------------------------------------------------- /frontend/src/utils/variable.js: -------------------------------------------------------------------------------- 1 | import { message } from 'antd' 2 | 3 | export const parseTempVar = text => { 4 | const allVar = text.match(/\${.*?\}/g) || [] // ['${xxx[text]:default}', '${ooo[file]:file/default.doc}'] 5 | const allVarList = allVar.map(cur => { 6 | const curVar = cur.slice(2, -1) // xxx[text]:default 或 ooo[file]:file/default.doc 7 | const typeList = curVar.match(/\[.*?\]/g) || [] // ['[text]'] 或 ['[file]'] 8 | let name = '' // 初始化变量字段名 9 | let type = 'text' // 初始化变量类型 10 | let defaultValue = '' // 初始化变量默认值 11 | if (typeList.length === 0) { // 如果正则未匹配到,说明没配置变量类型 12 | const defIndex = curVar.indexOf(':') // 此时看看有没有配置默认值 13 | name = defIndex === -1 ? curVar : curVar.slice(0, defIndex) 14 | defaultValue = defIndex === -1 ? '' : curVar.slice(defIndex + 1) 15 | } else { 16 | name = curVar.split(typeList[0])[0] 17 | type = typeList[0].slice(1, -1) === 'file' ? 'file' : 'text' 18 | const defIndex = curVar.split(typeList[0]).slice(1).join(typeList[0]).indexOf(':') 19 | defaultValue = defIndex === -1 ? '' : curVar.split(typeList[0]).slice(1).join(typeList[0]).slice(defIndex + 1) 20 | } 21 | return { 22 | var: [cur], // 变量 23 | name, // 变量字段名 24 | type, // 变量类型 text/file 25 | defaultValue, // 变量默认值 26 | value: '', // 变量真实值 27 | } 28 | }) 29 | 30 | // 变量列表按照变量字段名去掉空串并去重 31 | const map = new Map() 32 | allVarList.forEach(item => { 33 | // 前面覆盖后面就加这个判断,后面覆盖前面可以不加这个判断 -- (!map.has(item.name)) 34 | if (item.name !== '') { 35 | if (!map.has(item.name)) { 36 | map.set(item.name, item) 37 | } else { 38 | const cur = map.get(item.name) 39 | map.set(item.name, { ...cur, var: Array.from(new Set([ ...cur.var, ...item.var ])) }) 40 | } 41 | } 42 | }) 43 | 44 | return [...map.values()] 45 | } 46 | 47 | export const jsonValidator = str => { 48 | try { 49 | JSON.parse(str) 50 | } catch (e) { 51 | return false 52 | } 53 | return true 54 | } 55 | 56 | // 先给要复制的文本或者按钮加上点击事件后,并将要复制的值传过来 57 | export const copyValue = async val => { 58 | if (navigator.clipboard && window.isSecureContext) { 59 | message.success('Copy successful!') 60 | return navigator.clipboard.writeText(val) 61 | } else { 62 | // 创建text area 63 | const textArea = document.createElement('textarea') 64 | textArea.value = val 65 | // 使text area不在viewport,同时设置不可见 66 | document.body.appendChild(textArea) 67 | textArea.focus() 68 | textArea.select() 69 | message.success('Copy successful!') 70 | return new Promise((res, rej) => { 71 | // 执行复制命令并移除文本框 72 | document.execCommand('copy') ? res() : rej() 73 | textArea.remove() 74 | }) 75 | } 76 | } 77 | 78 | export const formMessage = { 79 | required: 'Please input something.' 80 | } 81 | -------------------------------------------------------------------------------- /frontend/theme.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | "primary-color": "#7340c8", 3 | "select-item-active-bg": "#f8f0ff", 4 | "table-row-hover-bg": "#f8f0ff", 5 | "white0": "rgba(255,255,255,0)", 6 | "white85": "rgba(255,255,255,0.85)", 7 | "white88": "rgba(255,255,255,0.88)", 8 | "white100": "rgba(255,255,255,1)", 9 | "black25": "rgba(0,0,0,0.25)", 10 | "black50": "rgba(0,0,0,0.5)", 11 | "black75": "rgba(0,0,0,0.75)", 12 | "black85": "rgba(0,0,0,0.85)", 13 | "black100": "rgba(0,0,0,1)", 14 | "color05": "rgba(115,64,200,0.05)", 15 | "color15": "rgba(115,64,200,0.15)", 16 | "color50": "rgba(115,64,200,0.5)", 17 | "text05": "rgba(15,15,15,0.05)", 18 | "text09": "rgba(15,15,15,0.09)", 19 | "text10": "rgba(15,15,15,0.1)", 20 | "text15": "rgba(15,15,15,0.15)", 21 | "text30": "rgba(15,15,15,0.3)", 22 | "text40": "rgba(15,15,15,0.4)", 23 | "text50": "rgba(15,15,15,0.5)", 24 | "text65": "rgba(15,15,15,0.65)", 25 | "text85": "rgba(15,15,15,0.85)", 26 | "background04": "rgba(16,38,58,0.04)", 27 | "background09": "rgba(16,38,58,0.09)", 28 | "background15": "rgba(16,38,58,0.15)", 29 | "background45": "rgba(16,38,58,0.45)", 30 | "background65": "rgba(16,38,58,0.65)", 31 | "background85": "rgba(16,38,58,0.85)", 32 | "background100": "rgba(16,38,58,1)", 33 | } -------------------------------------------------------------------------------- /frontend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "esnext", 4 | "module": "esnext", 5 | "moduleResolution": "node", 6 | "resolveJsonModule": true, 7 | "importHelpers": true, 8 | "jsx": "react-jsx", 9 | "esModuleInterop": true, 10 | "sourceMap": true, 11 | "baseUrl": "./", 12 | "strict": true, 13 | "paths": { 14 | "@/*": ["src/*"], 15 | "@@/*": ["src/.umi/*"] 16 | }, 17 | "allowSyntheticDefaultImports": true 18 | }, 19 | "include": [ 20 | "mock/**/*", 21 | "src/**/*", 22 | "config/**/*", 23 | ".umirc.ts", 24 | "typings.d.ts" 25 | ], 26 | "exclude": [ 27 | "node_modules", 28 | "lib", 29 | "es", 30 | "dist", 31 | "typings", 32 | "**/__test__", 33 | "test", 34 | "docs", 35 | "tests" 36 | ] 37 | } 38 | -------------------------------------------------------------------------------- /frontend/typings.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.css'; 2 | declare module '*.less'; 3 | declare module '*.png'; 4 | declare module '*.svg' { 5 | export function ReactComponent( 6 | props: React.SVGProps, 7 | ): React.ReactElement; 8 | const url: string; 9 | export default url; 10 | } 11 | --------------------------------------------------------------------------------