Showing preview only (443K chars total). Download the full file or copy to clipboard to get everything.
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: ''
---
<!--
Thanks for your interest in DB-GPT-Web! ❤️
Please check if there is no similar issue before creating this one.
-->
**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: ''
---
<!--
Thanks for your interest in DB-GPT-Web! ❤️
Please check if there is no similar issue before creating this one.
-->
### 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: ''
---
<!--
Thanks for your interest in Db-GPT-Web! ❤️
Please check if there is no similar issue before creating this one.
Please ask one question per issue.
-->
### 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
================================================
<h1 align="center">
<a href="https://dbgpt.site"><img width="96" src="https://github.com/eosphoros-ai/DB-GPT-Web/assets/10321453/062ee3ea-fac2-4437-a392-f4bc5451d116" alt="DB-GPT"></a>
<br>
DB-GPT-Web
</h1>
_<p align="center">DB-GPT Chat UI, LLM to Vision.</p>_
<p align="center">
<a href="https://github.com/eosphoros-ai/DB-GPT-Web/blob/main/LICENSE">
<img src="https://img.shields.io/badge/license-MIT-blue.svg?label=License&style=flat" />
</a>
<a href="https://github.com/eosphoros-ai/DB-GPT/releases">
<img alt="Release Notes" src="https://img.shields.io/github/release/eosphoros-ai/DB-GPT" />
</a>
<a href="https://github.com/eosphoros-ai/DB-GPT-Web/issues">
<img alt="Open Issues" src="https://img.shields.io/github/issues-raw/eosphoros-ai/DB-GPT-Web" />
</a>
<a href="https://discord.gg/7uQnPuveTY">
<img alt="Discord" src="https://dcbadge.vercel.app/api/server/7uQnPuveTY?compact=true&style=flat" />
</a>
</p>
---
## 👋 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<string>;
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<IChatContext>({
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<string>('');
const [isMenuExpand, setIsMenuExpand] = useState<boolean>(scene !== 'chat_dashboard');
const [dbParam, setDbParam] = useState<string>(db_param);
const [agent, setAgent] = useState<string>('');
const [history, setHistory] = useState<ChatHistoryResponse>([]);
const [docId, setDocId] = useState<number>();
const [mode, setMode] = useState<ThemeMode>('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 <ChatContext.Provider value={contextValue}>{children}</ChatContext.Provider>;
};
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<I18nKeys, string>;
}
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<T = any> = {
data: T;
err_code: string | null;
err_msg: string | null;
success: boolean;
};
export type ApiResponse<T = any, D = any> = AxiosResponse<ResponseType<T>, D>;
export type SuccessTuple<T = any, D = any> = [null, T, ResponseType<T>, ApiResponse<T, D>];
export type FailedTuple<T = any, D = any> = [Error | AxiosError<T, D>, 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 = <Params = any, Response = any, D = any>(url: string, params?: Params, config?: AxiosRequestConfig<D>) => {
return ins.get<Params, ApiResponse<Response>>(url, { params, ...config });
};
export const POST = <Data = any, Response = any, D = any>(url: string, data?: Data, config?: AxiosRequestConfig<D>) => {
return ins.post<Data, ApiResponse<Response>>(url, data, config);
};
export const PATCH = <Data = any, Response = any, D = any>(url: string, data?: Data, config?: AxiosRequestConfig<D>) => {
return ins.patch<Data, ApiResponse<Response>>(url, data, config);
};
export const PUT = <Data = any, Response = any, D = any>(url: string, data?: Data, config?: AxiosRequestConfig<D>) => {
return ins.put<Data, ApiResponse<Response>>(url, data, config);
};
export const DELETE = <Params = any, Response = any, D = any>(url: string, params?: Params, config?: AxiosRequestConfig<D>) => {
return ins.delete<Params, ApiResponse<Response>>(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<null, Array<SceneResponse>>('/api/v1/chat/dialogue/scenes');
};
export const newDialogue = (data: NewDialogueParam) => {
return POST<NewDialogueParam, IChatDialogueSchema>('/api/v1/chat/dialogue/new', data);
};
/** Database Page */
export const getDbList = () => {
return GET<null, DbListResponse>('/api/v1/chat/db/list');
};
export const getDbSupportType = () => {
return GET<null, DbSupportTypeResponse>('/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<PostDbParams, null>('/api/v1/chat/db/edit', data);
};
export const postDbAdd = (data: PostDbParams) => {
return POST<PostDbParams, null>('/api/v1/chat/db/add', data);
};
export const postDbTestConnect = (data: PostDbParams) => {
return POST<PostDbParams, null>('/api/v1/chat/db/test/connect', data);
};
/** Chat Page */
export const getDialogueList = () => {
return GET<null, DialogueListResponse>('/api/v1/chat/dialogue/list');
};
export const getUsableModels = () => {
return GET<null, Array<string>>('/api/v1/model/types');
};
export const postChatModeParamsList = (chatMode: string) => {
return POST<null, IDB[]>(`/api/v1/chat/mode/params/list?chat_mode=${chatMode}`);
};
export const postChatModeParamsInfoList = (chatMode: string) => {
return POST<null, Record<string, string>>(`/api/v1/chat/mode/params/info?chat_mode=${chatMode}`);
};
export const getChatHistory = (convId: string) => {
return GET<null, ChatHistoryResponse>(`/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<AxiosRequestConfig, 'headers'>;
}) => {
return POST<FormData, ChatHistoryResponse>(
`/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<null, GetEditorSQLRoundRequest>(`/api/v1/editor/sql/rounds?con_uid=${id}`);
};
export const postEditorSqlRun = (data: PostEditorSQLRunParams) => {
return POST<PostEditorSQLRunParams>(`/api/v1/editor/sql/run`, data);
};
export const postEditorChartRun = (data: PostEditorChartRunParams) => {
return POST<PostEditorChartRunParams, PostEditorChartRunResponse>(`/api/v1/editor/chart/run`, data);
};
export const postSqlEditorSubmit = (data: PostSQLEditorSubmitParams) => {
return POST<PostSQLEditorSubmitParams>(`/api/v1/sql/editor/submit`, data);
};
export const getEditorSql = (id: string, round: string | number) => {
return POST<GetEditorySqlParams, string | Array<any>>('/api/v1/editor/sql', { con_uid: id, round });
};
/** knowledge */
export const getArguments = (knowledgeName: string) => {
return POST<any, IArguments>(`/knowledge/${knowledgeName}/arguments`, {});
};
export const saveArguments = (knowledgeName: string, data: ArgumentsParams) => {
return POST<ArgumentsParams, IArguments>(`/knowledge/${knowledgeName}/argument/save`, data);
};
export const getSpaceList = () => {
return POST<any, Array<ISpace>>('/knowledge/space/list', {});
};
export const getDocumentList = (spaceName: string, data: Record<string, number | Array<number>>) => {
return POST<Record<string, number | Array<number>>, IDocumentResponse>(`/knowledge/${spaceName}/document/list`, data);
};
export const addDocument = (knowledgeName: string, data: DocumentParams) => {
return POST<DocumentParams, number>(`/knowledge/${knowledgeName}/document/add`, data);
};
export const addSpace = (data: AddKnowledgeParams) => {
return POST<AddKnowledgeParams, Array<any>>(`/knowledge/space/add`, data);
};
export const getChunkStrategies = () => {
return GET<null, Array<IChunkStrategyResponse>>('/knowledge/document/chunkstrategies');
};
export const syncDocument = (spaceName: string, data: Record<string, Array<number>>) => {
return POST<Record<string, Array<number>>, string | null>(`/knowledge/${spaceName}/document/sync`, data);
};
export const syncBatchDocument = (spaceName: string, data: Array<ISyncBatchParameter>) => {
return POST<Array<ISyncBatchParameter>, ISyncBatchResponse>(`/knowledge/${spaceName}/document/sync_batch`, data);
};
export const uploadDocument = (knowLedgeName: string, data: FormData) => {
return POST<FormData, number>(`/knowledge/${knowLedgeName}/document/upload`, data);
};
export const getChunkList = (spaceName: string, data: ChunkListParams) => {
return POST<ChunkListParams, IChunkList>(`/knowledge/${spaceName}/chunk/list`, data);
};
export const delDocument = (spaceName: string, data: Record<string, string>) => {
return POST<Record<string, string>, null>(`/knowledge/${spaceName}/document/delete`, data);
};
export const delSpace = (data: Record<string, string>) => {
return POST<Record<string, string>, null>(`/knowledge/space/delete`, data);
};
/** models */
export const getModelList = () => {
return GET<null, Array<IModelData>>('/api/v1/worker/model/list');
};
export const stopModel = (data: BaseModelParams) => {
return POST<BaseModelParams, boolean>('/api/v1/worker/model/stop', data);
};
export const startModel = (data: StartModelParams) => {
return POST<StartModelParams, boolean>('/api/v1/worker/model/start', data);
};
export const getSupportModels = () => {
return GET<null, Array<SupportModel>>('/api/v1/worker/model/params');
};
/** Agent */
export const postAgentQuery = (data: PostAgentQueryParams) => {
return POST<PostAgentQueryParams, PostAgentPluginResponse>('/api/v1/agent/query', data);
};
export const postAgentHubUpdate = (data?: PostAgentHubUpdateParams) => {
return POST<PostAgentHubUpdateParams>('/api/v1/agent/hub/update', data ?? { channel: '', url: '', branch: '', authorization: '' });
};
export const postAgentMy = (user?: string) => {
return POST<undefined, PostAgentMyPluginResponse>('/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<AxiosRequestConfig, 'headers'>) => {
return POST<FormData>('/api/v1/personal/agent/upload', data, {
params: { user },
headers: {
'Content-Type': 'multipart/form-data',
},
...config,
});
};
export const getDbgptsList = () => {
return GET<undefined, GetDBGPTsListResponse>('/api/v1/dbgpts/list');
};
/** chat feedback **/
export const getChatFeedBackSelect = () => {
return GET<null, FeedBack>(`/api/v1/feedback/select`, undefined);
};
export const getChatFeedBackItme = (conv_uid: string, conv_index: number) => {
return GET<null, Record<string, string>>(`/api/v1/feedback/find?conv_uid=${conv_uid}&conv_index=${conv_index}`, undefined);
};
export const postChatFeedBackForm = ({ data, config }: { data: ChatFeedBackSchema; config?: Omit<AxiosRequestConfig, 'headers'> }) => {
return POST<ChatFeedBackSchema, any>(`/api/v1/feedback/commit`, data, {
headers: {
'Content-Type': 'application/json',
},
...config,
});
};
/** prompt */
export const getPromptList = (data: PromptParams) => {
return POST<PromptParams, Array<IPrompt>>('/prompt/list', data);
};
export const updatePrompt = (data: UpdatePromptParams) => {
return POST<UpdatePromptParams, []>('/prompt/update', data);
};
export const addPrompt = (data: UpdatePromptParams) => {
return POST<UpdatePromptParams, []>('/prompt/add', data);
};
/** AWEL Flow */
export const addFlow = (data: IFlowUpdateParam) => {
return POST<IFlowUpdateParam, IFlow>('/api/v1/serve/awel/flows', data);
};
export const getFlows = () => {
return GET<null, IFlowResponse>('/api/v1/serve/awel/flows');
};
export const getFlowById = (id: string) => {
return GET<null, IFlow>(`/api/v1/serve/awel/flows/${id}`);
};
export const updateFlowById = (id: string, data: IFlowUpdateParam) => {
return PUT<IFlowUpdateParam, IFlow>(`/api/v1/serve/awel/flows/${id}`, data);
};
export const deleteFlowById = (id: string) => {
return DELETE<null, null>(`/api/v1/serve/awel/flows/${id}`);
};
export const getFlowNodes = () => {
return GET<null, Array<IFlowNode>>(`/api/v1/serve/awel/nodes`);
};
/** app */
export const addApp = (data: IApp) => {
return POST<IApp, []>('/api/v1/app/create', data);
};
export const getAppList = (data: Record<string, string>) => {
return POST<Record<string, string>, IAppData>('/api/v1/app/list', data);
};
export const collectApp = (data: Record<string, string>) => {
return POST<Record<string, string>, []>('/api/v1/app/collect', data);
};
export const unCollectApp = (data: Record<string, string>) => {
return POST<Record<string, string>, []>('/api/v1/app/uncollect', data);
};
export const delApp = (data: Record<string, string>) => {
return POST<Record<string, string>, []>('/api/v1/app/remove', data);
};
export const getAgents = () => {
return GET<object, IAgent[]>('/api/v1/agents/list', {});
};
export const getTeamMode = () => {
return GET<null, string[]>('/api/v1/team-mode/list');
};
export const getResourceType = () => {
return GET<null, string[]>('/api/v1/resource-type/list');
};
export const getResource = (data: Record<string, string>) => {
return GET<Record<string, string>, []>(`/api/v1/app/resources/list?type=${data.type}`);
};
export const updateApp = (data: IApp) => {
return POST<IApp, []>('/api/v1/app/edit', data);
};
export const getAppStrategy = () => {
return GET<null, []>(`/api/v1/llm-strategy/list`);
};
export const getAppStrategyValues = (type: string) => {
return GET<string, []>(`/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 = <T = any, D = any>(promise: Promise<ApiResponse<T, D>>, ignoreCodes?: '*' | (number | string)[]) => {
return promise
.then<SuccessTuple<T, D>>((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<FailedTuple<T, D>>((err: Error | AxiosError<T, D>) => {
let errMessage = err.message;
if (err instanceof AxiosError) {
try {
const { err_msg } = JSON.parse(err.request.response) as ResponseType<null>;
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<number | undefined>();
const [form] = Form.useForm<PostAgentQueryParams['filter']>();
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 <LoadingOutlined />;
}
return agent.installed ? (
<Tooltip title="Uninstall">
<div
className="w-full h-full"
onClick={() => {
pluginAction(agent.name, index, false);
}}
>
<ClearOutlined />
</div>
</Tooltip>
) : (
<Tooltip title="Install">
<div
className="w-full h-full"
onClick={() => {
pluginAction(agent.name, index, true);
}}
>
<DownloadOutlined />
</div>
</Tooltip>
);
},
[actionIndex, pluginAction],
);
return (
<Spin spinning={loading}>
<Form form={form} layout="inline" onFinish={refresh} className="mb-2">
<Form.Item className="!mb-2" name="name" label={'Name'}>
<Input allowClear className="w-48" />
</Form.Item>
<Form.Item>
<Button className="mr-2" type="primary" htmlType="submit" icon={<SearchOutlined />}>
{t('Search')}
</Button>
<Button loading={uploading} type="primary" icon={<SyncOutlined />} onClick={updateFromGithub}>
{t('Update_From_Github')}
</Button>
</Form.Item>
</Form>
{!agents.length && !loading && <MyEmpty error={isError} refresh={refresh} />}
<div className="flex flex-wrap gap-2 md:gap-4">
{agents.map((agent, index) => (
<Card
className="w-full md:w-1/2 lg:w-1/3 xl:w-1/4"
key={agent.id}
actions={[
renderAction(agent, index),
<Tooltip key="github" title="Github">
<div
className="w-full h-full"
onClick={() => {
window.open(agent.storage_url, '_blank');
}}
>
<GithubOutlined />
</div>
</Tooltip>,
]}
>
<Tooltip title={agent.name}>
<h2 className="mb-2 text-base font-semibold line-clamp-1">{agent.name}</h2>
</Tooltip>
{agent.author && <Tag>{agent.author}</Tag>}
{agent.version && <Tag>v{agent.version}</Tag>}
{agent.type && <Tag>Type {agent.type}</Tag>}
{agent.storage_channel && <Tag>{agent.storage_channel}</Tag>}
<Tooltip title={agent.description}>
<p className="mt-2 line-clamp-2 text-gray-400 text-sm">{agent.description}</p>
</Tooltip>
</Card>
))}
</div>
</Spin>
);
}
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<number | undefined>();
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 <LoadingOutlined />;
}
return (
<Tooltip title="Uninstall">
<div
className="w-full h-full"
onClick={() => {
uninstall(item.name, index);
}}
>
<ClearOutlined />
</div>
</Tooltip>
);
},
[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 (
<Spin spinning={loading}>
{contextHolder}
<div>
<Upload
disabled={loading}
className="mr-1"
beforeUpload={() => false}
name="file"
accept=".zip,.rar"
multiple={false}
onChange={onChange}
showUploadList={{
showDownloadIcon: false,
showPreviewIcon: false,
showRemoveIcon: false,
}}
itemRender={() => <></>}
>
<Button loading={uploading} type="primary" icon={<UploadOutlined />}>
{t('Upload')}
</Button>
</Upload>
</div>
{!data.length && !loading && <MyEmpty error={isError} refresh={refresh} />}
<div className="flex gap-2 md:gap-4">
{data.map((item, index) => (
<Card className="w-full md:w-1/2 lg:w-1/3 xl:w-1/4" key={item.id} actions={[renderAction(item, index)]}>
<Tooltip title={item.name}>
<h2 className="mb-2 text-base font-semibold line-clamp-1">{item.name}</h2>
</Tooltip>
{item.version && <Tag>v{item.version}</Tag>}
{item.type && <Tag>Type {item.type}</Tag>}
<Tooltip title={item.description}>
<p className="mt-2 line-clamp-2 text-gray-400 text-sm">{item.description}</p>
</Tooltip>
</Card>
))}
</div>
</Spin>
);
}
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<any>([...(editResources ?? [])]);
const [agent, setAgent] = useState<any>({ ...detail, resources: [] });
const [strategyOptions, setStrategyOptions] = useState<any>([]);
const [strategyValueOptions, setStrategyValueOptions] = useState<any>([]);
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 (
<div>
<div className="flex items-center mb-6 mt-6">
<div className="mr-2 w-16 text-center">{t('Prompt')}:</div>
<Input
required
className="mr-6 w-1/4"
value={agent.prompt_template}
onChange={(e) => {
updateAgent(e.target.value, 'prompt_template');
}}
/>
<div className="mr-2">{t('LLM_strategy')}:</div>
<Select
value={agent.llm_strategy}
options={strategyOptions}
className="w-1/6 mr-6"
onChange={(value) => {
updateAgent(value, 'llm_strategy');
getStrategyValues(value);
}}
/>
{strategyValueOptions && strategyValueOptions.length > 0 && (
<>
<div className="mr-2">{t('LLM_strategy_value')}:</div>
<Select
value={formatStrategyValue(agent.llm_strategy_value)}
className="w-1/4"
mode="multiple"
options={strategyValueOptions}
onChange={(value) => {
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');
}}
/>
</>
)}
</div>
<div className="mb-3 text-lg font-bold">{t('available_resources')}</div>
{resources.map((resource: any, index: number) => {
return (
<ResourceCard
resource={resource}
key={index}
index={index}
updateResourcesByIndex={updateResourcesByIndex}
resourceTypeOptions={resourceTypeOptions}
/>
);
})}
<Button type="primary" className="mt-2" size="middle" onClick={handelAdd}>
{t('add_resource')}
</Button>
</div>
);
}
================================================
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<string>(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: <WarningOutlined />,
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 (
<GPTCard
title={app.app_name}
icon={'/icons/node/vis.png'}
iconBorder={false}
desc={app.app_describe}
tags={[
{ text: languageMap[app.language], color: 'default' },
{ text: app.team_mode, color: 'default' },
]}
onClick={() => {
handleEdit(app);
}}
operations={[
{
label: t('Chat'),
children: <MessageFilled />,
onClick: handleChat,
},
{
label: t('collect'),
children: <StarFilled className={app.is_collected === 'false' ? 'text-gray-400' : 'text-yellow-400'} />,
onClick: collect,
},
{
label: t('Delete'),
children: <DeleteFilled />,
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<boolean>(false);
const [activeKey, setActiveKey] = useState<string>();
const [teamModal, setTeamModal] = useState<{ label: string; value: string }[]>();
const [agents, setAgents] = useState<TabsProps['items']>([]);
const [dropItems, setDropItems] = useState<IAgent[]>([]);
const [details, setDetails] = useState<IDetail[]>([...(app?.details || [])]);
const [flow, setFlow] = useState<IFlow>();
const [resourceTypes, setResourceTypes] = useState<string[]>();
const [curTeamModal, setCurTeamModal] = useState<TeamModals>(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: (
<AgentPanel
editResources={type === 'edit' && item.resources}
detail={{
key: item?.agent_name,
llm_strategy: item?.llm_strategy,
agent_name: item?.agent_name,
prompt_template: item?.prompt_template,
llm_strategy_value: item?.llm_strategy_value,
}}
updateDetailsByAgentKey={updateDetailsByAgentKey}
resourceTypes={resourceType}
/>
),
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: (
<AgentPanel
detail={{ key: newActiveKey, llm_strategy: 'default', agent_name: newActiveKey, prompt_template: '', llm_strategy_value: null }}
updateDetailsByAgentKey={updateDetailsByAgentKey}
resourceTypes={data}
/>
),
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 (
<Dropdown menu={{ items: dropItems }} trigger={['click']}>
<a className="h-8 flex items-center" onClick={(e) => e.preventDefault()}>
<Space>
<AddIcon />
</Space>
</a>
</Dropdown>
);
};
return (
<div>
<Modal
okText={t('Submit')}
title={type === 'edit' ? t('edit_application') : t('add_application')}
open={open}
width={'65%'}
onCancel={handleCancel}
onOk={handleSubmit}
destroyOnClose={true}
>
<Spin spinning={spinning}>
<Form
form={form}
preserve={false}
size="large"
className="mt-4 max-h-[70vh] overflow-auto h-[90vh]"
layout="horizontal"
labelAlign="left"
labelCol={{ span: 4 }}
initialValues={{
app_name: app.app_name,
app_describe: app.app_describe,
language: app.language || languageOptions[0].value,
team_mode: app.team_mode || 'auto_plan',
}}
autoComplete="off"
onFinish={handleSubmit}
>
<Form.Item<FieldType> label={t('app_name')} name="app_name" rules={[{ required: true, message: t('Please_input_the_name') }]}>
<Input placeholder={t('Please_input_the_name')} />
</Form.Item>
<Form.Item<FieldType>
label={t('Description')}
name="app_describe"
rules={[{ required: true, message: t('Please_input_the_description') }]}
>
<Input.TextArea rows={3} placeholder={t('Please_input_the_description')} />
</Form.Item>
<div className="flex w-full">
<Form.Item<FieldType> labelCol={{ span: 7 }} label={t('language')} name="language" className="w-1/2" rules={[{ required: true }]}>
<Select className="w-2/3 ml-4" placeholder={t('language_select_tips')} options={languageOptions} />
</Form.Item>
<Form.Item<FieldType> label={t('team_modal')} name="team_mode" className="w-1/2" labelCol={{ span: 6 }} rules={[{ required: true }]}>
<Select
defaultValue={app.team_mode || 'auto_plan'}
className="ml-4 w-72"
onChange={handleTeamModalChange}
placeholder={t('Please_input_the_work_modal')}
options={teamModal}
/>
</Form.Item>
</div>
{curTeamModal !== 'awel_layout' ? (
<>
<div className="mb-5">{t('Agents')}</div>
<Tabs addIcon={renderAddIcon()} type="editable-card" onChange={onChange} activeKey={activeKey} onEdit={onEdit} items={agents} />
</>
) : (
<DagLayout onFlowsChange={handleFlowsChange} teamContext={app.team_context} />
)}
</Form>
</Spin>
</Modal>
</div>
);
}
================================================
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<IFlow[]>();
const [flowsOptions, setFlowsOptions] = useState<any>();
const [curFlow, setCurFlow] = useState<IFlow>();
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 (
<div className="w-full h-[300px]">
<div className="mr-24 mb-4 mt-2">Flows:</div>
<div className="flex items-center mb-6">
<Select onChange={handleFlowsChange} value={curFlow?.name || flowsOptions?.[0]?.value} className="w-1/4" options={flowsOptions}></Select>
<Link href="/flow/canvas/" className="ml-6">
{t('edit_new_applications')}
</Link>
<div className="text-gray-500 ml-16">{curFlow?.description}</div>
</div>
{curFlow && (
<div className="w-full h-full border-[0.5px] border-dark-gray">
<PreviewFlow flowData={curFlow?.flow_data} />
</div>
)}
</div>
);
}
================================================
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<string>(editResource.type || resourceTypeOptions?.[0].label);
const [resourceValueOptions, setResourceValueOptions] = useState<any[]>([]);
const [resource, setResource] = useState<any>({
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 (
<Card
className="mb-3 dark:bg-[#232734] border-gray-200"
title={`${t('resource')} ${index + 1}`}
extra={
<DeleteFilled
className="text-[#ff1b2e] !text-lg"
onClick={() => {
handleDeleteResource();
}}
/>
}
>
<div className="flex-1">
<div className="flex items-center mb-6">
<div className="font-bold mr-4 w-32 text-center">
<span className="text-[#ff4d4f] font-normal">*</span> {t('resource_name')}:
</div>
<Input
className="w-1/3"
required
value={resource.name}
onInput={(e: React.ChangeEvent<HTMLInputElement>) => {
updateResource(e.target.value, 'name');
}}
/>
<div className="flex items-center">
<div className="font-bold w-32 text-center">{t('resource_dynamic')}</div>
<Switch
defaultChecked={editResource.is_dynamic || false}
style={{ background: resource.is_dynamic ? '#1677ff' : '#ccc' }}
onChange={(value) => {
updateResource(value, 'is_dynamic');
}}
/>
</div>
</div>
<div className="flex mb-5 items-center">
<div className="font-bold mr-4 w-32 text-center">{t('resource_type')}: </div>
<Select
className="w-1/3"
options={resourceTypeOptions}
value={resource.type || resourceTypeOptions?.[0]}
onChange={(value) => {
updateResource(value, 'type');
handleChange(value);
}}
/>
<div className="font-bold w-32 text-center">{t('resource_value')}:</div>
{resourceValueOptions?.length > 0 ? (
<Select
value={resource.value}
className="flex-1"
options={resourceValueOptions}
onChange={(value) => {
updateResource(value, 'value');
}}
/>
) : (
<Input
className="flex-1"
value={resource.value || editResource.value}
onInput={(e: React.ChangeEvent<HTMLInputElement>) => {
updateResource(e.target.value, 'value');
}}
/>
)}
</div>
</div>
</Card>
);
}
================================================
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<AdvisorConfig['ckbCfg']>;
customRule?: Partial<AdvisorConfig['ruleCfg']>;
};
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<Advisor>();
const [advices, setAdvices] = useState<Advice[]>([]);
const [renderChartType, setRenderChartType] = useState<ChartType>();
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 (
<Chart
key={chartTypeInput}
options={{
...spec,
theme: mode,
autoFit: true,
height: 300,
}}
/>
);
}
}
}, [advices, renderChartType]);
if (renderChartType) {
return (
<div>
<Row justify="start" className="mb-2">
<Col>{i18n.t('Advices')}</Col>
<Col style={{ marginLeft: 24 }}>
<Select
className="w-52"
value={renderChartType}
placeholder={'Chart Switcher'}
onChange={(value) => setRenderChartType(value)}
size={'small'}
>
{advices?.map((item) => {
const name = i18n.t(item.type as I18nKeys);
return (
<Option key={item.type} value={item.type}>
<Tooltip title={name} placement={'right'}>
<div>{name}</div>
</Tooltip>
</Option>
);
})}
</Select>
</Col>
</Row>
<div className="auto-chart-content">{visComponent}</div>
</div>
);
}
return <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description={'暂无合适的可视化视图'} />;
};
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 (
<div className="flex-1 min-w-0 p-4 bg-white dark:bg-theme-dark-container rounded">
<div className="h-full">
<div className="mb-2">{chart.chart_name}</div>
<div className="opacity-80 text-sm mb-2">{chart.chart_desc}</div>
<div className="h-[300px]">
<Chart
style={{ height: '100%' }}
options={{
autoFit: true,
theme: mode,
type: 'interval',
data: chart.values,
encode: { x: 'name', y: 'value', color: 'type' },
axis: {
x: {
labelAutoRotate: false,
},
},
}}
/>
</div>
</div>
</div>
);
}
================================================
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<ChartData>;
};
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 (
<div className="flex flex-col gap-3">
{chartRows?.map((chartRow, index) => (
<div key={`chart_row_${index}`} className={`${chartRow?.type !== 'IndicatorValue' ? 'flex gap-3' : ''}`}>
{chartRow.charts.map((chart) => {
if (chart.chart_type === 'IndicatorValue') {
return (
<div key={chart.chart_uid} className="flex flex-row gap-3">
{chart.values.map((item) => (
<div key={item.name} className="flex-1">
<Card sx={{ background: 'transparent' }}>
<CardContent className="justify-around">
<Typography gutterBottom component="div">
{item.name}
</Typography>
<Typography>{item.value}</Typography>
</CardContent>
</Card>
</div>
))}
</div>
);
} else if (chart.chart_type === 'LineChart') {
return <LineChart key={chart.chart_uid} chart={chart} />;
} else if (chart.chart_type === 'BarChart') {
return <BarChart key={chart.chart_uid} chart={chart} />;
} else if (chart.chart_type === 'Table') {
return <TableChart key={chart.chart_uid} chart={chart} />;
}
})}
</div>
))}
</div>
);
}
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 (
<div className="flex-1 min-w-0 p-4 bg-white dark:bg-theme-dark-container rounded">
<div className="h-full">
<div className="mb-2">{chart.chart_name}</div>
<div className="opacity-80 text-sm mb-2">{chart.chart_desc}</div>
<div className="h-[300px]">
<Chart
style={{ height: '100%' }}
options={{
autoFit: true,
theme: mode,
type: 'view',
data: chart.values,
children: [
{
type: 'line',
encode: {
x: 'name',
y: 'value',
color: 'type',
shape: 'smooth',
},
},
{
type: 'area',
encode: {
x: 'name',
y: 'value',
color: 'type',
shape: 'smooth',
},
legend: false,
style: {
fillOpacity: 0.15,
},
},
],
axis: {
x: {
labelAutoRotate: false,
},
},
}}
/>
</div>
</div>
</div>
);
}
================================================
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 (
<div className="flex-1 min-w-0 p-4 bg-white dark:bg-theme-dark-container rounded">
<div className="h-full">
<div className="mb-2">{chart.chart_name}</div>
<div className="opacity-80 text-sm mb-2">{chart.chart_desc}</div>
<div className="flex-1">
<Table aria-label="basic table" stripe="odd" hoverRow borderAxis="bothBetween">
<thead>
<tr>
{Object.keys(data).map((key) => (
<th key={key}>{key}</th>
))}
</tr>
</thead>
<tbody>
{Object.values(data)?.[0]?.map((value, i) => (
<tr key={i}>
{Object.keys(data)?.map((k) => (
<td key={k}>{data?.[k]?.[i].value || ''}</td>
))}
</tr>
))}
</tbody>
</Table>
</div>
</div>
</div>
);
}
================================================
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(/<table(\w*=[^>]+)>/gi, '<table $1>').replace(/<tr(\w*=[^>]+)>/gi, '<tr $1>');
}
function AgentContent({ content }: Props) {
const { scene } = useContext(ChatContext);
const isView = content.role === 'view';
return (
<div
className={classNames('relative w-full p-2 md:p-4 rounded-xl break-words', {
'bg-white dark:bg-[#232734]': isView,
'lg:w-full xl:w-full pl-0': ['chat_with_db_execute', 'chat_dashboard'].includes(scene),
})}
>
{isView ? (
<ReactMarkdown components={markdownComponents} rehypePlugins={[rehypeRaw]}>
{formatMarkdownVal(content.context)}
</ReactMarkdown>
) : (
<div className="">{content.context}</div>
)}
</div>
);
}
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<boolean>(false);
const [chartsData, setChartsData] = useState<Array<ChartData>>();
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<string, any>) => {
return new Promise<void>((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 (
<>
<MuiLoading visible={loading} />
<Header
refreshHistory={getHistory}
modelChange={(newModel: string) => {
setModel(newModel);
}}
/>
<div className="px-4 flex flex-1 flex-wrap overflow-hidden relative">
{!!chartsData?.length && (
<div className="w-full pb-4 xl:w-3/4 h-3/5 xl:pr-4 xl:h-full overflow-y-auto">
<Chart chartsData={chartsData} />
</div>
)}
{!chartsData?.length && scene === 'chat_dashboard' && (
<Empty
image="/empty.png"
imageStyle={{ width: 320, height: 320, margin: '0 auto', maxWidth: '100%', maxHeight: '100%' }}
className="w-full xl:w-3/4 h-3/5 xl:h-full pt-0 md:pt-10"
/>
)}
{/** chat panel */}
<div
className={classNames('flex flex-1 flex-col overflow-hidden', {
'px-0 xl:pl-4 h-2/5 w-full xl:w-auto xl:h-full border-t xl:border-t-0 xl:border-l dark:border-gray-800': scene === 'chat_dashboard',
'h-full lg:px-8': scene !== 'chat_dashboard',
})}
>
<Completion messages={history} onSubmit={handleChat} />
</div>
</div>
</>
);
};
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) => (
<div key={index} className="rounded my-4 md:my-6">
<div className="flex items-center mb-3 text-sm">
{item.model ? renderModelIcon(item.model) : <div className="rounded-full w-6 h-6 bg-gray-100" />}
<div className="ml-2 opacity-70">
{item.sender}
<SwapRightOutlined className="mx-2 text-base" />
{item.receiver}
</div>
</div>
<div className="whitespace-normal text-sm">
<ReactMarkdown components={markdownComponents}>{item.markdown}</ReactMarkdown>
</div>
</div>
))}
</>
);
}
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 (
<Collapse
bordered
className="my-3"
expandIcon={({ isActive }) => <CaretRightOutlined rotate={isActive ? 90 : 0} />}
items={data.map((item, index) => {
return {
key: index,
label: (
<div className="whitespace-normal">
<span>
{item.name} - {item.agent}
</span>
{item.status === 'complete' ? (
<CheckOutlined className="!text-green-500 ml-2" />
) : (
<ClockCircleOutlined className="!text-gray-500 ml-2" />
)}
</div>
),
children: <ReactMarkdown components={markdownComponents}>{item.markdown}</ReactMarkdown>,
};
})}
/>
);
}
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: <AutoChart data={data} chartType={getChartType(type)} />,
};
const SqlItem = {
key: 'sql',
label: 'SQL',
children: <CodePreview language="sql" code={format(sql ?? '', { language: 'mysql' }) as string} />,
};
const DataItem = {
key: 'data',
label: 'Data',
children: <Table dataSource={data} columns={columns} scroll={{ x: 'auto' }} />,
};
const TabItems: TabsProps['items'] = type === 'response_table' ? [DataItem, SqlItem] : [ChartItem, SqlItem, DataItem];
return <Tabs defaultActiveKey={type === 'response_table' ? 'data' : 'chart'} items={TabItems} size="small" />;
}
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 (
<div className="relative">
<Button
className="absolute right-3 top-2 text-gray-300 hover:!text-gray-200 bg-gray-700"
type="text"
icon={<CopyOutlined />}
onClick={() => {
const success = copy(code);
message[success ? 'success' : 'error'](success ? 'Copy success' : 'Copy failed');
}}
/>
<SyntaxHighlighter customStyle={customStyle} language={language} style={mode === 'dark' ? dark ?? coldarkDark : light ?? oneDark}>
{code}
</SyntaxHighlighter>
</div>
);
}
================================================
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<typeof ReactMarkdown>['0']['components'];
const customeTags: (keyof JSX.IntrinsicElements)[] = ['custom-view', 'chart-view', 'references', 'summary'];
function matchCustomeTagValues(context: string) {
const matchValues = customeTags.reduce<string[]>((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<typeof AgentPlans>[0]['data'];
return <AgentPlans data={data} />;
} catch (e) {
return <CodePreview language={lang} code={content} />;
}
}
if (lang === 'agent-messages') {
try {
const data = JSON.parse(content) as Parameters<typeof AgentMessages>[0]['data'];
return <AgentMessages data={data} />;
} catch (e) {
return <CodePreview language={lang} code={content} />;
}
}
if (lang === 'vis-convert-error') {
try {
const data = JSON.parse(content) as Parameters<typeof VisConvertError>[0]['data'];
return <VisConvertError data={data} />;
} catch (e) {
return <CodePreview language={lang} code={content} />;
}
}
if (lang === 'vis-dashboard') {
try {
const data = JSON.parse(content) as Parameters<typeof VisDashboard>[0]['data'];
return <VisDashboard data={data} />;
} catch (e) {
return <CodePreview language={lang} code={content} />;
}
}
if (lang === 'vis-chart') {
try {
const data = JSON.parse(content) as Parameters<typeof VisChart>[0]['data'];
return <VisChart data={data} />;
} catch (e) {
return <CodePreview language={lang} code={content} />;
}
}
if (lang === 'vis-plugin') {
try {
const data = JSON.parse(content) as Parameters<typeof VisPlugin>[0]['data'];
return <VisPlugin data={data} />;
} catch (e) {
return <CodePreview language={lang} code={content} />;
}
}
if (lang === 'vis-code') {
try {
const data = JSON.parse(content) as Parameters<typeof VisCode>[0]['data'];
return <VisCode data={data} />;
} catch (e) {
return <CodePreview language={lang} code={content} />;
}
}
return (
<>
{!inline ? (
<CodePreview code={context} language={lang} />
) : (
<code {...props} style={style} className="p-1 mx-1 rounded bg-theme-light dark:bg-theme-dark text-sm">
{children}
</code>
)}
<ReactMarkdown components={markdownComponents} rehypePlugins={[rehypeRaw]}>
{matchValues.join('\n')}
</ReactMarkdown>
</>
);
},
ul({ children }) {
return <ul className="py-1">{children}</ul>;
},
ol({ children }) {
return <ol className="py-1">{children}</ol>;
},
li({ children, ordered }) {
return <li className={`text-sm leading-7 ml-5 pl-2 text-gray-600 dark:text-gray-300 ${ordered ? 'list-decimal' : 'list-disc'}`}>{children}</li>;
},
table({ children }) {
return (
<table className="my-2 rounded-tl-md rounded-tr-md max-w-full bg-white dark:bg-gray-800 text-sm rounded-lg overflow-hidden">{children}</table>
);
},
thead({ children }) {
return <thead className="bg-[#fafafa] dark:bg-black font-semibold">{children}</thead>;
},
th({ children }) {
return <th className="!text-left p-4">{children}</th>;
},
td({ children }) {
return <td className="p-4 border-t border-[#f0f0f0] dark:border-gray-700">{children}</td>;
},
h1({ children }) {
return <h3 className="text-2xl font-bold my-4 border-b border-slate-300 pb-4">{children}</h3>;
},
h2({ children }) {
return <h3 className="text-xl font-bold my-3">{children}</h3>;
},
h3({ children }) {
return <h3 className="text-lg font-semibold my-2">{children}</h3>;
},
h4({ children }) {
return <h3 className="text-base font-semibold my-1">{children}</h3>;
},
a({ children, href }) {
return (
<div className="inline-block text-blue-600 dark:text-blue-400">
<LinkOutlined className="mr-1" />
<a href={href} target="_blank">
{children}
</a>
</div>
);
},
img({ src, alt }) {
return (
<div>
<Image
className="min-h-[1rem] max-w-full max-h-full border rounded"
src={src}
alt={alt}
placeholder={
<Tag icon={<SyncOutlined spin />} color="processing">
Image Loading...
</Tag>
}
fallback="/images/fallback.png"
/>
</div>
);
},
blockquote({ children }) {
return (
<blockquote className="py-4 px-6 border-l-4 border-blue-600 rounded bg-white my-2 text-gray-500 dark:bg-slate-800 dark:text-gray-200 dark:border-white shadow-sm">
{children}
</blockquote>
);
},
};
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: <AutoChart data={data?.data} chartType={getChartType(data?.type)} />,
};
const SqlItem = {
key: 'sql',
label: 'SQL',
children: <CodePreview code={format(data?.sql, { language: 'mysql' }) as string} language={'sql'} />,
};
const DataItem = {
key: 'data',
label: 'Data',
children: <Table dataSource={data?.data} columns={columns} />,
};
const TabItems: TabsProps['items'] = data?.type === 'response_table' ? [DataItem, SqlItem] : [ChartItem, SqlItem, DataItem];
return (
<div>
<Tabs defaultActiveKey={data?.type === 'response_table' ? 'data' : 'chart'} items={TabItems} size="small" />
{children}
</div>
);
},
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 <p className="text-sm text-red-500">Render Reference Error!</p>;
}
} else {
// new version, read from tag props.
try {
references = JSON.parse(references as string);
} catch (error) {
console.log('parse references failed', error);
return <p className="text-sm text-red-500">Render Reference Error!</p>;
}
}
if (!references || references?.length < 1) {
return null;
}
return (
<div className="border-t-[1px] border-gray-300 mt-3 py-2">
<p className="text-sm text-gray-500 dark:text-gray-400 mb-2">
<LinkOutlined className="mr-2" />
<span className="font-semibold">{title}</span>
</p>
{references.map((reference: Reference, index: number) => (
<div key={`file_${index}`} className="text-sm font-normal block ml-2 h-6 leading-6 overflow-hidden">
<span className="inline-block w-6">[{index + 1}]</span>
<span className="mr-2 lg:mr-4 text-blue-400">{reference.name}</span>
{reference?.chunks?.map((chunk: IChunk | number, index) => (
<span key={`chunk_${index}`}>
{typeof chunk === 'object' ? (
<Popover
content={
<div className="max-w-4xl">
<p className="mt-2 font-bold mr-2 border-t border-gray-500 pt-2">Content:</p>
<p>{chunk?.content || 'No Content'}</p>
<p className="mt-2 font-bold mr-2 border-t border-gray-500 pt-2">MetaData:</p>
<p>{chunk?.meta_info || 'No MetaData'}</p>
<p className="mt-2 font-bold mr-2 border-t border-gray-500 pt-2">Score:</p>
<p>{chunk?.recall_score || ''}</p>
</div>
}
title="Chunk Information"
>
<span className="cursor-pointer text-blue-500 ml-2" key={`chunk_content_${chunk?.id}`}>
{chunk?.id}
</span>
</Popover>
) : (
<span className="cursor-pointer text-blue-500 ml-2" key={`chunk_id_${chunk}`}>
{chunk}
</span>
)}
{index < reference?.chunks.length - 1 && <span key={`chunk_comma_${index}`}>,</span>}
</span>
))}
</div>
))}
</div>
);
},
summary: function ({ children }) {
return (
<div>
<p className="mb-2">
<ReadOutlined className="mr-2" />
<span className="font-semibold">Document Summary</span>
</p>
<div>{children}</div>
</div>
);
},
};
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<IChatDialogueMessageSchema, 'context'> & {
context:
| string
| {
template_name: string;
template_introduce: string;
};
};
isChartChat?: boolean;
onLinkClick?: () => void;
}
type MarkdownComponent = Parameters<typeof ReactMarkdown>['0']['components'];
type DBGPTView = {
name: string;
status: 'todo' | 'runing' | 'failed' | 'completed' | (string & {});
result?: string;
err_msg?: string;
};
const pluginViewStatusMapper: Record<DBGPTView['status'], { bgClass: string; icon: ReactNode }> = {
todo: {
bgClass: 'bg-gray-500',
icon: <ClockCircleOutlined className="ml-2" />,
},
runing: {
bgClass: 'bg-blue-500',
icon: <LoadingOutlined className="ml-2" />,
},
failed: {
bgClass: 'bg-red-500',
icon: <CloseOutlined className="ml-2" />,
},
completed: {
bgClass: 'bg-green-500',
icon: <CheckOutlined className="ml-2" />,
},
};
function formatMarkdownVal(val: string) {
return val
.replaceAll('\\n', '\n')
.replace(/<table(\w*=[^>]+)>/gi, '<table $1>')
.replace(/<tr(\w*=[^>]+)>/gi, '<tr $1>');
}
function ChatContent({ children, content, isChartChat, onLinkClick }: PropsWithChildren<Props>) {
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[^>]*>[^<]*<\/dbgpt-view>/gi, (matchVal) => {
try {
const pluginVal = matchVal.replaceAll('\n', '\\n').replace(/<[^>]*>|<\/[^>]*>/gm, '');
const pluginContext = JSON.parse(pluginVal) as DBGPTView;
const replacement = `<custom-view>${cacheIndex}</custom-view>`;
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<MarkdownComponent>(
() => ({
'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 (
<div className="bg-white dark:bg-[#212121] rounded-lg overflow-hidden my-2 flex flex-col lg:max-w-[80%]">
<div className={classNames('flex px-4 md:px-6 py-2 items-center text-white text-sm', bgClass)}>
{name}
{icon}
</div>
{result ? (
<div className="px-4 md:px-6 py-4 text-sm">
<ReactMarkdown components={markdownComponents} rehypePlugins={[rehypeRaw]}>
{result ?? ''}
</ReactMarkdown>
</div>
) : (
<div className="px-4 md:px-6 py-4 text-sm">{err_msg}</div>
)}
</div>
);
},
}),
[context, cachePluginContext],
);
if (!isRobot && !context) return <div className="h-12"></div>;
return (
<div
className={classNames('relative flex flex-wrap w-full p-2 md:p-4 rounded-xl break-words', {
'bg-white dark:bg-[#232734]': isRobot,
'lg:w-full xl:w-full pl-0': ['chat_with_db_execute', 'chat_dashboard'].includes(scene),
})}
>
<div className="mr-2 flex flex-shrink-0 items-center justify-center h-7 w-7 rounded-full text-lg sm:mr-4">
{isRobot ? renderModelIcon(model_name) || <RobotOutlined /> : <UserOutlined />}
</div>
<div className="flex-1 overflow-hidden items-center text-md leading-8 pb-2">
{/* User Input */}
{!isRobot && typeof context === 'string' && context}
{/* Render Report */}
{isRobot && isChartChat && typeof context === 'object' && (
<div>
{`[${context.template_name}]: `}
<span className="text-theme-primary cursor-pointer" onClick={onLinkClick}>
<CodeOutlined className="mr-1" />
{context.template_introduce || 'More Details'}
</span>
</div>
)}
{/* Markdown */}
{isRobot && typeof context === 'string' && (
<ReactMarkdown components={{ ...markdownComponents, ...extraMarkdownComponents }} rehypePlugins={[rehypeRaw]}>
{formatMarkdownVal(value)}
</ReactMarkdown>
)}
{!!relations?.length && (
<div className="flex flex-wrap mt-2">
{relations?.map((value, index) => (
<Tag color="#108ee9" key={value + index}>
{value}
</Tag>
))}
</div>
)}
</div>
{children}
</div>
);
}
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 <ChartView data={data.data} type={data.type} sql={data.sql} />;
}
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 (
<div className="bg-[#EAEAEB] rounded overflow-hidden border border-theme-primary dark:bg-theme-dark text-sm">
<div>
<div className="flex">
{data.code.map((item, index) => (
<div
key={index}
className={classNames('px-4 py-2 text-[#121417] dark:text-white cursor-pointer', {
'bg-white dark:bg-theme-dark-container': index === show,
})}
onClick={() => {
setShow(index);
}}
>
CODE {index + 1}: {item[0]}
</div>
))}
</div>
{data.code.length && (
<CodePreview
language={data.code[show][0]}
code={data.code[show][1]}
customStyle={{ maxHeight: 300, margin: 0 }}
light={oneLight}
dark={oneDark}
/>
)}
</div>
<div>
<div className="flex">
<div className="bg-white dark:bg-theme-dark-container px-4 py-2 text-[#121417] dark:text-white">
{t('Terminal')} {data.exit_success ? <CheckOutlined className="text-green-600" /> : <CloseOutlined className="text-red-600" />}
</div>
</div>
<div className="p-4 max-h-72 overflow-y-auto whitespace-normal bg-white dark:dark:bg-theme-dark">
<ReactMarkdown components={markdownComponents} remarkPlugins={[remarkGfm]}>
{data.log}
</ReactMarkdown>
</div>
</div>
</div>
);
}
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 (
<div className="rounded overflow-hidden">
<div className="p-3 text-white bg-red-500 whitespace-normal">{data.display_type}</div>
<div className="p-3 bg-red-50">
<div className="mb-2 whitespace-normal">{data.thought}</div>
<CodePreview code={format(data.sql)} language="sql" />
</div>
</div>
);
}
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 (
<div className="flex flex-col gap-3">
{charts.map((row, index) => (
<div key={`row-${index}`} className="flex gap-3">
{row.map((chart, subIndex) => (
<div
key={`chart-${subIndex}`}
className="flex flex-1 flex-col justify-between p-4 rounded border border-gray-200 dark:border-gray-500 whitespace-normal"
>
<div>
{chart.title && <div className="mb-2 text-lg">{chart.title}</div>}
{chart.describe && <div className="mb-4 text-sm text-gray-500">{chart.describe}</div>}
</div>
<AutoChart data={chart.data} chartType={getChartType(chart.type)} />
</div>
))}
</div>
))}
</div>
);
}
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<IVisPlugin['status'], { bgClass: string; icon: ReactNode }> = {
todo: {
bgClass: 'bg-gray-500',
icon: <ClockCircleOutlined className="ml-2" />,
},
runing: {
bgClass: 'bg-blue-500',
icon: <LoadingOutlined className="ml-2" />,
},
failed: {
bgClass: 'bg-red-500',
icon: <CloseOutlined className="ml-2" />,
},
complete: {
bgClass: 'bg-green-500',
icon: <CheckOutlined className="ml-2" />,
},
};
function VisPlugin({ data }: Props) {
const { bgClass, icon } = pluginViewStatusMapper[data.status] ?? {};
return (
<div className="bg-theme-light dark:bg-theme-dark-container rounded overflow-hidden my-2 flex flex-col lg:max-w-[80%]">
<div className={classNames('flex px-4 md:px-6 py-2 items-center text-white text-sm', bgClass)}>
{data.name}
{icon}
</div>
{data.result ? (
<div className="px-4 md:px-6 py-4 text-sm whitespace-normal">
<ReactMarkdown components={markdownComponents} rehypePlugins={[rehypeRaw]}>
{data.result ?? ''}
</ReactMarkdown>
</div>
) : (
<div className="px-4 md:px-6 py-4 text-sm">{data.err_msg}</div>
)}
</div>
);
}
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 (
<Dropdown onOpenChange={handleOpenChange}>
{contextHolder}
<Tooltip title={t('Rating')}>
<MenuButton slots={{ root: IconButton }} slotProps={{ root: { variant: 'plain', color: 'primary' } }} sx={{ borderRadius: 40 }}>
<MoreHoriz />
</MenuButton>
</Tooltip>
<Menu>
<MenuItem disabled sx={{ minHeight: 0 }} />
<Box
sx={{
width: '100%',
maxWidth: 350,
display: 'grid',
gap: 3,
padding: 1,
}}
>
<form onSubmit={handleSubmit}>
<Grid container spacing={0.5} columns={13} sx={{ flexGrow: 1 }}>
<Grid xs={3}>
<Item>{t('Q_A_Category')}</Item>
</Grid>
<Grid xs={10}>
<Select
action={action}
value={ques_type}
placeholder="Choose one…"
onChange={(event, newValue) => setQuesType(newValue ?? '')}
{...(ques_type && {
// display the button and remove select indicator
// when user has selected a value
endDecorator: (
<IconButton
size="sm"
variant="plain"
color="neutral"
onMouseDown={(event) => {
// don't open the popup when clicking on this button
event.stopPropagation();
}}
onClick={() => {
setQuesType('');
action.current?.focusVisible();
}}
>
<CloseRounded />
</IconButton>
),
indicator: null,
})}
sx={{ width: '100%' }}
>
{select_param &&
Object.keys(select_param)?.map((paramItem) => (
<Option key={paramItem} value={paramItem}>
{select_param[paramItem]}
</Option>
))}
</Select>
</Grid>
<Grid xs={3}>
<Item>
<Tooltip
title={
<Box>
<div>{t('feed_back_desc')}</div>
</Box>
}
variant="solid"
placement="left"
>
{t('Q_A_Rating')}
</Tooltip>
</Item>
</Grid>
<Grid xs={10} sx={{ pl: 0, ml: 0 }}>
<Slider
aria-label="Custom"
step={1}
min={0}
max={5}
valueLabelFormat={valueText}
valueLabelDisplay="on"
marks={marks}
sx={{ width: '90%', pt: 3, m: 2, ml: 1 }}
onChange={(event) => setScore(event.target?.value)}
value={score}
/>
</Grid>
<Grid xs={13}>
<Textarea
placeholder={t('Please_input_the_text')}
value={text}
onChange={(event) => setText(event.target.value)}
minRows={2}
maxRows={4}
endDecorator={
<Typography level="body-xs" sx={{ ml: 'auto' }}>
{t('input_count') + text.length + t('input_unit')}
</Typography>
}
sx={{ width: '100%', fontSize: 14 }}
/>
</Grid>
<Grid xs={13}>
<Button type="submit" variant="outlined" sx={{ width: '100%', height: '100%' }}>
{t('submit')}
</Button>
</Grid>
</Grid>
</form>
</Box>
</Menu>
</Dropdown>
);
};
export default ChatFeedback;
================================================
FILE: components/chat/completion.tsx
================================================
import { useState, useRef, useEffect, useMemo, useContext } from 'react';
import { useSearchParams } from 'next/navigation';
import MonacoEditor from './monaco-editor';
import ChatContent from './chat-content';
import ChatFeedback from './chat-feedback';
import { ChatContext } from '@/app/chat-context';
import { FeedBack, IChatDialogueMessageSchema } from '@/types/chat';
import classNames from 'classnames';
import { Empty, Modal, message, Tooltip } from 'antd';
import { renderModelIcon } from './header/model-selector';
import { cloneDeep } from 'lodash';
import copy from 'copy-to-clipboard';
import { useTranslation } from 'react-i18next';
import CompletionInput from '../common/completion-input';
import { useAsyncEffect } from 'ahooks';
import { STORAGE_INIT_MESSAGE_KET } from '@/utils';
import { Button, IconButton } from '@mui/joy';
import { CopyOutlined, RedoOutlined } from '@ant-design/icons';
import { getInitMessage } from '@/utils';
import { apiInterceptors, getChatFeedBackSelect } from '@/client/api';
import useSummary from '@/hooks/use-summary';
import AgentContent from './agent-content';
type Props = {
messages: IChatDialogueMessageSchema[];
onSubmit: (message: string, otherQueryBody?: Record<string, any>) => Promise<void>;
};
const Completion = ({ messages, onSubmit }: Props) => {
const { dbParam, currentDialogue, scene, model, refreshDialogList, chatId, agent, docId } = useContext(ChatContext);
const { t } = useTranslation();
const searchParams = useSearchParams();
const flowSelectParam = (searchParams && searchParams.get('select_param')) ?? '';
const spaceNameOriginal = (searchParams && searchParams.get('spaceNameOriginal')) ?? '';
const [isLoading, setIsLoading] = useState(false);
const [jsonModalOpen, setJsonModalOpen] = useState(false);
const [showMessages, setShowMessages] = useState(messages);
const [jsonValue, setJsonValue] = useState<string>('');
const [select_param, setSelectParam] = useState<FeedBack>();
const scrollableRef = useRef<HTMLDivElement>(null);
// const incremental = useMemo(() => scene === 'chat_flow', [scene]);
const isChartChat = useMemo(() => scene === 'chat_dashboard', [scene]);
const summary = useSummary();
const selectParam = useMemo(() => {
switch (scene) {
case 'chat_agent':
return agent;
case 'chat_excel':
return currentDialogue?.select_param;
case 'chat_flow':
return flowSelectParam;
default:
return spaceNameOriginal || dbParam;
}
}, [scene, agent, currentDialogue, dbParam, spaceNameOriginal, flowSelectParam]);
const handleChat = async (content: string) => {
if (isLoading || !content.trim()) return;
if (scene === 'chat_agent' && !agent) {
message.warning(t('choice_agent_tip'));
return;
}
try {
setIsLoading(true);
await onSubmit(content, {
select_param: selectParam ?? '',
// incremental,
});
} finally {
setIsLoading(false);
}
};
const handleJson2Obj = (jsonStr: string) => {
try {
return JSON.parse(jsonStr);
} catch (e) {
return jsonStr;
}
};
const [messageApi, contextHolder] = message.useMessage();
const onCopyContext = async (context: any) => {
const pureStr = context?.replace(/\trelations:.*/g, '');
const result = copy(pureStr);
if (result) {
if (pureStr) {
messageApi.open({ type: 'success', content: t('Copy_success') });
} else {
messageApi.open({ type: 'warning', content: t('Copy_nothing') });
}
} else {
messageApi.open({ type: 'error', content: t('Copry_error') });
}
};
const handleRetry = async () => {
if (isLoading || !docId) {
return;
}
setIsLoading(true);
await summary(docId);
setIsLoading(false);
};
useAsyncEffect(async () => {
const initMessage = getInitMessage();
if (initMessage && initMessage.id === chatId) {
await handleChat(initMessage.message);
refreshDialogList();
localStorage.removeItem(STORAGE_INIT_MESSAGE_KET);
}
}, [chatId]);
useEffect(() => {
let tempMessage: IChatDialogueMessageSchema[] = messages;
if (isChartChat) {
tempMessage = cloneDeep(messages).map((item) => {
if (item?.role === 'view' && typeof item?.context === 'string') {
item.context = handleJson2Obj(item?.context);
}
return item;
});
}
setShowMessages(tempMessage.filter((item) => ['view', 'human'].includes(item.role)));
}, [isChartChat, messages]);
useEffect(() => {
apiInterceptors(getChatFeedBackSelect())
.then((res) => {
setSelectParam(res[1] ?? {});
})
.catch((err) => {
console.log(err);
});
}, []);
useEffect(() => {
setTimeout(() => {
scrollableRef.current?.scrollTo(0, scrollableRef.current.scrollHeight);
}, 50);
}, [messages]);
return (
<>
{contextHolder}
<div ref={scrollableRef} className="flex flex-1 overflow-y-auto pb-8 w-full flex-col">
<div className="flex items-center flex-1 flex-col text-sm leading-6 text-slate-900 dark:text-slate-300 sm:text-base sm:leading-7">
{showMessages.length ? (
showMessages.map((content, index) => {
if (scene === 'chat_agent') {
return <AgentContent key={index} content={content} />;
}
return (
<ChatContent
key={index}
content={content}
isChartChat={isChartChat}
onLinkClick={() => {
setJsonModalOpen(true);
setJsonValue(JSON.stringify(content?.context, null, 2));
}}
>
{content.role === 'view' && (
<div className="flex w-full border-t border-gray-200 dark:border-theme-dark">
{scene === 'chat_knowledge' && content.retry ? (
<Button onClick={handleRetry} slots={{ root: IconButton }} slotProps={{ root: { variant: 'plain', color: 'primary' } }}>
<RedoOutlined />
<span className="text-sm">{t('Retry')}</span>
</Button>
) : null}
<div className="flex w-full flex-row-reverse">
<ChatFeedback
select_param={select_param}
conv_index={Math.ceil((index + 1) / 2)}
question={showMessages?.filter((e) => e?.role === 'human' && e?.order === content.order)[0]?.context}
knowledge_space={spaceNameOriginal || dbParam || ''}
/>
<Tooltip title={t('Copy')}>
<Button
onClick={() => onCopyContext(content?.context)}
slots={{ root: IconButton }}
slotProps={{ root: { variant: 'plain', color: 'primary' } }}
sx={{ borderRadius: 40 }}
>
<CopyOutlined />
</Button>
</Tooltip>
</div>
</div>
)}
</ChatContent>
);
})
) : (
<Empty
image="/empty.png"
imageStyle={{ width: 320, height: 320, margin: '0 auto', maxWidth: '100%', maxHeight: '100%' }}
className="flex items-center justify-center flex-col h-full w-full"
description="Start a conversation"
/>
)}
</div>
</div>
<div
className={classNames(
'relative after:absolute after:-top-8 after:h-8 after:w-full after:bg-gradient-to-t after:from-theme-light after:to-transparent dark:after:from-theme-dark',
{
'cursor-not-allowed': scene === 'chat_excel' && !currentDialogue?.select_param,
},
)}
>
<div className="flex flex-wrap w-full py-2 sm:pt-6 sm:pb-10 items-center">
{model && <div className="mr-2 flex">{renderModelIcon(model)}</div>}
<CompletionInput loading={isLoading} onSubmit={handleChat} handleFinish={setIsLoading} />
</div>
</div>
<Modal
title="JSON Editor"
open={jsonModalOpen}
width="60%"
cancelButtonProps={{
hidden: true,
}}
onOk={() => {
setJsonModalOpen(false);
}}
onCancel={() => {
setJsonModalOpen(false);
}}
>
<MonacoEditor className="w-full h-[500px]" language="json" value={jsonValue} />
</Modal>
</>
);
};
export default Completion;
================================================
FILE: components/chat/db-editor.tsx
================================================
import React from 'react';
import { useRequest } from 'ahooks';
import { Select, Option, Table, Box, Typography, Tooltip } from '@mui/joy';
import { Button } from 'antd';
import AutoAwesomeMotionIcon from '@mui/icons-material/AutoAwesomeMotion';
import { Input, Tree, Empty, Tabs } from 'antd';
import type { DataNode } from 'antd/es/tree';
import MonacoEditor from './monaco-editor';
import { sendGetRequest, sendSpacePostRequest } from '@/utils/request';
import { useSearchParams } from 'next/navigation';
import { OnChange } from '@monaco-editor/react';
import Header from './header';
import Chart from '../chart';
const { Search } = Input;
interface EditorValueProps {
sql?: string;
thoughts?: string;
title?: string;
showcase?: string;
}
interface RoundProps {
db_name: string;
round: number;
round_name: string;
}
interface IProps {
editorValue?: EditorValueProps;
chartData?: any;
tableData?: any;
handleChange: OnChange;
}
interface ITableTreeItem {
title: string;
key: string;
type: string;
default_value: string | null;
can_null: string;
comment: string | null;
children: Array<ITableTreeItem>;
}
function DbEditorContent({ editorValue, chartData, tableData, handleChange }: IProps) {
const chartWrapper = React.useMemo(() => {
if (!chartData) return <div></div>;
return (
<div className="flex-1 overflow-auto p-3" style={{ flexShrink: 0, overflow: 'hidden' }}>
<Chart chartsData={[chartData]} />
</div>
);
}, [chartData]);
return (
<>
<div className="flex-1 flex overflow-hidden">
<div className="flex-1" style={{ flexShrink: 0, overflow: 'auto' }}>
<MonacoEditor value={editorValue?.sql || ''} language="mysql" onChange={handleChange} thoughts={editorValue?.thoughts || ''} />
</div>
{chartWrapper}
</div>
<div className="h-96 border-[var(--joy-palette-divider)] border-t border-solid overflow-auto">
{tableData?.values?.length > 0 ? (
<Table aria-label="basic table" stickyHeader>
<thead>
<tr>
{tableData?.columns?.map((column: any, i: number) => (
<th key={column + i}>{column}</th>
))}
</tr>
</thead>
<tbody>
{tableData?.values?.map((value: any, i: number) => (
<tr key={i}>
{Object.keys(value)?.map((v) => (
<td key={v}>{value[v]}</td>
))}
</tr>
))}
</tbody>
</Table>
) : (
<div className="h-full flex justify-center items-center">
<Empty />
</div>
)}
</div>
</>
);
}
function DbEditor() {
const [expandedKeys, setExpandedKeys] = React.useState<React.Key[]>([]);
const [searchValue, setSearchValue] = React.useState('');
const [currentRound, setCurrentRound] = React.useState<null | string | number>();
const [autoExpandParent, setAutoExpandParent] = React.useState(true);
const [chartData, setChartData] = React.useState();
const [editorValue, setEditorValue] = React.useState<EditorValueProps | EditorValueProps[]>();
const [newEditorValue, setNewEditorValue] = React.useState<EditorValueProps>();
const [tableData, setTableData] = React.useState<{ columns: string[]; values: any }>();
const [currentTabIndex, setCurrentTabIndex] = React.useState<string>();
const searchParams = useSearchParams();
const id = searchParams?.get('id');
const scene = searchParams?.get('scene');
const { data: rounds, loading: roundsLoading } = useRequest(
async () =>
await sendGetRequest('/v1/editor/sql/rounds', {
con_uid: id,
}),
{
onSuccess: (res) => {
const lastItem = res?.data?.[res?.data?.length - 1];
if (lastItem) {
setCurrentRound(lastItem?.round);
}
},
},
);
const { run: runSql, loading: runLoading } = useRequest(
async () => {
const db_name = rounds?.data?.find((item) => item.round === currentRound)?.db_name;
return await sendSpacePostRequest(`/api/v1/editor/sql/run`, {
db_name,
sql: newEditorValue?.sql,
});
},
{
manual: true,
onSuccess: (res) => {
setTableData({
columns: res?.data?.colunms,
values: res?.data?.values,
});
},
},
);
const { run: runCharts, loading: runChartsLoading } = useRequest(
async () => {
const db_name = rounds?.data?.find((item) => item.round === currentRound)?.db_name;
const params: {
db_name: string;
sql?: string;
chart_type?: string;
} = {
db_name,
sql: newEditorValue?.sql,
};
if (scene === 'chat_dashboard') {
params['chart_type'] = newEditorValue?.showcase;
}
return await sendSpacePostRequest(`/api/v1/editor/chart/run`, params);
},
{
manual: true,
ready: !!newEditorValue?.sql,
onSuccess: (res) => {
if (res?.success) {
setTableData({
columns: res?.data?.sql_data?.colunms || [],
values: res?.data?.sql_data?.values || [],
});
if (!res?.data?.chart_values) {
setChartData(undefined);
} else {
setChartData({
type: res?.data?.chart_type,
values: res?.data?.chart_values,
title: newEditorValue?.title,
description: newEditorValue?.thoughts,
});
}
}
},
},
);
const { run: submitSql, loading: submitLoading } = useRequest(
async () => {
const db_name = rounds?.data?.find((item: RoundProps) => item.round === currentRound)?.db_name;
return await sendSpacePostRequest(`/api/v1/sql/editor/submit`, {
conv_uid: id,
db_name,
conv_round: currentRound,
old_sql: editorValue?.sql,
old_speak: editorValue?.thoughts,
new_sql: newEditorValue?.sql,
new_speak: newEditorValue?.thoughts?.match(/^\n--(.*)\n\n$/)?.[1]?.trim() || newEditorValue?.thoughts,
});
},
{
manual: true,
onSuccess: (res) => {
if (res?.success) {
runSql();
}
},
},
);
const { run: submitChart, loading: submitChartLoading } = useRequest(
async () => {
const db_name = rounds?.data?.find((item) => item.round === currentRound)?.db_name;
return await sendSpacePostRequest(`/api/v1/chart/editor/submit`, {
conv_uid: id,
chart_title: newEditorValue?.title,
db_name,
old_sql: editorValue?.[currentTabIndex]?.sql,
new_chart_type: newEditorValue?.showcase,
new_sql: newEditorValue?.sql,
new_comment: newEditorValue?.thoughts?.match(/^\n--(.*)\n\n$/)?.[1]?.trim() || newEditorValue?.thoughts,
gmt_create: new Date().getTime(),
});
},
{
manual: true,
onSuccess: (res) => {
if (res?.success) {
runCharts();
}
},
},
);
const { data: tables } = useRequest(
async () => {
const db_name = rounds?.data?.find((item: RoundProps) => item.round === currentRound)?.db_name;
return await sendGetRequest('/v1/editor/db/tables', {
db_name,
page_index: 1,
page_size: 200,
});
},
{
ready: !!rounds?.data?.find((item: RoundProps) => item.round === currentRound)?.db_name,
refreshDeps: [rounds?.data?.find((item: RoundProps) => item.round === currentRound)?.db_name],
},
);
const { run: handleGetEditorSql } = useRequest(
async (round) =>
await sendGetRequest('/v1/editor/sql', {
con_uid: id,
round,
}),
{
manual: true,
onSuccess: (res) => {
let sql = undefined;
try {
if (Array.isArray(res?.data)) {
sql = res?.data;
setCurrentTabIndex('0');
} else if (typeof res?.data === 'string') {
const d = JSON.parse(res?.data);
sql = d;
} else {
sql = res?.data;
}
} catch (e) {
console.log(e);
} finally {
setEditorValue(sql);
if (Array.isArray(sql)) {
setNewEditorValue(sql?.[Number(currentTabIndex || 0)]);
} else {
setNewEditorValue(sql);
}
}
},
},
);
const treeData = React.useMemo(() => {
const loop = (data: Array<ITableTreeItem>, parentKey?: string | number): DataNode[] =>
data.map((item: ITableTreeItem) => {
const strTitle = item.title;
const index = strTitle.indexOf(searchValue);
const beforeStr = strTitle.substring(0, index);
const afterStr = strTitle.slice(index + searchValue.length);
const showTitle =
index > -1 ? (
<Tooltip title={(item?.comment || item?.title) + (item?.can_null === 'YES' ? '(can null)' : `(can't null)`)}>
<span>
{beforeStr}
<span className="text-[#1677ff]">{searchValue}</span>
{afterStr}
{item?.type && (
<Typography gutterBottom level="body3" className="pl-0.5" style={{ display: 'inline' }}>
{`[${item?.type}]`}
</Typography>
)}
</span>
</Tooltip>
) : (
<Tooltip title={(item?.comment || item?.title) + (item?.can_null === 'YES' ? '(can null)' : `(can't null)`)}>
<span>
{strTitle}
{item?.type && (
<Typography gutterBottom level="body3" className="pl-0.5" style={{ display: 'inline' }}>
{`[${item?.type}]`}
</Typography>
)}
</span>
</Tooltip>
);
if (item.children) {
const itemKey = parentKey ? String(parentKey) + '_' + item.key : item.key;
return { title: strTitle, showTitle, key: itemKey, children: loop(item.children, itemKey) };
}
return {
title: strTitle,
showTitle,
key: item.key,
};
});
if (tables?.data) {
// default expand first node
setExpandedKeys([tables?.data.key]);
return loop([tables?.data]);
}
return [];
}, [searchValue, tables]);
const dataList = React.useMemo(() => {
let res: { key: string | number; title: string; parentKey?: string | number }[] = [];
const generateList = (data: DataNode[], parentKey?: string | number) => {
if (!data || data?.length <= 0) return;
for (let i = 0; i < data.length; i++) {
const node = data[i];
const { key, title } = node;
res.push({ key, title: title as string, parentKey });
if (node.children) {
generateList(node.children, key);
}
}
};
if (treeData) {
generateList(treeData);
}
return res;
}, [treeData]);
const getParentKey = (key: React.Key, tree: DataNode[]): React.Key => {
let parentKey: React.Key;
for (let i = 0; i < tree.length; i++) {
const node = tree[i];
if (node.children) {
if (node.children.some((item) => item.key === key)) {
parentKey = node.key;
} else if (getParentKey(key, node.children)) {
parentKey = getParentKey(key, node.children);
}
}
}
return parentKey!;
};
const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { value } = e.target;
if (tables?.data) {
if (!value) {
setExpandedKeys([]);
} else {
const newExpandedKeys = dataList
.map((item) => {
if (item.title.indexOf(value) > -1) {
return getParentKey(item.key, treeData);
}
return null;
})
.filter((item, i, self) => item && self.indexOf(item) === i);
setExpandedKeys(newExpandedKeys as React.Key[]);
}
setSearchValue(value);
setAutoExpandParent(true);
}
};
React.useEffect(() => {
if (currentRound) {
handleGetEditorSql(currentRound);
}
}, [handleGetEditorSql, currentRound]);
React.useEffect(() => {
if (editorValue && scene === 'chat_dashboard' && currentTabIndex) {
runCharts();
}
}, [currentTabIndex, scene, editorValue, runCharts]);
React.useEffect(() => {
if (editorValue && scene !== 'chat_dashboard') {
runSql();
}
}, [scene, editorValue, runSql]);
function resolveSqlAndThoughts(value: string | undefined) {
if (!value) {
return { sql: '', thoughts: '' };
}
const match = value && value.match(/(--.*)\n([\s\S]*)/);
let thoughts = '';
let sql;
if (match && match.length >= 3) {
thoughts = match[1];
sql = match[2];
}
return { sql, thoughts };
}
return (
<div className="flex flex-col w-full h-full">
<Header />
<div className="relative flex flex-1 overflow-auto">
<div
className="text h-full border-[var(--joy-palette-divider)] border-r border-solid p-3 max-h-full overflow-auto"
style={{ width: '300px' }}
>
<div className="absolute right-4 top-2 z-10">
<Button
className="mr-2"
type="primary"
loading={runLoading || runChartsLoading}
onClick={async () => {
if (scene === 'chat_dashboard') {
runCharts();
} else {
runSql();
}
}}
>
Run
</Button>
<Button
loading={submitLoading || submitChartLoading}
onClick={async () => {
if (scene === 'chat_dashboard') {
await submitChart();
} else {
await submitSql();
}
}}
>
Save
</Button>
</div>
<div className="flex items-center py-3">
<Select
className="h-4 min-w-[240px]"
size="sm"
value={currentRound as string | null | undefined}
onChange={(e: React.SyntheticEvent | null, newValue: string | null) => {
setCurrentRound(newValue);
}}
>
{rounds?.data?.map((item: RoundProps) => (
<Option key={item?.round} value={item?.round}>
{item?.round_name}
</Option>
))}
</Select>
<AutoAwesomeMotionIcon className="ml-2" />
</div>
<Search style={{ marginBottom: 8 }} placeholder="Search" onChange={onChange} />
{treeData && treeData.length > 0 && (
<Tree
onExpand={(newExpandedKeys: React.Key[]) => {
setExpandedKeys(newExpandedKeys);
setAutoExpandParent(false);
}}
expandedKeys={expandedKeys}
autoExpandParent={autoExpandParent}
treeData={treeData}
fieldNames={{
title: 'showTitle',
}}
/>
)}
</div>
<div className="flex flex-col flex-1 max-w-full overflow-hidden">
{Array.isArray(editorValue) ? (
<>
<Box
className="h-full"
sx={{
'.ant-tabs-content, .ant-tabs-tabpane-active': {
height: '100%',
},
'& .ant-tabs-card.ant-tabs-top >.ant-tabs-nav .ant-tabs-tab, & .ant-tabs-card.ant-tabs-top >div>.ant-tabs-nav .ant-tabs-tab': {
borderRadius: '0',
},
}}
>
<Tabs
className="h-full dark:text-white px-2"
activeKey={currentTabIndex}
onChange={(activeKey) => {
setCurrentTabIndex(activeKey);
setNewEditorValue(editorValue?.[Number(activeKey)]);
}}
items={editorValue?.map((item, i) => ({
key: i + '',
label: item?.title,
children: (
<div className="flex flex-col h-full">
<DbEditorContent
editorValue={item}
handleChange={(value) => {
const { sql, thoughts } = resolveSqlAndThoughts(value);
setNewEditorValue((old) => {
return Object.assign({}, old, {
sql,
thoughts,
});
});
}}
tableData={tableData}
chartData={chartData}
/>
</div>
),
}))}
/>
</Box>
</>
) : (
<DbEditorContent
editorValue={editorValue}
handleChange={(value) => {
const { sql, thoughts } = resolveSqlAndThoughts(value);
setNewEditorValue((old) => {
return Object.assign({}, old, {
sql,
thoughts,
});
});
}}
tableData={tableData}
chartData={undefined}
/>
)}
</div>
</div>
</div>
);
}
export default DbEditor;
================================================
FILE: components/chat/doc-list.tsx
================================================
import { IDocument } from '@/types/knowledge';
import { Button, Tooltip } from 'antd';
import { useRouter } from 'next/router';
import React from 'react';
import FileStatusIcon from '../common/FileStatusIcon';
interface IProps {
documents: IDocument[];
dbParam?: string;
}
export default function DocList(props: IProps) {
const { documents, dbParam } = props;
const router = useRouter();
const handleClick = (id: number) => {
router.push(`/knowledge/chunk/?spaceName=${dbParam}&id=${id}`);
};
if (!documents?.length) return null;
return (
<div className="absolute flex overflow-scroll h-12 top-[-35px] w-full z-10">
{documents.map((doc) => {
let color;
switch (doc.status) {
case 'RUNNING':
color = '#2db7f5';
break;
case 'FINISHED':
color = '#87d068';
break;
case 'FAILED':
color = '#f50';
break;
default:
color = '#87d068';
break;
}
return (
<Tooltip key={doc.id} title={doc.result}>
<Button
style={{ color }}
onClick={() => {
handleClick(doc.id);
}}
className="shrink flex items-center mr-3"
>
<FileStatusIcon document={doc} />
{doc.doc_name}
</Button>
</Tooltip>
);
})}
</div>
);
}
================================================
FILE: components/chat/doc-upload.tsx
================================================
import { ChatContext } from '@/app/chat-context';
import { apiInterceptors, uploadDocument } from '@/client/api';
import useSummary from '@/hooks/use-summary';
import { PaperClipOutlined } from '@ant-design/icons';
import { Button, Upload } from 'antd';
import React, { useContext, useState } from 'react';
interface IProps {
className?: string;
handleFinish?: (data: boolean) => void;
onUploadFinish: () => void;
}
export default function DocUpload(props: IProps) {
const { dbParam, setDocId } = useContext(ChatContext);
const { onUploadFinish, handleFinish } = props;
const summary = useSummary();
const [loading, setLoading] = useState<boolean>(false);
const handleUpload = async (data: any) => {
setLoading(true);
const formData = new FormData();
formData.append('doc_name', data.file.name);
formData.append('doc_file', data.file);
formData.append('doc_type', 'DOCUMENT');
const res = await apiInterceptors(uploadDocument(dbParam || 'default', formData));
if (!res[1]) {
setLoading(false);
return;
}
setDocId(res[1]);
onUploadFinish();
setLoading(false);
handleFinish?.(true);
await summary(res[1]);
handleFinish?.(false);
};
return (
<Upload
customRequest={handleUpload}
showUploadList={false}
maxCount={1}
multiple={false}
className="absolute z-10 top-2 left-2"
accept=".pdf,.ppt,.pptx,.xls,.xlsx,.doc,.docx,.txt,.md"
>
<Button loading={loading} size="small" shape="circle" icon={<PaperClipOutlined />}></Button>
</Upload>
);
}
================================================
FILE: components/chat/header/agent-selector.tsx
================================================
import { ChatContext } from '@/app/chat-context';
import { apiInterceptors, getDbgptsList } from '@/client/api';
import { useRequest } from 'ahooks';
import { Select } from 'antd';
import { useContext } from 'react';
import { useTranslation } from 'react-i18next';
function AgentSelector() {
const { t } = useTranslation();
const { agent, setAgent } = useContext(ChatContext);
const { data = [] } = useRequest(async () => {
const [, res] = await apiInterceptors(getDbgptsList());
return res ?? [];
});
return (
<Select
className="w-60"
value={agent}
placeholder={t('Select_Plugins')}
options={data.map((item) => ({ label: item.app_name, value: item.app_code }))}
allowClear
onChange={(val) => {
setAgent?.(val);
}}
/>
);
}
export default AgentSelector;
================================================
FILE: components/chat/header/chat-excel.tsx
================================================
import ExcelUpload from './excel-upload';
import { LinkOutlined } from '@ant-design/icons';
import { useContext } from 'react';
import { ChatContext } from '@/app/chat-context';
interface Props {
onComplete?: () => void;
}
function ChatExcel({ onComplete }: Props) {
const { currentDialogue, scene, chatId } = useContext(ChatContext);
if (scene !== 'chat_excel') return null;
return (
<div className="max-w-md h-full relative">
{currentDialogue ? (
<div className="flex h-8 overflow-hidden rounded">
<div className="flex items-center justify-center px-2 bg-gray-600 text-lg">
<LinkOutlined className="text-white" />
</div>
<div className="flex items-center justify-center px-3 bg-gray-100 text-xs rounded-tr rounded-br dark:text-gray-800 truncate">
{currentDialogue.select_param}
</div>
</div>
) : (
<ExcelUpload convUid={chatId} chatMode={scene} onComplete={onComplete} />
)}
</div>
);
}
export default ChatExcel;
================================================
FILE: components/chat/header/db-selector.tsx
================================================
import { ChatContext } from '@/app/chat-context';
import { apiInterceptors, postChatModeParamsList } from '@/client/api';
import { IDB } from '@/types/chat';
import { dbMapper } from '@/utils';
import { useAsyncEffect } from 'ahooks';
import { Select } from 'antd';
import { useContext, useEffect, useMemo, useState } from 'react';
import DBIcon from '@/components/common/db-icon';
function DBSelector() {
const { scene, dbParam, setDbParam } = useContext(ChatContext);
const [dbs, setDbs] = useState<IDB[]>([]);
useAsyncEffect(async () => {
const [, res] = await apiInterceptors(postChatModeParamsList(scene as string));
setDbs(res ?? []);
}, [scene]);
const dbOpts = useMemo(
() =>
dbs.map?.((db: IDB) => {
return { name: db.param, ...dbMapper[db.type] };
}),
[dbs],
);
useEffect(() => {
if (dbOpts?.length && !dbParam) {
setDbParam(dbOpts[0].name);
}
}, [dbOpts, setDbParam, dbParam]);
if (!dbOpts?.length) return null;
return (
<Select
value={dbParam}
className="w-36"
onChange={(val) => {
setDbParam(val);
}}
>
{dbOpts.map((item) => (
<Select.Option key={item.name}>
<DBIcon width={24} height={24} src={item.icon} label={item.label} className="w-[1.5em] h-[1.5em] mr-1 inline-block mt-[-4px]" />
{item.name}
</Select.Option>
))}
</Select>
);
}
export default DBSelector;
================================================
FILE: components/chat/header/excel-upload.tsx
================================================
import { PropsWithChildren, useContext, useState } from 'react';
import { Upload, UploadProps, Button, message, UploadFile, Tooltip } from 'antd';
import { LinkOutlined, SelectOutlined, UploadOutlined } from '@ant-design/icons';
import { apiInterceptors, postChatModeParamsFileLoad } from '@/client/api';
import { ChatContext } from '@/app/chat-context';
interface Props {
convUid: string;
chatMode: string;
onComplete?: () => void;
}
function ExcelUpload({ convUid, chatMode, onComplete, ...props }: PropsWithChildren<Props & UploadProps>) {
const [loading, setLoading] = useState(false);
const [messageApi, contextHolder] = message.useMessage();
const [fileList, setFileList] = useState<UploadFile[]>([]);
const [percent, setPercent] = useState<number>();
const { model } = useContext(ChatContext);
const onChange: UploadProps['onChange'] = async (info) => {
if (!info) {
message.error('Please select the *.(csv|xlsx|xls) file');
return;
}
if (!/\.(csv|xlsx|xls)$/.test(info.file.name ?? '')) {
message.error('File type must be csv, xlsx or xls');
return;
}
setFileList([info.file]);
};
const onUpload = async () => {
setLoading(true);
try {
const formData = new FormData();
formData.append('doc_file', fileList[0] as any);
messageApi.open({ content: `Uploading ${fileList[0].name}`, type: 'loading', duration: 0 });
const [err] = await apiInterceptors(
postChatModeParamsFileLoad({
convUid,
chatMode,
data: formData,
model,
config: {
/** timeout 1h */
timeout: 1000 * 60 * 60,
onUploadProgress: (progressEvent) => {
const progress = Math.ceil((progressEvent.loaded / (progressEvent.total || 0)) * 100);
setPercent(progress);
},
},
}),
);
if (err) return;
message.success('success');
onComplete?.();
} catch (e: any) {
message.error(e?.message || 'Upload Error');
} finally {
setLoading(false);
messageApi.destroy();
}
};
return (
<>
<div className="flex items-start gap-2">
{contextHolder}
<Tooltip placement="bottom" title="File cannot be changed after upload">
<Upload
disabled={loading}
className="mr-1"
beforeUpload={() => false}
fileList={fileList}
name="file"
accept=".csv,.xlsx,.xls"
multiple={false}
onChange={onChange}
showUploadList={{
showDownloadIcon: false,
showPreviewIcon: false,
showRemoveIcon: false,
}}
itemRender={() => <></>}
{...props}
>
<Button className="flex justify-center items-center" type="primary" disabled={loading} icon={<SelectOutlined />}>
Select File
</Button>
</Upload>
</Tooltip>
<Button
type="primary"
loading={loading}
className="flex justify-center items-center"
disabled={!fileList.length}
icon={<UploadOutlined />}
onClick={onUpload}
>
{loading ? (percent === 100 ? 'Analysis' : 'Uploading') : 'Upload'}
</Button>
{!!fileList.length && (
<div className="mt-2 text-gray-500 text-sm flex items-center">
<LinkOutlined className="mr-2" />
<span>{fileList[0]?.name}</span>
</div>
)}
</div>
</>
);
}
export default ExcelUpload;
================================================
FILE: components/chat/header/index.tsx
================================================
import { useContext } from 'react';
import ChatExcel from './chat-excel';
import { ChatContext } from '@/app/chat-context';
import ModeTab from '@/components/chat/mode-tab';
import ModelSelector from '@/components/chat/header/model-selector';
import DBSelector from './db-selector';
import AgentSelector from './agent-selector';
/**
* chat header
*/
interface Props {
refreshHistory?: () => Promise<void>;
modelChange?: (val: string) => void;
}
function Header({ refreshHistory, modelChange }: Props) {
const { scene, refreshDialogList } = useContext(ChatContext);
return (
<div className="w-full py-2 px-4 md:px-4 flex flex-wrap items-center justify-center gap-1 md:gap-4">
{/* Models Selector */}
<ModelSelector onChange={modelChange} />
{/* DB Selector */}
<DBSelector />
{/* Excel Upload */}
{scene === 'chat_excel' && (
<ChatExcel
onComplete={() => {
refreshDialogList?.();
refreshHistory?.();
}}
/>
)}
{/* Agent Selector */}
{scene === 'chat_agent' && <AgentSelector />}
<ModeTab />
</div>
);
}
export default Header;
================================================
FILE: components/chat/header/model-selector.tsx
================================================
/**
* multi-models selector
*/
import { ChatContext } from '@/app/chat-context';
import { Select } from 'antd';
import { MODEL_ICON_MAP } from '@/utils/constants';
import Image from 'next/image';
import { useContext } from 'react';
import { useTranslation } from 'react-i18next';
interface Props {
onChange?: (model: string) => void;
}
const DEFAULT_ICON_URL = '/models/huggingface.svg';
export function renderModelIcon(model?: string, props?: { width: number; height: number }) {
const { width, height } = props || {};
if (!model) return null;
return (
<Image
className="rounded-full border border-gray-200 object-contain bg-white inline-block"
width={width || 24}
height={height || 24}
src={MODEL_ICON_MAP[model]?.icon || DEFAULT_ICON_URL}
alt="llm"
/>
);
}
function ModelSelector({ onChange }: Props) {
const { t } = useTranslation();
const { modelList, model } = useContext(ChatContext);
if (!modelList || modelList.length <= 0) {
return null;
}
return (
<Select
value={model}
placeholder={t('choose_model')}
className="w-52"
onChange={(val) => {
onChange?.(val);
}}
>
{modelList.map((item) => (
<Select.Option key={item}>
<div className="flex items-center">
{renderModelIcon(item)}
<span className="ml-2">{MODEL_ICON_MAP[item]?.label || item}</span>
</div>
</Select.Option>
))}
</Select>
);
}
export default ModelSelector;
================================================
FILE: components/chat/mode-tab/index.css
================================================
.model-tab::before {
content: "";
position: absolute;
left: 0.4rem;
width: 45%;
height: 80%;
background-color: #ffc800;
border-radius: 2rem;
transition: all .4s;
}
.editor-tab::before {
left: calc(50%);
}
================================================
FILE: components/chat/mode-tab/index.tsx
================================================
import './index.css';
import { useContext } from 'react';
import { ChatContext } from '@/app/chat-context';
import { Radio } from 'antd';
import Icon, { AppstoreFilled } from '@ant-design/icons';
import { StarsSvg } from '@/components/icons';
export default function ModeTab() {
const { isContract, setIsContract, scene } = useContext(ChatContext);
const isShow = scene && ['chat_with_db_execute', 'chat_dashboard'].includes(scene as string);
if (!isShow) {
return null;
}
return (
<Radio.Group
value={isContract}
defaultValue={true}
buttonStyle="solid"
onChange={() => {
setIsContract(!isContract);
}}
>
<Radio.Button value={false}>
<Icon component={StarsSvg} className="mr-1" />
Preview
</Radio.Button>
<Radio.Button value={true}>
<AppstoreFilled className="mr-1" />
Editor
</Radio.Button>
</Radio.Group>
);
}
================================================
FILE: components/chat/monaco-editor.tsx
================================================
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api.js';
import Editor, { OnChange, loader } from '@monaco-editor/react';
import classNames from 'classnames';
import { useMemo } from 'react';
import { format } from 'sql-formatter';
loader.config({ monaco });
interface MonacoEditorProps {
className?: string;
value: string;
language: string;
onChange?: OnChange;
thoughts?: string;
}
export default function MonacoEditor({ className, value, language = 'mysql', onChange, thoughts }: MonacoEditorProps) {
// merge value and thoughts
const editorValue = useMemo(() => {
if (language !== 'mysql') {
return value;
}
if (thoughts && thoughts.length > 0) {
return format(`-- ${thoughts} \n${value}`);
}
return format(value);
}, [value, thoughts]);
return (
<Editor
className={classNames(className)}
value={editorValue}
language={language}
onChange={onChange}
theme="vs-dark"
options={{
minimap: {
enabled: false,
},
wordWrap: 'on',
}}
/>
);
}
================================================
FILE: components/common/FileStatusIcon.tsx
================================================
import { IDocument } from '@/types/knowledge';
import React from 'react';
import { FileDone, FileSync } from '../icons';
import FileError from '../icons/file-error';
interface IProps {
document: IDocument;
}
export default function FileStatusIcon({ document }: IProps) {
switch (document.status) {
case 'RUNNING':
return <FileSync />;
case 'FINISHED':
return <FileDone />;
case 'FAILED':
return <FileError />;
default:
return <FileDone />;
}
}
================================================
FILE: components/common/MyEmpty.tsx
================================================
import { Button, Empty } from 'antd';
import { useTranslation } from 'react-i18next';
interface Props {
error?: boolean;
description?: string;
refresh?: () => void;
}
function MyEmpty({ error, description, refresh }: Props) {
const { t } = useTranslation();
return (
<Empty
image="/empty.png"
imageStyle={{ width: 320, height: 320, margin: '0 auto', maxWidth: '100%', maxHeight: '100%' }}
className="flex items-center justify-center flex-col h-full w-full"
description={
error ? (
<Button type="primary" onClick={refresh}>
{t('try_again')}
</Button>
) : (
description ?? t('no_data')
)
}
/>
);
}
export default MyEmpty;
================================================
FILE: components/common/chat-dialog.tsx
================================================
import useChat from '@/hooks/use-chat';
import CompletionInput from './completion-input';
import { useCallback, useState } from 'react';
import { IChatDialogueMessageSchema, IChatDialogueSchema } from '@/types/chat';
import AgentContent from '../chat/agent-content';
import { renderModelIcon } from '../chat/header/model-selector';
import MyEmpty from './MyEmpty';
import { CaretLeftOutlined } from '@ant-design/icons';
import classNames from 'classnames';
import { useRequest } from 'ahooks';
import { apiInterceptors, newDialogue } from '@/client/api';
import ChatContent from '../chat/chat-content';
interface Props {
title?: string;
completionApi?: string;
chatMode: IChatDialogueSchema['chat_mode'];
chatParams?: {
select_param?: string;
} & Record<string, string>;
model?: string;
}
function ChatDialog({ title, chatMode, completionApi, chatParams, model = '' }: Props) {
const chat = useChat({ queryAgentURL: completionApi });
const [loading, setLoading] = useState(false);
const [list, setList] = useState<IChatDialogueMessageSchema[]>([]);
const [open, setOpen] = useState(false);
const { data } = useRequest(
async () => {
const [, res] = await apiInterceptors(newDialogue({ chat_mode: chatMode }));
return res;
},
{
ready: !!chatMode,
},
);
const handleChat = useCallback(
(content: string) => {
if (!data) return;
return new Promise<void>((resolve) => {
const tempList: IChatDialogueMessageSchema[] = [
...list,
{ 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 = tempList.length - 1;
setList([...tempList]);
setLoading(true);
chat({
chatId: data?.conv_uid,
data: { ...chatParams, chat_mode: chatMode, model_name: model, user_input: content },
onMessage: (message) => {
tempList[index].context = message;
setList([...tempList]);
},
onDone: () => {
resolve();
},
onClose: () => {
resolve();
},
onError: (message) => {
tempList[index].context = message;
setList([...tempList]);
resolve();
},
}).finally(() => {
setLoading(false);
});
});
},
[chat, list, data?.conv_uid],
);
return (
<div
className={classNames(
'fixed top-0 right-0 w-[30rem] h-screen flex flex-col bg-white dark:bg-theme-dark-container shadow-[-5px_0_40px_-4px_rgba(100,100,100,.1)] transition-transform duration-300',
{
'translate-x-0': open,
'translate-x-full': !open,
},
)}
>
{title && <div className="p-4 border-b border-solid border-gray-100">{title}</div>}
<div className="flex-1 overflow-y-auto px-2">
{list.map((item, index) => (
<>{chatParams?.chat_mode === 'chat_agent' ? <AgentContent key={index} content={item} /> : <ChatContent key={index} content={item} />}</>
))}
{!list.length && <MyEmpty description="" />}
</div>
<div className="flex w-full p-4 border-t border-solid border-gray-100 items-center">
{model && <div className="mr-2 flex">{renderModelIcon(model)}</div>}
<CompletionInput loading={loading} onSubmit={handleChat} />
</div>
<div
className="flex items-center justify-center rounded-tl rounded-bl cursor-pointer w-5 h-11 absolute top-[50%] -left-5 -translate-y-[50%] bg-white"
onClick={() => {
setOpen(!open);
}}
>
<CaretLeftOutlined />
</div>
</div>
);
}
export default ChatDialog;
================================================
FILE: components/common/completion-input.tsx
================================================
import { SendOutlined } from '@ant-design/icons';
import { Button, Input } from 'antd';
import { PropsWithChildren, useContext, useEffect, useMemo, useRef, useState } from 'react';
import PromptBot from './prompt-bot';
import DocUpload from '../chat/doc-upload';
import DocList from '../chat/doc-list';
import { IDocument } from '@/types/knowledge';
import { ChatContext } from '@/app/chat-context';
import { apiInterceptors, getDocumentList } from '@/client/api';
type TextAreaProps = Omit<Parameters<typeof Input.TextArea>[0], 'value' | 'onPressEnter' | 'onChange' | 'onSubmit'>;
interface Props {
loading?: boolean;
onSubmit: (val: string) => void;
handleFinish?: (val: boolean) => void;
}
function CompletionInput({ children, loading, onSubmit, handleFinish, ...props }: PropsWithChildren<Props & TextAreaProps>) {
const { dbParam, scene } = useContext(ChatContext);
const [userInput, setUserInput] = useState('');
const showUpload = useMemo(() => scene === 'chat_knowledge', [scene]);
const [documents, setDocuments] = useState<IDocument[]>([]);
const uploadCountRef = useRef(0);
useEffect(() => {
showUpload && fetchDocuments();
}, [dbParam]);
async function fetchDocuments() {
if (!dbParam) {
return null;
}
const [_, data] = await apiInterceptors(
getDocumentList(dbParam, {
page: 1,
page_size: uploadCountRef.current,
}),
);
setDocuments(data?.data!);
}
const onUploadFinish = async () => {
uploadCountRef.current += 1;
await fetchDocuments();
};
return (
<div className="flex-1 relative">
<DocList documents={documents} dbParam={dbParam} />
{showUpload && <DocUpload handleFinish={handleFinish} onUploadFinish={onUploadFinish} className="absolute z-10 top-2 left-2" />}
<Input.TextArea
className={`flex-1 ${showUpload ? 'pl-10' : ''} pr-10`}
size="large"
value={userInput}
autoSize={{ minRows: 1, maxRows: 4 }}
{...props}
onPressEnter={(e) => {
if (!userInput.trim()) return;
if (e.keyCode === 13) {
if (e.shiftKey) {
setUserInput((state) => state + '\n');
return;
}
onSubmit(userInput);
setTimeout(() => {
setUserInput('');
}, 0);
}
}}
onChange={(e) => {
if (typeof props.maxLength === 'number') {
setUserInput(e.target.value.substring(0, props.maxLength));
return;
}
setUserInput(e.target.value);
}}
/>
<Button
className="ml-2 flex items-center justify-center absolute right-0 bottom-0"
size="large"
type="text"
loading={loading}
icon={<SendOutlined />}
onClick={() => {
onSubmit(userInput);
}}
/>
<PromptBot
submit={(prompt) => {
setUserInput(userInput + prompt);
}}
/>
{children}
</div>
);
}
export default CompletionInput;
================================================
FILE: components/common/db-icon.tsx
================================================
import Image from 'next/image';
interface IProps {
width?: number;
height?: number;
src: string;
label: string;
className?: string;
}
function DBIcon({ src, label, width, height, className }: IProps) {
return (
<Image
className={`w-11 h-11 rounded-full mr-4 border border-gray-200 object-contain bg-white ${className}`}
width={width || 44}
height={height || 44}
src={src}
alt={label || 'db-icon'}
/>
);
}
export default DBIcon;
================================================
FILE: components/common/gpt-card.tsx
================================================
import React, { HtmlHTMLAttributes, PropsWithChildren, ReactNode, memo, useCallback, useMemo } from 'react';
import { Tag, TagProps, Tooltip } from 'antd';
import classNames from 'classnames';
import Image from 'next/image';
interface Props {
title: string
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
SYMBOL INDEX (357 symbols across 128 files)
FILE: app/chat-context.tsx
type ThemeMode (line 8) | type ThemeMode = 'dark' | 'light';
type IChatContext (line 10) | interface IChatContext {
function getDefaultTheme (line 36) | function getDefaultTheme(): ThemeMode {
FILE: app/i18n.ts
type I18nKeys (line 224) | type I18nKeys = keyof typeof en;
type Resources (line 226) | interface Resources {
type CustomTypeOptions (line 467) | interface CustomTypeOptions {
FILE: client/api/index.ts
type ResponseType (line 3) | type ResponseType<T = any> = {
type ApiResponse (line 10) | type ApiResponse<T = any, D = any> = AxiosResponse<ResponseType<T>, D>;
type SuccessTuple (line 12) | type SuccessTuple<T = any, D = any> = [null, T, ResponseType<T>, ApiResp...
type FailedTuple (line 14) | type FailedTuple<T = any, D = any> = [Error | AxiosError<T, D>, null, nu...
constant LONG_TIME_API (line 20) | const LONG_TIME_API: string[] = [
FILE: components/agent/market-plugins.tsx
function MarketPlugins (line 10) | function MarketPlugins() {
FILE: components/agent/my-plugins.tsx
function MyPlugins (line 10) | function MyPlugins() {
FILE: components/app/agent-panel.tsx
type IProps (line 7) | interface IProps {
function AgentPanel (line 14) | function AgentPanel(props: IProps) {
FILE: components/app/app-card.tsx
type IProps (line 11) | interface IProps {
function AppCard (line 20) | function AppCard(props: IProps) {
FILE: components/app/app-modal.tsx
type TargetKey (line 12) | type TargetKey = string;
type FieldType (line 14) | type FieldType = {
type IAgent (line 21) | type IAgent = {
type IProps (line 28) | interface IProps {
type TeamModals (line 36) | type TeamModals = 'awel_layout' | 'singe_agent' | 'auto_plan';
function AppModal (line 38) | function AppModal(props: IProps) {
FILE: components/app/dag-layout.tsx
type IProps (line 9) | interface IProps {
function DagLayout (line 14) | function DagLayout(props: IProps) {
FILE: components/app/resource-card.tsx
type IProps (line 7) | interface IProps {
function ResourceCard (line 14) | function ResourceCard(props: IProps) {
FILE: components/chart/autoChart/advisor/pipeline.ts
type CustomRecommendConfig (line 5) | type CustomRecommendConfig = {
FILE: components/chart/autoChart/advisor/utils.ts
function defaultAdvicesFilter (line 4) | function defaultAdvicesFilter(props: { advices: Advice[] }) {
function hasSubset (line 21) | function hasSubset(array1: any[], array2: any[]): boolean {
function intersects (line 25) | function intersects(array1: any[], array2: any[]): boolean {
function LOM2EncodingType (line 29) | function LOM2EncodingType(lom: string) {
FILE: components/chart/autoChart/charts/index.ts
type CustomChartsType (line 8) | type CustomChartsType = 'multi_line_chart' | 'multi_measure_column_chart...
FILE: components/chart/autoChart/charts/util.ts
type BasicDataPropertyForAdvice (line 1) | type BasicDataPropertyForAdvice = any;
function processDateEncode (line 9) | function processDateEncode(field: string, dataProps: BasicDataPropertyFo...
FILE: components/chart/autoChart/helpers/index.ts
type BackEndChartType (line 4) | type BackEndChartType =
type ChartType (line 13) | type ChartType = ChartId | CustomChartsType;
FILE: components/chart/autoChart/types.ts
type ChartType (line 3) | type ChartType = ChartId | string;
type Specification (line 5) | type Specification = Advice['spec'] | any;
type RuleConfig (line 6) | type RuleConfig = AdvisorConfig['ruleCfg'];
type AutoChartProps (line 8) | type AutoChartProps = {
type ChartKnowledge (line 21) | type ChartKnowledge = PureChartKnowledge & { toSpec?: any };
type CustomChart (line 23) | type CustomChart = {
type GetChartConfigProps (line 32) | type GetChartConfigProps = {
type CustomAdvisorConfig (line 39) | type CustomAdvisorConfig = {
FILE: components/chart/bar-chart.tsx
function BarChart (line 6) | function BarChart({ chart }: { key: string; chart: ChartData }) {
FILE: components/chart/index.tsx
type Props (line 8) | type Props = {
function Chart (line 12) | function Chart({ chartsData }: Props) {
FILE: components/chart/line-chart.tsx
function LineChart (line 6) | function LineChart({ chart }: { chart: ChartData }) {
FILE: components/chart/table-chart.tsx
function TableChart (line 5) | function TableChart({ chart }: { key: string; chart: ChartData }) {
FILE: components/chat/agent-content.tsx
type Props (line 9) | interface Props {
function formatMarkdownVal (line 13) | function formatMarkdownVal(val: string) {
function AgentContent (line 17) | function AgentContent({ content }: Props) {
FILE: components/chat/chat-content/agent-messages.tsx
type Props (line 6) | interface Props {
function AgentMessages (line 15) | function AgentMessages({ data }: Props) {
FILE: components/chat/chat-content/agent-plans.tsx
type Props (line 6) | interface Props {
function AgentPlans (line 16) | function AgentPlans({ data }: Props) {
FILE: components/chat/chat-content/chart-view.tsx
function ChartView (line 8) | function ChartView({ data, type, sql }: { data: Datum[]; type: BackEndCh...
FILE: components/chat/chat-content/code-preview.tsx
type Props (line 9) | interface Props {
function CodePreview (line 17) | function CodePreview({ code, light, dark, language, customStyle }: Props) {
FILE: components/chat/chat-content/config.tsx
type MarkdownComponent (line 19) | type MarkdownComponent = Parameters<typeof ReactMarkdown>['0']['componen...
function matchCustomeTagValues (line 23) | function matchCustomeTagValues(context: string) {
method code (line 36) | code({ inline, node, className, children, style, ...props }) {
method ul (line 124) | ul({ children }) {
method ol (line 127) | ol({ children }) {
method li (line 130) | li({ children, ordered }) {
method table (line 133) | table({ children }) {
method thead (line 138) | thead({ children }) {
method th (line 141) | th({ children }) {
method td (line 144) | td({ children }) {
method h1 (line 147) | h1({ children }) {
method h2 (line 150) | h2({ children }) {
method h3 (line 153) | h3({ children }) {
method h4 (line 156) | h4({ children }) {
method a (line 159) | a({ children, href }) {
method img (line 169) | img({ src, alt }) {
method blockquote (line 186) | blockquote({ children }) {
FILE: components/chat/chat-content/index.tsx
type Props (line 12) | interface Props {
type MarkdownComponent (line 25) | type MarkdownComponent = Parameters<typeof ReactMarkdown>['0']['componen...
type DBGPTView (line 27) | type DBGPTView = {
function formatMarkdownVal (line 53) | function formatMarkdownVal(val: string) {
function ChatContent (line 60) | function ChatContent({ children, content, isChartChat, onLinkClick }: Pr...
FILE: components/chat/chat-content/vis-chart.tsx
type Props (line 5) | interface Props {
function VisChart (line 15) | function VisChart({ data }: Props) {
FILE: components/chat/chat-content/vis-code.tsx
type Props (line 11) | interface Props {
function VisCode (line 20) | function VisCode({ data }: Props) {
FILE: components/chat/chat-content/vis-convert-error.tsx
type Props (line 4) | interface Props {
function VisConvertError (line 12) | function VisConvertError({ data }: Props) {
FILE: components/chat/chat-content/vis-dashboard.tsx
type Props (line 5) | interface Props {
function VisDashboard (line 22) | function VisDashboard({ data }: Props) {
FILE: components/chat/chat-content/vis-plugin.tsx
type IVisPlugin (line 8) | interface IVisPlugin {
type Props (line 19) | interface Props {
function VisPlugin (line 42) | function VisPlugin({ data }: Props) {
FILE: components/chat/chat-feedback.tsx
type Props (line 27) | type Props = {
function valueText (line 73) | function valueText(value: number) {
FILE: components/chat/completion.tsx
type Props (line 24) | type Props = {
FILE: components/chat/db-editor.tsx
type EditorValueProps (line 17) | interface EditorValueProps {
type RoundProps (line 24) | interface RoundProps {
type IProps (line 30) | interface IProps {
type ITableTreeItem (line 37) | interface ITableTreeItem {
function DbEditorContent (line 47) | function DbEditorContent({ editorValue, chartData, tableData, handleChan...
function DbEditor (line 95) | function DbEditor() {
FILE: components/chat/doc-list.tsx
type IProps (line 7) | interface IProps {
function DocList (line 12) | function DocList(props: IProps) {
FILE: components/chat/doc-upload.tsx
type IProps (line 9) | interface IProps {
function DocUpload (line 14) | function DocUpload(props: IProps) {
FILE: components/chat/header/agent-selector.tsx
function AgentSelector (line 8) | function AgentSelector() {
FILE: components/chat/header/chat-excel.tsx
type Props (line 6) | interface Props {
function ChatExcel (line 10) | function ChatExcel({ onComplete }: Props) {
FILE: components/chat/header/db-selector.tsx
function DBSelector (line 10) | function DBSelector() {
FILE: components/chat/header/excel-upload.tsx
type Props (line 7) | interface Props {
function ExcelUpload (line 13) | function ExcelUpload({ convUid, chatMode, onComplete, ...props }: PropsW...
FILE: components/chat/header/index.tsx
type Props (line 12) | interface Props {
function Header (line 17) | function Header({ refreshHistory, modelChange }: Props) {
FILE: components/chat/header/model-selector.tsx
type Props (line 12) | interface Props {
constant DEFAULT_ICON_URL (line 16) | const DEFAULT_ICON_URL = '/models/huggingface.svg';
function renderModelIcon (line 18) | function renderModelIcon(model?: string, props?: { width: number; height...
function ModelSelector (line 34) | function ModelSelector({ onChange }: Props) {
FILE: components/chat/mode-tab/index.tsx
function ModeTab (line 8) | function ModeTab() {
FILE: components/chat/monaco-editor.tsx
type MonacoEditorProps (line 9) | interface MonacoEditorProps {
function MonacoEditor (line 17) | function MonacoEditor({ className, value, language = 'mysql', onChange, ...
FILE: components/common/FileStatusIcon.tsx
type IProps (line 6) | interface IProps {
function FileStatusIcon (line 10) | function FileStatusIcon({ document }: IProps) {
FILE: components/common/MyEmpty.tsx
type Props (line 4) | interface Props {
function MyEmpty (line 10) | function MyEmpty({ error, description, refresh }: Props) {
FILE: components/common/chat-dialog.tsx
type Props (line 14) | interface Props {
function ChatDialog (line 24) | function ChatDialog({ title, chatMode, completionApi, chatParams, model ...
FILE: components/common/completion-input.tsx
type TextAreaProps (line 11) | type TextAreaProps = Omit<Parameters<typeof Input.TextArea>[0], 'value' ...
type Props (line 13) | interface Props {
function CompletionInput (line 19) | function CompletionInput({ children, loading, onSubmit, handleFinish, .....
FILE: components/common/db-icon.tsx
type IProps (line 3) | interface IProps {
function DBIcon (line 11) | function DBIcon({ src, label, width, height, className }: IProps) {
FILE: components/common/gpt-card.tsx
type Props (line 6) | interface Props {
function GPTCard (line 30) | function GPTCard({
FILE: components/common/icon-wrapper.tsx
type IconWrapperProps (line 4) | interface IconWrapperProps {
FILE: components/common/loading.tsx
function MuiLoading (line 3) | function MuiLoading({ visible }: { visible: boolean }) {
FILE: components/common/prompt-bot.tsx
type SelectTableProps (line 7) | type SelectTableProps = {
type PromptBotProps (line 48) | type PromptBotProps = {
FILE: components/database/form-dialog.tsx
type DBItem (line 9) | type DBItem = DbListResponse[0];
type Props (line 11) | interface Props {
function FormDialog (line 21) | function FormDialog({ open, choiceDBType, dbTypeList, editValue, dbNames...
FILE: components/flow/add-nodes.tsx
type GroupType (line 12) | type GroupType = { category: string; categoryLabel: string; nodes: IFlow...
function getNodes (line 26) | async function getNodes() {
function groupNodes (line 39) | function groupNodes(data: IFlowNode[]) {
function searchNode (line 91) | function searchNode(val: string) {
FILE: components/flow/button-edge.tsx
function onEdgeClick (line 15) | function onEdgeClick(event: React.MouseEvent, id: string) {
FILE: components/flow/canvas-node.tsx
type CanvasNodeProps (line 14) | type CanvasNodeProps = {
constant ICON_PATH_PREFIX (line 18) | const ICON_PATH_PREFIX = '/icons/node/';
function TypeLabel (line 20) | function TypeLabel({ label }: { label: string }) {
function onHover (line 30) | function onHover() {
function onLeave (line 34) | function onLeave() {
function copyNode (line 38) | function copyNode(e: React.MouseEvent<HTMLButtonElement, MouseEvent>) {
function deleteNode (line 67) | function deleteNode(e: React.MouseEvent<HTMLButtonElement, MouseEvent>) {
function renderOutput (line 74) | function renderOutput(data: IFlowNode) {
FILE: components/flow/flow-card.tsx
type FlowCardProps (line 21) | interface FlowCardProps {
function deleteFlow (line 33) | async function deleteFlow() {
function cardClick (line 40) | function cardClick() {
method onOk (line 65) | async onOk() {
FILE: components/flow/node-handler.tsx
type NodeHandlerProps (line 12) | interface NodeHandlerProps {
function isValidConnection (line 26) | function isValidConnection(connection: Connection) {
function showRelatedNodes (line 52) | function showRelatedNodes() {
FILE: components/flow/node-param-handler.tsx
type NodeParamHandlerProps (line 8) | interface NodeParamHandlerProps {
function handleChange (line 17) | function handleChange(value: any) {
FILE: components/flow/static-nodes.tsx
function onDragStart (line 9) | function onDragStart(event: DragEvent, node: IFlowNode) {
FILE: components/icons/add-icon.tsx
function AddIcon (line 3) | function AddIcon() {
FILE: components/icons/collect.tsx
function CollectIcon (line 3) | function CollectIcon() {
FILE: components/icons/collected.tsx
function CollectedIcon (line 3) | function CollectedIcon() {
FILE: components/icons/colorful-chat.tsx
function ColorfulChat (line 1) | function ColorfulChat() {
FILE: components/icons/colorful-dashboard.tsx
function ColorfulDashboard (line 1) | function ColorfulDashboard() {
FILE: components/icons/colorful-data.tsx
function ColorfulData (line 1) | function ColorfulData() {
FILE: components/icons/colorful-db.tsx
function ColorfulDB (line 1) | function ColorfulDB() {
FILE: components/icons/colorful-doc.tsx
function ColorfulDoc (line 1) | function ColorfulDoc() {
FILE: components/icons/colorful-excel.tsx
function ColorfulExcel (line 1) | function ColorfulExcel() {
FILE: components/icons/colorful-plugin.tsx
function ColorfulPlugin (line 1) | function ColorfulPlugin() {
FILE: components/icons/dark-svg.tsx
function DarkSvg (line 1) | function DarkSvg() {
FILE: components/icons/db-svg.tsx
function DBSvg (line 1) | function DBSvg() {
FILE: components/icons/done-icon.tsx
function DoneIcon (line 1) | function DoneIcon() {
FILE: components/icons/file-done.tsx
function FileDone (line 3) | function FileDone() {
FILE: components/icons/file-error.tsx
function FileError (line 3) | function FileError() {
FILE: components/icons/file-sync.tsx
function FileSync (line 3) | function FileSync() {
FILE: components/icons/knowledge.tsx
function knowledge (line 3) | function knowledge() {
FILE: components/icons/model-svg.tsx
function ModelSvg (line 1) | function ModelSvg() {
FILE: components/icons/pending-icon.tsx
function PendingIcon (line 1) | function PendingIcon() {
FILE: components/icons/stars-svg.tsx
function StarsSvg (line 1) | function StarsSvg() {
FILE: components/icons/sunny-svg.tsx
function SunnySvg (line 1) | function SunnySvg() {
FILE: components/icons/sync-icon.tsx
function SyncIcon (line 1) | function SyncIcon() {
FILE: components/knowledge/arguments-modal.tsx
type IProps (line 11) | interface IProps {
function ArgumentsModal (line 17) | function ArgumentsModal({ space, argumentsShow, setArgumentsShow }: IPro...
FILE: components/knowledge/doc-icon.tsx
function DocIcon (line 3) | function DocIcon({ type }: { type: string }) {
FILE: components/knowledge/doc-panel.tsx
type IProps (line 12) | interface IProps {
function DocPanel (line 20) | function DocPanel(props: IProps) {
FILE: components/knowledge/doc-type-form.tsx
type IProps (line 6) | type IProps = {
function DocTypeForm (line 10) | function DocTypeForm(props: IProps) {
FILE: components/knowledge/doc-upload-form.tsx
type FileParams (line 11) | type FileParams = {
type IProps (line 16) | type IProps = {
type FieldType (line 23) | type FieldType = {
function DocUploadForm (line 34) | function DocUploadForm(props: IProps) {
FILE: components/knowledge/segmentation.tsx
type IProps (line 10) | type IProps = {
type FieldType (line 17) | type FieldType = {
function Segmentation (line 23) | function Segmentation(props: IProps) {
FILE: components/knowledge/space-card.tsx
type IProps (line 12) | interface IProps {
function SpaceCard (line 20) | function SpaceCard(props: IProps) {
FILE: components/knowledge/space-form.tsx
type FieldType (line 7) | type FieldType = {
type IProps (line 13) | type IProps = {
function SpaceForm (line 17) | function SpaceForm(props: IProps) {
FILE: components/knowledge/strategy-form.tsx
type IProps (line 7) | type IProps = {
function StrategyForm (line 17) | function StrategyForm({ strategies, docType, fileName, field }: IProps) {
FILE: components/layout/side-bar.tsx
type SettingItem (line 31) | type SettingItem = {
type RouteItem (line 39) | type RouteItem = {
function menuItemStyle (line 46) | function menuItemStyle(active?: boolean) {
function smallMenuItemStyle (line 52) | function smallMenuItemStyle(active?: boolean) {
function SideBar (line 58) | function SideBar() {
FILE: components/layout/top-progress-bar.tsx
function load (line 9) | function load() {
function stop (line 21) | function stop() {
function TopProgressBar (line 59) | function TopProgressBar() {
FILE: components/model/model-card.tsx
type Props (line 11) | interface Props {
function ModelCard (line 15) | function ModelCard({ info }: Props) {
FILE: components/model/model-form.tsx
function ModelForm (line 10) | function ModelForm({ onCancel, onSuccess }: { onCancel: () => void; onSu...
FILE: components/model/model-params.tsx
type ParamValues (line 5) | interface ParamValues {
function ModelParams (line 9) | function ModelParams({ params, form }: { params: Array<SupportModelParam...
FILE: components/prompt/prompt-form.tsx
type IProps (line 6) | interface IProps {
FILE: genAntdCss.ts
type DoExtraStyleOptions (line 7) | type DoExtraStyleOptions = {
function doExtraStyle (line 12) | function doExtraStyle({ cache, dir = 'antd-output', baseFileName = 'antd...
FILE: global.d.ts
type IntrinsicElements (line 2) | interface IntrinsicElements {
FILE: hooks/use-chat.ts
type Props (line 6) | type Props = {
type ChatParams (line 10) | type ChatParams = {
method onopen (line 49) | async onopen(response) {
method onclose (line 54) | onclose() {
method onerror (line 58) | onerror(err) {
FILE: pages/_app.tsx
function CssWrapper (line 27) | function CssWrapper({ children }: { children: React.ReactElement }) {
function LayoutWrapper (line 59) | function LayoutWrapper({ children }: { children: React.ReactNode }) {
function MyApp (line 84) | function MyApp({ Component, pageProps }: AppProps) {
FILE: pages/_document.tsx
class MyDocument (line 5) | class MyDocument extends Document {
method getInitialProps (line 6) | static async getInitialProps(ctx: DocumentContext) {
method render (line 37) | render() {
FILE: pages/agent/index.tsx
function Agent (line 7) | function Agent() {
FILE: pages/app/index.tsx
type TabKey (line 11) | type TabKey = 'app' | 'collected';
type ModalType (line 13) | type ModalType = 'edit' | 'add';
function App (line 15) | function App() {
FILE: pages/chat/index.tsx
function Chat (line 9) | function Chat() {
FILE: pages/database/index.tsx
type DBItem (line 13) | type DBItem = DbListResponse[0];
function isFileDb (line 15) | function isFileDb(dbTypeList: DBOption[], dbType: DBType) {
function Database (line 19) | function Database() {
FILE: pages/flow/canvas/index.tsx
type Props (line 18) | interface Props {
function getFlowData (line 40) | async function getFlowData() {
function onNodesClick (line 68) | function onNodesClick(event: any, clickedNode: Node) {
function onConnect (line 87) | function onConnect(connection: Connection) {
function labelChange (line 142) | function labelChange(e: React.ChangeEvent<HTMLInputElement>) {
function clickSave (line 153) | function clickSave() {
function handleSaveFlow (line 178) | async function handleSaveFlow() {
method validator (line 260) | validator(_, value) {
function CanvasWrapper (line 314) | function CanvasWrapper() {
FILE: pages/flow/index.tsx
function Flow (line 12) | function Flow() {
FILE: pages/index.tsx
function renderSceneIcon (line 51) | function renderSceneIcon(scene: string) {
FILE: pages/knowledge/chunk/index.tsx
constant DEDAULT_PAGE_SIZE (line 8) | const DEDAULT_PAGE_SIZE = 10;
function ChunkList (line 10) | function ChunkList() {
FILE: pages/knowledge/index.tsx
function getSpaces (line 29) | async function getSpaces() {
function onAddDoc (line 55) | function onAddDoc(spaceName: string) {
FILE: pages/models/index.tsx
function Models (line 9) | function Models() {
FILE: pages/prompt/index.tsx
type FormType (line 70) | type FormType = Ref<FormInstance<any>> | undefined;
FILE: types/agent.ts
type PostAgentHubUpdateParams (line 1) | type PostAgentHubUpdateParams = {
type PostAgentQueryParams (line 8) | type PostAgentQueryParams = {
type IAgentPlugin (line 23) | type IAgentPlugin = {
type PostAgentPluginResponse (line 38) | type PostAgentPluginResponse = {
type IMyPlugin (line 46) | type IMyPlugin = {
type PostAgentMyPluginResponse (line 61) | type PostAgentMyPluginResponse = IMyPlugin[];
type GetDBGPTsListResponse (line 63) | type GetDBGPTsListResponse = {
FILE: types/app.ts
type IApp (line 2) | type IApp = {
type IAppData (line 34) | type IAppData = {
type AgentParams (line 42) | type AgentParams = {
type IAgent (line 63) | type IAgent = {
type ITeamModal (line 69) | type ITeamModal = {
type IResource (line 75) | type IResource = {
type IDetail (line 82) | type IDetail = {
FILE: types/chat.ts
type ChartValue (line 1) | type ChartValue = {
type ChartData (line 10) | type ChartData = {
type SceneResponse (line 20) | type SceneResponse = {
type NewDialogueParam (line 28) | type NewDialogueParam = {
type ChatHistoryResponse (line 33) | type ChatHistoryResponse = IChatDialogueMessageSchema[];
type IChatDialogueSchema (line 35) | type IChatDialogueSchema = {
type DialogueListResponse (line 52) | type DialogueListResponse = IChatDialogueSchema[];
type IChatDialogueMessageSchema (line 54) | type IChatDialogueMessageSchema = {
type ModelType (line 63) | type ModelType =
type LLMOption (line 98) | type LLMOption = { label: string; icon: string };
type FeedBack (line 100) | type FeedBack = {
type Reference (line 107) | type Reference = {
type IDB (line 112) | type IDB = {
FILE: types/db.ts
type DBOption (line 1) | type DBOption = { label: string; value: DBType; disabled?: boolean; isFi...
type DBType (line 3) | type DBType =
type IChatDbSchema (line 21) | type IChatDbSchema = {
type DbListResponse (line 32) | type DbListResponse = IChatDbSchema[];
type IChatDbSupportTypeSchema (line 34) | type IChatDbSupportTypeSchema = {
type DbSupportTypeResponse (line 39) | type DbSupportTypeResponse = IChatDbSupportTypeSchema[];
type PostDbParams (line 41) | type PostDbParams = Partial<DbListResponse[0] & { file_path: string }>;
type ChatFeedBackSchema (line 43) | type ChatFeedBackSchema = {
type PromptProps (line 53) | type PromptProps = {
FILE: types/editor.ts
type IEditorSQLRound (line 1) | type IEditorSQLRound = {
type GetEditorSQLRoundRequest (line 7) | type GetEditorSQLRoundRequest = IEditorSQLRound[];
type PostEditorSQLRunParams (line 9) | type PostEditorSQLRunParams = {
type PostEditorChartRunParams (line 14) | type PostEditorChartRunParams = {
type PostEditorChartRunResponse (line 20) | type PostEditorChartRunResponse = {
type PostSQLEditorSubmitParams (line 31) | type PostSQLEditorSubmitParams = {
type PostEditorSqlParams (line 41) | type PostEditorSqlParams = {
type PostEditorSqlRequest (line 46) | type PostEditorSqlRequest = {};
type GetEditorySqlParams (line 48) | type GetEditorySqlParams = { con_uid: string; round: string | number };
FILE: types/flow.ts
type FlowState (line 3) | type FlowState = 'deployed' | 'developing' | 'initializing' | 'testing' ...
type IFlowUpdateParam (line 5) | type IFlowUpdateParam = {
type IFlow (line 15) | type IFlow = {
type IFlowResponse (line 30) | type IFlowResponse = {
type IFlowNodeParameter (line 38) | type IFlowNodeParameter = {
type IFlowNodeInput (line 54) | type IFlowNodeInput = {
type IFlowNodeOutput (line 66) | type IFlowNodeOutput = {
type IFlowNode (line 77) | type IFlowNode = Node & {
type Position (line 98) | interface Position {
type IFlowDataNode (line 105) | type IFlowDataNode = {
type IFlowDataEdge (line 116) | type IFlowDataEdge = {
type IFlowDataViewport (line 127) | type IFlowDataViewport = {
type IFlowData (line 133) | type IFlowData = {
FILE: types/knowledge.ts
type ISpace (line 3) | interface ISpace {
type AddKnowledgeParams (line 14) | type AddKnowledgeParams = {
type BaseDocumentParams (line 21) | type BaseDocumentParams = {
type Embedding (line 27) | type Embedding = {
type Prompt (line 36) | type Prompt = {
type Summary (line 42) | type Summary = {
type IArguments (line 46) | type IArguments = {
type DocumentParams (line 52) | type DocumentParams = {
type IDocument (line 59) | type IDocument = {
type IDocumentResponse (line 75) | type IDocumentResponse = {
type IStrategyParameter (line 81) | type IStrategyParameter = {
type IChunkStrategyResponse (line 88) | type IChunkStrategyResponse = {
type IStrategyProps (line 96) | type IStrategyProps = {
type ISyncBatchParameter (line 102) | type ISyncBatchParameter = {
type ISyncBatchResponse (line 108) | type ISyncBatchResponse = {
type ChunkListParams (line 112) | type ChunkListParams = {
type IChunk (line 118) | type IChunk = {
type IChunkList (line 129) | type IChunkList = {
type ArgumentsParams (line 135) | type ArgumentsParams = {
type StepChangeParams (line 139) | type StepChangeParams = {
type File (line 146) | type File = {
type SummaryParams (line 152) | type SummaryParams = {
FILE: types/model.ts
type IModelData (line 1) | type IModelData = {
type BaseModelParams (line 17) | type BaseModelParams = {
type ModelParams (line 25) | type ModelParams = {
type StartModelParams (line 34) | type StartModelParams = {
type ExtMetadata (line 42) | interface ExtMetadata {
type SupportModelParams (line 46) | type SupportModelParams = {
type SupportModel (line 57) | type SupportModel = {
FILE: types/prompt.ts
type PromptParams (line 1) | type PromptParams = {
type UpdatePromptParams (line 9) | interface UpdatePromptParams extends IPrompt {
type IPrompt (line 13) | interface IPrompt {
FILE: utils/constants.ts
constant MODEL_ICON_MAP (line 4) | const MODEL_ICON_MAP: Record<ModelType, { label: string; icon: string }>...
FILE: utils/index.ts
constant STORAGE_THEME_KEY (line 2) | const STORAGE_THEME_KEY = '__db_gpt_theme_key';
constant STORAGE_LANG_KEY (line 4) | const STORAGE_LANG_KEY = '__db_gpt_lng_key';
constant STORAGE_INIT_MESSAGE_KET (line 6) | const STORAGE_INIT_MESSAGE_KET = '__db_gpt_im_key';
constant FLOW_NODES_KEY (line 8) | const FLOW_NODES_KEY = '__db_gpt_static_flow_nodes_key';
FILE: utils/request.ts
constant DEFAULT_HEADERS (line 5) | const DEFAULT_HEADERS = {
FILE: utils/storage.ts
function getInitMessage (line 3) | function getInitMessage() {
Condensed preview — 163 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (438K chars).
[
{
"path": ".eslintrc.json",
"chars": 40,
"preview": "{\n \"extends\": \"next/core-web-vitals\"\n}\n"
},
{
"path": ".github/ISSUE_TEMPLATE/✨-feature-request.md",
"chars": 603,
"preview": "---\nname: \"✨ Feature Request\"\nabout: Suggest a new feature idea.\ntitle: \"[feature]\"\nlabels: ''\nassignees: ''\n\n---\n\n**Is "
},
{
"path": ".github/ISSUE_TEMPLATE/🐛-bug-report.md",
"chars": 992,
"preview": "---\nname: \"\\U0001F41B Bug report\"\nabout: Report an unexpected problem or unintended behavior.\ntitle: \"[BUG] \"\nlabels: ''"
},
{
"path": ".github/ISSUE_TEMPLATE/📜-documentation.md",
"chars": 681,
"preview": "---\nname: \"\\U0001F4DC Documentation\"\nabout: Correct spelling errors, improvements or additions to documentation files (R"
},
{
"path": ".github/ISSUE_TEMPLATE/🔧-improvement.md",
"chars": 301,
"preview": "---\nname: \"\\U0001F527 Improvement\"\nabout: Suggest an idea which is not a feature.\ntitle: ''\nlabels: ''\nassignees: ''\n\n--"
},
{
"path": ".github/ISSUE_TEMPLATE/🙋-question.md",
"chars": 283,
"preview": "---\nname: \"\\U0001F64B Question\"\nabout: Ask a question about DB-GPT-Web.\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n<!--\nTh"
},
{
"path": ".gitignore",
"chars": 387,
"preview": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pn"
},
{
"path": ".npmrc",
"chars": 34,
"preview": "registry=http://registry.npmjs.org"
},
{
"path": ".vscode/extensions.json",
"chars": 269,
"preview": "{\n \"recommendations\": [\n \"bradlc.vscode-tailwindcss\",\n \"esbenp.prettier-vscode\",\n \"dbaeumer.vscode-eslint\",\n "
},
{
"path": ".vscode/settings.json",
"chars": 689,
"preview": "{\n \"editor.formatOnSave\": true,\n \"editor.codeActionsOnSave\": {\n \"source.fixAll\": \"explicit\"\n },\n \"prettier.arrowP"
},
{
"path": "README.md",
"chars": 2853,
"preview": "\n<h1 align=\"center\">\n <a href=\"https://dbgpt.site\"><img width=\"96\" src=\"https://github.com/eosphoros-ai/DB-GPT-Web/asse"
},
{
"path": "app/chat-context.tsx",
"chars": 4128,
"preview": "import { createContext, useEffect, useMemo, useState } from 'react';\nimport { apiInterceptors, getDialogueList, getUsabl"
},
{
"path": "app/i18n.ts",
"chars": 14079,
"preview": "import i18n from 'i18next';\nimport { initReactI18next } from 'react-i18next';\n\nconst en = {\n Knowledge_Space: 'Knowledg"
},
{
"path": "client/api/index.ts",
"chars": 2216,
"preview": "import axios, { AxiosRequestConfig, AxiosError, AxiosResponse } from 'axios';\n\nexport type ResponseType<T = any> = {\n d"
},
{
"path": "client/api/request.ts",
"chars": 11617,
"preview": "import { AxiosRequestConfig } from 'axios';\nimport { DELETE, GET, POST, PUT } from '.';\nimport { DbListResponse, DbSuppo"
},
{
"path": "client/api/tools/index.ts",
"chars": 32,
"preview": "export * from './interceptors';\n"
},
{
"path": "client/api/tools/interceptors.ts",
"chars": 1488,
"preview": "import { AxiosError } from 'axios';\nimport { ApiResponse, FailedTuple, SuccessTuple, ResponseType } from '../';\nimport {"
},
{
"path": "components/agent/market-plugins.tsx",
"chars": 4876,
"preview": "import { apiInterceptors, postAgentHubUpdate, postAgentInstall, postAgentQuery, postAgentUninstall } from '@/client/api'"
},
{
"path": "components/agent/my-plugins.tsx",
"chars": 3924,
"preview": "import { apiInterceptors, postAgentMy, postAgentUninstall, postAgentUpload } from '@/client/api';\nimport { IMyPlugin } f"
},
{
"path": "components/app/agent-panel.tsx",
"chars": 4782,
"preview": "import { apiInterceptors, getAppStrategy, getAppStrategyValues, getResource } from '@/client/api';\nimport { Button, Inpu"
},
{
"path": "components/app/app-card.tsx",
"chars": 3078,
"preview": "import React, { useContext, useEffect, useState } from 'react';\nimport { Modal } from 'antd';\nimport { apiInterceptors, "
},
{
"path": "components/app/app-modal.tsx",
"chars": 10615,
"preview": "import { AgentParams, IAgent as IAgentParams, IApp, IDetail } from '@/types/app';\nimport { Dropdown, Form, Input, Modal,"
},
{
"path": "components/app/dag-layout.tsx",
"chars": 1952,
"preview": "import React, { useEffect, useState } from 'react';\nimport PreviewFlow from '../flow/preview-flow';\nimport { apiIntercep"
},
{
"path": "components/app/resource-card.tsx",
"chars": 4484,
"preview": "import { apiInterceptors, getResource } from '@/client/api';\nimport { DeleteFilled } from '@ant-design/icons';\nimport { "
},
{
"path": "components/chart/autoChart/advisor/pipeline.ts",
"chars": 2486,
"preview": "import { Advisor, CkbConfig } from '@antv/ava';\nimport type { Advice, AdviseParams, AdvisorConfig, ChartKnowledgeBase } "
},
{
"path": "components/chart/autoChart/advisor/rule.ts",
"chars": 33,
"preview": "export const customRuleCfg = {};\n"
},
{
"path": "components/chart/autoChart/advisor/utils.ts",
"chars": 1075,
"preview": "import { isNull } from 'lodash';\nimport type { Advice } from '@antv/ava';\n\nexport function defaultAdvicesFilter(props: {"
},
{
"path": "components/chart/autoChart/charts/index.ts",
"chars": 464,
"preview": "import multi_line_chart from './multi-line-chart';\nimport multi_measure_column_chart from './multi-measure-column-chart'"
},
{
"path": "components/chart/autoChart/charts/multi-line-chart.ts",
"chars": 2085,
"preview": "import { hasSubset, intersects } from '../advisor/utils';\nimport { processDateEncode } from './util';\nimport type { Char"
},
{
"path": "components/chart/autoChart/charts/multi-measure-column-chart.ts",
"chars": 1973,
"preview": "import { hasSubset } from '../advisor/utils';\n\nimport type { ChartKnowledge, CustomChart, GetChartConfigProps, Specifica"
},
{
"path": "components/chart/autoChart/charts/multi-measure-line-chart.ts",
"chars": 2048,
"preview": "import { hasSubset, intersects } from '../advisor/utils';\nimport { processDateEncode } from './util';\nimport type { Char"
},
{
"path": "components/chart/autoChart/charts/util.ts",
"chars": 399,
"preview": "type BasicDataPropertyForAdvice = any;\n\n/**\n * Process date column to new Date().\n * @param field\n * @param dataProps\n *"
},
{
"path": "components/chart/autoChart/helpers/index.ts",
"chars": 993,
"preview": "import { ChartId } from '@antv/ava';\nimport { CustomChartsType } from '../charts';\n\nexport type BackEndChartType =\n | '"
},
{
"path": "components/chart/autoChart/index.tsx",
"chars": 3403,
"preview": "import { Empty, Row, Col, Select, Tooltip } from 'antd';\nimport { Advice, Advisor } from '@antv/ava';\nimport { Chart } f"
},
{
"path": "components/chart/autoChart/types.ts",
"chars": 1084,
"preview": "import { Advice, AdvisorConfig, ChartId, Datum, FieldInfo, PureChartKnowledge } from '@antv/ava';\n\nexport type ChartType"
},
{
"path": "components/chart/bar-chart.tsx",
"chars": 1051,
"preview": "import { ChatContext } from '@/app/chat-context';\nimport { ChartData } from '@/types/chat';\nimport { Chart } from '@berr"
},
{
"path": "components/chart/index.tsx",
"chars": 2854,
"preview": "import { Card, CardContent, Typography } from '@mui/joy';\nimport BarChart from './bar-chart';\nimport LineChart from './l"
},
{
"path": "components/chart/line-chart.tsx",
"chars": 1640,
"preview": "import { ChartData } from '@/types/chat';\nimport { Chart } from '@berryv/g2-react';\nimport { useContext } from 'react';\n"
},
{
"path": "components/chart/table-chart.tsx",
"chars": 1190,
"preview": "import { ChartData } from '@/types/chat';\nimport { Table } from '@mui/joy';\nimport { groupBy } from 'lodash';\n\nexport de"
},
{
"path": "components/chat/agent-content.tsx",
"chars": 1223,
"preview": "import { ChatContext } from '@/app/chat-context';\nimport { IChatDialogueMessageSchema } from '@/types/chat';\nimport clas"
},
{
"path": "components/chat/chat-container.tsx",
"chars": 4771,
"preview": "import React, { useCallback, useContext, useEffect, useState } from 'react';\nimport { useAsyncEffect } from 'ahooks';\nim"
},
{
"path": "components/chat/chat-content/agent-messages.tsx",
"chars": 1133,
"preview": "import ReactMarkdown from 'react-markdown';\nimport markdownComponents from './config';\nimport { renderModelIcon } from '"
},
{
"path": "components/chat/chat-content/agent-plans.tsx",
"chars": 1242,
"preview": "import { CaretRightOutlined, CheckOutlined, ClockCircleOutlined } from '@ant-design/icons';\nimport { Collapse } from 'an"
},
{
"path": "components/chat/chat-content/chart-view.tsx",
"chars": 1286,
"preview": "import { Datum } from '@antv/ava';\nimport { Table, Tabs, TabsProps } from 'antd';\nimport React from 'react';\nimport { fo"
},
{
"path": "components/chat/chat-content/code-preview.tsx",
"chars": 1261,
"preview": "import { Button, message } from 'antd';\nimport { CopyOutlined } from '@ant-design/icons';\nimport { oneDark, coldarkDark "
},
{
"path": "components/chat/chat-content/config.tsx",
"chars": 11079,
"preview": "import { LinkOutlined, ReadOutlined, SyncOutlined } from '@ant-design/icons';\nimport ReactMarkdown from 'react-markdown'"
},
{
"path": "components/chat/chat-content/index.tsx",
"chars": 6074,
"preview": "import { PropsWithChildren, ReactNode, memo, useContext, useMemo } from 'react';\nimport { CheckOutlined, ClockCircleOutl"
},
{
"path": "components/chat/chat-content/vis-chart.tsx",
"chars": 408,
"preview": "import { BackEndChartType } from '@/components/chart';\nimport ChartView from './chart-view';\nimport { Datum } from '@ant"
},
{
"path": "components/chat/chat-content/vis-code.tsx",
"chars": 2214,
"preview": "import ReactMarkdown from 'react-markdown';\nimport remarkGfm from 'remark-gfm';\nimport markdownComponents from './config"
},
{
"path": "components/chat/chat-content/vis-convert-error.tsx",
"chars": 615,
"preview": "import { format } from 'sql-formatter';\nimport { CodePreview } from './code-preview';\n\ninterface Props {\n data: {\n d"
},
{
"path": "components/chat/chat-content/vis-dashboard.tsx",
"chars": 1720,
"preview": "import { AutoChart, BackEndChartType, getChartType } from '@/components/chart';\nimport { Datum } from '@antv/ava';\nimpor"
},
{
"path": "components/chat/chat-content/vis-plugin.tsx",
"chars": 1816,
"preview": "import { CheckOutlined, ClockCircleOutlined, CloseOutlined, LoadingOutlined } from '@ant-design/icons';\nimport className"
},
{
"path": "components/chat/chat-feedback.tsx",
"chars": 7330,
"preview": "import React, { useState, useRef, useCallback, useEffect, useContext } from 'react';\nimport { MoreHoriz, CloseRounded } "
},
{
"path": "components/chat/completion.tsx",
"chars": 8881,
"preview": "import { useState, useRef, useEffect, useMemo, useContext } from 'react';\nimport { useSearchParams } from 'next/navigati"
},
{
"path": "components/chat/db-editor.tsx",
"chars": 17809,
"preview": "import React from 'react';\nimport { useRequest } from 'ahooks';\nimport { Select, Option, Table, Box, Typography, Tooltip"
},
{
"path": "components/chat/doc-list.tsx",
"chars": 1465,
"preview": "import { IDocument } from '@/types/knowledge';\nimport { Button, Tooltip } from 'antd';\nimport { useRouter } from 'next/r"
},
{
"path": "components/chat/doc-upload.tsx",
"chars": 1584,
"preview": "import { ChatContext } from '@/app/chat-context';\nimport { apiInterceptors, uploadDocument } from '@/client/api';\n\nimpor"
},
{
"path": "components/chat/header/agent-selector.tsx",
"chars": 835,
"preview": "import { ChatContext } from '@/app/chat-context';\nimport { apiInterceptors, getDbgptsList } from '@/client/api';\nimport "
},
{
"path": "components/chat/header/chat-excel.tsx",
"chars": 1044,
"preview": "import ExcelUpload from './excel-upload';\nimport { LinkOutlined } from '@ant-design/icons';\nimport { useContext } from '"
},
{
"path": "components/chat/header/db-selector.tsx",
"chars": 1448,
"preview": "import { ChatContext } from '@/app/chat-context';\nimport { apiInterceptors, postChatModeParamsList } from '@/client/api'"
},
{
"path": "components/chat/header/excel-upload.tsx",
"chars": 3635,
"preview": "import { PropsWithChildren, useContext, useState } from 'react';\nimport { Upload, UploadProps, Button, message, UploadFi"
},
{
"path": "components/chat/header/index.tsx",
"chars": 1168,
"preview": "import { useContext } from 'react';\nimport ChatExcel from './chat-excel';\nimport { ChatContext } from '@/app/chat-contex"
},
{
"path": "components/chat/header/model-selector.tsx",
"chars": 1524,
"preview": "/**\n * multi-models selector\n */\n\nimport { ChatContext } from '@/app/chat-context';\nimport { Select } from 'antd';\nimpor"
},
{
"path": "components/chat/mode-tab/index.css",
"chars": 222,
"preview": ".model-tab::before {\n content: \"\";\n position: absolute;\n left: 0.4rem;\n width: 45%;\n height: 80%;\n background-colo"
},
{
"path": "components/chat/mode-tab/index.tsx",
"chars": 937,
"preview": "import './index.css';\nimport { useContext } from 'react';\nimport { ChatContext } from '@/app/chat-context';\nimport { Rad"
},
{
"path": "components/chat/monaco-editor.tsx",
"chars": 1089,
"preview": "import * as monaco from 'monaco-editor/esm/vs/editor/editor.api.js';\nimport Editor, { OnChange, loader } from '@monaco-e"
},
{
"path": "components/common/FileStatusIcon.tsx",
"chars": 492,
"preview": "import { IDocument } from '@/types/knowledge';\nimport React from 'react';\nimport { FileDone, FileSync } from '../icons';"
},
{
"path": "components/common/MyEmpty.tsx",
"chars": 740,
"preview": "import { Button, Empty } from 'antd';\nimport { useTranslation } from 'react-i18next';\n\ninterface Props {\n error?: boole"
},
{
"path": "components/common/chat-dialog.tsx",
"chars": 3810,
"preview": "import useChat from '@/hooks/use-chat';\nimport CompletionInput from './completion-input';\nimport { useCallback, useState"
},
{
"path": "components/common/completion-input.tsx",
"chars": 3057,
"preview": "import { SendOutlined } from '@ant-design/icons';\nimport { Button, Input } from 'antd';\nimport { PropsWithChildren, useC"
},
{
"path": "components/common/db-icon.tsx",
"chars": 483,
"preview": "import Image from 'next/image';\n\ninterface IProps {\n width?: number;\n height?: number;\n src: string;\n label: string;"
},
{
"path": "components/common/gpt-card.tsx",
"chars": 3912,
"preview": "import React, { HtmlHTMLAttributes, PropsWithChildren, ReactNode, memo, useCallback, useMemo } from 'react';\nimport { Ta"
},
{
"path": "components/common/icon-wrapper.tsx",
"chars": 577,
"preview": "import classNames from 'classnames';\nimport React from 'react';\n\ninterface IconWrapperProps {\n children: React.ReactNod"
},
{
"path": "components/common/loading.tsx",
"chars": 436,
"preview": "import { LoadingOutlined } from '@ant-design/icons';\n\nfunction MuiLoading({ visible }: { visible: boolean }) {\n if (!vi"
},
{
"path": "components/common/prompt-bot.tsx",
"chars": 3188,
"preview": "import { useState } from 'react';\nimport { List, FloatButton, Popover, Tooltip, Form, message, Select, ConfigProvider } "
},
{
"path": "components/database/form-dialog.tsx",
"chars": 4371,
"preview": "/* eslint-disable react-hooks/exhaustive-deps */\nimport { Button, Form, Input, InputNumber, Modal, Select, message } fro"
},
{
"path": "components/flow/add-nodes.tsx",
"chars": 5107,
"preview": "import { apiInterceptors, getFlowNodes } from '@/client/api';\nimport { IFlowNode } from '@/types/flow';\nimport { PlusOut"
},
{
"path": "components/flow/button-edge.tsx",
"chars": 1315,
"preview": "import React from 'react';\nimport { getBezierPath, EdgeProps, BaseEdge, useReactFlow } from 'reactflow';\n\nconst ButtonEd"
},
{
"path": "components/flow/canvas-node.tsx",
"chars": 5249,
"preview": "import { IFlowNode } from '@/types/flow';\nimport Image from 'next/image';\nimport NodeParamHandler from './node-param-han"
},
{
"path": "components/flow/flow-card.tsx",
"chars": 3611,
"preview": "import { apiInterceptors, deleteFlowById, newDialogue } from '@/client/api';\nimport { IFlow } from '@/types/flow';\nimpor"
},
{
"path": "components/flow/node-handler.tsx",
"chars": 6236,
"preview": "import { Popconfirm, Tooltip, Typography, message } from 'antd';\nimport React from 'react';\nimport { Connection, Handle,"
},
{
"path": "components/flow/node-param-handler.tsx",
"chars": 3380,
"preview": "import { IFlowNode, IFlowNodeParameter } from '@/types/flow';\nimport { Checkbox, Input, InputNumber, Select, Tooltip } f"
},
{
"path": "components/flow/preview-flow.tsx",
"chars": 629,
"preview": "import { IFlowData } from '@/types/flow';\nimport React from 'react';\nimport ReactFlow, { Background } from 'reactflow';\n"
},
{
"path": "components/flow/required-icon.tsx",
"chars": 269,
"preview": "import React from 'react';\n\nconst RequiredIcon: React.FC<{ optional?: boolean | undefined }> = ({ optional }) => {\n if "
},
{
"path": "components/flow/static-nodes.tsx",
"chars": 1382,
"preview": "import { IFlowNode } from '@/types/flow';\nimport { Avatar, Empty, List } from 'antd';\nimport React, { DragEvent } from '"
},
{
"path": "components/icons/add-icon.tsx",
"chars": 413,
"preview": "import React from 'react';\n\nexport default function AddIcon() {\n return (\n <svg viewBox=\"0 0 1024 1024\" version=\"1.1"
},
{
"path": "components/icons/collect.tsx",
"chars": 2002,
"preview": "import React from 'react';\n\nexport default function CollectIcon() {\n return (\n <svg viewBox=\"0 0 1040 1024\" version="
},
{
"path": "components/icons/collected.tsx",
"chars": 935,
"preview": "import React from 'react';\n\nexport default function CollectedIcon() {\n return (\n <svg viewBox=\"0 0 1024 1024\" versi"
},
{
"path": "components/icons/colorful-chat.tsx",
"chars": 3034,
"preview": "function ColorfulChat() {\n return (\n <svg className=\"w-full h-full\" viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"htt"
},
{
"path": "components/icons/colorful-dashboard.tsx",
"chars": 1647,
"preview": "function ColorfulDashboard() {\n return (\n <svg viewBox=\"0 0 1116 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/s"
},
{
"path": "components/icons/colorful-data.tsx",
"chars": 1890,
"preview": "function ColorfulData() {\n return (\n <svg viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p"
},
{
"path": "components/icons/colorful-db.tsx",
"chars": 5772,
"preview": "function ColorfulDB() {\n return (\n <svg viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-i"
},
{
"path": "components/icons/colorful-doc.tsx",
"chars": 978,
"preview": "function ColorfulDoc() {\n return (\n <svg viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-"
},
{
"path": "components/icons/colorful-excel.tsx",
"chars": 1576,
"preview": "function ColorfulExcel() {\n return (\n <svg viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" "
},
{
"path": "components/icons/colorful-plugin.tsx",
"chars": 1529,
"preview": "function ColorfulPlugin() {\n return (\n <svg viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\""
},
{
"path": "components/icons/dark-svg.tsx",
"chars": 495,
"preview": "function DarkSvg() {\n return (\n <svg width=\"1em\" height=\"1em\" fill=\"currentColor\" viewBox=\"0 0 1024 1024\">\n <pa"
},
{
"path": "components/icons/db-svg.tsx",
"chars": 838,
"preview": "function DBSvg() {\n return (\n <svg width=\"1em\" height=\"1em\" fill=\"currentColor\" viewBox=\"0 0 1024 1024\" version=\"1.1"
},
{
"path": "components/icons/done-icon.tsx",
"chars": 1280,
"preview": "export default function DoneIcon() {\n return (\n <svg viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/"
},
{
"path": "components/icons/file-done.tsx",
"chars": 1325,
"preview": "import React from 'react';\n\nexport default function FileDone() {\n return (\n <svg className=\"mr-1\" viewBox=\"0 0 1024 "
},
{
"path": "components/icons/file-error.tsx",
"chars": 1263,
"preview": "import React from 'react';\n\nexport default function FileError() {\n return (\n <svg className=\"mr-1\" viewBox=\"0 0 1024"
},
{
"path": "components/icons/file-sync.tsx",
"chars": 1504,
"preview": "import React from 'react';\n\nexport default function FileSync() {\n return (\n <svg className=\"mr-1\" viewBox=\"0 0 1024 "
},
{
"path": "components/icons/index.tsx",
"chars": 1026,
"preview": "import ColorfulDashboard from './colorful-dashboard';\nimport ColorfulData from './colorful-data';\nimport ColorfulDB from"
},
{
"path": "components/icons/knowledge.tsx",
"chars": 1029,
"preview": "import React from 'react';\n\nexport default function knowledge() {\n return (\n <svg viewBox=\"0 0 1024 1024\" version=\"1"
},
{
"path": "components/icons/model-svg.tsx",
"chars": 1367,
"preview": "function ModelSvg() {\n return (\n <svg width=\"1em\" height=\"1em\" fill=\"currentColor\" viewBox=\"0 0 1024 1024\" version=\""
},
{
"path": "components/icons/pending-icon.tsx",
"chars": 791,
"preview": "export default function PendingIcon() {\n return (\n <svg viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.o"
},
{
"path": "components/icons/stars-svg.tsx",
"chars": 1188,
"preview": "function StarsSvg() {\n return (\n <svg width=\"1em\" height=\"1em\" fill=\"currentColor\" viewBox=\"0 0 1024 1024\" version=\""
},
{
"path": "components/icons/sunny-svg.tsx",
"chars": 632,
"preview": "function SunnySvg() {\n return (\n <svg width=\"1em\" height=\"1em\" fill=\"currentColor\" viewBox=\"0 0 1024 1024\">\n <p"
},
{
"path": "components/icons/sync-icon.tsx",
"chars": 1968,
"preview": "export default function SyncIcon() {\n return (\n <svg viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/"
},
{
"path": "components/knowledge/arguments-modal.tsx",
"chars": 5792,
"preview": "import React, { useEffect, useState } from 'react';\nimport { Modal, Tabs, Button, Input, Form, Col, Row, Spin } from 'an"
},
{
"path": "components/knowledge/doc-icon.tsx",
"chars": 443,
"preview": "import { FileTextFilled, FileWordTwoTone, IeCircleFilled } from '@ant-design/icons';\n\nexport default function DocIcon({ "
},
{
"path": "components/knowledge/doc-panel.tsx",
"chars": 7102,
"preview": "import React, { useEffect, useMemo, useRef, useState } from 'react';\nimport { Button, Card, Space, Divider, Empty, Spin,"
},
{
"path": "components/knowledge/doc-type-form.tsx",
"chars": 1255,
"preview": "import { StepChangeParams } from '@/types/knowledge';\nimport { Card } from 'antd';\nimport { useTranslation } from 'react"
},
{
"path": "components/knowledge/doc-upload-form.tsx",
"chars": 6461,
"preview": "import { Button, Form, Input, Upload, Spin, message } from 'antd';\nimport React, { useState } from 'react';\nimport { use"
},
{
"path": "components/knowledge/segmentation.tsx",
"chars": 6783,
"preview": "import { apiInterceptors, getChunkStrategies, getDocumentList, syncBatchDocument } from '@/client/api';\nimport { File, I"
},
{
"path": "components/knowledge/space-card.tsx",
"chars": 3068,
"preview": "import { Popover, ConfigProvider, Modal, Badge } from 'antd';\nimport { useRouter } from 'next/router';\nimport Image from"
},
{
"path": "components/knowledge/space-form.tsx",
"chars": 2550,
"preview": "import { addSpace, apiInterceptors } from '@/client/api';\nimport { StepChangeParams } from '@/types/knowledge';\nimport {"
},
{
"path": "components/knowledge/strategy-form.tsx",
"chars": 3103,
"preview": "import { IChunkStrategyResponse } from '@/types/knowledge';\nimport { Alert, Checkbox, Form, FormListFieldData, Input, In"
},
{
"path": "components/layout/side-bar.tsx",
"chars": 11283,
"preview": "import { ChatContext } from '@/app/chat-context';\nimport { apiInterceptors, delDialogue } from '@/client/api';\nimport { "
},
{
"path": "components/layout/top-progress-bar.tsx",
"chars": 1187,
"preview": "import Router from 'next/router';\nimport NProgress from 'nprogress';\n\nlet timer: any;\nlet state: any;\nlet activeRequests"
},
{
"path": "components/model/model-card.tsx",
"chars": 2451,
"preview": "import React, { useState } from 'react';\nimport { IModelData } from '@/types/model';\nimport { useTranslation } from 'rea"
},
{
"path": "components/model/model-form.tsx",
"chars": 3440,
"preview": "import { apiInterceptors, getSupportModels, startModel } from '@/client/api';\nimport { SupportModel, SupportModelParams "
},
{
"path": "components/model/model-params.tsx",
"chars": 1615,
"preview": "import { SupportModelParams } from '@/types/model';\nimport { Checkbox, Form, FormInstance, Input, InputNumber } from 'an"
},
{
"path": "components/prompt/prompt-form.tsx",
"chars": 2003,
"preview": "import React, { Ref, forwardRef, useEffect, useState } from 'react';\nimport { Form, Input, Spin, Select, FormInstance } "
},
{
"path": "defaultTheme.ts",
"chars": 1600,
"preview": "import { extendTheme } from '@mui/joy/styles';\nimport colors from '@mui/joy/colors';\n\nexport const joyTheme = extendThem"
},
{
"path": "genAntdCss.ts",
"chars": 1029,
"preview": "import { createHash } from 'crypto';\nimport fs from 'fs';\nimport path from 'path';\nimport { extractStyle } from '@ant-de"
},
{
"path": "global.d.ts",
"chars": 498,
"preview": "declare namespace JSX {\n interface IntrinsicElements {\n summary: React.DetailedHTMLProps<React.HTMLAttributes<HTMLEl"
},
{
"path": "hooks/use-chat.ts",
"chars": 2494,
"preview": "import { EventStreamContentType, fetchEventSource } from '@microsoft/fetch-event-source';\nimport { message } from 'antd'"
},
{
"path": "hooks/use-summary.ts",
"chars": 1272,
"preview": "import { ChatContext } from '@/app/chat-context';\nimport { ChatHistoryResponse } from '@/types/chat';\nimport { useCallba"
},
{
"path": "next.config.js",
"chars": 329,
"preview": "/** @type {import('next').NextConfig} */\nconst nextConfig = {\n output: 'export',\n experimental: {\n esmExternals: 'l"
},
{
"path": "nprogress.css",
"chars": 651,
"preview": "/* Make clicks pass-through */\n#nprogress {\n pointer-events: none;\n}\n\n#nprogress .bar {\n background: var(--joy-palette"
},
{
"path": "package.json",
"chars": 1829,
"preview": "{\n \"name\": \"db-gpt-web\",\n \"version\": \"0.1.0\",\n \"private\": true,\n \"scripts\": {\n \"start\": \"next start\",\n \"dev\": "
},
{
"path": "pages/_app.tsx",
"chars": 2946,
"preview": "import type { AppProps } from 'next/app';\nimport React, { useContext, useEffect, useRef } from 'react';\nimport SideBar f"
},
{
"path": "pages/_document.tsx",
"chars": 1559,
"preview": "import { createCache, StyleProvider } from '@ant-design/cssinjs';\nimport Document, { DocumentContext, Head, Html, Main, "
},
{
"path": "pages/agent/index.tsx",
"chars": 897,
"preview": "import MarketPlugins from '@/components/agent/market-plugins';\nimport MyPlugins from '@/components/agent/my-plugins';\nim"
},
{
"path": "pages/app/index.tsx",
"chars": 3158,
"preview": "import AppModal from '@/components/app/app-modal';\nimport AppCard from '@/components/app/app-card';\nimport { Button, Spi"
},
{
"path": "pages/chat/index.tsx",
"chars": 830,
"preview": "import React, { useContext, useEffect } from 'react';\nimport { useRouter } from 'next/router';\nimport { ChatContext } fr"
},
{
"path": "pages/database/index.tsx",
"chars": 7040,
"preview": "import React, { useMemo, useState } from 'react';\nimport { useAsyncEffect } from 'ahooks';\nimport { Badge, Button, Card,"
},
{
"path": "pages/flow/canvas/index.tsx",
"chars": 10651,
"preview": "import { addFlow, apiInterceptors, getFlowById, updateFlowById } from '@/client/api';\nimport MuiLoading from '@/componen"
},
{
"path": "pages/flow/index.tsx",
"chars": 3737,
"preview": "import { addFlow, apiInterceptors, getFlows } from '@/client/api';\nimport MyEmpty from '@/components/common/MyEmpty';\nim"
},
{
"path": "pages/index.tsx",
"chars": 5193,
"preview": "import { useRequest } from 'ahooks';\nimport { useContext, useState } from 'react';\nimport { Divider, Spin, Tag } from 'a"
},
{
"path": "pages/knowledge/chunk/index.tsx",
"chars": 2942,
"preview": "import React, { useEffect, useState } from 'react';\nimport { useRouter } from 'next/router';\nimport { Breadcrumb, Card, "
},
{
"path": "pages/knowledge/index.tsx",
"chars": 3679,
"preview": "import React, { useState, useEffect } from 'react';\nimport { PlusOutlined } from '@ant-design/icons';\nimport { Button, M"
},
{
"path": "pages/models/index.tsx",
"chars": 1562,
"preview": "import { apiInterceptors, getModelList } from '@/client/api';\nimport ModelCard from '@/components/model/model-card';\nimp"
},
{
"path": "pages/prompt/index.tsx",
"chars": 4895,
"preview": "import { useState, useEffect, useRef, Ref } from 'react';\nimport type { ColumnsType } from 'antd/es/table';\nimport type "
},
{
"path": "postcss.config.js",
"chars": 82,
"preview": "module.exports = {\n plugins: {\n tailwindcss: {},\n autoprefixer: {},\n },\n}\n"
},
{
"path": "styles/globals.css",
"chars": 1663,
"preview": "@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\nbody {\n margin: 0;\n font-family: var(--joy-fontFamily-body"
},
{
"path": "tailwind.config.js",
"chars": 722,
"preview": "const defaultTheme = require('tailwindcss/defaultTheme');\n\n/** @type {import('tailwindcss').Config} */\nmodule.exports = "
},
{
"path": "tsconfig.json",
"chars": 638,
"preview": "{\n \"compilerOptions\": {\n \"target\": \"es5\",\n \"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n \"allowJs\": true,\n \"sk"
},
{
"path": "types/agent.ts",
"chars": 1423,
"preview": "export type PostAgentHubUpdateParams = {\n channel: string;\n url: string;\n branch: string;\n authorization: string;\n};"
},
{
"path": "types/app.ts",
"chars": 1287,
"preview": "// app\nexport type IApp = {\n app_code: string;\n /**\n * 应用名\n */\n app_name: string;\n /**\n * 应用描述信息/简介\n */\n ap"
},
{
"path": "types/chat.ts",
"chars": 2151,
"preview": "type ChartValue = {\n name: string;\n type: string;\n value: number;\n};\n\n/**\n * dashboard chart type\n */\nexport type Cha"
},
{
"path": "types/db.ts",
"chars": 1250,
"preview": "export type DBOption = { label: string; value: DBType; disabled?: boolean; isFileDb?: boolean; icon: string; desc?: stri"
},
{
"path": "types/editor.ts",
"chars": 977,
"preview": "export type IEditorSQLRound = {\n db_name: string;\n round: number;\n round_name: string;\n};\n\nexport type GetEditorSQLRo"
},
{
"path": "types/flow.ts",
"chars": 2638,
"preview": "import { Node } from 'reactflow';\n\nexport type FlowState = 'deployed' | 'developing' | 'initializing' | 'testing' | 'dis"
},
{
"path": "types/knowledge.ts",
"chars": 2804,
"preview": "import { type } from 'os';\n\nexport interface ISpace {\n context?: any;\n desc: string;\n docs: string | number;\n gmt_cr"
},
{
"path": "types/model.ts",
"chars": 1255,
"preview": "export type IModelData = {\n chat_scene: string;\n model_name: string;\n model_type: string;\n host: string;\n port: num"
},
{
"path": "types/prompt.ts",
"chars": 457,
"preview": "export type PromptParams = {\n prompt_type: string;\n current: number;\n pageSize: number;\n hideOnSinglePage: boolean;\n"
},
{
"path": "utils/constants.ts",
"chars": 5401,
"preview": "import { ModelType } from '@/types/chat';\nimport { DBType } from '@/types/db';\n\nexport const MODEL_ICON_MAP: Record<Mode"
},
{
"path": "utils/ctx-axios.ts",
"chars": 239,
"preview": "import axios from 'axios';\n\nconst api = axios.create({\n baseURL: process.env.API_BASE_URL,\n});\n\napi.defaults.timeout = "
},
{
"path": "utils/flow.ts",
"chars": 3107,
"preview": "import { IFlowData, IFlowDataNode, IFlowNode } from '@/types/flow';\nimport { Node } from 'reactflow';\n\nexport const getU"
},
{
"path": "utils/index.ts",
"chars": 354,
"preview": "/** Theme */\nexport const STORAGE_THEME_KEY = '__db_gpt_theme_key';\n/** Language */\nexport const STORAGE_LANG_KEY = '__d"
},
{
"path": "utils/request.ts",
"chars": 2345,
"preview": "import { message } from 'antd';\nimport axios from './ctx-axios';\nimport { isPlainObject } from 'lodash';\n\nconst DEFAULT_"
},
{
"path": "utils/storage.ts",
"chars": 301,
"preview": "import { STORAGE_INIT_MESSAGE_KET } from '@/utils';\n\nexport function getInitMessage() {\n const value = localStorage.get"
}
]
About this extraction
This page contains the full source code of the eosphoros-ai/DB-GPT-Web GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 163 files (400.5 KB), approximately 118.0k tokens, and a symbol index with 357 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.