Repository: eosphoros-ai/DB-GPT-Web Branch: main Commit: 4b26a9dbdf9e Files: 163 Total size: 400.5 KB Directory structure: gitextract_231z167n/ ├── .eslintrc.json ├── .github/ │ └── ISSUE_TEMPLATE/ │ ├── ✨-feature-request.md │ ├── 🐛-bug-report.md │ ├── 📜-documentation.md │ ├── 🔧-improvement.md │ └── 🙋-question.md ├── .gitignore ├── .npmrc ├── .vscode/ │ ├── extensions.json │ └── settings.json ├── README.md ├── app/ │ ├── chat-context.tsx │ └── i18n.ts ├── client/ │ └── api/ │ ├── index.ts │ ├── request.ts │ └── tools/ │ ├── index.ts │ └── interceptors.ts ├── components/ │ ├── agent/ │ │ ├── market-plugins.tsx │ │ └── my-plugins.tsx │ ├── app/ │ │ ├── agent-panel.tsx │ │ ├── app-card.tsx │ │ ├── app-modal.tsx │ │ ├── dag-layout.tsx │ │ └── resource-card.tsx │ ├── chart/ │ │ ├── autoChart/ │ │ │ ├── advisor/ │ │ │ │ ├── pipeline.ts │ │ │ │ ├── rule.ts │ │ │ │ └── utils.ts │ │ │ ├── charts/ │ │ │ │ ├── index.ts │ │ │ │ ├── multi-line-chart.ts │ │ │ │ ├── multi-measure-column-chart.ts │ │ │ │ ├── multi-measure-line-chart.ts │ │ │ │ └── util.ts │ │ │ ├── helpers/ │ │ │ │ └── index.ts │ │ │ ├── index.tsx │ │ │ └── types.ts │ │ ├── bar-chart.tsx │ │ ├── index.tsx │ │ ├── line-chart.tsx │ │ └── table-chart.tsx │ ├── chat/ │ │ ├── agent-content.tsx │ │ ├── chat-container.tsx │ │ ├── chat-content/ │ │ │ ├── agent-messages.tsx │ │ │ ├── agent-plans.tsx │ │ │ ├── chart-view.tsx │ │ │ ├── code-preview.tsx │ │ │ ├── config.tsx │ │ │ ├── index.tsx │ │ │ ├── vis-chart.tsx │ │ │ ├── vis-code.tsx │ │ │ ├── vis-convert-error.tsx │ │ │ ├── vis-dashboard.tsx │ │ │ └── vis-plugin.tsx │ │ ├── chat-feedback.tsx │ │ ├── completion.tsx │ │ ├── db-editor.tsx │ │ ├── doc-list.tsx │ │ ├── doc-upload.tsx │ │ ├── header/ │ │ │ ├── agent-selector.tsx │ │ │ ├── chat-excel.tsx │ │ │ ├── db-selector.tsx │ │ │ ├── excel-upload.tsx │ │ │ ├── index.tsx │ │ │ └── model-selector.tsx │ │ ├── mode-tab/ │ │ │ ├── index.css │ │ │ └── index.tsx │ │ └── monaco-editor.tsx │ ├── common/ │ │ ├── FileStatusIcon.tsx │ │ ├── MyEmpty.tsx │ │ ├── chat-dialog.tsx │ │ ├── completion-input.tsx │ │ ├── db-icon.tsx │ │ ├── gpt-card.tsx │ │ ├── icon-wrapper.tsx │ │ ├── loading.tsx │ │ └── prompt-bot.tsx │ ├── database/ │ │ └── form-dialog.tsx │ ├── flow/ │ │ ├── add-nodes.tsx │ │ ├── button-edge.tsx │ │ ├── canvas-node.tsx │ │ ├── flow-card.tsx │ │ ├── node-handler.tsx │ │ ├── node-param-handler.tsx │ │ ├── preview-flow.tsx │ │ ├── required-icon.tsx │ │ └── static-nodes.tsx │ ├── icons/ │ │ ├── add-icon.tsx │ │ ├── collect.tsx │ │ ├── collected.tsx │ │ ├── colorful-chat.tsx │ │ ├── colorful-dashboard.tsx │ │ ├── colorful-data.tsx │ │ ├── colorful-db.tsx │ │ ├── colorful-doc.tsx │ │ ├── colorful-excel.tsx │ │ ├── colorful-plugin.tsx │ │ ├── dark-svg.tsx │ │ ├── db-svg.tsx │ │ ├── done-icon.tsx │ │ ├── file-done.tsx │ │ ├── file-error.tsx │ │ ├── file-sync.tsx │ │ ├── index.tsx │ │ ├── knowledge.tsx │ │ ├── model-svg.tsx │ │ ├── pending-icon.tsx │ │ ├── stars-svg.tsx │ │ ├── sunny-svg.tsx │ │ └── sync-icon.tsx │ ├── knowledge/ │ │ ├── arguments-modal.tsx │ │ ├── doc-icon.tsx │ │ ├── doc-panel.tsx │ │ ├── doc-type-form.tsx │ │ ├── doc-upload-form.tsx │ │ ├── segmentation.tsx │ │ ├── space-card.tsx │ │ ├── space-form.tsx │ │ └── strategy-form.tsx │ ├── layout/ │ │ ├── side-bar.tsx │ │ └── top-progress-bar.tsx │ ├── model/ │ │ ├── model-card.tsx │ │ ├── model-form.tsx │ │ └── model-params.tsx │ └── prompt/ │ └── prompt-form.tsx ├── defaultTheme.ts ├── genAntdCss.ts ├── global.d.ts ├── hooks/ │ ├── use-chat.ts │ └── use-summary.ts ├── next.config.js ├── nprogress.css ├── package.json ├── pages/ │ ├── _app.tsx │ ├── _document.tsx │ ├── agent/ │ │ └── index.tsx │ ├── app/ │ │ └── index.tsx │ ├── chat/ │ │ └── index.tsx │ ├── database/ │ │ └── index.tsx │ ├── flow/ │ │ ├── canvas/ │ │ │ └── index.tsx │ │ └── index.tsx │ ├── index.tsx │ ├── knowledge/ │ │ ├── chunk/ │ │ │ └── index.tsx │ │ └── index.tsx │ ├── models/ │ │ └── index.tsx │ └── prompt/ │ └── index.tsx ├── postcss.config.js ├── styles/ │ └── globals.css ├── tailwind.config.js ├── tsconfig.json ├── types/ │ ├── agent.ts │ ├── app.ts │ ├── chat.ts │ ├── db.ts │ ├── editor.ts │ ├── flow.ts │ ├── knowledge.ts │ ├── model.ts │ └── prompt.ts └── utils/ ├── constants.ts ├── ctx-axios.ts ├── flow.ts ├── index.ts ├── request.ts └── storage.ts ================================================ FILE CONTENTS ================================================ ================================================ FILE: .eslintrc.json ================================================ { "extends": "next/core-web-vitals" } ================================================ FILE: .github/ISSUE_TEMPLATE/✨-feature-request.md ================================================ --- name: "✨ Feature Request" about: Suggest a new feature idea. title: "[feature]" labels: '' assignees: '' --- **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] **Describe the solution you'd like** A clear and concise description of what you want to happen. **Describe alternatives you've considered** A clear and concise description of any alternative solutions or features you've considered. **Additional context** Add any other context or screenshots about the feature request here. ================================================ FILE: .github/ISSUE_TEMPLATE/🐛-bug-report.md ================================================ --- name: "\U0001F41B Bug report" about: Report an unexpected problem or unintended behavior. title: "[BUG] " labels: '' assignees: '' --- **Describe the bug** A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See error **Expected behavior** A clear and concise description of what you expected to happen. **Screenshots** If applicable, add screenshots to help explain your problem. **Desktop (please complete the following information):** - OS: [e.g. iOS] - Browser [e.g. chrome, safari] - Version [e.g. 22] **Smartphone (please complete the following information):** - Device: [e.g. iPhone6] - OS: [e.g. iOS8.1] - Browser [e.g. stock browser, safari] - Version [e.g. 22] **Additional context** Add any other context about the problem here. ================================================ FILE: .github/ISSUE_TEMPLATE/📜-documentation.md ================================================ --- name: "\U0001F4DC Documentation" about: Correct spelling errors, improvements or additions to documentation files (README, CONTRIBUTING...). title: "[Doc]" labels: '' assignees: '' --- **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] **Describe the solution you'd like** A clear and concise description of what you want to happen. **Describe alternatives you've considered** A clear and concise description of any alternative solutions or features you've considered. **Additional context** Add any other context or screenshots about the feature request here. ================================================ FILE: .github/ISSUE_TEMPLATE/🔧-improvement.md ================================================ --- name: "\U0001F527 Improvement" about: Suggest an idea which is not a feature. title: '' labels: '' assignees: '' --- ### Expected Behavior ### Actual Behavior ### Proposal ================================================ FILE: .github/ISSUE_TEMPLATE/🙋-question.md ================================================ --- name: "\U0001F64B Question" about: Ask a question about DB-GPT-Web. title: '' labels: '' assignees: '' --- ### Question ================================================ FILE: .gitignore ================================================ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. # dependencies /node_modules /.pnp .pnp.js # testing /coverage .idea # next.js /.next/ /out/ # production /build # misc .DS_Store *.pem # debug npm-debug.log* yarn-debug.log* yarn-error.log* mock # local env files .env.prod .env # vercel .vercel # typescript *.tsbuildinfo next-env.d.ts .next ================================================ FILE: .npmrc ================================================ registry=http://registry.npmjs.org ================================================ FILE: .vscode/extensions.json ================================================ { "recommendations": [ "bradlc.vscode-tailwindcss", "esbenp.prettier-vscode", "dbaeumer.vscode-eslint", "formulahendry.auto-close-tag", "formulahendry.auto-rename-tag", "vincaslt.highlight-matching-tag", "ionutvmi.path-autocomplete" ] } ================================================ FILE: .vscode/settings.json ================================================ { "editor.formatOnSave": true, "editor.codeActionsOnSave": { "source.fixAll": "explicit" }, "prettier.arrowParens": "always", "prettier.tabWidth": 2, "prettier.printWidth": 150, "prettier.singleQuote": true, "prettier.semi": true, "prettier.trailingComma": "all", "[typescript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, "[typescriptreact]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, "[css]": { "editor.defaultFormatter": "esbenp.prettier-vscode" }, "tailwindCSS.includeLanguages": { "plaintext": "typescriptreact" }, "editor.quickSuggestions": { "strings": true }, "cSpell.words": ["DBGPT"] } ================================================ FILE: README.md ================================================

DB-GPT
DB-GPT-Web

_

DB-GPT Chat UI, LLM to Vision.

_

Release Notes Open Issues Discord

--- ## 👋 Introduction ***DB-GPT-Web*** is an **Open source chat UI** for [**DB-GPT**](https://github.com/eosphoros-ai/DB-GPT). Also, it is a **LLM to Vision** solution. [DB-GPT-Web](https://dbgpt.site) is an Open source Tailwind and Next.js based chat UI for AI and GPT projects. It beautify a lot of markdown labels, such as `table`, `thead`, `th`, `td`, `code`, `h1`, `h2`, `ul`, `li`, `a`, `img`. Also it define some custom labels to adapted to AI-specific scenarios. Such as `plugin running`, `knowledge name`, `Chart view`, and so on. ## 💪🏻 Getting Started ### Prerequisites - [Node.js](https://nodejs.org/) >= 16 - [npm](https://npmjs.com/) >= 8 - Supported OSes: Linux, macOS and Windows ### Installation ```sh # Install dependencies npm install ``` ### Usage ```sh cp .env.example .env ``` edit the `API_BASE_URL` to the real address ```sh # development model npm run dev ``` ## 🚀 Use In DB-GPT ```sh npm run compile # copy compile file to DB-GPT static file dictory cp -rf out/* ../dbgpt/app/static ``` ## 📚 Documentation For full documentation, visit [document](https://docs.dbgpt.site/). ## Usage [react-markdown](https://github.com/remarkjs/react-markdown#readme) for markdown support. [ant-design](https://github.com/ant-design/ant-design) for ui components. [next.js](https://github.com/vercel/next.js) for server side rendering. [@antv/g2](https://github.com/antvis/g2#readme) for charts. ## License DB-GPT-Web is licensed under the [MIT License](LICENSE). --- Enjoy using DB-GPT-Web to build stunning UIs for your AI and GPT projects. 🌟 If you find it helpful, don't forget to give it a star on GitHub! Stars are like little virtual hugs that keep us going! We appreciate every single one we receive. For any queries or issues, feel free to open an [issue](https://github.com/eosphoros-ai/DB-GPT-Web/issues) on the repository. Happy coding! 😊 ================================================ FILE: app/chat-context.tsx ================================================ import { createContext, useEffect, useMemo, useState } from 'react'; import { apiInterceptors, getDialogueList, getUsableModels } from '@/client/api'; import { useRequest } from 'ahooks'; import { ChatHistoryResponse, DialogueListResponse, IChatDialogueSchema } from '@/types/chat'; import { useSearchParams } from 'next/navigation'; import { STORAGE_THEME_KEY } from '@/utils'; type ThemeMode = 'dark' | 'light'; interface IChatContext { mode: ThemeMode; isContract?: boolean; isMenuExpand?: boolean; scene: IChatDialogueSchema['chat_mode'] | (string & {}); chatId: string; model: string; dbParam?: string; modelList: Array; agent: string; dialogueList?: DialogueListResponse; setAgent?: (val: string) => void; setMode: (mode: ThemeMode) => void; setModel: (val: string) => void; setIsContract: (val: boolean) => void; setIsMenuExpand: (val: boolean) => void; setDbParam: (val: string) => void; queryDialogueList: () => void; refreshDialogList: () => void; currentDialogue?: DialogueListResponse[0]; history: ChatHistoryResponse; setHistory: (val: ChatHistoryResponse) => void; docId?: number; setDocId: (docId: number) => void; } function getDefaultTheme(): ThemeMode { const theme = localStorage.getItem(STORAGE_THEME_KEY) as ThemeMode; if (theme) return theme; return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; } const ChatContext = createContext({ mode: 'light', scene: '', chatId: '', modelList: [], model: '', dbParam: undefined, dialogueList: [], agent: '', setAgent: () => {}, setModel: () => {}, setIsContract: () => {}, setIsMenuExpand: () => {}, setDbParam: () => void 0, queryDialogueList: () => {}, refreshDialogList: () => {}, setMode: () => void 0, history: [], setHistory: () => {}, docId: undefined, setDocId: () => {}, }); const ChatContextProvider = ({ children }: { children: React.ReactElement }) => { const searchParams = useSearchParams(); const chatId = searchParams?.get('id') ?? ''; const scene = searchParams?.get('scene') ?? ''; const db_param = searchParams?.get('db_param') ?? ''; const [isContract, setIsContract] = useState(false); const [model, setModel] = useState(''); const [isMenuExpand, setIsMenuExpand] = useState(scene !== 'chat_dashboard'); const [dbParam, setDbParam] = useState(db_param); const [agent, setAgent] = useState(''); const [history, setHistory] = useState([]); const [docId, setDocId] = useState(); const [mode, setMode] = useState('light'); const { run: queryDialogueList, data: dialogueList = [], refresh: refreshDialogList, } = useRequest( async () => { const [, res] = await apiInterceptors(getDialogueList()); return res ?? []; }, { manual: true, }, ); useEffect(() => { if (dialogueList.length && scene === 'chat_agent') { const agent = dialogueList.find((item) => item.conv_uid === chatId)?.select_param; agent && setAgent(agent); } }, [dialogueList, scene, chatId]); const { data: modelList = [] } = useRequest(async () => { const [, res] = await apiInterceptors(getUsableModels()); return res ?? []; }); useEffect(() => { setMode(getDefaultTheme()); }, []); useEffect(() => { setModel(modelList[0]); }, [modelList, modelList?.length]); const currentDialogue = useMemo(() => dialogueList.find((item: any) => item.conv_uid === chatId), [chatId, dialogueList]); const contextValue = { isContract, isMenuExpand, scene, chatId, modelList, model, dbParam: dbParam || db_param, dialogueList, agent, setAgent, mode, setMode, setModel, setIsContract, setIsMenuExpand, setDbParam, queryDialogueList, refreshDialogList, currentDialogue, history, setHistory, docId, setDocId, }; return {children}; }; export { ChatContext, ChatContextProvider }; ================================================ FILE: app/i18n.ts ================================================ import i18n from 'i18next'; import { initReactI18next } from 'react-i18next'; const en = { Knowledge_Space: 'Knowledge', space: 'space', Vector: 'Vector', Owner: 'Owner', Count: 'Count', File_type_Invalid: 'The file type is invalid', Knowledge_Space_Config: 'Space Config', Choose_a_Datasource_type: 'Datasource type', Segmentation: 'Segmentation', No_parameter: `No segementation parameter required.`, Knowledge_Space_Name: 'Knowledge Space Name', Please_input_the_name: 'Please input the name', Please_input_the_owner: 'Please input the owner', Please_select_file: 'Please select one file', Description: 'Description', Please_input_the_description: 'Please input the description', Next: 'Next', the_name_can_only_contain: 'the name can only contain numbers, letters, Chinese characters, "-" and "_"', Text: 'Text', 'Fill your raw text': 'Fill your raw text', URL: 'URL', Fetch_the_content_of_a_URL: 'Fetch the content of a URL', Document: 'Document', Upload_a_document: 'Upload a document, document type can be PDF, CSV, Text, PowerPoint, Word, Markdown', Name: 'Name', Text_Source: 'Text Source(Optional)', Please_input_the_text_source: 'Please input the text source', Sync: 'Sync', Back: 'Back', Finish: 'Finish', Web_Page_URL: 'Web Page URL', Please_input_the_Web_Page_URL: 'Please input the Web Page URL', Select_or_Drop_file: 'Select or Drop file', Documents: 'Documents', Chat: 'Chat', Add_Datasource: 'Add Datasource', Arguments: 'Arguments', Type: 'Type', Size: 'Size', Last_Sync: 'Last Sync', Status: 'Status', Result: 'Result', Details: 'Details', Delete: 'Delete', Operation: 'Operation', Submit: 'Submit', Chunks: 'Chunks', Content: 'Content', Meta_Data: 'Meta Data', Please_select_a_file: 'Please select a file', Please_input_the_text: 'Please input the text', Embedding: 'Embedding', topk: 'topk', the_top_k_vectors: 'the top k vectors based on similarity score', recall_score: 'recall_score', Set_a_threshold_score: 'Set a threshold score for the retrieval of similar vectors', recall_type: 'recall_type', model: 'model', A_model_used: 'A model used to create vector representations of text or other data', Automatic: 'Automatic', Process: 'Process', Automatic_desc: 'Automatically set segmentation and preprocessing rules.', chunk_size: 'chunk_size', The_size_of_the_data_chunks: 'The size of the data chunks used in processing', chunk_overlap: 'chunk_overlap', The_amount_of_overlap: 'The amount of overlap between adjacent data chunks', Prompt: 'Prompt', scene: 'scene', A_contextual_parameter: 'A contextual parameter used to define the setting or environment in which the prompt is being used', template: 'template', structure_or_format: 'A pre-defined structure or format for the prompt, which can help ensure that the AI system generates responses that are consistent with the desired style or tone.', max_token: 'max_token', max_iteration: 'max_iteration', concurrency_limit: 'concurrency_limit', The_maximum_number_of_tokens: 'The maximum number of tokens or words allowed in a prompt', Theme: 'Theme', Port: 'Port', Username: 'Username', Password: 'Password', Remark: 'Remark', Edit: 'Edit', Database: 'Database', Data_Source: 'Data Center', Close_Sidebar: 'Fold', Show_Sidebar: 'UnFold', language: 'Language', choose_model: 'Please choose a model', data_center_desc: 'DB-GPT also offers a user-friendly data center management interface for efficient data maintenance.', create_database: 'Create Database', create_knowledge: 'Create Knowledge', path: 'Path', model_manage: 'Models', stop_model_success: 'Stop model success', create_model: 'Create Model', model_select_tips: 'Please select a model', language_select_tips: 'Please select a language', submit: 'Submit', close: 'Close', start_model_success: 'Start model success', download_model_tip: 'Please download model first.', Plugins: 'Plugins', try_again: 'Try again', no_data: 'No data', Open_Sidebar: 'Unfold', cancel: 'Cancel', Edit_Success: 'Edit Success', Add: 'Add', Add_Success: 'Add Success', Error_Message: 'Something Error', Please_Input: 'Please Input', Prompt_Info_Scene: 'Scene', Prompt_Info_Sub_Scene: 'Sub Scene', Prompt_Info_Name: 'Name', Prompt_Info_Content: 'Content', Public: 'Public', Private: 'Private', Lowest: 'Lowest', Missed: 'Missed', Lost: 'Lost', Incorrect: 'Incorrect', Verbose: 'Verbose', Best: 'Best', Rating: 'Rating', Q_A_Category: 'Q&A Category', Q_A_Rating: 'Q&A Rating', feed_back_desc: '0: No results\n' + '1: Results exist, but they are irrelevant, the question is not understood\n' + '2: Results exist, the question is understood, but it indicates that the question cannot be answered\n' + '3: Results exist, the question is understood, and an answer is given, but the answer is incorrect\n' + '4: Results exist, the question is understood, the answer is correct, but it is verbose and lacks a summary\n' + '5: Results exist, the question is understood, the answer is correct, the reasoning is correct, and a summary is provided, concise and to the point\n', input_count: 'Total input', input_unit: 'characters', Copy: 'Copy', Copy_success: 'Content copied successfully', Copy_nothing: 'Content copied is empty', Copry_error: 'Copy failed', Click_Select: 'Click&Select', Quick_Start: 'Quick Start', Select_Plugins: 'Select Plugins', Search: 'Search', Update_From_Github: 'Upload From Github', Reset: 'Reset', Upload: 'Upload', Market_Plugins: 'Market Plugin', My_Plugins: 'My Plugins', Del_Knowledge_Tips: 'Do you want delete the Space', Del_Document_Tips: 'Do you want delete the Document', Tips: 'Tips', Limit_Upload_File_Count_Tips: 'Only one file can be uploaded at a time', To_Plugin_Market: 'Go to the Plugin Market', Summary: 'Summary', stacked_column_chart: 'Stacked Column', column_chart: 'Column', percent_stacked_column_chart: 'Percent Stacked Column', grouped_column_chart: 'Grouped Column', time_column: 'Time Column', pie_chart: 'Pie', line_chart: 'Line', area_chart: 'Area', stacked_area_chart: 'Stacked Area', scatter_plot: 'Scatter', bubble_chart: 'Bubble', stacked_bar_chart: 'Stacked Bar', bar_chart: 'Bar', percent_stacked_bar_chart: 'Percent Stacked Bar', grouped_bar_chart: 'Grouped Bar', water_fall_chart: 'Waterfall', table: 'Table', multi_line_chart: 'Multi Line', multi_measure_column_chart: 'Multi Measure Column', multi_measure_line_chart: 'Multi Measure Line', Advices: 'Advices', Retry: 'Retry', Load_more: 'load more', new_chat: 'New Chat', choice_agent_tip: 'Please choose an agent', no_context_tip: 'Please enter your question', Terminal: 'Terminal', awel_flow: 'AWEL Flow', save: 'Save', add_node: 'Add Node', no_node: 'No Node', connect_warning: 'Nodes cannot be connected', flow_modal_title: 'Save Flow', flow_name: 'Flow Name', flow_description: 'Flow Description', flow_name_required: 'Please enter the flow name', flow_description_required: 'Please enter the flow description', save_flow_success: 'Save flow success', delete_flow_confirm: 'Are you sure you want to delete this flow?', related_nodes: 'Related Nodes', add_resource: 'Add Resource', team_modal: 'Work Modal', App: 'App', resource_name: 'Resource Name', resource_type: 'Resource Type', resource_value: 'Value', resource_dynamic: 'Dynamic', Please_input_the_work_modal: 'Please select the work modal', available_resources: ' Available Resources', edit_new_applications: 'Edit new applications', collect: 'Collect', collected: 'Collected', create: 'Create', Agents: 'Agents', edit_application: 'edit application', add_application: 'add application', app_name: 'App Name', LLM_strategy: 'LLM Strategy', LLM_strategy_value: 'LLM Strategy Value', resource: 'Resource', operators: 'Operators', Chinese: 'Chinese', English: 'English', } as const; export type I18nKeys = keyof typeof en; export interface Resources { translation: Record; } const zh: Resources['translation'] = { Knowledge_Space: '知识库', space: '知识库', Vector: '向量', Owner: '创建人', Count: '文档数', File_type_Invalid: '文件类型错误', Knowledge_Space_Config: '知识库配置', Choose_a_Datasource_type: '知识库类型', Segmentation: '分片', No_parameter: '不需要配置分片参数', Knowledge_Space_Name: '知识库名称', Please_input_the_name: '请输入名称', Please_input_the_owner: '请输入创建人', Please_select_file: '请至少选择一个文件', Description: '描述', Please_input_the_description: '请输入描述', Next: '下一步', the_name_can_only_contain: '名称只能包含数字、字母、中文字符、-或_', Text: '文本', 'Fill your raw text': '填写您的原始文本', URL: '网址', Fetch_the_content_of_a_URL: '获取 URL 的内容', Document: '文档', Upload_a_document: '上传文档,文档类型可以是PDF、CSV、Text、PowerPoint、Word、Markdown', Name: '名称', Text_Source: '文本来源(可选)', Please_input_the_text_source: '请输入文本来源', Sync: '同步', Back: '上一步', Finish: '完成', Web_Page_URL: '网页网址', Please_input_the_Web_Page_URL: '请输入网页网址', Select_or_Drop_file: '选择或拖拽文件', Documents: '文档', Chat: '对话', Add_Datasource: '添加数据源', Arguments: '参数', Type: '类型', Size: '切片', Last_Sync: '上次同步时间', Status: '状态', Result: '结果', Details: '明细', Delete: '删除', Operation: '操作', Submit: '提交', close: '关闭', Chunks: '切片', Content: '内容', Meta_Data: '元数据', Please_select_a_file: '请上传一个文件', Please_input_the_text: '请输入文本', Embedding: '嵌入', topk: 'TopK', the_top_k_vectors: '基于相似度得分的前 k 个向量', recall_score: '召回分数', Set_a_threshold_score: '设置相似向量检索的阈值分数', recall_type: '召回类型', model: '模型', A_model_used: '用于创建文本或其他数据的矢量表示的模型', Automatic: '自动切片', Process: '切片处理', Automatic_desc: '自动设置分割和预处理规则。', chunk_size: '块大小', The_size_of_the_data_chunks: '处理中使用的数据块的大小', chunk_overlap: '块重叠', The_amount_of_overlap: '相邻数据块之间的重叠量', scene: '场景', A_contextual_parameter: '用于定义使用提示的设置或环境的上下文参数', template: '模板', structure_or_format: '预定义的提示结构或格式,有助于确保人工智能系统生成与所需风格或语气一致的响应。', max_token: '最大令牌', max_iteration: '最大迭代', concurrency_limit: '并发限制', The_maximum_number_of_tokens: '提示中允许的最大标记或单词数', Theme: '主题', Port: '端口', Username: '用户名', Password: '密码', Remark: '备注', Edit: '编辑', Database: '数据库', Data_Source: '数据中心', Close_Sidebar: '收起', Show_Sidebar: '展开', language: '语言', choose_model: '请选择一个模型', data_center_desc: 'DB-GPT支持数据库交互和基于文档的对话,它还提供了一个用户友好的数据中心管理界面。', create_database: '创建数据库', create_knowledge: '创建知识库', path: '路径', model_manage: '模型管理', stop_model_success: '模型停止成功', create_model: '创建模型', model_select_tips: '请选择一个模型', submit: '提交', start_model_success: '启动模型成功', download_model_tip: '请先下载模型!', Plugins: '插件列表', try_again: '刷新重试', no_data: '暂无数据', Prompt: '提示语', Open_Sidebar: '展开', cancel: '取消', Edit_Success: '编辑成功', Add: '新增', Add_Success: '新增成功', Error_Message: '出错了', Please_Input: '请输入', Prompt_Info_Scene: '场景', Prompt_Info_Sub_Scene: '次级场景', Prompt_Info_Name: '名称', Prompt_Info_Content: '内容', Public: '公共', Private: '私有', Lowest: '渣渣', Missed: '没理解', Lost: '答不了', Incorrect: '答错了', Verbose: '较啰嗦', Best: '真棒', Rating: '评分', Q_A_Category: '问答类别', Q_A_Rating: '问答评分', feed_back_desc: '0: 无结果\n' + '1: 有结果,但是在文不对题,没有理解问题\n' + '2: 有结果,理解了问题,但是提示回答不了这个问题\n' + '3: 有结果,理解了问题,并做出回答,但是回答的结果错误\n' + '4: 有结果,理解了问题,回答结果正确,但是比较啰嗦,缺乏总结\n' + '5: 有结果,理解了问题,回答结果正确,推理正确,并给出了总结,言简意赅\n', input_count: '共计输入', input_unit: '字', Copy: '复制', Copy_success: '内容复制成功', Copy_nothing: '内容复制为空', Copry_error: '复制失败', Click_Select: '点击选择', Quick_Start: '快速开始', Select_Plugins: '选择插件', Search: '搜索', Reset: '重置', Update_From_Github: '更新Github插件', Upload: '上传', Market_Plugins: '插件市场', My_Plugins: '我的插件', Del_Knowledge_Tips: '你确定删除该知识库吗', Del_Document_Tips: '你确定删除该文档吗', Tips: '提示', Limit_Upload_File_Count_Tips: '一次只能上传一个文件', To_Plugin_Market: '前往插件市场', Summary: '总结', stacked_column_chart: '堆叠柱状图', column_chart: '柱状图', percent_stacked_column_chart: '百分比堆叠柱状图', grouped_column_chart: '簇形柱状图', time_column: '簇形柱状图', pie_chart: '饼图', line_chart: '折线图', area_chart: '面积图', stacked_area_chart: '堆叠面积图', scatter_plot: '散点图', bubble_chart: '气泡图', stacked_bar_chart: '堆叠条形图', bar_chart: '条形图', percent_stacked_bar_chart: '百分比堆叠条形图', grouped_bar_chart: '簇形条形图', water_fall_chart: '瀑布图', table: '表格', multi_line_chart: '多折线图', multi_measure_column_chart: '多指标柱形图', multi_measure_line_chart: '多指标折线图', Advices: '自动推荐', Retry: '重试', Load_more: '加载更多', new_chat: '创建会话', choice_agent_tip: '请选择代理', no_context_tip: '请输入你的问题', Terminal: '终端', awel_flow: 'AWEL 工作流', save: '保存', add_node: '添加节点', no_node: '没有可编排节点', connect_warning: '节点无法连接', flow_modal_title: '保存工作流', flow_name: '工作流名称', flow_description: '工作流描述', flow_name_required: '请输入工作流名称', flow_description_required: '请输入工作流描述', save_flow_success: '保存工作流成功', delete_flow_confirm: '确定删除该工作流吗?', related_nodes: '关联节点', language_select_tips: '请选择语言', add_resource: '添加资源', team_modal: '工作模式', App: '应用程序', resource: '资源', resource_name: '资源名', resource_type: '资源类型', resource_value: '参数', resource_dynamic: '动态', Please_input_the_work_modal: '请选择工作模式', available_resources: '可用资源', edit_new_applications: '编辑新的应用', collect: '收藏', collected: '已收藏', create: '创建', Agents: '智能体', edit_application: '编辑应用', add_application: '添加应用', app_name: '应用名称', LLM_strategy: '模型策略', LLM_strategy_value: '模型策略参数', operators: '算子', Chinese: '中文', English: '英文', } as const; i18n.use(initReactI18next).init({ resources: { en: { translation: en, }, zh: { translation: zh, }, }, lng: 'en', interpolation: { escapeValue: false, }, }); export default i18n; declare module 'i18next' { interface CustomTypeOptions { resources: Resources; } } ================================================ FILE: client/api/index.ts ================================================ import axios, { AxiosRequestConfig, AxiosError, AxiosResponse } from 'axios'; export type ResponseType = { data: T; err_code: string | null; err_msg: string | null; success: boolean; }; export type ApiResponse = AxiosResponse, D>; export type SuccessTuple = [null, T, ResponseType, ApiResponse]; export type FailedTuple = [Error | AxiosError, null, null, null]; const ins = axios.create({ baseURL: process.env.API_BASE_URL ?? '', }); const LONG_TIME_API: string[] = [ '/db/add', '/db/test/connect', '/db/summary', '/params/file/load', '/chat/prepare', '/model/start', '/model/stop', '/editor/sql/run', '/sql/editor/submit', '/editor/chart/run', '/chart/editor/submit', '/document/upload', '/document/sync', '/agent/install', '/agent/uninstall', '/personal/agent/upload', ]; ins.interceptors.request.use((request) => { const isLongTimeApi = LONG_TIME_API.some((item) => request.url && request.url.indexOf(item) >= 0); if (!request.timeout) { request.timeout = isLongTimeApi ? 60000 : 10000; } return request; }); export const GET = (url: string, params?: Params, config?: AxiosRequestConfig) => { return ins.get>(url, { params, ...config }); }; export const POST = (url: string, data?: Data, config?: AxiosRequestConfig) => { return ins.post>(url, data, config); }; export const PATCH = (url: string, data?: Data, config?: AxiosRequestConfig) => { return ins.patch>(url, data, config); }; export const PUT = (url: string, data?: Data, config?: AxiosRequestConfig) => { return ins.put>(url, data, config); }; export const DELETE = (url: string, params?: Params, config?: AxiosRequestConfig) => { return ins.delete>(url, { params, ...config }); }; export * from './tools'; export * from './request'; ================================================ FILE: client/api/request.ts ================================================ import { AxiosRequestConfig } from 'axios'; import { DELETE, GET, POST, PUT } from '.'; import { DbListResponse, DbSupportTypeResponse, PostDbParams, ChatFeedBackSchema } from '@/types/db'; import { DialogueListResponse, IChatDialogueSchema, NewDialogueParam, SceneResponse, ChatHistoryResponse, FeedBack, IDB } from '@/types/chat'; import { IModelData, StartModelParams, BaseModelParams, SupportModel } from '@/types/model'; import { GetEditorSQLRoundRequest, GetEditorySqlParams, PostEditorChartRunParams, PostEditorChartRunResponse, PostEditorSQLRunParams, PostSQLEditorSubmitParams, } from '@/types/editor'; import { PostAgentHubUpdateParams, PostAgentQueryParams, PostAgentPluginResponse, PostAgentMyPluginResponse, GetDBGPTsListResponse, } from '@/types/agent'; import { AddKnowledgeParams, ArgumentsParams, ChunkListParams, DocumentParams, IArguments, IChunkList, IChunkStrategyResponse, IDocumentResponse, ISpace, ISyncBatchParameter, ISyncBatchResponse, } from '@/types/knowledge'; import { UpdatePromptParams, IPrompt, PromptParams } from '@/types/prompt'; import { IFlow, IFlowNode, IFlowResponse, IFlowUpdateParam } from '@/types/flow'; import { IAgent, IApp, IAppData, ITeamModal } from '@/types/app'; /** App */ export const postScenes = () => { return POST>('/api/v1/chat/dialogue/scenes'); }; export const newDialogue = (data: NewDialogueParam) => { return POST('/api/v1/chat/dialogue/new', data); }; /** Database Page */ export const getDbList = () => { return GET('/api/v1/chat/db/list'); }; export const getDbSupportType = () => { return GET('/api/v1/chat/db/support/type'); }; export const postDbDelete = (dbName: string) => { return POST(`/api/v1/chat/db/delete?db_name=${dbName}`); }; export const postDbEdit = (data: PostDbParams) => { return POST('/api/v1/chat/db/edit', data); }; export const postDbAdd = (data: PostDbParams) => { return POST('/api/v1/chat/db/add', data); }; export const postDbTestConnect = (data: PostDbParams) => { return POST('/api/v1/chat/db/test/connect', data); }; /** Chat Page */ export const getDialogueList = () => { return GET('/api/v1/chat/dialogue/list'); }; export const getUsableModels = () => { return GET>('/api/v1/model/types'); }; export const postChatModeParamsList = (chatMode: string) => { return POST(`/api/v1/chat/mode/params/list?chat_mode=${chatMode}`); }; export const postChatModeParamsInfoList = (chatMode: string) => { return POST>(`/api/v1/chat/mode/params/info?chat_mode=${chatMode}`); }; export const getChatHistory = (convId: string) => { return GET(`/api/v1/chat/dialogue/messages/history?con_uid=${convId}`); }; export const postChatModeParamsFileLoad = ({ convUid, chatMode, data, config, model, }: { convUid: string; chatMode: string; data: FormData; model: string; config?: Omit; }) => { return POST( `/api/v1/chat/mode/params/file/load?conv_uid=${convUid}&chat_mode=${chatMode}&model_name=${model}`, data, { headers: { 'Content-Type': 'multipart/form-data', }, ...config, }, ); }; /** Menu */ export const delDialogue = (conv_uid: string) => { return POST(`/api/v1/chat/dialogue/delete?con_uid=${conv_uid}`); }; /** Editor */ export const getEditorSqlRounds = (id: string) => { return GET(`/api/v1/editor/sql/rounds?con_uid=${id}`); }; export const postEditorSqlRun = (data: PostEditorSQLRunParams) => { return POST(`/api/v1/editor/sql/run`, data); }; export const postEditorChartRun = (data: PostEditorChartRunParams) => { return POST(`/api/v1/editor/chart/run`, data); }; export const postSqlEditorSubmit = (data: PostSQLEditorSubmitParams) => { return POST(`/api/v1/sql/editor/submit`, data); }; export const getEditorSql = (id: string, round: string | number) => { return POST>('/api/v1/editor/sql', { con_uid: id, round }); }; /** knowledge */ export const getArguments = (knowledgeName: string) => { return POST(`/knowledge/${knowledgeName}/arguments`, {}); }; export const saveArguments = (knowledgeName: string, data: ArgumentsParams) => { return POST(`/knowledge/${knowledgeName}/argument/save`, data); }; export const getSpaceList = () => { return POST>('/knowledge/space/list', {}); }; export const getDocumentList = (spaceName: string, data: Record>) => { return POST>, IDocumentResponse>(`/knowledge/${spaceName}/document/list`, data); }; export const addDocument = (knowledgeName: string, data: DocumentParams) => { return POST(`/knowledge/${knowledgeName}/document/add`, data); }; export const addSpace = (data: AddKnowledgeParams) => { return POST>(`/knowledge/space/add`, data); }; export const getChunkStrategies = () => { return GET>('/knowledge/document/chunkstrategies'); }; export const syncDocument = (spaceName: string, data: Record>) => { return POST>, string | null>(`/knowledge/${spaceName}/document/sync`, data); }; export const syncBatchDocument = (spaceName: string, data: Array) => { return POST, ISyncBatchResponse>(`/knowledge/${spaceName}/document/sync_batch`, data); }; export const uploadDocument = (knowLedgeName: string, data: FormData) => { return POST(`/knowledge/${knowLedgeName}/document/upload`, data); }; export const getChunkList = (spaceName: string, data: ChunkListParams) => { return POST(`/knowledge/${spaceName}/chunk/list`, data); }; export const delDocument = (spaceName: string, data: Record) => { return POST, null>(`/knowledge/${spaceName}/document/delete`, data); }; export const delSpace = (data: Record) => { return POST, null>(`/knowledge/space/delete`, data); }; /** models */ export const getModelList = () => { return GET>('/api/v1/worker/model/list'); }; export const stopModel = (data: BaseModelParams) => { return POST('/api/v1/worker/model/stop', data); }; export const startModel = (data: StartModelParams) => { return POST('/api/v1/worker/model/start', data); }; export const getSupportModels = () => { return GET>('/api/v1/worker/model/params'); }; /** Agent */ export const postAgentQuery = (data: PostAgentQueryParams) => { return POST('/api/v1/agent/query', data); }; export const postAgentHubUpdate = (data?: PostAgentHubUpdateParams) => { return POST('/api/v1/agent/hub/update', data ?? { channel: '', url: '', branch: '', authorization: '' }); }; export const postAgentMy = (user?: string) => { return POST('/api/v1/agent/my', undefined, { params: { user } }); }; export const postAgentInstall = (pluginName: string, user?: string) => { return POST('/api/v1/agent/install', undefined, { params: { plugin_name: pluginName, user }, timeout: 60000 }); }; export const postAgentUninstall = (pluginName: string, user?: string) => { return POST('/api/v1/agent/uninstall', undefined, { params: { plugin_name: pluginName, user }, timeout: 60000 }); }; export const postAgentUpload = (user = '', data: FormData, config?: Omit) => { return POST('/api/v1/personal/agent/upload', data, { params: { user }, headers: { 'Content-Type': 'multipart/form-data', }, ...config, }); }; export const getDbgptsList = () => { return GET('/api/v1/dbgpts/list'); }; /** chat feedback **/ export const getChatFeedBackSelect = () => { return GET(`/api/v1/feedback/select`, undefined); }; export const getChatFeedBackItme = (conv_uid: string, conv_index: number) => { return GET>(`/api/v1/feedback/find?conv_uid=${conv_uid}&conv_index=${conv_index}`, undefined); }; export const postChatFeedBackForm = ({ data, config }: { data: ChatFeedBackSchema; config?: Omit }) => { return POST(`/api/v1/feedback/commit`, data, { headers: { 'Content-Type': 'application/json', }, ...config, }); }; /** prompt */ export const getPromptList = (data: PromptParams) => { return POST>('/prompt/list', data); }; export const updatePrompt = (data: UpdatePromptParams) => { return POST('/prompt/update', data); }; export const addPrompt = (data: UpdatePromptParams) => { return POST('/prompt/add', data); }; /** AWEL Flow */ export const addFlow = (data: IFlowUpdateParam) => { return POST('/api/v1/serve/awel/flows', data); }; export const getFlows = () => { return GET('/api/v1/serve/awel/flows'); }; export const getFlowById = (id: string) => { return GET(`/api/v1/serve/awel/flows/${id}`); }; export const updateFlowById = (id: string, data: IFlowUpdateParam) => { return PUT(`/api/v1/serve/awel/flows/${id}`, data); }; export const deleteFlowById = (id: string) => { return DELETE(`/api/v1/serve/awel/flows/${id}`); }; export const getFlowNodes = () => { return GET>(`/api/v1/serve/awel/nodes`); }; /** app */ export const addApp = (data: IApp) => { return POST('/api/v1/app/create', data); }; export const getAppList = (data: Record) => { return POST, IAppData>('/api/v1/app/list', data); }; export const collectApp = (data: Record) => { return POST, []>('/api/v1/app/collect', data); }; export const unCollectApp = (data: Record) => { return POST, []>('/api/v1/app/uncollect', data); }; export const delApp = (data: Record) => { return POST, []>('/api/v1/app/remove', data); }; export const getAgents = () => { return GET('/api/v1/agents/list', {}); }; export const getTeamMode = () => { return GET('/api/v1/team-mode/list'); }; export const getResourceType = () => { return GET('/api/v1/resource-type/list'); }; export const getResource = (data: Record) => { return GET, []>(`/api/v1/app/resources/list?type=${data.type}`); }; export const updateApp = (data: IApp) => { return POST('/api/v1/app/edit', data); }; export const getAppStrategy = () => { return GET(`/api/v1/llm-strategy/list`); }; export const getAppStrategyValues = (type: string) => { return GET(`/api/v1/llm-strategy/value/list?type=${type}`); }; ================================================ FILE: client/api/tools/index.ts ================================================ export * from './interceptors'; ================================================ FILE: client/api/tools/interceptors.ts ================================================ import { AxiosError } from 'axios'; import { ApiResponse, FailedTuple, SuccessTuple, ResponseType } from '../'; import { notification } from 'antd'; /** * Response processing * * @param promise request * @param ignoreCodes ignore error codes * @returns */ export const apiInterceptors = (promise: Promise>, ignoreCodes?: '*' | (number | string)[]) => { return promise .then>((response) => { const { data } = response; if (!data) { throw new Error('Network Error!'); } if (!data.success) { if (ignoreCodes === '*' || (data.err_code && ignoreCodes && ignoreCodes.includes(data.err_code))) { return [null, data.data, data, response]; } else { notification.error({ message: `Request error`, description: data?.err_msg ?? 'The interface is abnormal. Please try again later', }); } } return [null, data.data, data, response]; }) .catch>((err: Error | AxiosError) => { let errMessage = err.message; if (err instanceof AxiosError) { try { const { err_msg } = JSON.parse(err.request.response) as ResponseType; err_msg && (errMessage = err_msg); } catch (e) {} } notification.error({ message: `Request error`, description: errMessage, }); return [err, null, null, null]; }); }; ================================================ FILE: components/agent/market-plugins.tsx ================================================ import { apiInterceptors, postAgentHubUpdate, postAgentInstall, postAgentQuery, postAgentUninstall } from '@/client/api'; import { IAgentPlugin, PostAgentQueryParams } from '@/types/agent'; import { useRequest } from 'ahooks'; import { Button, Card, Form, Input, Spin, Tag, Tooltip, message } from 'antd'; import { useCallback, useMemo, useState } from 'react'; import MyEmpty from '../common/MyEmpty'; import { ClearOutlined, DownloadOutlined, GithubOutlined, LoadingOutlined, SearchOutlined, SyncOutlined } from '@ant-design/icons'; import { useTranslation } from 'react-i18next'; function MarketPlugins() { const { t } = useTranslation(); const [uploading, setUploading] = useState(false); const [isError, setIsError] = useState(false); const [actionIndex, setActionIndex] = useState(); const [form] = Form.useForm(); const pagination = useMemo<{ pageNo: number; pageSize: number }>( () => ({ pageNo: 1, pageSize: 20, }), [], ); const { data: agents = [], loading, refresh, } = useRequest(async () => { const queryParams: PostAgentQueryParams = { page_index: pagination.pageNo, page_size: pagination.pageSize, filter: form.getFieldsValue(), }; const [err, res] = await apiInterceptors(postAgentQuery(queryParams)); setIsError(!!err); return res?.datas ?? []; }); const updateFromGithub = async () => { try { setUploading(true); const [err] = await apiInterceptors(postAgentHubUpdate()); if (err) return; message.success('success'); refresh(); } finally { setUploading(false); } }; const pluginAction = useCallback( async (name: string, index: number, isInstall: boolean) => { if (actionIndex) return; setActionIndex(index); const [err] = await apiInterceptors((isInstall ? postAgentInstall : postAgentUninstall)(name)); if (!err) { message.success('success'); refresh(); } setActionIndex(undefined); }, [actionIndex, refresh], ); const renderAction = useCallback( (agent: IAgentPlugin, index: number) => { if (index === actionIndex) { return ; } return agent.installed ? (
{ pluginAction(agent.name, index, false); }} >
) : (
{ pluginAction(agent.name, index, true); }} >
); }, [actionIndex, pluginAction], ); return (
{!agents.length && !loading && }
{agents.map((agent, index) => (
{ window.open(agent.storage_url, '_blank'); }} >
, ]} >

{agent.name}

{agent.author && {agent.author}} {agent.version && v{agent.version}} {agent.type && Type {agent.type}} {agent.storage_channel && {agent.storage_channel}}

{agent.description}

))}
); } export default MarketPlugins; ================================================ FILE: components/agent/my-plugins.tsx ================================================ import { apiInterceptors, postAgentMy, postAgentUninstall, postAgentUpload } from '@/client/api'; import { IMyPlugin } from '@/types/agent'; import { useRequest } from 'ahooks'; import { Button, Card, Spin, Tag, Tooltip, Upload, UploadProps, message } from 'antd'; import { useCallback, useState } from 'react'; import MyEmpty from '../common/MyEmpty'; import { ClearOutlined, LoadingOutlined, UploadOutlined } from '@ant-design/icons'; import { useTranslation } from 'react-i18next'; function MyPlugins() { const { t } = useTranslation(); const [messageApi, contextHolder] = message.useMessage(); const [uploading, setUploading] = useState(false); const [isError, setIsError] = useState(false); const [actionIndex, setActionIndex] = useState(); const { data = [], loading, refresh, } = useRequest(async () => { const [err, res] = await apiInterceptors(postAgentMy()); setIsError(!!err); return res ?? []; }); const uninstall = async (name: string, index: number) => { if (actionIndex) return; setActionIndex(index); const [err] = await apiInterceptors(postAgentUninstall(name)); message[err ? 'error' : 'success'](err ? 'failed' : 'success'); !err && refresh(); setActionIndex(undefined); }; const renderAction = useCallback( (item: IMyPlugin, index: number) => { if (index === actionIndex) { return ; } return (
{ uninstall(item.name, index); }} >
); }, [actionIndex], ); const onChange: UploadProps['onChange'] = async (info) => { if (!info) { message.error('Please select the *.zip,*.rar file'); return; } try { const file = info.file; setUploading(true); const formData = new FormData(); formData.append('doc_file', file as any); messageApi.open({ content: `Uploading ${file.name}`, type: 'loading', duration: 0 }); const [err] = await apiInterceptors(postAgentUpload(undefined, formData, { timeout: 60000 })); if (err) return; message.success('success'); refresh(); } catch (e: any) { message.error(e?.message || 'Upload Error'); } finally { setUploading(false); messageApi.destroy(); } }; return ( {contextHolder}
false} name="file" accept=".zip,.rar" multiple={false} onChange={onChange} showUploadList={{ showDownloadIcon: false, showPreviewIcon: false, showRemoveIcon: false, }} itemRender={() => <>} >
{!data.length && !loading && }
{data.map((item, index) => (

{item.name}

{item.version && v{item.version}} {item.type && Type {item.type}}

{item.description}

))}
); } export default MyPlugins; ================================================ FILE: components/app/agent-panel.tsx ================================================ import { apiInterceptors, getAppStrategy, getAppStrategyValues, getResource } from '@/client/api'; import { Button, Input, Select } from 'antd'; import React, { useEffect, useMemo, useState } from 'react'; import ResourceCard from './resource-card'; import { useTranslation } from 'react-i18next'; interface IProps { resourceTypes: any; updateDetailsByAgentKey: (key: string, data: any) => void; detail: any; editResources?: any; } export default function AgentPanel(props: IProps) { const { resourceTypes, updateDetailsByAgentKey, detail, editResources } = props; const { t } = useTranslation(); const [resources, setResources] = useState([...(editResources ?? [])]); const [agent, setAgent] = useState({ ...detail, resources: [] }); const [strategyOptions, setStrategyOptions] = useState([]); const [strategyValueOptions, setStrategyValueOptions] = useState([]); const updateResourcesByIndex = (data: any, index: number) => { setResources((resources: any) => { const tempResources = [...resources]; if (!data) { return tempResources.filter((_: any, indey) => index !== indey); } return tempResources.map((item: any, indey) => { if (index === indey) { return data; } else { return item; } }); }); }; const getStrategy = async () => { const [_, data] = await apiInterceptors(getAppStrategy()); if (data) { setStrategyOptions(data?.map((item) => ({ label: item, value: item }))); } }; const getStrategyValues = async (type: string) => { const [_, data] = await apiInterceptors(getAppStrategyValues(type)); if (data) { setStrategyValueOptions(data.map((item) => ({ label: item, value: item })) ?? []); } }; const formatStrategyValue = (value: string) => { return !value ? [] : value.split(','); }; useEffect(() => { getStrategy(); getStrategyValues(detail.llm_strategy); }, []); useEffect(() => { updateAgent(resources, 'resources'); }, [resources]); const updateAgent = (data: any, type: string) => { const tempAgent = { ...agent }; tempAgent[type] = data; setAgent(tempAgent); updateDetailsByAgentKey(detail.key, tempAgent); }; const handelAdd = () => { setResources([...resources, { name: '', type: '', introduce: '', value: '', is_dynamic: '' }]); }; const resourceTypeOptions = useMemo(() => { return resourceTypes?.map((item: string) => { return { label: item, value: item, }; }); }, [resourceTypes]); return (
{t('Prompt')}:
{ updateAgent(e.target.value, 'prompt_template'); }} />
{t('LLM_strategy')}:
{ if (!value || value?.length === 0) { updateAgent(null, 'llm_strategy_value'); return null; } const curValue = value.reduce((pre: string, cur: string, index: number) => { if (index === 0) { return cur; } else { return `${pre},${cur}`; } }, ''); updateAgent(curValue, 'llm_strategy_value'); }} /> )}
{t('available_resources')}
{resources.map((resource: any, index: number) => { return ( ); })}
); } ================================================ FILE: components/app/app-card.tsx ================================================ import React, { useContext, useEffect, useState } from 'react'; import { Modal } from 'antd'; import { apiInterceptors, collectApp, delApp, newDialogue, unCollectApp } from '@/client/api'; import { IApp } from '@/types/app'; import { DeleteFilled, MessageFilled, StarFilled, WarningOutlined } from '@ant-design/icons'; import { useTranslation } from 'react-i18next'; import { useRouter } from 'next/router'; import { ChatContext } from '@/app/chat-context'; import GPTCard from '../common/gpt-card'; interface IProps { updateApps: (data?: { is_collected: boolean }) => void; app: IApp; handleEdit: (app: any) => void; isCollected: boolean; } const { confirm } = Modal; export default function AppCard(props: IProps) { const { updateApps, app, handleEdit, isCollected } = props; const { model } = useContext(ChatContext); const router = useRouter(); const [isCollect, setIsCollect] = useState(app.is_collected); const { setAgent: setAgentToChat } = useContext(ChatContext); const { t } = useTranslation(); const languageMap = { en: t('English'), zh: t('Chinese'), }; const showDeleteConfirm = () => { confirm({ title: t('Tips'), icon: , content: `do you want delete the application?`, okText: 'Yes', okType: 'danger', cancelText: 'No', async onOk() { await apiInterceptors(delApp({ app_code: app.app_code })); updateApps(isCollected ? { is_collected: isCollected } : undefined); }, }); }; useEffect(() => { setIsCollect(app.is_collected); }, [app]); const collect = async () => { const [error] = await apiInterceptors(isCollect === 'true' ? unCollectApp({ app_code: app.app_code }) : collectApp({ app_code: app.app_code })); if (error) return; updateApps(isCollected ? { is_collected: isCollected } : undefined); setIsCollect(isCollect === 'true' ? 'false' : 'true'); }; const handleChat = async () => { setAgentToChat?.(app.app_code); const [, res] = await apiInterceptors(newDialogue({ chat_mode: 'chat_agent' })); if (res) { router.push(`/chat/?scene=chat_agent&id=${res.conv_uid}${model ? `&model=${model}` : ''}`); } }; return ( { handleEdit(app); }} operations={[ { label: t('Chat'), children: , onClick: handleChat, }, { label: t('collect'), children: , onClick: collect, }, { label: t('Delete'), children: , onClick: () => { showDeleteConfirm(); }, }, ]} /> ); } ================================================ FILE: components/app/app-modal.tsx ================================================ import { AgentParams, IAgent as IAgentParams, IApp, IDetail } from '@/types/app'; import { Dropdown, Form, Input, Modal, Select, Space, Spin, Tabs } from 'antd'; import React, { useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import AddIcon from '../icons/add-icon'; import AgentPanel from './agent-panel'; import { addApp, apiInterceptors, getAgents, getResourceType, getTeamMode, updateApp } from '@/client/api'; import type { TabsProps } from 'antd'; import DagLayout from './dag-layout'; import { IFlow } from '@/types/flow'; type TargetKey = string; type FieldType = { app_name: string; app_describe: string; language: string; team_mode: string; }; type IAgent = { label: string; children?: React.ReactNode; onClick?: () => void; key: number | string; }; interface IProps { handleCancel: () => void; open: boolean; updateApps: () => void; type: string; app?: any; } type TeamModals = 'awel_layout' | 'singe_agent' | 'auto_plan'; export default function AppModal(props: IProps) { const { handleCancel, open, updateApps, type, app } = props; const { t } = useTranslation(); const [spinning, setSpinning] = useState(false); const [activeKey, setActiveKey] = useState(); const [teamModal, setTeamModal] = useState<{ label: string; value: string }[]>(); const [agents, setAgents] = useState([]); const [dropItems, setDropItems] = useState([]); const [details, setDetails] = useState([...(app?.details || [])]); const [flow, setFlow] = useState(); const [resourceTypes, setResourceTypes] = useState(); const [curTeamModal, setCurTeamModal] = useState(app.team_modal || 'auto_plan'); const [form] = Form.useForm(); const languageOptions = [ { value: 'zh', label: t('Chinese') }, { value: 'en', label: t('English') }, ]; const onChange = (newActiveKey: string) => { setActiveKey(newActiveKey); }; const createApp = async (app: IApp) => { await apiInterceptors(type === 'add' ? addApp(app) : updateApp(app)); await updateApps(); }; const initApp = async () => { const appDetails = app.details; const [_, resourceType] = await apiInterceptors(getResourceType()); if (appDetails?.length > 0) { setAgents( appDetails?.map((item: AgentParams) => { return { label: item?.agent_name, children: ( ), key: item?.agent_name, }; }), ); } }; const fetchTeamModal = async () => { const [_, data] = await apiInterceptors(getTeamMode()); if (!data) return null; const teamModalOptions = data.map((item) => { return { value: item, label: item }; }); setTeamModal(teamModalOptions); }; const fetchAgent = async () => { const [_, data] = await apiInterceptors(getAgents()); if (!data) { return null; } setDropItems( data .map((agent) => { return { label: agent.name, key: agent.name, onClick: () => { add(agent); }, agent, }; }) .filter((item) => { if (!app.details || app.details?.length === 0) { return item; } return app?.details?.every((detail: AgentParams) => detail.agent_name !== item.label); }), ); }; const handleFlowsChange = (data: IFlow) => { setFlow(data); }; const fetchResourceType = async () => { const [_, data] = await apiInterceptors(getResourceType()); if (data) { setResourceTypes(data); } }; useEffect(() => { fetchTeamModal(); fetchAgent(); fetchResourceType(); }, []); useEffect(() => { type === 'edit' && initApp(); }, [resourceTypes]); useEffect(() => { setCurTeamModal(app.team_mode || 'auto_plan'); }, [app]); const updateDetailsByAgentKey = (key: string, data: IDetail) => { setDetails((details: IDetail[]) => { return details.map((detail: IDetail) => { return key === (detail.agent_name || detail.key) ? data : detail; }); }); }; const add = async (tabBar: IAgentParams) => { const newActiveKey = tabBar.name; const [_, data] = await apiInterceptors(getResourceType()); setActiveKey(newActiveKey); setDetails((details: IDetail[]) => { return [...details, { key: newActiveKey, name: '', llm_strategy: 'priority' }]; }); setAgents((items: any) => { return [ ...items, { label: newActiveKey, children: ( ), key: newActiveKey, }, ]; }); setDropItems((items) => { return items.filter((item) => item.key !== tabBar.name); }); }; const remove = (targetKey: TargetKey) => { let newActiveKey = activeKey; let lastIndex = -1; if (!agents) { return null; } agents.forEach((item, i) => { if (item.key === targetKey) { lastIndex = i - 1; } }); const newPanes = agents.filter((item) => item.key !== targetKey); if (newPanes.length && newActiveKey === targetKey) { if (lastIndex >= 0) { newActiveKey = newPanes[lastIndex].key; } else { newActiveKey = newPanes[0].key; } } setDetails((details: IDetail[]) => { return details?.filter((detail: any) => { return (detail.agent_name || detail.key) !== targetKey; }); }); setAgents(newPanes); setActiveKey(newActiveKey); setDropItems((items: any) => { return [ ...items, { label: targetKey, key: targetKey, onClick: () => { add({ name: targetKey, describe: '', system_message: '' }); }, }, ]; }); }; const onEdit = (targetKey: any, action: 'add' | 'remove') => { if (action === 'add') { // add(); } else { remove(targetKey); } }; const handleSubmit = async () => { const isValidate = await form.validateFields(); if (!isValidate) { return; } setSpinning(true); const data = { ...form.getFieldsValue(), }; if (type === 'edit') { data.app_code = app.app_code; } if (data.team_mode !== 'awel_layout') { data.details = details; } else { const tempFlow = { ...flow }; delete tempFlow.flow_data; data.team_context = tempFlow; } try { await createApp(data); } catch (error) { return; } setSpinning(false); handleCancel(); }; const handleTeamModalChange = (value: TeamModals) => { setCurTeamModal(value); }; const renderAddIcon = () => { return ( e.preventDefault()}> ); }; return (
label={t('app_name')} name="app_name" rules={[{ required: true, message: t('Please_input_the_name') }]}> label={t('Description')} name="app_describe" rules={[{ required: true, message: t('Please_input_the_description') }]} >
labelCol={{ span: 7 }} label={t('language')} name="language" className="w-1/2" rules={[{ required: true }]}>
{curTeamModal !== 'awel_layout' ? ( <>
{t('Agents')}
) : ( )}
); } ================================================ FILE: components/app/dag-layout.tsx ================================================ import React, { useEffect, useState } from 'react'; import PreviewFlow from '../flow/preview-flow'; import { apiInterceptors, getFlows } from '@/client/api'; import { IFlow } from '@/types/flow'; import { Select } from 'antd'; import Link from 'next/link'; import { t } from 'i18next'; interface IProps { onFlowsChange: (data: any) => void; teamContext: any; } export default function DagLayout(props: IProps) { const { onFlowsChange, teamContext } = props; const [flows, setFlows] = useState(); const [flowsOptions, setFlowsOptions] = useState(); const [curFlow, setCurFlow] = useState(); const fetchFlows = async () => { const [_, data] = await apiInterceptors(getFlows()); if (data) { setFlowsOptions(data?.items?.map((item: IFlow) => ({ label: item.name, value: item.name }))); setFlows(data.items); onFlowsChange(data?.items[0]); } }; const handleFlowsChange = (value: string) => { setCurFlow(flows?.find((item) => value === item.name)); onFlowsChange(flows?.find((item) => value === item.name)); }; useEffect(() => { fetchFlows(); }, []); useEffect(() => { setCurFlow(flows?.find((item) => teamContext?.name === item.name) || flows?.[0]); }, [teamContext, flows]); return (
Flows:
{t('edit_new_applications')}
{curFlow?.description}
{curFlow && (
)}
); } ================================================ FILE: components/app/resource-card.tsx ================================================ import { apiInterceptors, getResource } from '@/client/api'; import { DeleteFilled } from '@ant-design/icons'; import { Button, Card, ConfigProvider, Input, Select, Switch } from 'antd'; import React, { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; interface IProps { resourceTypeOptions: any[]; updateResourcesByIndex: (data: any, index: number) => void; index: number; resource: any; } export default function ResourceCard(props: IProps) { const { resourceTypeOptions, updateResourcesByIndex, index, resource: editResource } = props; const { t } = useTranslation(); const [resourceType, setResourceType] = useState(editResource.type || resourceTypeOptions?.[0].label); const [resourceValueOptions, setResourceValueOptions] = useState([]); const [resource, setResource] = useState({ name: editResource.name, type: editResource.type, value: editResource.value, is_dynamic: editResource.is_dynamic || false, }); const fetchResource = async () => { const [_, data] = await apiInterceptors(getResource({ type: resourceType })); if (data) { setResourceValueOptions( data?.map((item) => { return { label: item, value: item }; }), ); } else { setResourceValueOptions([]); } }; const handleChange = (value: string) => { setResourceType(value); }; const updateResource = (value: any, type: string) => { const tempResource = resource; tempResource[type] = value; setResource(tempResource); updateResourcesByIndex(tempResource, index); }; const handleDeleteResource = () => { updateResourcesByIndex(null, index); }; useEffect(() => { fetchResource(); updateResource(resource.type || resourceType, 'type'); }, [resourceType]); useEffect(() => { updateResource(resourceValueOptions[0]?.label || editResource.value, 'value'); setResource({ ...resource, value: resourceValueOptions[0]?.label || editResource.value }); }, [resourceValueOptions]); return ( { handleDeleteResource(); }} /> } >
* {t('resource_name')}:
) => { updateResource(e.target.value, 'name'); }} />
{t('resource_dynamic')}
{ updateResource(value, 'is_dynamic'); }} />
{t('resource_type')}:
{ updateResource(value, 'value'); }} /> ) : ( ) => { updateResource(e.target.value, 'value'); }} /> )}
); } ================================================ FILE: components/chart/autoChart/advisor/pipeline.ts ================================================ import { Advisor, CkbConfig } from '@antv/ava'; import type { Advice, AdviseParams, AdvisorConfig, ChartKnowledgeBase } from '@antv/ava'; import type { CustomAdvisorConfig, RuleConfig, Specification } from '../types'; export type CustomRecommendConfig = { customCKB?: Partial; customRule?: Partial; }; export const customizeAdvisor = (props: CustomAdvisorConfig): Advisor => { const { charts, scopeOfCharts: CKBCfg, ruleConfig: ruleCfg } = props; const customCKB: ChartKnowledgeBase = {}; charts?.forEach((chart) => { /** 若用户自定义的图表 id 与内置图表 id 相同,内置图表将被覆盖 */ if (!chart.chartKnowledge.toSpec) { chart.chartKnowledge.toSpec = (data: any, dataProps: any) => { return { dataProps } as Specification; }; } else { const oriFunc = chart.chartKnowledge.toSpec; chart.chartKnowledge.toSpec = (data: any, dataProps: any) => { return { ...oriFunc(data, dataProps), dataProps: dataProps, } as Specification; }; } customCKB[chart.chartType] = chart.chartKnowledge; }); // 步骤一:如果有 exclude 项,先从给到的 CKB 中剔除部分选定的图表类型 if (CKBCfg?.exclude) { CKBCfg.exclude.forEach((chartType: string) => { if (Object.keys(customCKB).includes(chartType)) { delete customCKB[chartType]; } }); } // 步骤二:如果有 include 项,则从当前(剔除后的)CKB中,只保留 include 中的图表类型。 if (CKBCfg?.include) { const include = CKBCfg.include; Object.keys(customCKB).forEach((chartType: string) => { if (!include.includes(chartType)) { delete customCKB[chartType]; } }); } const CKBConfig: CkbConfig = { ...CKBCfg, custom: customCKB, }; const ruleConfig: RuleConfig = { ...ruleCfg, }; const myAdvisor = new Advisor({ ckbCfg: CKBConfig, ruleCfg: ruleConfig, }); return myAdvisor; }; /** 主推荐流程 */ export const getVisAdvices = (props: any): Advice[] => { const { data, dataMetaMap, myChartAdvisor } = props; /** * 若输入中有信息能够获取列的类型( Interval, Nominal, Time ),则将这个 信息传给 Advisor * 主要是读取 levelOfMeasureMents 这个字段,即 dataMetaMap[item].levelOfMeasurements */ const customDataProps = dataMetaMap ? Object.keys(dataMetaMap).map((item) => { return { name: item, ...dataMetaMap[item] }; }) : null; const allAdvices = myChartAdvisor?.adviseWithLog({ data, dataProps: customDataProps as AdviseParams['dataProps'], }); return allAdvices?.advices ?? []; }; ================================================ FILE: components/chart/autoChart/advisor/rule.ts ================================================ export const customRuleCfg = {}; ================================================ FILE: components/chart/autoChart/advisor/utils.ts ================================================ import { isNull } from 'lodash'; import type { Advice } from '@antv/ava'; export function defaultAdvicesFilter(props: { advices: Advice[] }) { const { advices } = props; return advices; } export const compare = (f1: any, f2: any) => { if (isNull(f1.distinct) || isNull(f2.distinct)) { if (f1.distinct! < f2!.distinct!) { return 1; } if (f1.distinct! > f2.distinct!) { return -1; } return 0; } return 0; }; export function hasSubset(array1: any[], array2: any[]): boolean { return array2.every((e) => array1.includes(e)); } export function intersects(array1: any[], array2: any[]): boolean { return array2.some((e) => array1.includes(e)); } export function LOM2EncodingType(lom: string) { switch (lom) { case 'Nominal': return 'nominal'; case 'Ordinal': return 'ordinal'; case 'Interval': return 'quantitative'; case 'Time': return 'temporal'; case 'Continuous': return 'quantitative'; case 'Discrete': return 'nominal'; default: return 'nominal'; } } ================================================ FILE: components/chart/autoChart/charts/index.ts ================================================ import multi_line_chart from './multi-line-chart'; import multi_measure_column_chart from './multi-measure-column-chart'; import multi_measure_line_chart from './multi-measure-line-chart'; import type { CustomChart } from '../types'; export const customCharts: CustomChart[] = [multi_line_chart, multi_measure_column_chart, multi_measure_line_chart]; export type CustomChartsType = 'multi_line_chart' | 'multi_measure_column_chart' | 'multi_measure_line_chart'; ================================================ FILE: components/chart/autoChart/charts/multi-line-chart.ts ================================================ import { hasSubset, intersects } from '../advisor/utils'; import { processDateEncode } from './util'; import type { ChartKnowledge, CustomChart, GetChartConfigProps, Specification } from '../types'; const getChartSpec = (data: GetChartConfigProps['data'], dataProps: GetChartConfigProps['dataProps']) => { const field4X = dataProps.find((field) => // @ts-ignore intersects(field.levelOfMeasurements, ['Time', 'Ordinal']), ); // @ts-ignore const field4Y = dataProps.filter((field) => hasSubset(field.levelOfMeasurements, ['Interval'])); const field4Nominal = dataProps.find((field) => // @ts-ignore hasSubset(field.levelOfMeasurements, ['Nominal']), ); if (!field4X || !field4Y) return null; const spec: Specification = { type: 'view', autoFit: true, data, children: [], }; field4Y.forEach((field) => { const singleLine: Specification = { type: 'line', encode: { x: processDateEncode(field4X.name as string, dataProps), y: field.name, }, }; if (field4Nominal) { singleLine.encode.color = field4Nominal.name; } spec.children.push(singleLine); }); return spec; }; const ckb: ChartKnowledge = { id: 'multi_line_chart', name: 'multi_line_chart', alias: ['multi_line_chart'], family: ['LineCharts'], def: 'multi_line_chart uses lines with segments to show changes in data in a ordinal dimension', purpose: ['Comparison', 'Trend'], coord: ['Cartesian2D'], category: ['Statistic'], shape: ['Lines'], dataPres: [ { minQty: 1, maxQty: 1, fieldConditions: ['Time', 'Ordinal'] }, { minQty: 1, maxQty: '*', fieldConditions: ['Interval'] }, { minQty: 0, maxQty: 1, fieldConditions: ['Nominal'] }, ], channel: ['Color', 'Direction', 'Position'], recRate: 'Recommended', toSpec: getChartSpec, }; /* 订制一个图表需要的所有参数 */ export const multi_line_chart: CustomChart = { /* 图表唯一 Id */ chartType: 'multi_line_chart', /* 图表知识 */ chartKnowledge: ckb as ChartKnowledge, /** 图表中文名 */ chineseName: '折线图', }; export default multi_line_chart; ================================================ FILE: components/chart/autoChart/charts/multi-measure-column-chart.ts ================================================ import { hasSubset } from '../advisor/utils'; import type { ChartKnowledge, CustomChart, GetChartConfigProps, Specification } from '../types'; const getChartSpec = (data: GetChartConfigProps['data'], dataProps: GetChartConfigProps['dataProps']) => { try { // @ts-ignore const field4Y = dataProps?.filter((field) => hasSubset(field.levelOfMeasurements, ['Interval'])); const field4Nominal = dataProps?.find((field) => // @ts-ignore hasSubset(field.levelOfMeasurements, ['Nominal']), ); if (!field4Nominal || !field4Y) return null; const spec: Specification = { type: 'view', data, children: [], }; field4Y?.forEach((field) => { const singleLine: Specification = { type: 'interval', encode: { x: field4Nominal.name, y: field.name, color: () => field.name, series: () => field.name, }, }; spec.children.push(singleLine); }); return spec; } catch (err) { console.log(err); return null; } }; const ckb: ChartKnowledge = { id: 'multi_measure_column_chart', name: 'multi_measure_column_chart', alias: ['multi_measure_column_chart'], family: ['ColumnCharts'], def: 'multi_measure_column_chart uses lines with segments to show changes in data in a ordinal dimension', purpose: ['Comparison', 'Distribution'], coord: ['Cartesian2D'], category: ['Statistic'], shape: ['Lines'], dataPres: [ { minQty: 1, maxQty: '*', fieldConditions: ['Interval'] }, { minQty: 1, maxQty: 1, fieldConditions: ['Nominal'] }, ], channel: ['Color', 'Direction', 'Position'], recRate: 'Recommended', toSpec: getChartSpec, }; /* 订制一个图表需要的所有参数 */ export const multi_measure_column_chart: CustomChart = { /* 图表唯一 Id */ chartType: 'multi_measure_column_chart', /* 图表知识 */ chartKnowledge: ckb as ChartKnowledge, /** 图表中文名 */ chineseName: '折线图', }; export default multi_measure_column_chart; ================================================ FILE: components/chart/autoChart/charts/multi-measure-line-chart.ts ================================================ import { hasSubset, intersects } from '../advisor/utils'; import { processDateEncode } from './util'; import type { ChartKnowledge, CustomChart, GetChartConfigProps, Specification } from '../types'; const getChartSpec = (data: GetChartConfigProps['data'], dataProps: GetChartConfigProps['dataProps']) => { try { // @ts-ignore const field4Y = dataProps?.filter((field) => hasSubset(field.levelOfMeasurements, ['Interval'])); const field4Nominal = dataProps?.find((field) => // @ts-ignore hasSubset(field.levelOfMeasurements, ['Nominal']), ); if (!field4Nominal || !field4Y) return null; const spec: Specification = { type: 'view', data, children: [], }; field4Y?.forEach((field) => { const singleLine: Specification = { type: 'line', encode: { x: processDateEncode(field4Nominal.name as string, dataProps), y: field.name, color: () => field.name, series: () => field.name, }, }; spec.children.push(singleLine); }); return spec; } catch (err) { console.log(err); return null; } }; const ckb: ChartKnowledge = { id: 'multi_measure_line_chart', name: 'multi_measure_line_chart', alias: ['multi_measure_line_chart'], family: ['LineCharts'], def: 'multi_measure_line_chart uses lines with segments to show changes in data in a ordinal dimension', purpose: ['Comparison', 'Distribution'], coord: ['Cartesian2D'], category: ['Statistic'], shape: ['Lines'], dataPres: [ { minQty: 1, maxQty: '*', fieldConditions: ['Interval'] }, { minQty: 1, maxQty: 1, fieldConditions: ['Nominal'] }, ], channel: ['Color', 'Direction', 'Position'], recRate: 'Recommended', toSpec: getChartSpec, }; /* 订制一个图表需要的所有参数 */ export const multi_measure_line_chart: CustomChart = { /* 图表唯一 Id */ chartType: 'multi_measure_line_chart', /* 图表知识 */ chartKnowledge: ckb as ChartKnowledge, /** 图表中文名 */ chineseName: '折线图', }; export default multi_measure_line_chart; ================================================ FILE: components/chart/autoChart/charts/util.ts ================================================ type BasicDataPropertyForAdvice = any; /** * Process date column to new Date(). * @param field * @param dataProps * @returns */ export function processDateEncode(field: string, dataProps: BasicDataPropertyForAdvice[]) { const dp = dataProps.find((dataProp) => dataProp.name === field); if (dp?.recommendation === 'date') { return (d: any) => new Date(d[field]); } return field; } ================================================ FILE: components/chart/autoChart/helpers/index.ts ================================================ import { ChartId } from '@antv/ava'; import { CustomChartsType } from '../charts'; export type BackEndChartType = | 'response_line_chart' | 'response_bar_chart' | 'response_pie_chart' | 'response_scatter_chart' | 'response_area_chart' | 'response_heatmap_chart' | 'response_table'; type ChartType = ChartId | CustomChartsType; export const getChartType = (backendChartType: BackEndChartType): ChartType[] => { if (backendChartType === 'response_line_chart') { return ['multi_line_chart', 'multi_measure_line_chart']; } if (backendChartType === 'response_bar_chart') { return ['multi_measure_column_chart']; } if (backendChartType === 'response_pie_chart') { return ['pie_chart']; } if (backendChartType === 'response_scatter_chart') { return ['scatter_plot']; } if (backendChartType === 'response_area_chart') { return ['area_chart']; } if (backendChartType === 'response_heatmap_chart') { return ['heatmap']; } return []; }; ================================================ FILE: components/chart/autoChart/index.tsx ================================================ import { Empty, Row, Col, Select, Tooltip } from 'antd'; import { Advice, Advisor } from '@antv/ava'; import { Chart } from '@berryv/g2-react'; import i18n, { I18nKeys } from '@/app/i18n'; import { customizeAdvisor, getVisAdvices } from './advisor/pipeline'; import { useContext, useEffect, useMemo, useState } from 'react'; import { defaultAdvicesFilter } from './advisor/utils'; import { AutoChartProps, ChartType, CustomAdvisorConfig, CustomChart, Specification } from './types'; import { customCharts } from './charts'; import { ChatContext } from '@/app/chat-context'; const { Option } = Select; export const AutoChart = (props: AutoChartProps) => { const { data, chartType, scopeOfCharts, ruleConfig } = props; const { mode } = useContext(ChatContext); const [advisor, setAdvisor] = useState(); const [advices, setAdvices] = useState([]); const [renderChartType, setRenderChartType] = useState(); useEffect(() => { const input_charts: CustomChart[] = customCharts; const advisorConfig: CustomAdvisorConfig = { charts: input_charts, scopeOfCharts: undefined, ruleConfig, }; setAdvisor(customizeAdvisor(advisorConfig)); }, [ruleConfig, scopeOfCharts]); useEffect(() => { if (data && advisor) { const avaAdvices = getVisAdvices({ data, myChartAdvisor: advisor, }); const filteredAdvices = defaultAdvicesFilter({ advices: avaAdvices, }); filteredAdvices.sort((a, b) => { return chartType.indexOf(b.type) - chartType?.indexOf(a.type); }); setAdvices(filteredAdvices); setRenderChartType(filteredAdvices[0]?.type as ChartType); } }, [data, advisor, chartType]); const visComponent = useMemo(() => { /* Advices exist, render the chart. */ if (advices?.length > 0) { const chartTypeInput = renderChartType ?? advices[0].type; const spec: Specification = advices?.find((item: Advice) => item.type === chartTypeInput)?.spec ?? undefined; if (spec) { return ( ); } } }, [advices, renderChartType]); if (renderChartType) { return (
{i18n.t('Advices')}
{visComponent}
); } return ; }; export * from './helpers'; ================================================ FILE: components/chart/autoChart/types.ts ================================================ import { Advice, AdvisorConfig, ChartId, Datum, FieldInfo, PureChartKnowledge } from '@antv/ava'; export type ChartType = ChartId | string; export type Specification = Advice['spec'] | any; export type RuleConfig = AdvisorConfig['ruleCfg']; export type AutoChartProps = { data: Datum[]; /** Chart type which are suggestted. */ chartType: ChartType[]; /** Charts exclude or include. */ scopeOfCharts?: { exclude?: string[]; include?: string[]; }; /** Customize rules. */ ruleConfig?: RuleConfig; }; export type ChartKnowledge = PureChartKnowledge & { toSpec?: any }; export type CustomChart = { /** Chart type ID, unique. */ chartType: ChartType; /** Chart knowledge. */ chartKnowledge: ChartKnowledge; /** Chart name. */ chineseName?: string; }; export type GetChartConfigProps = { data: Datum[]; spec: Specification; dataProps: FieldInfo[]; chartType?: ChartType; }; export type CustomAdvisorConfig = { charts?: CustomChart[]; scopeOfCharts?: { exclude?: string[]; include?: string[]; }; ruleConfig?: RuleConfig; }; ================================================ FILE: components/chart/bar-chart.tsx ================================================ import { ChatContext } from '@/app/chat-context'; import { ChartData } from '@/types/chat'; import { Chart } from '@berryv/g2-react'; import { useContext } from 'react'; export default function BarChart({ chart }: { key: string; chart: ChartData }) { const { mode } = useContext(ChatContext); return (
{chart.chart_name}
{chart.chart_desc}
); } ================================================ FILE: components/chart/index.tsx ================================================ import { Card, CardContent, Typography } from '@mui/joy'; import BarChart from './bar-chart'; import LineChart from './line-chart'; import TableChart from './table-chart'; import { ChartData } from '@/types/chat'; import { useMemo } from 'react'; type Props = { chartsData: Array; }; function Chart({ chartsData }: Props) { const chartRows = useMemo(() => { if (chartsData) { let res = []; // 若是有类型为 IndicatorValue 的,提出去,独占一行 const chartCalc = chartsData?.filter((item) => item.chart_type === 'IndicatorValue'); if (chartCalc.length > 0) { res.push({ charts: chartCalc, type: 'IndicatorValue', }); } let otherCharts = chartsData?.filter((item) => item.chart_type !== 'IndicatorValue'); let otherLength = otherCharts.length; let curIndex = 0; // charts 数量 3~8个,暂定每行排序 let chartLengthMap = [[0], [1], [2], [1, 2], [1, 3], [2, 1, 2], [2, 1, 3], [3, 1, 3], [3, 2, 3]]; chartLengthMap[otherLength].forEach((item) => { if (item > 0) { const rowsItem = otherCharts.slice(curIndex, curIndex + item); curIndex = curIndex + item; res.push({ charts: rowsItem, }); } }); return res; } return undefined; }, [chartsData]); return (
{chartRows?.map((chartRow, index) => (
{chartRow.charts.map((chart) => { if (chart.chart_type === 'IndicatorValue') { return (
{chart.values.map((item) => (
{item.name} {item.value}
))}
); } else if (chart.chart_type === 'LineChart') { return ; } else if (chart.chart_type === 'BarChart') { return ; } else if (chart.chart_type === 'Table') { return ; } })}
))}
); } export * from './autoChart'; export default Chart; ================================================ FILE: components/chart/line-chart.tsx ================================================ import { ChartData } from '@/types/chat'; import { Chart } from '@berryv/g2-react'; import { useContext } from 'react'; import { ChatContext } from '@/app/chat-context'; export default function LineChart({ chart }: { chart: ChartData }) { const { mode } = useContext(ChatContext); return (
{chart.chart_name}
{chart.chart_desc}
); } ================================================ FILE: components/chart/table-chart.tsx ================================================ import { ChartData } from '@/types/chat'; import { Table } from '@mui/joy'; import { groupBy } from 'lodash'; export default function TableChart({ chart }: { key: string; chart: ChartData }) { const data = groupBy(chart.values, 'type'); return (
{chart.chart_name}
{chart.chart_desc}
{Object.keys(data).map((key) => ( ))} {Object.values(data)?.[0]?.map((value, i) => ( {Object.keys(data)?.map((k) => ( ))} ))}
{key}
{data?.[k]?.[i].value || ''}
); } ================================================ FILE: components/chat/agent-content.tsx ================================================ import { ChatContext } from '@/app/chat-context'; import { IChatDialogueMessageSchema } from '@/types/chat'; import classNames from 'classnames'; import { memo, useContext } from 'react'; import ReactMarkdown from 'react-markdown'; import markdownComponents from './chat-content/config'; import rehypeRaw from 'rehype-raw'; interface Props { content: IChatDialogueMessageSchema; } function formatMarkdownVal(val: string) { return val.replace(/]+)>/gi, '').replace(/]+)>/gi, ''); } function AgentContent({ content }: Props) { const { scene } = useContext(ChatContext); const isView = content.role === 'view'; return (
{isView ? ( {formatMarkdownVal(content.context)} ) : (
{content.context}
)}
); } export default memo(AgentContent); ================================================ FILE: components/chat/chat-container.tsx ================================================ import React, { useCallback, useContext, useEffect, useState } from 'react'; import { useAsyncEffect } from 'ahooks'; import useChat from '@/hooks/use-chat'; import Completion from './completion'; import { ChartData, ChatHistoryResponse } from '@/types/chat'; import { apiInterceptors, getChatHistory } from '@/client/api'; import { ChatContext } from '@/app/chat-context'; import Header from './header'; import Chart from '../chart'; import classNames from 'classnames'; import MuiLoading from '../common/loading'; import { Empty } from 'antd'; import { useSearchParams } from 'next/navigation'; import { getInitMessage } from '@/utils'; const ChatContainer = () => { const searchParams = useSearchParams(); const { scene, chatId, model, agent, setModel, history, setHistory } = useContext(ChatContext); const chat = useChat({}); const initMessage = (searchParams && searchParams.get('initMessage')) ?? ''; const [loading, setLoading] = useState(false); const [chartsData, setChartsData] = useState>(); const getHistory = async () => { setLoading(true); const [, res] = await apiInterceptors(getChatHistory(chatId)); setHistory(res ?? []); setLoading(false); }; const getChartsData = (list: ChatHistoryResponse) => { const contextTemp = list[list.length - 1]?.context; if (contextTemp) { try { const contextObj = JSON.parse(contextTemp); setChartsData(contextObj?.template_name === 'report' ? contextObj?.charts : undefined); } catch (e) { setChartsData(undefined); } } }; useAsyncEffect(async () => { const initMessage = getInitMessage(); if (initMessage && initMessage.id === chatId) return; await getHistory(); }, [initMessage, chatId]); useEffect(() => { if (!history.length) return; /** use last view model_name as default model name */ const lastView = history.filter((i) => i.role === 'view')?.slice(-1)?.[0]; lastView?.model_name && setModel(lastView.model_name); getChartsData(history); }, [history.length]); useEffect(() => { return () => { setHistory([]); }; }, []); const handleChat = useCallback( (content: string, data?: Record) => { return new Promise((resolve) => { const tempHistory: ChatHistoryResponse = [ ...history, { role: 'human', context: content, model_name: model, order: 0, time_stamp: 0 }, { role: 'view', context: '', model_name: model, order: 0, time_stamp: 0 }, ]; const index = tempHistory.length - 1; setHistory([...tempHistory]); chat({ data: { ...data, chat_mode: scene || 'chat_normal', model_name: model, user_input: content }, chatId, onMessage: (message) => { if (data?.incremental) { tempHistory[index].context += message; } else { tempHistory[index].context = message; } setHistory([...tempHistory]); }, onDone: () => { getChartsData(tempHistory); resolve(); }, onClose: () => { getChartsData(tempHistory); resolve(); }, onError: (message) => { tempHistory[index].context = message; setHistory([...tempHistory]); resolve(); }, }); }); }, [history, chat, chatId, model, agent, scene], ); return ( <>
{ setModel(newModel); }} />
{!!chartsData?.length && (
)} {!chartsData?.length && scene === 'chat_dashboard' && ( )} {/** chat panel */}
); }; export default ChatContainer; ================================================ FILE: components/chat/chat-content/agent-messages.tsx ================================================ import ReactMarkdown from 'react-markdown'; import markdownComponents from './config'; import { renderModelIcon } from '../header/model-selector'; import { SwapRightOutlined } from '@ant-design/icons'; interface Props { data: { sender: string; receiver: string; model: string | null; markdown: string; }[]; } function AgentMessages({ data }: Props) { if (!data || !data.length) return null; return ( <> {data.map((item, index) => (
{item.model ? renderModelIcon(item.model) :
}
{item.sender} {item.receiver}
{item.markdown}
))} ); } export default AgentMessages; ================================================ FILE: components/chat/chat-content/agent-plans.tsx ================================================ import { CaretRightOutlined, CheckOutlined, ClockCircleOutlined } from '@ant-design/icons'; import { Collapse } from 'antd'; import ReactMarkdown from 'react-markdown'; import markdownComponents from './config'; interface Props { data: { name: string; num: number; status: 'complete' | 'todo'; agent: string; markdown: string; }[]; } function AgentPlans({ data }: Props) { if (!data || !data.length) return null; return ( } items={data.map((item, index) => { return { key: index, label: (
{item.name} - {item.agent} {item.status === 'complete' ? ( ) : ( )}
), children: {item.markdown}, }; })} /> ); } export default AgentPlans; ================================================ FILE: components/chat/chat-content/chart-view.tsx ================================================ import { Datum } from '@antv/ava'; import { Table, Tabs, TabsProps } from 'antd'; import React from 'react'; import { format } from 'sql-formatter'; import { AutoChart, BackEndChartType, getChartType } from '@/components/chart/autoChart'; import { CodePreview } from './code-preview'; function ChartView({ data, type, sql }: { data: Datum[]; type: BackEndChartType; sql: string }) { const columns = data?.[0] ? Object.keys(data?.[0])?.map((item) => { return { title: item, dataIndex: item, key: item, }; }) : []; const ChartItem = { key: 'chart', label: 'Chart', children: , }; const SqlItem = { key: 'sql', label: 'SQL', children: , }; const DataItem = { key: 'data', label: 'Data', children:
, }; const TabItems: TabsProps['items'] = type === 'response_table' ? [DataItem, SqlItem] : [ChartItem, SqlItem, DataItem]; return ; } export default ChartView; ================================================ FILE: components/chat/chat-content/code-preview.tsx ================================================ import { Button, message } from 'antd'; import { CopyOutlined } from '@ant-design/icons'; import { oneDark, coldarkDark } from 'react-syntax-highlighter/dist/esm/styles/prism'; import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; import copy from 'copy-to-clipboard'; import { CSSProperties, useContext } from 'react'; import { ChatContext } from '@/app/chat-context'; interface Props { code: string; language: string; customStyle?: CSSProperties; light?: { [key: string]: CSSProperties }; dark?: { [key: string]: CSSProperties }; } export function CodePreview({ code, light, dark, language, customStyle }: Props) { const { mode } = useContext(ChatContext); return (
); } ================================================ FILE: components/chat/chat-content/config.tsx ================================================ import { LinkOutlined, ReadOutlined, SyncOutlined } from '@ant-design/icons'; import ReactMarkdown from 'react-markdown'; import { Table, Image, Tag, Tabs, TabsProps, Popover } from 'antd'; import { format } from 'sql-formatter'; import { Reference } from '@/types/chat'; import { AutoChart, BackEndChartType, getChartType } from '@/components/chart'; import { CodePreview } from './code-preview'; import { Datum } from '@antv/ava'; import rehypeRaw from 'rehype-raw'; import { IChunk } from '@/types/knowledge'; import AgentPlans from './agent-plans'; import AgentMessages from './agent-messages'; import VisConvertError from './vis-convert-error'; import VisChart from './vis-chart'; import VisDashboard from './vis-dashboard'; import VisPlugin from './vis-plugin'; import VisCode from './vis-code'; type MarkdownComponent = Parameters['0']['components']; const customeTags: (keyof JSX.IntrinsicElements)[] = ['custom-view', 'chart-view', 'references', 'summary']; function matchCustomeTagValues(context: string) { const matchValues = customeTags.reduce((acc, tagName) => { const tagReg = new RegExp(`<${tagName}[^>]*\/?>`, 'gi'); context = context.replace(tagReg, (matchVal) => { acc.push(matchVal); return ''; }); return acc; }, []); return { context, matchValues }; } const basicComponents: MarkdownComponent = { code({ inline, node, className, children, style, ...props }) { const content = String(children); /** * @description * In some cases, tags are nested within code syntax, * so it is necessary to extract the tags present in the code block and render them separately. */ const { context, matchValues } = matchCustomeTagValues(content); const lang = className?.replace('language-', '') || 'javascript'; if (lang === 'agent-plans') { try { const data = JSON.parse(content) as Parameters[0]['data']; return ; } catch (e) { return ; } } if (lang === 'agent-messages') { try { const data = JSON.parse(content) as Parameters[0]['data']; return ; } catch (e) { return ; } } if (lang === 'vis-convert-error') { try { const data = JSON.parse(content) as Parameters[0]['data']; return ; } catch (e) { return ; } } if (lang === 'vis-dashboard') { try { const data = JSON.parse(content) as Parameters[0]['data']; return ; } catch (e) { return ; } } if (lang === 'vis-chart') { try { const data = JSON.parse(content) as Parameters[0]['data']; return ; } catch (e) { return ; } } if (lang === 'vis-plugin') { try { const data = JSON.parse(content) as Parameters[0]['data']; return ; } catch (e) { return ; } } if (lang === 'vis-code') { try { const data = JSON.parse(content) as Parameters[0]['data']; return ; } catch (e) { return ; } } return ( <> {!inline ? ( ) : ( {children} )} {matchValues.join('\n')} ); }, ul({ children }) { return
    {children}
; }, ol({ children }) { return
    {children}
; }, li({ children, ordered }) { return
  • {children}
  • ; }, table({ children }) { return (
    {children}
    ); }, thead({ children }) { return {children}; }, th({ children }) { return {children}; }, td({ children }) { return {children}; }, h1({ children }) { return

    {children}

    ; }, h2({ children }) { return

    {children}

    ; }, h3({ children }) { return

    {children}

    ; }, h4({ children }) { return

    {children}

    ; }, a({ children, href }) { return ( ); }, img({ src, alt }) { return (
    {alt}} color="processing"> Image Loading... } fallback="/images/fallback.png" />
    ); }, blockquote({ children }) { return (
    {children}
    ); }, }; const extraComponents: MarkdownComponent = { 'chart-view': function ({ content, children }) { let data: { data: Datum[]; type: BackEndChartType; sql: string; }; try { data = JSON.parse(content as string); } catch (e) { console.log(e, content); data = { type: 'response_table', sql: '', data: [], }; } const columns = data?.data?.[0] ? Object.keys(data?.data?.[0])?.map((item) => { return { title: item, dataIndex: item, key: item, }; }) : []; const ChartItem = { key: 'chart', label: 'Chart', children: , }; const SqlItem = { key: 'sql', label: 'SQL', children: , }; const DataItem = { key: 'data', label: 'Data', children: , }; const TabItems: TabsProps['items'] = data?.type === 'response_table' ? [DataItem, SqlItem] : [ChartItem, SqlItem, DataItem]; return (
    {children}
    ); }, references: function ({ title, references, children }) { let referenceData; // Low version compatibility, read data from children if (children) { try { referenceData = JSON.parse(children as string); title = referenceData.title; references = referenceData.references; } catch (error) { console.log('parse references failed', error); return

    Render Reference Error!

    ; } } else { // new version, read from tag props. try { references = JSON.parse(references as string); } catch (error) { console.log('parse references failed', error); return

    Render Reference Error!

    ; } } if (!references || references?.length < 1) { return null; } return (

    {title}

    {references.map((reference: Reference, index: number) => (
    [{index + 1}] {reference.name} {reference?.chunks?.map((chunk: IChunk | number, index) => ( {typeof chunk === 'object' ? (

    Content:

    {chunk?.content || 'No Content'}

    MetaData:

    {chunk?.meta_info || 'No MetaData'}

    Score:

    {chunk?.recall_score || ''}

    } title="Chunk Information" > {chunk?.id} ) : ( {chunk} )} {index < reference?.chunks.length - 1 && ,} ))}
    ))} ); }, summary: function ({ children }) { return (

    Document Summary

    {children}
    ); }, }; const markdownComponents = { ...basicComponents, ...extraComponents, }; export default markdownComponents; ================================================ FILE: components/chat/chat-content/index.tsx ================================================ import { PropsWithChildren, ReactNode, memo, useContext, useMemo } from 'react'; import { CheckOutlined, ClockCircleOutlined, CloseOutlined, CodeOutlined, LoadingOutlined, RobotOutlined, UserOutlined } from '@ant-design/icons'; import ReactMarkdown from 'react-markdown'; import { IChatDialogueMessageSchema } from '@/types/chat'; import rehypeRaw from 'rehype-raw'; import classNames from 'classnames'; import { Tag } from 'antd'; import { renderModelIcon } from '../header/model-selector'; import { ChatContext } from '@/app/chat-context'; import markdownComponents from './config'; interface Props { content: Omit & { context: | string | { template_name: string; template_introduce: string; }; }; isChartChat?: boolean; onLinkClick?: () => void; } type MarkdownComponent = Parameters['0']['components']; type DBGPTView = { name: string; status: 'todo' | 'runing' | 'failed' | 'completed' | (string & {}); result?: string; err_msg?: string; }; const pluginViewStatusMapper: Record = { todo: { bgClass: 'bg-gray-500', icon: , }, runing: { bgClass: 'bg-blue-500', icon: , }, failed: { bgClass: 'bg-red-500', icon: , }, completed: { bgClass: 'bg-green-500', icon: , }, }; function formatMarkdownVal(val: string) { return val .replaceAll('\\n', '\n') .replace(/]+)>/gi, '
    ') .replace(/]+)>/gi, ''); } function ChatContent({ children, content, isChartChat, onLinkClick }: PropsWithChildren) { const { scene } = useContext(ChatContext); const { context, model_name, role } = content; const isRobot = role === 'view'; const { relations, value, cachePluginContext } = useMemo<{ relations: string[]; value: string; cachePluginContext: DBGPTView[] }>(() => { if (typeof context !== 'string') { return { relations: [], value: '', cachePluginContext: [], }; } const [value, relation] = context.split('\trelations:'); const relations = relation ? relation.split(',') : []; const cachePluginContext: DBGPTView[] = []; let cacheIndex = 0; const result = value.replace(/]*>[^<]*<\/dbgpt-view>/gi, (matchVal) => { try { const pluginVal = matchVal.replaceAll('\n', '\\n').replace(/<[^>]*>|<\/[^>]*>/gm, ''); const pluginContext = JSON.parse(pluginVal) as DBGPTView; const replacement = `${cacheIndex}`; cachePluginContext.push({ ...pluginContext, result: formatMarkdownVal(pluginContext.result ?? ''), }); cacheIndex++; return replacement; } catch (e) { console.log((e as any).message, e); return matchVal; } }); return { relations, cachePluginContext, value: result, }; }, [context]); const extraMarkdownComponents = useMemo( () => ({ 'custom-view'({ children }) { const index = +children.toString(); if (!cachePluginContext[index]) { return children; } const { name, status, err_msg, result } = cachePluginContext[index]; const { bgClass, icon } = pluginViewStatusMapper[status] ?? {}; return (
    {name} {icon}
    {result ? (
    {result ?? ''}
    ) : (
    {err_msg}
    )}
    ); }, }), [context, cachePluginContext], ); if (!isRobot && !context) return
    ; return (
    {isRobot ? renderModelIcon(model_name) || : }
    {/* User Input */} {!isRobot && typeof context === 'string' && context} {/* Render Report */} {isRobot && isChartChat && typeof context === 'object' && (
    {`[${context.template_name}]: `} {context.template_introduce || 'More Details'}
    )} {/* Markdown */} {isRobot && typeof context === 'string' && ( {formatMarkdownVal(value)} )} {!!relations?.length && (
    {relations?.map((value, index) => ( {value} ))}
    )}
    {children}
    ); } export default memo(ChatContent); ================================================ FILE: components/chat/chat-content/vis-chart.tsx ================================================ import { BackEndChartType } from '@/components/chart'; import ChartView from './chart-view'; import { Datum } from '@antv/ava'; interface Props { data: { data: Datum[]; describe: string; title: string; type: BackEndChartType; sql: string; }; } function VisChart({ data }: Props) { return ; } export default VisChart; ================================================ FILE: components/chat/chat-content/vis-code.tsx ================================================ import ReactMarkdown from 'react-markdown'; import remarkGfm from 'remark-gfm'; import markdownComponents from './config'; import { CodePreview } from './code-preview'; import classNames from 'classnames'; import { useState } from 'react'; import { CheckOutlined, CloseOutlined } from '@ant-design/icons'; import { useTranslation } from 'react-i18next'; import { oneLight, oneDark } from 'react-syntax-highlighter/dist/esm/styles/prism'; interface Props { data: { code: string[]; exit_success: true; language: string; log: string; }; } function VisCode({ data }: Props) { const { t } = useTranslation(); const [show, setShow] = useState(0); return (
    {data.code.map((item, index) => (
    { setShow(index); }} > CODE {index + 1}: {item[0]}
    ))}
    {data.code.length && ( )}
    {t('Terminal')} {data.exit_success ? : }
    {data.log}
    ); } export default VisCode; ================================================ FILE: components/chat/chat-content/vis-convert-error.tsx ================================================ import { format } from 'sql-formatter'; import { CodePreview } from './code-preview'; interface Props { data: { display_type: string; sql: string; thought: string; }; } function VisConvertError({ data }: Props) { return (
    {data.display_type}
    {data.thought}
    ); } export default VisConvertError; ================================================ FILE: components/chat/chat-content/vis-dashboard.tsx ================================================ import { AutoChart, BackEndChartType, getChartType } from '@/components/chart'; import { Datum } from '@antv/ava'; import { useMemo } from 'react'; interface Props { data: { data: { data: Datum[]; describe: string; title: string; type: BackEndChartType; sql: string; }[]; title: string | null; display_strategy: string; chart_count: number; }; } const chartLayout = [[2], [1, 2], [1, 3], [2, 1, 2], [2, 1, 3], [3, 1, 3], [3, 2, 3]]; function VisDashboard({ data }: Props) { const charts = useMemo(() => { if (data.chart_count > 1) { const layout = chartLayout[data.chart_count - 2]; let prevIndex = 0; return layout.map((item) => { const items = data.data.slice(prevIndex, prevIndex + item); prevIndex = item; return items; }); } return [data.data]; }, [data.data, data.chart_count]); return (
    {charts.map((row, index) => (
    {row.map((chart, subIndex) => (
    {chart.title &&
    {chart.title}
    } {chart.describe &&
    {chart.describe}
    }
    ))}
    ))}
    ); } export default VisDashboard; ================================================ FILE: components/chat/chat-content/vis-plugin.tsx ================================================ import { CheckOutlined, ClockCircleOutlined, CloseOutlined, LoadingOutlined } from '@ant-design/icons'; import classNames from 'classnames'; import { ReactNode } from 'react'; import ReactMarkdown from 'react-markdown'; import markdownComponents from './config'; import rehypeRaw from 'rehype-raw'; interface IVisPlugin { name: string; args: { query: string; }; status: 'todo' | 'runing' | 'failed' | 'complete' | (string & {}); logo: string | null; result: string; err_msg: string | null; } interface Props { data: IVisPlugin; } const pluginViewStatusMapper: Record = { todo: { bgClass: 'bg-gray-500', icon: , }, runing: { bgClass: 'bg-blue-500', icon: , }, failed: { bgClass: 'bg-red-500', icon: , }, complete: { bgClass: 'bg-green-500', icon: , }, }; function VisPlugin({ data }: Props) { const { bgClass, icon } = pluginViewStatusMapper[data.status] ?? {}; return (
    {data.name} {icon}
    {data.result ? (
    {data.result ?? ''}
    ) : (
    {data.err_msg}
    )}
    ); } export default VisPlugin; ================================================ FILE: components/chat/chat-feedback.tsx ================================================ import React, { useState, useRef, useCallback, useEffect, useContext } from 'react'; import { MoreHoriz, CloseRounded } from '@mui/icons-material'; import { MenuButton, Button, Menu, MenuItem, Dropdown, Box, Grid, IconButton, Slider, Select, Option, Textarea, Typography, styled, Sheet, } from '@mui/joy'; import { message, Tooltip } from 'antd'; import { apiInterceptors, getChatFeedBackItme, postChatFeedBackForm } from '@/client/api'; import { ChatContext } from '@/app/chat-context'; import { ChatFeedBackSchema } from '@/types/db'; import { useTranslation } from 'react-i18next'; import { FeedBack } from '@/types/chat'; type Props = { conv_index: number; question: any; knowledge_space: string; select_param?: FeedBack; }; const ChatFeedback = ({ conv_index, question, knowledge_space, select_param }: Props) => { const { t } = useTranslation(); const { chatId } = useContext(ChatContext); const [ques_type, setQuesType] = useState(''); const [score, setScore] = useState(4); const [text, setText] = useState(''); const action = useRef(null); const [messageApi, contextHolder] = message.useMessage(); const handleOpenChange = useCallback( (event: any, isOpen: boolean) => { if (isOpen) { apiInterceptors(getChatFeedBackItme(chatId, conv_index)) .then((res) => { const finddata = res[1] ?? {}; setQuesType(finddata.ques_type ?? ''); setScore(parseInt(finddata.score ?? '4')); setText(finddata.messages ?? ''); }) .catch((err) => { console.log(err); }); } else { setQuesType(''); setScore(4); setText(''); } }, [chatId, conv_index], ); const marks = [ { value: 0, label: '0' }, { value: 1, label: '1' }, { value: 2, label: '2' }, { value: 3, label: '3' }, { value: 4, label: '4' }, { value: 5, label: '5' }, ]; function valueText(value: number) { return { 0: t('Lowest'), 1: t('Missed'), 2: t('Lost'), 3: t('Incorrect'), 4: t('Verbose'), 5: t('Best'), }[value]; } const Item = styled(Sheet)(({ theme }) => ({ backgroundColor: theme.palette.mode === 'dark' ? '#FBFCFD' : '#0E0E10', ...theme.typography['body-sm'], padding: theme.spacing(1), display: 'flex', alignItems: 'center', justifyContent: 'center', borderRadius: 4, width: '100%', height: '100%', })); const handleSubmit = (event: any) => { event.preventDefault(); const formData: ChatFeedBackSchema = { conv_uid: chatId, conv_index: conv_index, question: question, knowledge_space: knowledge_space, score: score, ques_type: ques_type, messages: text, }; console.log(formData); apiInterceptors( postChatFeedBackForm({ data: formData, }), ) .then((res) => { messageApi.open({ type: 'success', content: 'save success' }); }) .catch((err) => { messageApi.open({ type: 'error', content: 'save error' }); }); }; return ( {contextHolder}
    {t('Q_A_Category')}
    {t('feed_back_desc')}
    } variant="solid" placement="left" > {t('Q_A_Rating')} setScore(event.target?.value)} value={score} />