[
  {
    "path": ".eslintrc.json",
    "content": "{\n  \"extends\": \"next/core-web-vitals\"\n}\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/✨-feature-request.md",
    "content": "---\nname: \"✨ Feature Request\"\nabout: Suggest a new feature idea.\ntitle: \"[feature]\"\nlabels: ''\nassignees: ''\n\n---\n\n**Is your feature request related to a problem? Please describe.**\nA clear and concise description of what the problem is. Ex. I'm always frustrated when [...]\n\n**Describe the solution you'd like**\nA clear and concise description of what you want to happen.\n\n**Describe alternatives you've considered**\nA clear and concise description of any alternative solutions or features you've considered.\n\n**Additional context**\nAdd any other context or screenshots about the feature request here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/🐛-bug-report.md",
    "content": "---\nname: \"\\U0001F41B Bug report\"\nabout: Report an unexpected problem or unintended behavior.\ntitle: \"[BUG] \"\nlabels: ''\nassignees: ''\n\n---\n\n<!--\nThanks for your interest in DB-GPT-Web! ❤️\nPlease check if there is no similar issue before creating this one.\n-->\n\n**Describe the bug**\nA clear and concise description of what the bug is.\n\n**To Reproduce**\nSteps to reproduce the behavior:\n1. Go to '...'\n2. Click on '....'\n3. Scroll down to '....'\n4. See error\n\n**Expected behavior**\nA clear and concise description of what you expected to happen.\n\n**Screenshots**\nIf applicable, add screenshots to help explain your problem.\n\n**Desktop (please complete the following information):**\n - OS: [e.g. iOS]\n - Browser [e.g. chrome, safari]\n - Version [e.g. 22]\n\n**Smartphone (please complete the following information):**\n - Device: [e.g. iPhone6]\n - OS: [e.g. iOS8.1]\n - Browser [e.g. stock browser, safari]\n - Version [e.g. 22]\n\n**Additional context**\nAdd any other context about the problem here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/📜-documentation.md",
    "content": "---\nname: \"\\U0001F4DC Documentation\"\nabout: Correct spelling errors, improvements or additions to documentation files (README,\n  CONTRIBUTING...).\ntitle: \"[Doc]\"\nlabels: ''\nassignees: ''\n\n---\n\n**Is your feature request related to a problem? Please describe.**\nA clear and concise description of what the problem is. Ex. I'm always frustrated when [...]\n\n**Describe the solution you'd like**\nA clear and concise description of what you want to happen.\n\n**Describe alternatives you've considered**\nA clear and concise description of any alternative solutions or features you've considered.\n\n**Additional context**\nAdd any other context or screenshots about the feature request here.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/🔧-improvement.md",
    "content": "---\nname: \"\\U0001F527 Improvement\"\nabout: Suggest an idea which is not a feature.\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n<!--\nThanks for your interest in DB-GPT-Web! ❤️\nPlease check if there is no similar issue before creating this one.\n-->\n\n### Expected Behavior\n\n### Actual Behavior\n\n### Proposal\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/🙋-question.md",
    "content": "---\nname: \"\\U0001F64B Question\"\nabout: Ask a question about DB-GPT-Web.\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n<!--\nThanks for your interest in Db-GPT-Web! ❤️\nPlease check if there is no similar issue before creating this one.\n\nPlease ask one question per issue.\n-->\n\n### Question\n"
  },
  {
    "path": ".gitignore",
    "content": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pnp\n.pnp.js\n\n# testing\n/coverage\n\n.idea\n# next.js\n/.next/\n/out/\n\n# production\n/build\n\n# misc\n.DS_Store\n*.pem\n\n# debug\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nmock\n\n# local env files\n.env.prod\n.env\n\n# vercel\n.vercel\n\n# typescript\n*.tsbuildinfo\nnext-env.d.ts\n.next"
  },
  {
    "path": ".npmrc",
    "content": "registry=http://registry.npmjs.org"
  },
  {
    "path": ".vscode/extensions.json",
    "content": "{\n  \"recommendations\": [\n    \"bradlc.vscode-tailwindcss\",\n    \"esbenp.prettier-vscode\",\n    \"dbaeumer.vscode-eslint\",\n    \"formulahendry.auto-close-tag\",\n    \"formulahendry.auto-rename-tag\",\n    \"vincaslt.highlight-matching-tag\",\n    \"ionutvmi.path-autocomplete\"\n  ]\n}\n"
  },
  {
    "path": ".vscode/settings.json",
    "content": "{\n  \"editor.formatOnSave\": true,\n  \"editor.codeActionsOnSave\": {\n    \"source.fixAll\": \"explicit\"\n  },\n  \"prettier.arrowParens\": \"always\",\n  \"prettier.tabWidth\": 2,\n  \"prettier.printWidth\": 150,\n  \"prettier.singleQuote\": true,\n  \"prettier.semi\": true,\n  \"prettier.trailingComma\": \"all\",\n  \"[typescript]\": {\n    \"editor.defaultFormatter\": \"esbenp.prettier-vscode\"\n  },\n  \"[typescriptreact]\": {\n    \"editor.defaultFormatter\": \"esbenp.prettier-vscode\"\n  },\n  \"[css]\": {\n    \"editor.defaultFormatter\": \"esbenp.prettier-vscode\"\n  },\n  \"tailwindCSS.includeLanguages\": {\n    \"plaintext\": \"typescriptreact\"\n  },\n  \"editor.quickSuggestions\": {\n    \"strings\": true\n  },\n  \"cSpell.words\": [\"DBGPT\"]\n}\n"
  },
  {
    "path": "README.md",
    "content": "\n<h1 align=\"center\">\n  <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>\n  <br>\n  DB-GPT-Web\n</h1>\n\n_<p align=\"center\">DB-GPT Chat UI, LLM to Vision.</p>_\n\n<p align=\"center\">\n  <a href=\"https://github.com/eosphoros-ai/DB-GPT-Web/blob/main/LICENSE\">\n    <img src=\"https://img.shields.io/badge/license-MIT-blue.svg?label=License&style=flat\" />\n  </a>\n  <a href=\"https://github.com/eosphoros-ai/DB-GPT/releases\">\n    <img alt=\"Release Notes\" src=\"https://img.shields.io/github/release/eosphoros-ai/DB-GPT\" />\n  </a>\n  <a href=\"https://github.com/eosphoros-ai/DB-GPT-Web/issues\">\n    <img alt=\"Open Issues\" src=\"https://img.shields.io/github/issues-raw/eosphoros-ai/DB-GPT-Web\" />\n  </a>\n  <a href=\"https://discord.gg/7uQnPuveTY\">\n    <img alt=\"Discord\" src=\"https://dcbadge.vercel.app/api/server/7uQnPuveTY?compact=true&style=flat\" />\n  </a>\n</p>\n\n---\n\n## 👋 Introduction\n\n***DB-GPT-Web*** is an **Open source chat UI** for [**DB-GPT**](https://github.com/eosphoros-ai/DB-GPT).\nAlso, it is a **LLM to Vision** solution. \n\n[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.\n\n## 💪🏻 Getting Started\n\n### Prerequisites\n\n- [Node.js](https://nodejs.org/) >= 16\n- [npm](https://npmjs.com/) >= 8\n- Supported OSes: Linux, macOS and Windows\n\n### Installation\n\n```sh\n# Install dependencies\nnpm install\n```\n\n### Usage\n```sh\ncp .env.example .env\n```\nedit the `API_BASE_URL` to the real address\n\n```sh\n# development model\nnpm run dev\n```\n\n## 🚀 Use In DB-GPT\n\n```sh\nnpm run compile\n\n# copy compile file to DB-GPT static file dictory\ncp -rf out/* ../dbgpt/app/static \n\n```\n\n## 📚 Documentation\n\nFor full documentation, visit [document](https://docs.dbgpt.site/).\n\n\n## Usage\n  [react-markdown](https://github.com/remarkjs/react-markdown#readme) for markdown support.\n  [ant-design](https://github.com/ant-design/ant-design) for ui components.\n  [next.js](https://github.com/vercel/next.js) for server side rendering.\n  [@antv/g2](https://github.com/antvis/g2#readme) for charts.\n\n## License\n\nDB-GPT-Web is licensed under the [MIT License](LICENSE).\n\n---\n\nEnjoy using DB-GPT-Web to build stunning UIs for your AI and GPT projects.\n\n🌟 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.\n\nFor any queries or issues, feel free to open an [issue](https://github.com/eosphoros-ai/DB-GPT-Web/issues) on the repository.\n\nHappy coding! 😊"
  },
  {
    "path": "app/chat-context.tsx",
    "content": "import { createContext, useEffect, useMemo, useState } from 'react';\nimport { apiInterceptors, getDialogueList, getUsableModels } from '@/client/api';\nimport { useRequest } from 'ahooks';\nimport { ChatHistoryResponse, DialogueListResponse, IChatDialogueSchema } from '@/types/chat';\nimport { useSearchParams } from 'next/navigation';\nimport { STORAGE_THEME_KEY } from '@/utils';\n\ntype ThemeMode = 'dark' | 'light';\n\ninterface IChatContext {\n  mode: ThemeMode;\n  isContract?: boolean;\n  isMenuExpand?: boolean;\n  scene: IChatDialogueSchema['chat_mode'] | (string & {});\n  chatId: string;\n  model: string;\n  dbParam?: string;\n  modelList: Array<string>;\n  agent: string;\n  dialogueList?: DialogueListResponse;\n  setAgent?: (val: string) => void;\n  setMode: (mode: ThemeMode) => void;\n  setModel: (val: string) => void;\n  setIsContract: (val: boolean) => void;\n  setIsMenuExpand: (val: boolean) => void;\n  setDbParam: (val: string) => void;\n  queryDialogueList: () => void;\n  refreshDialogList: () => void;\n  currentDialogue?: DialogueListResponse[0];\n  history: ChatHistoryResponse;\n  setHistory: (val: ChatHistoryResponse) => void;\n  docId?: number;\n  setDocId: (docId: number) => void;\n}\n\nfunction getDefaultTheme(): ThemeMode {\n  const theme = localStorage.getItem(STORAGE_THEME_KEY) as ThemeMode;\n  if (theme) return theme;\n  return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';\n}\n\nconst ChatContext = createContext<IChatContext>({\n  mode: 'light',\n  scene: '',\n  chatId: '',\n  modelList: [],\n  model: '',\n  dbParam: undefined,\n  dialogueList: [],\n  agent: '',\n  setAgent: () => {},\n  setModel: () => {},\n  setIsContract: () => {},\n  setIsMenuExpand: () => {},\n  setDbParam: () => void 0,\n  queryDialogueList: () => {},\n  refreshDialogList: () => {},\n  setMode: () => void 0,\n  history: [],\n  setHistory: () => {},\n  docId: undefined,\n  setDocId: () => {},\n});\n\nconst ChatContextProvider = ({ children }: { children: React.ReactElement }) => {\n  const searchParams = useSearchParams();\n  const chatId = searchParams?.get('id') ?? '';\n  const scene = searchParams?.get('scene') ?? '';\n  const db_param = searchParams?.get('db_param') ?? '';\n\n  const [isContract, setIsContract] = useState(false);\n  const [model, setModel] = useState<string>('');\n  const [isMenuExpand, setIsMenuExpand] = useState<boolean>(scene !== 'chat_dashboard');\n  const [dbParam, setDbParam] = useState<string>(db_param);\n  const [agent, setAgent] = useState<string>('');\n  const [history, setHistory] = useState<ChatHistoryResponse>([]);\n  const [docId, setDocId] = useState<number>();\n  const [mode, setMode] = useState<ThemeMode>('light');\n\n  const {\n    run: queryDialogueList,\n    data: dialogueList = [],\n    refresh: refreshDialogList,\n  } = useRequest(\n    async () => {\n      const [, res] = await apiInterceptors(getDialogueList());\n      return res ?? [];\n    },\n    {\n      manual: true,\n    },\n  );\n\n  useEffect(() => {\n    if (dialogueList.length && scene === 'chat_agent') {\n      const agent = dialogueList.find((item) => item.conv_uid === chatId)?.select_param;\n      agent && setAgent(agent);\n    }\n  }, [dialogueList, scene, chatId]);\n\n  const { data: modelList = [] } = useRequest(async () => {\n    const [, res] = await apiInterceptors(getUsableModels());\n    return res ?? [];\n  });\n\n  useEffect(() => {\n    setMode(getDefaultTheme());\n  }, []);\n\n  useEffect(() => {\n    setModel(modelList[0]);\n  }, [modelList, modelList?.length]);\n\n  const currentDialogue = useMemo(() => dialogueList.find((item: any) => item.conv_uid === chatId), [chatId, dialogueList]);\n  const contextValue = {\n    isContract,\n    isMenuExpand,\n    scene,\n    chatId,\n    modelList,\n    model,\n    dbParam: dbParam || db_param,\n    dialogueList,\n    agent,\n    setAgent,\n    mode,\n    setMode,\n    setModel,\n    setIsContract,\n    setIsMenuExpand,\n    setDbParam,\n    queryDialogueList,\n    refreshDialogList,\n    currentDialogue,\n    history,\n    setHistory,\n    docId,\n    setDocId,\n  };\n  return <ChatContext.Provider value={contextValue}>{children}</ChatContext.Provider>;\n};\n\nexport { ChatContext, ChatContextProvider };\n"
  },
  {
    "path": "app/i18n.ts",
    "content": "import i18n from 'i18next';\nimport { initReactI18next } from 'react-i18next';\n\nconst en = {\n  Knowledge_Space: 'Knowledge',\n  space: 'space',\n  Vector: 'Vector',\n  Owner: 'Owner',\n  Count: 'Count',\n  File_type_Invalid: 'The file type is invalid',\n  Knowledge_Space_Config: 'Space Config',\n  Choose_a_Datasource_type: 'Datasource type',\n  Segmentation: 'Segmentation',\n  No_parameter: `No segementation parameter required.`,\n  Knowledge_Space_Name: 'Knowledge Space Name',\n  Please_input_the_name: 'Please input the name',\n  Please_input_the_owner: 'Please input the owner',\n  Please_select_file: 'Please select one file',\n  Description: 'Description',\n  Please_input_the_description: 'Please input the description',\n  Next: 'Next',\n  the_name_can_only_contain: 'the name can only contain numbers, letters, Chinese characters, \"-\" and \"_\"',\n  Text: 'Text',\n  'Fill your raw text': 'Fill your raw text',\n  URL: 'URL',\n  Fetch_the_content_of_a_URL: 'Fetch the content of a URL',\n  Document: 'Document',\n  Upload_a_document: 'Upload a document, document type can be PDF, CSV, Text, PowerPoint, Word, Markdown',\n  Name: 'Name',\n  Text_Source: 'Text Source(Optional)',\n  Please_input_the_text_source: 'Please input the text source',\n  Sync: 'Sync',\n  Back: 'Back',\n  Finish: 'Finish',\n  Web_Page_URL: 'Web Page URL',\n  Please_input_the_Web_Page_URL: 'Please input the Web Page URL',\n  Select_or_Drop_file: 'Select or Drop file',\n  Documents: 'Documents',\n  Chat: 'Chat',\n  Add_Datasource: 'Add Datasource',\n  Arguments: 'Arguments',\n  Type: 'Type',\n  Size: 'Size',\n  Last_Sync: 'Last Sync',\n  Status: 'Status',\n  Result: 'Result',\n  Details: 'Details',\n  Delete: 'Delete',\n  Operation: 'Operation',\n  Submit: 'Submit',\n  Chunks: 'Chunks',\n  Content: 'Content',\n  Meta_Data: 'Meta Data',\n  Please_select_a_file: 'Please select a file',\n  Please_input_the_text: 'Please input the text',\n  Embedding: 'Embedding',\n  topk: 'topk',\n  the_top_k_vectors: 'the top k vectors based on similarity score',\n  recall_score: 'recall_score',\n  Set_a_threshold_score: 'Set a threshold score for the retrieval of similar vectors',\n  recall_type: 'recall_type',\n  model: 'model',\n  A_model_used: 'A model used to create vector representations of text or other data',\n  Automatic: 'Automatic',\n  Process: 'Process',\n  Automatic_desc: 'Automatically set segmentation and preprocessing rules.',\n  chunk_size: 'chunk_size',\n  The_size_of_the_data_chunks: 'The size of the data chunks used in processing',\n  chunk_overlap: 'chunk_overlap',\n  The_amount_of_overlap: 'The amount of overlap between adjacent data chunks',\n  Prompt: 'Prompt',\n  scene: 'scene',\n  A_contextual_parameter: 'A contextual parameter used to define the setting or environment in which the prompt is being used',\n  template: 'template',\n  structure_or_format:\n    '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.',\n  max_token: 'max_token',\n  max_iteration: 'max_iteration',\n  concurrency_limit: 'concurrency_limit',\n  The_maximum_number_of_tokens: 'The maximum number of tokens or words allowed in a prompt',\n  Theme: 'Theme',\n  Port: 'Port',\n  Username: 'Username',\n  Password: 'Password',\n  Remark: 'Remark',\n  Edit: 'Edit',\n  Database: 'Database',\n  Data_Source: 'Data Center',\n  Close_Sidebar: 'Fold',\n  Show_Sidebar: 'UnFold',\n  language: 'Language',\n  choose_model: 'Please choose a model',\n  data_center_desc: 'DB-GPT also offers a user-friendly data center management interface for efficient data maintenance.',\n  create_database: 'Create Database',\n  create_knowledge: 'Create Knowledge',\n  path: 'Path',\n  model_manage: 'Models',\n  stop_model_success: 'Stop model success',\n  create_model: 'Create Model',\n  model_select_tips: 'Please select a model',\n  language_select_tips: 'Please select a language',\n  submit: 'Submit',\n  close: 'Close',\n  start_model_success: 'Start model success',\n  download_model_tip: 'Please download model first.',\n  Plugins: 'Plugins',\n  try_again: 'Try again',\n  no_data: 'No data',\n  Open_Sidebar: 'Unfold',\n  cancel: 'Cancel',\n  Edit_Success: 'Edit Success',\n  Add: 'Add',\n  Add_Success: 'Add Success',\n  Error_Message: 'Something Error',\n  Please_Input: 'Please Input',\n  Prompt_Info_Scene: 'Scene',\n  Prompt_Info_Sub_Scene: 'Sub Scene',\n  Prompt_Info_Name: 'Name',\n  Prompt_Info_Content: 'Content',\n  Public: 'Public',\n  Private: 'Private',\n  Lowest: 'Lowest',\n  Missed: 'Missed',\n  Lost: 'Lost',\n  Incorrect: 'Incorrect',\n  Verbose: 'Verbose',\n  Best: 'Best',\n  Rating: 'Rating',\n  Q_A_Category: 'Q&A Category',\n  Q_A_Rating: 'Q&A Rating',\n  feed_back_desc:\n    '0: No results\\n' +\n    '1: Results exist, but they are irrelevant, the question is not understood\\n' +\n    '2: Results exist, the question is understood, but it indicates that the question cannot be answered\\n' +\n    '3: Results exist, the question is understood, and an answer is given, but the answer is incorrect\\n' +\n    '4: Results exist, the question is understood, the answer is correct, but it is verbose and lacks a summary\\n' +\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',\n  input_count: 'Total input',\n  input_unit: 'characters',\n  Copy: 'Copy',\n  Copy_success: 'Content copied successfully',\n  Copy_nothing: 'Content copied is empty',\n  Copry_error: 'Copy failed',\n  Click_Select: 'Click&Select',\n  Quick_Start: 'Quick Start',\n  Select_Plugins: 'Select Plugins',\n  Search: 'Search',\n  Update_From_Github: 'Upload From Github',\n  Reset: 'Reset',\n  Upload: 'Upload',\n  Market_Plugins: 'Market Plugin',\n  My_Plugins: 'My Plugins',\n  Del_Knowledge_Tips: 'Do you want delete the Space',\n  Del_Document_Tips: 'Do you want delete the Document',\n  Tips: 'Tips',\n  Limit_Upload_File_Count_Tips: 'Only one file can be uploaded at a time',\n  To_Plugin_Market: 'Go to the Plugin Market',\n  Summary: 'Summary',\n  stacked_column_chart: 'Stacked Column',\n  column_chart: 'Column',\n  percent_stacked_column_chart: 'Percent Stacked Column',\n  grouped_column_chart: 'Grouped Column',\n  time_column: 'Time Column',\n  pie_chart: 'Pie',\n  line_chart: 'Line',\n  area_chart: 'Area',\n  stacked_area_chart: 'Stacked Area',\n  scatter_plot: 'Scatter',\n  bubble_chart: 'Bubble',\n  stacked_bar_chart: 'Stacked Bar',\n  bar_chart: 'Bar',\n  percent_stacked_bar_chart: 'Percent Stacked Bar',\n  grouped_bar_chart: 'Grouped Bar',\n  water_fall_chart: 'Waterfall',\n  table: 'Table',\n  multi_line_chart: 'Multi Line',\n  multi_measure_column_chart: 'Multi Measure Column',\n  multi_measure_line_chart: 'Multi Measure Line',\n  Advices: 'Advices',\n  Retry: 'Retry',\n  Load_more: 'load more',\n  new_chat: 'New Chat',\n  choice_agent_tip: 'Please choose an agent',\n  no_context_tip: 'Please enter your question',\n  Terminal: 'Terminal',\n  awel_flow: 'AWEL Flow',\n  save: 'Save',\n  add_node: 'Add Node',\n  no_node: 'No Node',\n  connect_warning: 'Nodes cannot be connected',\n  flow_modal_title: 'Save Flow',\n  flow_name: 'Flow Name',\n  flow_description: 'Flow Description',\n  flow_name_required: 'Please enter the flow name',\n  flow_description_required: 'Please enter the flow description',\n  save_flow_success: 'Save flow success',\n  delete_flow_confirm: 'Are you sure you want to delete this flow?',\n  related_nodes: 'Related Nodes',\n  add_resource: 'Add Resource',\n  team_modal: 'Work Modal',\n  App: 'App',\n  resource_name: 'Resource Name',\n  resource_type: 'Resource Type',\n  resource_value: 'Value',\n  resource_dynamic: 'Dynamic',\n  Please_input_the_work_modal: 'Please select the work modal',\n  available_resources: ' Available Resources',\n  edit_new_applications: 'Edit new applications',\n  collect: 'Collect',\n  collected: 'Collected',\n  create: 'Create',\n  Agents: 'Agents',\n  edit_application: 'edit application',\n  add_application: 'add application',\n  app_name: 'App Name',\n  LLM_strategy: 'LLM Strategy',\n  LLM_strategy_value: 'LLM Strategy Value',\n  resource: 'Resource',\n  operators: 'Operators',\n  Chinese: 'Chinese',\n  English: 'English',\n} as const;\n\nexport type I18nKeys = keyof typeof en;\n\nexport interface Resources {\n  translation: Record<I18nKeys, string>;\n}\n\nconst zh: Resources['translation'] = {\n  Knowledge_Space: '知识库',\n  space: '知识库',\n  Vector: '向量',\n  Owner: '创建人',\n  Count: '文档数',\n  File_type_Invalid: '文件类型错误',\n  Knowledge_Space_Config: '知识库配置',\n  Choose_a_Datasource_type: '知识库类型',\n  Segmentation: '分片',\n  No_parameter: '不需要配置分片参数',\n  Knowledge_Space_Name: '知识库名称',\n  Please_input_the_name: '请输入名称',\n  Please_input_the_owner: '请输入创建人',\n  Please_select_file: '请至少选择一个文件',\n  Description: '描述',\n  Please_input_the_description: '请输入描述',\n  Next: '下一步',\n  the_name_can_only_contain: '名称只能包含数字、字母、中文字符、-或_',\n  Text: '文本',\n  'Fill your raw text': '填写您的原始文本',\n  URL: '网址',\n  Fetch_the_content_of_a_URL: '获取 URL 的内容',\n  Document: '文档',\n  Upload_a_document: '上传文档，文档类型可以是PDF、CSV、Text、PowerPoint、Word、Markdown',\n  Name: '名称',\n  Text_Source: '文本来源（可选）',\n  Please_input_the_text_source: '请输入文本来源',\n  Sync: '同步',\n  Back: '上一步',\n  Finish: '完成',\n  Web_Page_URL: '网页网址',\n  Please_input_the_Web_Page_URL: '请输入网页网址',\n  Select_or_Drop_file: '选择或拖拽文件',\n  Documents: '文档',\n  Chat: '对话',\n  Add_Datasource: '添加数据源',\n  Arguments: '参数',\n  Type: '类型',\n  Size: '切片',\n  Last_Sync: '上次同步时间',\n  Status: '状态',\n  Result: '结果',\n  Details: '明细',\n  Delete: '删除',\n  Operation: '操作',\n  Submit: '提交',\n  close: '关闭',\n  Chunks: '切片',\n  Content: '内容',\n  Meta_Data: '元数据',\n  Please_select_a_file: '请上传一个文件',\n  Please_input_the_text: '请输入文本',\n  Embedding: '嵌入',\n  topk: 'TopK',\n  the_top_k_vectors: '基于相似度得分的前 k 个向量',\n  recall_score: '召回分数',\n  Set_a_threshold_score: '设置相似向量检索的阈值分数',\n  recall_type: '召回类型',\n  model: '模型',\n  A_model_used: '用于创建文本或其他数据的矢量表示的模型',\n  Automatic: '自动切片',\n  Process: '切片处理',\n  Automatic_desc: '自动设置分割和预处理规则。',\n  chunk_size: '块大小',\n  The_size_of_the_data_chunks: '处理中使用的数据块的大小',\n  chunk_overlap: '块重叠',\n  The_amount_of_overlap: '相邻数据块之间的重叠量',\n  scene: '场景',\n  A_contextual_parameter: '用于定义使用提示的设置或环境的上下文参数',\n  template: '模板',\n  structure_or_format: '预定义的提示结构或格式，有助于确保人工智能系统生成与所需风格或语气一致的响应。',\n  max_token: '最大令牌',\n  max_iteration: '最大迭代',\n  concurrency_limit: '并发限制',\n  The_maximum_number_of_tokens: '提示中允许的最大标记或单词数',\n  Theme: '主题',\n  Port: '端口',\n  Username: '用户名',\n  Password: '密码',\n  Remark: '备注',\n  Edit: '编辑',\n  Database: '数据库',\n  Data_Source: '数据中心',\n  Close_Sidebar: '收起',\n  Show_Sidebar: '展开',\n  language: '语言',\n  choose_model: '请选择一个模型',\n  data_center_desc: 'DB-GPT支持数据库交互和基于文档的对话，它还提供了一个用户友好的数据中心管理界面。',\n  create_database: '创建数据库',\n  create_knowledge: '创建知识库',\n  path: '路径',\n  model_manage: '模型管理',\n  stop_model_success: '模型停止成功',\n  create_model: '创建模型',\n  model_select_tips: '请选择一个模型',\n  submit: '提交',\n  start_model_success: '启动模型成功',\n  download_model_tip: '请先下载模型！',\n  Plugins: '插件列表',\n  try_again: '刷新重试',\n  no_data: '暂无数据',\n  Prompt: '提示语',\n  Open_Sidebar: '展开',\n  cancel: '取消',\n  Edit_Success: '编辑成功',\n  Add: '新增',\n  Add_Success: '新增成功',\n  Error_Message: '出错了',\n  Please_Input: '请输入',\n  Prompt_Info_Scene: '场景',\n  Prompt_Info_Sub_Scene: '次级场景',\n  Prompt_Info_Name: '名称',\n  Prompt_Info_Content: '内容',\n  Public: '公共',\n  Private: '私有',\n  Lowest: '渣渣',\n  Missed: '没理解',\n  Lost: '答不了',\n  Incorrect: '答错了',\n  Verbose: '较啰嗦',\n  Best: '真棒',\n  Rating: '评分',\n  Q_A_Category: '问答类别',\n  Q_A_Rating: '问答评分',\n  feed_back_desc:\n    '0: 无结果\\n' +\n    '1: 有结果，但是在文不对题，没有理解问题\\n' +\n    '2: 有结果，理解了问题，但是提示回答不了这个问题\\n' +\n    '3: 有结果，理解了问题，并做出回答，但是回答的结果错误\\n' +\n    '4: 有结果，理解了问题，回答结果正确，但是比较啰嗦，缺乏总结\\n' +\n    '5: 有结果，理解了问题，回答结果正确，推理正确，并给出了总结，言简意赅\\n',\n  input_count: '共计输入',\n  input_unit: '字',\n  Copy: '复制',\n  Copy_success: '内容复制成功',\n  Copy_nothing: '内容复制为空',\n  Copry_error: '复制失败',\n  Click_Select: '点击选择',\n  Quick_Start: '快速开始',\n  Select_Plugins: '选择插件',\n  Search: '搜索',\n  Reset: '重置',\n  Update_From_Github: '更新Github插件',\n  Upload: '上传',\n  Market_Plugins: '插件市场',\n  My_Plugins: '我的插件',\n  Del_Knowledge_Tips: '你确定删除该知识库吗',\n  Del_Document_Tips: '你确定删除该文档吗',\n  Tips: '提示',\n  Limit_Upload_File_Count_Tips: '一次只能上传一个文件',\n  To_Plugin_Market: '前往插件市场',\n  Summary: '总结',\n  stacked_column_chart: '堆叠柱状图',\n  column_chart: '柱状图',\n  percent_stacked_column_chart: '百分比堆叠柱状图',\n  grouped_column_chart: '簇形柱状图',\n  time_column: '簇形柱状图',\n  pie_chart: '饼图',\n  line_chart: '折线图',\n  area_chart: '面积图',\n  stacked_area_chart: '堆叠面积图',\n  scatter_plot: '散点图',\n  bubble_chart: '气泡图',\n  stacked_bar_chart: '堆叠条形图',\n  bar_chart: '条形图',\n  percent_stacked_bar_chart: '百分比堆叠条形图',\n  grouped_bar_chart: '簇形条形图',\n  water_fall_chart: '瀑布图',\n  table: '表格',\n  multi_line_chart: '多折线图',\n  multi_measure_column_chart: '多指标柱形图',\n  multi_measure_line_chart: '多指标折线图',\n  Advices: '自动推荐',\n  Retry: '重试',\n  Load_more: '加载更多',\n  new_chat: '创建会话',\n  choice_agent_tip: '请选择代理',\n  no_context_tip: '请输入你的问题',\n  Terminal: '终端',\n  awel_flow: 'AWEL 工作流',\n  save: '保存',\n  add_node: '添加节点',\n  no_node: '没有可编排节点',\n  connect_warning: '节点无法连接',\n  flow_modal_title: '保存工作流',\n  flow_name: '工作流名称',\n  flow_description: '工作流描述',\n  flow_name_required: '请输入工作流名称',\n  flow_description_required: '请输入工作流描述',\n  save_flow_success: '保存工作流成功',\n  delete_flow_confirm: '确定删除该工作流吗？',\n  related_nodes: '关联节点',\n  language_select_tips: '请选择语言',\n  add_resource: '添加资源',\n  team_modal: '工作模式',\n  App: '应用程序',\n  resource: '资源',\n  resource_name: '资源名',\n  resource_type: '资源类型',\n  resource_value: '参数',\n  resource_dynamic: '动态',\n  Please_input_the_work_modal: '请选择工作模式',\n  available_resources: '可用资源',\n  edit_new_applications: '编辑新的应用',\n  collect: '收藏',\n  collected: '已收藏',\n  create: '创建',\n  Agents: '智能体',\n  edit_application: '编辑应用',\n  add_application: '添加应用',\n  app_name: '应用名称',\n  LLM_strategy: '模型策略',\n  LLM_strategy_value: '模型策略参数',\n  operators: '算子',\n  Chinese: '中文',\n  English: '英文',\n} as const;\n\ni18n.use(initReactI18next).init({\n  resources: {\n    en: {\n      translation: en,\n    },\n    zh: {\n      translation: zh,\n    },\n  },\n  lng: 'en',\n  interpolation: {\n    escapeValue: false,\n  },\n});\n\nexport default i18n;\n\ndeclare module 'i18next' {\n  interface CustomTypeOptions {\n    resources: Resources;\n  }\n}\n"
  },
  {
    "path": "client/api/index.ts",
    "content": "import axios, { AxiosRequestConfig, AxiosError, AxiosResponse } from 'axios';\n\nexport type ResponseType<T = any> = {\n  data: T;\n  err_code: string | null;\n  err_msg: string | null;\n  success: boolean;\n};\n\nexport type ApiResponse<T = any, D = any> = AxiosResponse<ResponseType<T>, D>;\n\nexport type SuccessTuple<T = any, D = any> = [null, T, ResponseType<T>, ApiResponse<T, D>];\n\nexport type FailedTuple<T = any, D = any> = [Error | AxiosError<T, D>, null, null, null];\n\nconst ins = axios.create({\n  baseURL: process.env.API_BASE_URL ?? '',\n});\n\nconst LONG_TIME_API: string[] = [\n  '/db/add',\n  '/db/test/connect',\n  '/db/summary',\n  '/params/file/load',\n  '/chat/prepare',\n  '/model/start',\n  '/model/stop',\n  '/editor/sql/run',\n  '/sql/editor/submit',\n  '/editor/chart/run',\n  '/chart/editor/submit',\n  '/document/upload',\n  '/document/sync',\n  '/agent/install',\n  '/agent/uninstall',\n  '/personal/agent/upload',\n];\n\nins.interceptors.request.use((request) => {\n  const isLongTimeApi = LONG_TIME_API.some((item) => request.url && request.url.indexOf(item) >= 0);\n  if (!request.timeout) {\n    request.timeout = isLongTimeApi ? 60000 : 10000;\n  }\n  return request;\n});\n\nexport const GET = <Params = any, Response = any, D = any>(url: string, params?: Params, config?: AxiosRequestConfig<D>) => {\n  return ins.get<Params, ApiResponse<Response>>(url, { params, ...config });\n};\n\nexport const POST = <Data = any, Response = any, D = any>(url: string, data?: Data, config?: AxiosRequestConfig<D>) => {\n  return ins.post<Data, ApiResponse<Response>>(url, data, config);\n};\n\nexport const PATCH = <Data = any, Response = any, D = any>(url: string, data?: Data, config?: AxiosRequestConfig<D>) => {\n  return ins.patch<Data, ApiResponse<Response>>(url, data, config);\n};\n\nexport const PUT = <Data = any, Response = any, D = any>(url: string, data?: Data, config?: AxiosRequestConfig<D>) => {\n  return ins.put<Data, ApiResponse<Response>>(url, data, config);\n};\n\nexport const DELETE = <Params = any, Response = any, D = any>(url: string, params?: Params, config?: AxiosRequestConfig<D>) => {\n  return ins.delete<Params, ApiResponse<Response>>(url, { params, ...config });\n};\n\nexport * from './tools';\nexport * from './request';\n"
  },
  {
    "path": "client/api/request.ts",
    "content": "import { AxiosRequestConfig } from 'axios';\nimport { DELETE, GET, POST, PUT } from '.';\nimport { DbListResponse, DbSupportTypeResponse, PostDbParams, ChatFeedBackSchema } from '@/types/db';\nimport { DialogueListResponse, IChatDialogueSchema, NewDialogueParam, SceneResponse, ChatHistoryResponse, FeedBack, IDB } from '@/types/chat';\nimport { IModelData, StartModelParams, BaseModelParams, SupportModel } from '@/types/model';\nimport {\n  GetEditorSQLRoundRequest,\n  GetEditorySqlParams,\n  PostEditorChartRunParams,\n  PostEditorChartRunResponse,\n  PostEditorSQLRunParams,\n  PostSQLEditorSubmitParams,\n} from '@/types/editor';\nimport {\n  PostAgentHubUpdateParams,\n  PostAgentQueryParams,\n  PostAgentPluginResponse,\n  PostAgentMyPluginResponse,\n  GetDBGPTsListResponse,\n} from '@/types/agent';\nimport {\n  AddKnowledgeParams,\n  ArgumentsParams,\n  ChunkListParams,\n  DocumentParams,\n  IArguments,\n  IChunkList,\n  IChunkStrategyResponse,\n  IDocumentResponse,\n  ISpace,\n  ISyncBatchParameter,\n  ISyncBatchResponse,\n} from '@/types/knowledge';\nimport { UpdatePromptParams, IPrompt, PromptParams } from '@/types/prompt';\nimport { IFlow, IFlowNode, IFlowResponse, IFlowUpdateParam } from '@/types/flow';\nimport { IAgent, IApp, IAppData, ITeamModal } from '@/types/app';\n\n/** App */\nexport const postScenes = () => {\n  return POST<null, Array<SceneResponse>>('/api/v1/chat/dialogue/scenes');\n};\nexport const newDialogue = (data: NewDialogueParam) => {\n  return POST<NewDialogueParam, IChatDialogueSchema>('/api/v1/chat/dialogue/new', data);\n};\n\n/** Database Page */\nexport const getDbList = () => {\n  return GET<null, DbListResponse>('/api/v1/chat/db/list');\n};\nexport const getDbSupportType = () => {\n  return GET<null, DbSupportTypeResponse>('/api/v1/chat/db/support/type');\n};\nexport const postDbDelete = (dbName: string) => {\n  return POST(`/api/v1/chat/db/delete?db_name=${dbName}`);\n};\nexport const postDbEdit = (data: PostDbParams) => {\n  return POST<PostDbParams, null>('/api/v1/chat/db/edit', data);\n};\nexport const postDbAdd = (data: PostDbParams) => {\n  return POST<PostDbParams, null>('/api/v1/chat/db/add', data);\n};\nexport const postDbTestConnect = (data: PostDbParams) => {\n  return POST<PostDbParams, null>('/api/v1/chat/db/test/connect', data);\n};\n\n/** Chat Page */\nexport const getDialogueList = () => {\n  return GET<null, DialogueListResponse>('/api/v1/chat/dialogue/list');\n};\nexport const getUsableModels = () => {\n  return GET<null, Array<string>>('/api/v1/model/types');\n};\nexport const postChatModeParamsList = (chatMode: string) => {\n  return POST<null, IDB[]>(`/api/v1/chat/mode/params/list?chat_mode=${chatMode}`);\n};\nexport const postChatModeParamsInfoList = (chatMode: string) => {\n  return POST<null, Record<string, string>>(`/api/v1/chat/mode/params/info?chat_mode=${chatMode}`);\n};\nexport const getChatHistory = (convId: string) => {\n  return GET<null, ChatHistoryResponse>(`/api/v1/chat/dialogue/messages/history?con_uid=${convId}`);\n};\nexport const postChatModeParamsFileLoad = ({\n  convUid,\n  chatMode,\n  data,\n  config,\n  model,\n}: {\n  convUid: string;\n  chatMode: string;\n  data: FormData;\n  model: string;\n  config?: Omit<AxiosRequestConfig, 'headers'>;\n}) => {\n  return POST<FormData, ChatHistoryResponse>(\n    `/api/v1/chat/mode/params/file/load?conv_uid=${convUid}&chat_mode=${chatMode}&model_name=${model}`,\n    data,\n    {\n      headers: {\n        'Content-Type': 'multipart/form-data',\n      },\n      ...config,\n    },\n  );\n};\n\n/** Menu */\nexport const delDialogue = (conv_uid: string) => {\n  return POST(`/api/v1/chat/dialogue/delete?con_uid=${conv_uid}`);\n};\n\n/** Editor */\nexport const getEditorSqlRounds = (id: string) => {\n  return GET<null, GetEditorSQLRoundRequest>(`/api/v1/editor/sql/rounds?con_uid=${id}`);\n};\nexport const postEditorSqlRun = (data: PostEditorSQLRunParams) => {\n  return POST<PostEditorSQLRunParams>(`/api/v1/editor/sql/run`, data);\n};\nexport const postEditorChartRun = (data: PostEditorChartRunParams) => {\n  return POST<PostEditorChartRunParams, PostEditorChartRunResponse>(`/api/v1/editor/chart/run`, data);\n};\nexport const postSqlEditorSubmit = (data: PostSQLEditorSubmitParams) => {\n  return POST<PostSQLEditorSubmitParams>(`/api/v1/sql/editor/submit`, data);\n};\nexport const getEditorSql = (id: string, round: string | number) => {\n  return POST<GetEditorySqlParams, string | Array<any>>('/api/v1/editor/sql', { con_uid: id, round });\n};\n\n/** knowledge */\nexport const getArguments = (knowledgeName: string) => {\n  return POST<any, IArguments>(`/knowledge/${knowledgeName}/arguments`, {});\n};\nexport const saveArguments = (knowledgeName: string, data: ArgumentsParams) => {\n  return POST<ArgumentsParams, IArguments>(`/knowledge/${knowledgeName}/argument/save`, data);\n};\n\nexport const getSpaceList = () => {\n  return POST<any, Array<ISpace>>('/knowledge/space/list', {});\n};\nexport const getDocumentList = (spaceName: string, data: Record<string, number | Array<number>>) => {\n  return POST<Record<string, number | Array<number>>, IDocumentResponse>(`/knowledge/${spaceName}/document/list`, data);\n};\n\nexport const addDocument = (knowledgeName: string, data: DocumentParams) => {\n  return POST<DocumentParams, number>(`/knowledge/${knowledgeName}/document/add`, data);\n};\n\nexport const addSpace = (data: AddKnowledgeParams) => {\n  return POST<AddKnowledgeParams, Array<any>>(`/knowledge/space/add`, data);\n};\n\nexport const getChunkStrategies = () => {\n  return GET<null, Array<IChunkStrategyResponse>>('/knowledge/document/chunkstrategies');\n};\n\nexport const syncDocument = (spaceName: string, data: Record<string, Array<number>>) => {\n  return POST<Record<string, Array<number>>, string | null>(`/knowledge/${spaceName}/document/sync`, data);\n};\n\nexport const syncBatchDocument = (spaceName: string, data: Array<ISyncBatchParameter>) => {\n  return POST<Array<ISyncBatchParameter>, ISyncBatchResponse>(`/knowledge/${spaceName}/document/sync_batch`, data);\n};\n\nexport const uploadDocument = (knowLedgeName: string, data: FormData) => {\n  return POST<FormData, number>(`/knowledge/${knowLedgeName}/document/upload`, data);\n};\n\nexport const getChunkList = (spaceName: string, data: ChunkListParams) => {\n  return POST<ChunkListParams, IChunkList>(`/knowledge/${spaceName}/chunk/list`, data);\n};\n\nexport const delDocument = (spaceName: string, data: Record<string, string>) => {\n  return POST<Record<string, string>, null>(`/knowledge/${spaceName}/document/delete`, data);\n};\n\nexport const delSpace = (data: Record<string, string>) => {\n  return POST<Record<string, string>, null>(`/knowledge/space/delete`, data);\n};\n\n/** models */\nexport const getModelList = () => {\n  return GET<null, Array<IModelData>>('/api/v1/worker/model/list');\n};\n\nexport const stopModel = (data: BaseModelParams) => {\n  return POST<BaseModelParams, boolean>('/api/v1/worker/model/stop', data);\n};\n\nexport const startModel = (data: StartModelParams) => {\n  return POST<StartModelParams, boolean>('/api/v1/worker/model/start', data);\n};\n\nexport const getSupportModels = () => {\n  return GET<null, Array<SupportModel>>('/api/v1/worker/model/params');\n};\n\n/** Agent */\nexport const postAgentQuery = (data: PostAgentQueryParams) => {\n  return POST<PostAgentQueryParams, PostAgentPluginResponse>('/api/v1/agent/query', data);\n};\nexport const postAgentHubUpdate = (data?: PostAgentHubUpdateParams) => {\n  return POST<PostAgentHubUpdateParams>('/api/v1/agent/hub/update', data ?? { channel: '', url: '', branch: '', authorization: '' });\n};\nexport const postAgentMy = (user?: string) => {\n  return POST<undefined, PostAgentMyPluginResponse>('/api/v1/agent/my', undefined, { params: { user } });\n};\nexport const postAgentInstall = (pluginName: string, user?: string) => {\n  return POST('/api/v1/agent/install', undefined, { params: { plugin_name: pluginName, user }, timeout: 60000 });\n};\nexport const postAgentUninstall = (pluginName: string, user?: string) => {\n  return POST('/api/v1/agent/uninstall', undefined, { params: { plugin_name: pluginName, user }, timeout: 60000 });\n};\nexport const postAgentUpload = (user = '', data: FormData, config?: Omit<AxiosRequestConfig, 'headers'>) => {\n  return POST<FormData>('/api/v1/personal/agent/upload', data, {\n    params: { user },\n    headers: {\n      'Content-Type': 'multipart/form-data',\n    },\n    ...config,\n  });\n};\nexport const getDbgptsList = () => {\n  return GET<undefined, GetDBGPTsListResponse>('/api/v1/dbgpts/list');\n};\n\n/** chat feedback **/\nexport const getChatFeedBackSelect = () => {\n  return GET<null, FeedBack>(`/api/v1/feedback/select`, undefined);\n};\nexport const getChatFeedBackItme = (conv_uid: string, conv_index: number) => {\n  return GET<null, Record<string, string>>(`/api/v1/feedback/find?conv_uid=${conv_uid}&conv_index=${conv_index}`, undefined);\n};\nexport const postChatFeedBackForm = ({ data, config }: { data: ChatFeedBackSchema; config?: Omit<AxiosRequestConfig, 'headers'> }) => {\n  return POST<ChatFeedBackSchema, any>(`/api/v1/feedback/commit`, data, {\n    headers: {\n      'Content-Type': 'application/json',\n    },\n    ...config,\n  });\n};\n\n/** prompt */\nexport const getPromptList = (data: PromptParams) => {\n  return POST<PromptParams, Array<IPrompt>>('/prompt/list', data);\n};\n\nexport const updatePrompt = (data: UpdatePromptParams) => {\n  return POST<UpdatePromptParams, []>('/prompt/update', data);\n};\n\nexport const addPrompt = (data: UpdatePromptParams) => {\n  return POST<UpdatePromptParams, []>('/prompt/add', data);\n};\n\n/** AWEL Flow */\nexport const addFlow = (data: IFlowUpdateParam) => {\n  return POST<IFlowUpdateParam, IFlow>('/api/v1/serve/awel/flows', data);\n};\n\nexport const getFlows = () => {\n  return GET<null, IFlowResponse>('/api/v1/serve/awel/flows');\n};\n\nexport const getFlowById = (id: string) => {\n  return GET<null, IFlow>(`/api/v1/serve/awel/flows/${id}`);\n};\n\nexport const updateFlowById = (id: string, data: IFlowUpdateParam) => {\n  return PUT<IFlowUpdateParam, IFlow>(`/api/v1/serve/awel/flows/${id}`, data);\n};\n\nexport const deleteFlowById = (id: string) => {\n  return DELETE<null, null>(`/api/v1/serve/awel/flows/${id}`);\n};\n\nexport const getFlowNodes = () => {\n  return GET<null, Array<IFlowNode>>(`/api/v1/serve/awel/nodes`);\n};\n\n/** app */\nexport const addApp = (data: IApp) => {\n  return POST<IApp, []>('/api/v1/app/create', data);\n};\n\nexport const getAppList = (data: Record<string, string>) => {\n  return POST<Record<string, string>, IAppData>('/api/v1/app/list', data);\n};\n\nexport const collectApp = (data: Record<string, string>) => {\n  return POST<Record<string, string>, []>('/api/v1/app/collect', data);\n};\n\nexport const unCollectApp = (data: Record<string, string>) => {\n  return POST<Record<string, string>, []>('/api/v1/app/uncollect', data);\n};\n\nexport const delApp = (data: Record<string, string>) => {\n  return POST<Record<string, string>, []>('/api/v1/app/remove', data);\n};\n\nexport const getAgents = () => {\n  return GET<object, IAgent[]>('/api/v1/agents/list', {});\n};\n\nexport const getTeamMode = () => {\n  return GET<null, string[]>('/api/v1/team-mode/list');\n};\n\nexport const getResourceType = () => {\n  return GET<null, string[]>('/api/v1/resource-type/list');\n};\n\nexport const getResource = (data: Record<string, string>) => {\n  return GET<Record<string, string>, []>(`/api/v1/app/resources/list?type=${data.type}`);\n};\n\nexport const updateApp = (data: IApp) => {\n  return POST<IApp, []>('/api/v1/app/edit', data);\n};\n\nexport const getAppStrategy = () => {\n  return GET<null, []>(`/api/v1/llm-strategy/list`);\n};\n\nexport const getAppStrategyValues = (type: string) => {\n  return GET<string, []>(`/api/v1/llm-strategy/value/list?type=${type}`);\n};\n"
  },
  {
    "path": "client/api/tools/index.ts",
    "content": "export * from './interceptors';\n"
  },
  {
    "path": "client/api/tools/interceptors.ts",
    "content": "import { AxiosError } from 'axios';\nimport { ApiResponse, FailedTuple, SuccessTuple, ResponseType } from '../';\nimport { notification } from 'antd';\n\n/**\n * Response processing\n *\n * @param promise request\n * @param ignoreCodes ignore error codes\n * @returns\n */\nexport const apiInterceptors = <T = any, D = any>(promise: Promise<ApiResponse<T, D>>, ignoreCodes?: '*' | (number | string)[]) => {\n  return promise\n    .then<SuccessTuple<T, D>>((response) => {\n      const { data } = response;\n      if (!data) {\n        throw new Error('Network Error!');\n      }\n      if (!data.success) {\n        if (ignoreCodes === '*' || (data.err_code && ignoreCodes && ignoreCodes.includes(data.err_code))) {\n          return [null, data.data, data, response];\n        } else {\n          notification.error({\n            message: `Request error`,\n            description: data?.err_msg ?? 'The interface is abnormal. Please try again later',\n          });\n        }\n      }\n      return [null, data.data, data, response];\n    })\n    .catch<FailedTuple<T, D>>((err: Error | AxiosError<T, D>) => {\n      let errMessage = err.message;\n      if (err instanceof AxiosError) {\n        try {\n          const { err_msg } = JSON.parse(err.request.response) as ResponseType<null>;\n          err_msg && (errMessage = err_msg);\n        } catch (e) {}\n      }\n      notification.error({\n        message: `Request error`,\n        description: errMessage,\n      });\n      return [err, null, null, null];\n    });\n};\n"
  },
  {
    "path": "components/agent/market-plugins.tsx",
    "content": "import { apiInterceptors, postAgentHubUpdate, postAgentInstall, postAgentQuery, postAgentUninstall } from '@/client/api';\nimport { IAgentPlugin, PostAgentQueryParams } from '@/types/agent';\nimport { useRequest } from 'ahooks';\nimport { Button, Card, Form, Input, Spin, Tag, Tooltip, message } from 'antd';\nimport { useCallback, useMemo, useState } from 'react';\nimport MyEmpty from '../common/MyEmpty';\nimport { ClearOutlined, DownloadOutlined, GithubOutlined, LoadingOutlined, SearchOutlined, SyncOutlined } from '@ant-design/icons';\nimport { useTranslation } from 'react-i18next';\n\nfunction MarketPlugins() {\n  const { t } = useTranslation();\n\n  const [uploading, setUploading] = useState(false);\n  const [isError, setIsError] = useState(false);\n  const [actionIndex, setActionIndex] = useState<number | undefined>();\n\n  const [form] = Form.useForm<PostAgentQueryParams['filter']>();\n\n  const pagination = useMemo<{ pageNo: number; pageSize: number }>(\n    () => ({\n      pageNo: 1,\n      pageSize: 20,\n    }),\n    [],\n  );\n\n  const {\n    data: agents = [],\n    loading,\n    refresh,\n  } = useRequest(async () => {\n    const queryParams: PostAgentQueryParams = {\n      page_index: pagination.pageNo,\n      page_size: pagination.pageSize,\n      filter: form.getFieldsValue(),\n    };\n    const [err, res] = await apiInterceptors(postAgentQuery(queryParams));\n    setIsError(!!err);\n    return res?.datas ?? [];\n  });\n\n  const updateFromGithub = async () => {\n    try {\n      setUploading(true);\n      const [err] = await apiInterceptors(postAgentHubUpdate());\n      if (err) return;\n      message.success('success');\n      refresh();\n    } finally {\n      setUploading(false);\n    }\n  };\n\n  const pluginAction = useCallback(\n    async (name: string, index: number, isInstall: boolean) => {\n      if (actionIndex) return;\n      setActionIndex(index);\n      const [err] = await apiInterceptors((isInstall ? postAgentInstall : postAgentUninstall)(name));\n      if (!err) {\n        message.success('success');\n        refresh();\n      }\n      setActionIndex(undefined);\n    },\n    [actionIndex, refresh],\n  );\n\n  const renderAction = useCallback(\n    (agent: IAgentPlugin, index: number) => {\n      if (index === actionIndex) {\n        return <LoadingOutlined />;\n      }\n      return agent.installed ? (\n        <Tooltip title=\"Uninstall\">\n          <div\n            className=\"w-full h-full\"\n            onClick={() => {\n              pluginAction(agent.name, index, false);\n            }}\n          >\n            <ClearOutlined />\n          </div>\n        </Tooltip>\n      ) : (\n        <Tooltip title=\"Install\">\n          <div\n            className=\"w-full h-full\"\n            onClick={() => {\n              pluginAction(agent.name, index, true);\n            }}\n          >\n            <DownloadOutlined />\n          </div>\n        </Tooltip>\n      );\n    },\n    [actionIndex, pluginAction],\n  );\n\n  return (\n    <Spin spinning={loading}>\n      <Form form={form} layout=\"inline\" onFinish={refresh} className=\"mb-2\">\n        <Form.Item className=\"!mb-2\" name=\"name\" label={'Name'}>\n          <Input allowClear className=\"w-48\" />\n        </Form.Item>\n        <Form.Item>\n          <Button className=\"mr-2\" type=\"primary\" htmlType=\"submit\" icon={<SearchOutlined />}>\n            {t('Search')}\n          </Button>\n          <Button loading={uploading} type=\"primary\" icon={<SyncOutlined />} onClick={updateFromGithub}>\n            {t('Update_From_Github')}\n          </Button>\n        </Form.Item>\n      </Form>\n      {!agents.length && !loading && <MyEmpty error={isError} refresh={refresh} />}\n      <div className=\"flex flex-wrap gap-2 md:gap-4\">\n        {agents.map((agent, index) => (\n          <Card\n            className=\"w-full md:w-1/2 lg:w-1/3 xl:w-1/4\"\n            key={agent.id}\n            actions={[\n              renderAction(agent, index),\n              <Tooltip key=\"github\" title=\"Github\">\n                <div\n                  className=\"w-full h-full\"\n                  onClick={() => {\n                    window.open(agent.storage_url, '_blank');\n                  }}\n                >\n                  <GithubOutlined />\n                </div>\n              </Tooltip>,\n            ]}\n          >\n            <Tooltip title={agent.name}>\n              <h2 className=\"mb-2 text-base font-semibold line-clamp-1\">{agent.name}</h2>\n            </Tooltip>\n            {agent.author && <Tag>{agent.author}</Tag>}\n            {agent.version && <Tag>v{agent.version}</Tag>}\n            {agent.type && <Tag>Type {agent.type}</Tag>}\n            {agent.storage_channel && <Tag>{agent.storage_channel}</Tag>}\n            <Tooltip title={agent.description}>\n              <p className=\"mt-2 line-clamp-2 text-gray-400 text-sm\">{agent.description}</p>\n            </Tooltip>\n          </Card>\n        ))}\n      </div>\n    </Spin>\n  );\n}\n\nexport default MarketPlugins;\n"
  },
  {
    "path": "components/agent/my-plugins.tsx",
    "content": "import { apiInterceptors, postAgentMy, postAgentUninstall, postAgentUpload } from '@/client/api';\nimport { IMyPlugin } from '@/types/agent';\nimport { useRequest } from 'ahooks';\nimport { Button, Card, Spin, Tag, Tooltip, Upload, UploadProps, message } from 'antd';\nimport { useCallback, useState } from 'react';\nimport MyEmpty from '../common/MyEmpty';\nimport { ClearOutlined, LoadingOutlined, UploadOutlined } from '@ant-design/icons';\nimport { useTranslation } from 'react-i18next';\n\nfunction MyPlugins() {\n  const { t } = useTranslation();\n  const [messageApi, contextHolder] = message.useMessage();\n\n  const [uploading, setUploading] = useState(false);\n  const [isError, setIsError] = useState(false);\n  const [actionIndex, setActionIndex] = useState<number | undefined>();\n\n  const {\n    data = [],\n    loading,\n    refresh,\n  } = useRequest(async () => {\n    const [err, res] = await apiInterceptors(postAgentMy());\n    setIsError(!!err);\n    return res ?? [];\n  });\n\n  const uninstall = async (name: string, index: number) => {\n    if (actionIndex) return;\n    setActionIndex(index);\n    const [err] = await apiInterceptors(postAgentUninstall(name));\n    message[err ? 'error' : 'success'](err ? 'failed' : 'success');\n    !err && refresh();\n    setActionIndex(undefined);\n  };\n\n  const renderAction = useCallback(\n    (item: IMyPlugin, index: number) => {\n      if (index === actionIndex) {\n        return <LoadingOutlined />;\n      }\n      return (\n        <Tooltip title=\"Uninstall\">\n          <div\n            className=\"w-full h-full\"\n            onClick={() => {\n              uninstall(item.name, index);\n            }}\n          >\n            <ClearOutlined />\n          </div>\n        </Tooltip>\n      );\n    },\n    [actionIndex],\n  );\n\n  const onChange: UploadProps['onChange'] = async (info) => {\n    if (!info) {\n      message.error('Please select the *.zip,*.rar file');\n      return;\n    }\n    try {\n      const file = info.file;\n      setUploading(true);\n      const formData = new FormData();\n      formData.append('doc_file', file as any);\n      messageApi.open({ content: `Uploading ${file.name}`, type: 'loading', duration: 0 });\n      const [err] = await apiInterceptors(postAgentUpload(undefined, formData, { timeout: 60000 }));\n      if (err) return;\n      message.success('success');\n      refresh();\n    } catch (e: any) {\n      message.error(e?.message || 'Upload Error');\n    } finally {\n      setUploading(false);\n      messageApi.destroy();\n    }\n  };\n\n  return (\n    <Spin spinning={loading}>\n      {contextHolder}\n      <div>\n        <Upload\n          disabled={loading}\n          className=\"mr-1\"\n          beforeUpload={() => false}\n          name=\"file\"\n          accept=\".zip,.rar\"\n          multiple={false}\n          onChange={onChange}\n          showUploadList={{\n            showDownloadIcon: false,\n            showPreviewIcon: false,\n            showRemoveIcon: false,\n          }}\n          itemRender={() => <></>}\n        >\n          <Button loading={uploading} type=\"primary\" icon={<UploadOutlined />}>\n            {t('Upload')}\n          </Button>\n        </Upload>\n      </div>\n      {!data.length && !loading && <MyEmpty error={isError} refresh={refresh} />}\n      <div className=\"flex gap-2 md:gap-4\">\n        {data.map((item, index) => (\n          <Card className=\"w-full md:w-1/2 lg:w-1/3 xl:w-1/4\" key={item.id} actions={[renderAction(item, index)]}>\n            <Tooltip title={item.name}>\n              <h2 className=\"mb-2 text-base font-semibold line-clamp-1\">{item.name}</h2>\n            </Tooltip>\n            {item.version && <Tag>v{item.version}</Tag>}\n            {item.type && <Tag>Type {item.type}</Tag>}\n            <Tooltip title={item.description}>\n              <p className=\"mt-2 line-clamp-2 text-gray-400 text-sm\">{item.description}</p>\n            </Tooltip>\n          </Card>\n        ))}\n      </div>\n    </Spin>\n  );\n}\n\nexport default MyPlugins;\n"
  },
  {
    "path": "components/app/agent-panel.tsx",
    "content": "import { apiInterceptors, getAppStrategy, getAppStrategyValues, getResource } from '@/client/api';\nimport { Button, Input, Select } from 'antd';\nimport React, { useEffect, useMemo, useState } from 'react';\nimport ResourceCard from './resource-card';\nimport { useTranslation } from 'react-i18next';\n\ninterface IProps {\n  resourceTypes: any;\n  updateDetailsByAgentKey: (key: string, data: any) => void;\n  detail: any;\n  editResources?: any;\n}\n\nexport default function AgentPanel(props: IProps) {\n  const { resourceTypes, updateDetailsByAgentKey, detail, editResources } = props;\n  const { t } = useTranslation();\n\n  const [resources, setResources] = useState<any>([...(editResources ?? [])]);\n  const [agent, setAgent] = useState<any>({ ...detail, resources: [] });\n  const [strategyOptions, setStrategyOptions] = useState<any>([]);\n  const [strategyValueOptions, setStrategyValueOptions] = useState<any>([]);\n\n  const updateResourcesByIndex = (data: any, index: number) => {\n    setResources((resources: any) => {\n      const tempResources = [...resources];\n      if (!data) {\n        return tempResources.filter((_: any, indey) => index !== indey);\n      }\n\n      return tempResources.map((item: any, indey) => {\n        if (index === indey) {\n          return data;\n        } else {\n          return item;\n        }\n      });\n    });\n  };\n\n  const getStrategy = async () => {\n    const [_, data] = await apiInterceptors(getAppStrategy());\n    if (data) {\n      setStrategyOptions(data?.map((item) => ({ label: item, value: item })));\n    }\n  };\n\n  const getStrategyValues = async (type: string) => {\n    const [_, data] = await apiInterceptors(getAppStrategyValues(type));\n    if (data) {\n      setStrategyValueOptions(data.map((item) => ({ label: item, value: item })) ?? []);\n    }\n  };\n\n  const formatStrategyValue = (value: string) => {\n    return !value ? [] : value.split(',');\n  };\n\n  useEffect(() => {\n    getStrategy();\n    getStrategyValues(detail.llm_strategy);\n  }, []);\n\n  useEffect(() => {\n    updateAgent(resources, 'resources');\n  }, [resources]);\n\n  const updateAgent = (data: any, type: string) => {\n    const tempAgent = { ...agent };\n    tempAgent[type] = data;\n\n    setAgent(tempAgent);\n\n    updateDetailsByAgentKey(detail.key, tempAgent);\n  };\n\n  const handelAdd = () => {\n    setResources([...resources, { name: '', type: '', introduce: '', value: '', is_dynamic: '' }]);\n  };\n\n  const resourceTypeOptions = useMemo(() => {\n    return resourceTypes?.map((item: string) => {\n      return {\n        label: item,\n        value: item,\n      };\n    });\n  }, [resourceTypes]);\n\n  return (\n    <div>\n      <div className=\"flex items-center mb-6 mt-6\">\n        <div className=\"mr-2 w-16 text-center\">{t('Prompt')}:</div>\n        <Input\n          required\n          className=\"mr-6 w-1/4\"\n          value={agent.prompt_template}\n          onChange={(e) => {\n            updateAgent(e.target.value, 'prompt_template');\n          }}\n        />\n        <div className=\"mr-2\">{t('LLM_strategy')}:</div>\n        <Select\n          value={agent.llm_strategy}\n          options={strategyOptions}\n          className=\"w-1/6 mr-6\"\n          onChange={(value) => {\n            updateAgent(value, 'llm_strategy');\n            getStrategyValues(value);\n          }}\n        />\n        {strategyValueOptions && strategyValueOptions.length > 0 && (\n          <>\n            <div className=\"mr-2\">{t('LLM_strategy_value')}:</div>\n            <Select\n              value={formatStrategyValue(agent.llm_strategy_value)}\n              className=\"w-1/4\"\n              mode=\"multiple\"\n              options={strategyValueOptions}\n              onChange={(value) => {\n                if (!value || value?.length === 0) {\n                  updateAgent(null, 'llm_strategy_value');\n                  return null;\n                }\n\n                const curValue = value.reduce((pre: string, cur: string, index: number) => {\n                  if (index === 0) {\n                    return cur;\n                  } else {\n                    return `${pre},${cur}`;\n                  }\n                }, '');\n\n                updateAgent(curValue, 'llm_strategy_value');\n              }}\n            />\n          </>\n        )}\n      </div>\n      <div className=\"mb-3 text-lg font-bold\">{t('available_resources')}</div>\n      {resources.map((resource: any, index: number) => {\n        return (\n          <ResourceCard\n            resource={resource}\n            key={index}\n            index={index}\n            updateResourcesByIndex={updateResourcesByIndex}\n            resourceTypeOptions={resourceTypeOptions}\n          />\n        );\n      })}\n      <Button type=\"primary\" className=\"mt-2\" size=\"middle\" onClick={handelAdd}>\n        {t('add_resource')}\n      </Button>\n    </div>\n  );\n}\n"
  },
  {
    "path": "components/app/app-card.tsx",
    "content": "import React, { useContext, useEffect, useState } from 'react';\nimport { Modal } from 'antd';\nimport { apiInterceptors, collectApp, delApp, newDialogue, unCollectApp } from '@/client/api';\nimport { IApp } from '@/types/app';\nimport { DeleteFilled, MessageFilled, StarFilled, WarningOutlined } from '@ant-design/icons';\nimport { useTranslation } from 'react-i18next';\nimport { useRouter } from 'next/router';\nimport { ChatContext } from '@/app/chat-context';\nimport GPTCard from '../common/gpt-card';\n\ninterface IProps {\n  updateApps: (data?: { is_collected: boolean }) => void;\n  app: IApp;\n  handleEdit: (app: any) => void;\n  isCollected: boolean;\n}\n\nconst { confirm } = Modal;\n\nexport default function AppCard(props: IProps) {\n  const { updateApps, app, handleEdit, isCollected } = props;\n  const { model } = useContext(ChatContext);\n  const router = useRouter();\n\n  const [isCollect, setIsCollect] = useState<string>(app.is_collected);\n  const { setAgent: setAgentToChat } = useContext(ChatContext);\n\n  const { t } = useTranslation();\n\n  const languageMap = {\n    en: t('English'),\n    zh: t('Chinese'),\n  };\n\n  const showDeleteConfirm = () => {\n    confirm({\n      title: t('Tips'),\n      icon: <WarningOutlined />,\n      content: `do you want delete the application?`,\n      okText: 'Yes',\n      okType: 'danger',\n      cancelText: 'No',\n      async onOk() {\n        await apiInterceptors(delApp({ app_code: app.app_code }));\n        updateApps(isCollected ? { is_collected: isCollected } : undefined);\n      },\n    });\n  };\n\n  useEffect(() => {\n    setIsCollect(app.is_collected);\n  }, [app]);\n\n  const collect = async () => {\n    const [error] = await apiInterceptors(isCollect === 'true' ? unCollectApp({ app_code: app.app_code }) : collectApp({ app_code: app.app_code }));\n    if (error) return;\n    updateApps(isCollected ? { is_collected: isCollected } : undefined);\n    setIsCollect(isCollect === 'true' ? 'false' : 'true');\n  };\n\n  const handleChat = async () => {\n    setAgentToChat?.(app.app_code);\n    const [, res] = await apiInterceptors(newDialogue({ chat_mode: 'chat_agent' }));\n    if (res) {\n      router.push(`/chat/?scene=chat_agent&id=${res.conv_uid}${model ? `&model=${model}` : ''}`);\n    }\n  };\n\n  return (\n    <GPTCard\n      title={app.app_name}\n      icon={'/icons/node/vis.png'}\n      iconBorder={false}\n      desc={app.app_describe}\n      tags={[\n        { text: languageMap[app.language], color: 'default' },\n        { text: app.team_mode, color: 'default' },\n      ]}\n      onClick={() => {\n        handleEdit(app);\n      }}\n      operations={[\n        {\n          label: t('Chat'),\n          children: <MessageFilled />,\n          onClick: handleChat,\n        },\n        {\n          label: t('collect'),\n          children: <StarFilled className={app.is_collected === 'false' ? 'text-gray-400' : 'text-yellow-400'} />,\n          onClick: collect,\n        },\n        {\n          label: t('Delete'),\n          children: <DeleteFilled />,\n          onClick: () => {\n            showDeleteConfirm();\n          },\n        },\n      ]}\n    />\n  );\n}\n"
  },
  {
    "path": "components/app/app-modal.tsx",
    "content": "import { AgentParams, IAgent as IAgentParams, IApp, IDetail } from '@/types/app';\nimport { Dropdown, Form, Input, Modal, Select, Space, Spin, Tabs } from 'antd';\nimport React, { useEffect, useRef, useState } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport AddIcon from '../icons/add-icon';\nimport AgentPanel from './agent-panel';\nimport { addApp, apiInterceptors, getAgents, getResourceType, getTeamMode, updateApp } from '@/client/api';\nimport type { TabsProps } from 'antd';\nimport DagLayout from './dag-layout';\nimport { IFlow } from '@/types/flow';\n\ntype TargetKey = string;\n\ntype FieldType = {\n  app_name: string;\n  app_describe: string;\n  language: string;\n  team_mode: string;\n};\n\ntype IAgent = {\n  label: string;\n  children?: React.ReactNode;\n  onClick?: () => void;\n  key: number | string;\n};\n\ninterface IProps {\n  handleCancel: () => void;\n  open: boolean;\n  updateApps: () => void;\n  type: string;\n  app?: any;\n}\n\ntype TeamModals = 'awel_layout' | 'singe_agent' | 'auto_plan';\n\nexport default function AppModal(props: IProps) {\n  const { handleCancel, open, updateApps, type, app } = props;\n\n  const { t } = useTranslation();\n  const [spinning, setSpinning] = useState<boolean>(false);\n  const [activeKey, setActiveKey] = useState<string>();\n  const [teamModal, setTeamModal] = useState<{ label: string; value: string }[]>();\n  const [agents, setAgents] = useState<TabsProps['items']>([]);\n  const [dropItems, setDropItems] = useState<IAgent[]>([]);\n  const [details, setDetails] = useState<IDetail[]>([...(app?.details || [])]);\n  const [flow, setFlow] = useState<IFlow>();\n  const [resourceTypes, setResourceTypes] = useState<string[]>();\n  const [curTeamModal, setCurTeamModal] = useState<TeamModals>(app.team_modal || 'auto_plan');\n\n  const [form] = Form.useForm();\n\n  const languageOptions = [\n    { value: 'zh', label: t('Chinese') },\n    { value: 'en', label: t('English') },\n  ];\n\n  const onChange = (newActiveKey: string) => {\n    setActiveKey(newActiveKey);\n  };\n\n  const createApp = async (app: IApp) => {\n    await apiInterceptors(type === 'add' ? addApp(app) : updateApp(app));\n    await updateApps();\n  };\n\n  const initApp = async () => {\n    const appDetails = app.details;\n    const [_, resourceType] = await apiInterceptors(getResourceType());\n\n    if (appDetails?.length > 0) {\n      setAgents(\n        appDetails?.map((item: AgentParams) => {\n          return {\n            label: item?.agent_name,\n            children: (\n              <AgentPanel\n                editResources={type === 'edit' && item.resources}\n                detail={{\n                  key: item?.agent_name,\n                  llm_strategy: item?.llm_strategy,\n                  agent_name: item?.agent_name,\n                  prompt_template: item?.prompt_template,\n                  llm_strategy_value: item?.llm_strategy_value,\n                }}\n                updateDetailsByAgentKey={updateDetailsByAgentKey}\n                resourceTypes={resourceType}\n              />\n            ),\n            key: item?.agent_name,\n          };\n        }),\n      );\n    }\n  };\n\n  const fetchTeamModal = async () => {\n    const [_, data] = await apiInterceptors(getTeamMode());\n    if (!data) return null;\n\n    const teamModalOptions = data.map((item) => {\n      return { value: item, label: item };\n    });\n    setTeamModal(teamModalOptions);\n  };\n\n  const fetchAgent = async () => {\n    const [_, data] = await apiInterceptors(getAgents());\n    if (!data) {\n      return null;\n    }\n\n    setDropItems(\n      data\n        .map((agent) => {\n          return {\n            label: agent.name,\n            key: agent.name,\n            onClick: () => {\n              add(agent);\n            },\n            agent,\n          };\n        })\n        .filter((item) => {\n          if (!app.details || app.details?.length === 0) {\n            return item;\n          }\n          return app?.details?.every((detail: AgentParams) => detail.agent_name !== item.label);\n        }),\n    );\n  };\n\n  const handleFlowsChange = (data: IFlow) => {\n    setFlow(data);\n  };\n\n  const fetchResourceType = async () => {\n    const [_, data] = await apiInterceptors(getResourceType());\n    if (data) {\n      setResourceTypes(data);\n    }\n  };\n\n  useEffect(() => {\n    fetchTeamModal();\n    fetchAgent();\n    fetchResourceType();\n  }, []);\n\n  useEffect(() => {\n    type === 'edit' && initApp();\n  }, [resourceTypes]);\n\n  useEffect(() => {\n    setCurTeamModal(app.team_mode || 'auto_plan');\n  }, [app]);\n\n  const updateDetailsByAgentKey = (key: string, data: IDetail) => {\n    setDetails((details: IDetail[]) => {\n      return details.map((detail: IDetail) => {\n        return key === (detail.agent_name || detail.key) ? data : detail;\n      });\n    });\n  };\n\n  const add = async (tabBar: IAgentParams) => {\n    const newActiveKey = tabBar.name;\n    const [_, data] = await apiInterceptors(getResourceType());\n\n    setActiveKey(newActiveKey);\n\n    setDetails((details: IDetail[]) => {\n      return [...details, { key: newActiveKey, name: '', llm_strategy: 'priority' }];\n    });\n\n    setAgents((items: any) => {\n      return [\n        ...items,\n        {\n          label: newActiveKey,\n          children: (\n            <AgentPanel\n              detail={{ key: newActiveKey, llm_strategy: 'default', agent_name: newActiveKey, prompt_template: '', llm_strategy_value: null }}\n              updateDetailsByAgentKey={updateDetailsByAgentKey}\n              resourceTypes={data}\n            />\n          ),\n          key: newActiveKey,\n        },\n      ];\n    });\n\n    setDropItems((items) => {\n      return items.filter((item) => item.key !== tabBar.name);\n    });\n  };\n\n  const remove = (targetKey: TargetKey) => {\n    let newActiveKey = activeKey;\n    let lastIndex = -1;\n\n    if (!agents) {\n      return null;\n    }\n\n    agents.forEach((item, i) => {\n      if (item.key === targetKey) {\n        lastIndex = i - 1;\n      }\n    });\n\n    const newPanes = agents.filter((item) => item.key !== targetKey);\n    if (newPanes.length && newActiveKey === targetKey) {\n      if (lastIndex >= 0) {\n        newActiveKey = newPanes[lastIndex].key;\n      } else {\n        newActiveKey = newPanes[0].key;\n      }\n    }\n    setDetails((details: IDetail[]) => {\n      return details?.filter((detail: any) => {\n        return (detail.agent_name || detail.key) !== targetKey;\n      });\n    });\n\n    setAgents(newPanes);\n    setActiveKey(newActiveKey);\n    setDropItems((items: any) => {\n      return [\n        ...items,\n        {\n          label: targetKey,\n          key: targetKey,\n          onClick: () => {\n            add({ name: targetKey, describe: '', system_message: '' });\n          },\n        },\n      ];\n    });\n  };\n\n  const onEdit = (targetKey: any, action: 'add' | 'remove') => {\n    if (action === 'add') {\n      // add();\n    } else {\n      remove(targetKey);\n    }\n  };\n\n  const handleSubmit = async () => {\n    const isValidate = await form.validateFields();\n\n    if (!isValidate) {\n      return;\n    }\n    setSpinning(true);\n\n    const data = {\n      ...form.getFieldsValue(),\n    };\n    if (type === 'edit') {\n      data.app_code = app.app_code;\n    }\n\n    if (data.team_mode !== 'awel_layout') {\n      data.details = details;\n    } else {\n      const tempFlow = { ...flow };\n      delete tempFlow.flow_data;\n      data.team_context = tempFlow;\n    }\n\n    try {\n      await createApp(data);\n    } catch (error) {\n      return;\n    }\n\n    setSpinning(false);\n    handleCancel();\n  };\n\n  const handleTeamModalChange = (value: TeamModals) => {\n    setCurTeamModal(value);\n  };\n\n  const renderAddIcon = () => {\n    return (\n      <Dropdown menu={{ items: dropItems }} trigger={['click']}>\n        <a className=\"h-8 flex items-center\" onClick={(e) => e.preventDefault()}>\n          <Space>\n            <AddIcon />\n          </Space>\n        </a>\n      </Dropdown>\n    );\n  };\n\n  return (\n    <div>\n      <Modal\n        okText={t('Submit')}\n        title={type === 'edit' ? t('edit_application') : t('add_application')}\n        open={open}\n        width={'65%'}\n        onCancel={handleCancel}\n        onOk={handleSubmit}\n        destroyOnClose={true}\n      >\n        <Spin spinning={spinning}>\n          <Form\n            form={form}\n            preserve={false}\n            size=\"large\"\n            className=\"mt-4 max-h-[70vh] overflow-auto h-[90vh]\"\n            layout=\"horizontal\"\n            labelAlign=\"left\"\n            labelCol={{ span: 4 }}\n            initialValues={{\n              app_name: app.app_name,\n              app_describe: app.app_describe,\n              language: app.language || languageOptions[0].value,\n              team_mode: app.team_mode || 'auto_plan',\n            }}\n            autoComplete=\"off\"\n            onFinish={handleSubmit}\n          >\n            <Form.Item<FieldType> label={t('app_name')} name=\"app_name\" rules={[{ required: true, message: t('Please_input_the_name') }]}>\n              <Input placeholder={t('Please_input_the_name')} />\n            </Form.Item>\n            <Form.Item<FieldType>\n              label={t('Description')}\n              name=\"app_describe\"\n              rules={[{ required: true, message: t('Please_input_the_description') }]}\n            >\n              <Input.TextArea rows={3} placeholder={t('Please_input_the_description')} />\n            </Form.Item>\n            <div className=\"flex w-full\">\n              <Form.Item<FieldType> labelCol={{ span: 7 }} label={t('language')} name=\"language\" className=\"w-1/2\" rules={[{ required: true }]}>\n                <Select className=\"w-2/3 ml-4\" placeholder={t('language_select_tips')} options={languageOptions} />\n              </Form.Item>\n              <Form.Item<FieldType> label={t('team_modal')} name=\"team_mode\" className=\"w-1/2\" labelCol={{ span: 6 }} rules={[{ required: true }]}>\n                <Select\n                  defaultValue={app.team_mode || 'auto_plan'}\n                  className=\"ml-4 w-72\"\n                  onChange={handleTeamModalChange}\n                  placeholder={t('Please_input_the_work_modal')}\n                  options={teamModal}\n                />\n              </Form.Item>\n            </div>\n            {curTeamModal !== 'awel_layout' ? (\n              <>\n                <div className=\"mb-5\">{t('Agents')}</div>\n                <Tabs addIcon={renderAddIcon()} type=\"editable-card\" onChange={onChange} activeKey={activeKey} onEdit={onEdit} items={agents} />\n              </>\n            ) : (\n              <DagLayout onFlowsChange={handleFlowsChange} teamContext={app.team_context} />\n            )}\n          </Form>\n        </Spin>\n      </Modal>\n    </div>\n  );\n}\n"
  },
  {
    "path": "components/app/dag-layout.tsx",
    "content": "import React, { useEffect, useState } from 'react';\nimport PreviewFlow from '../flow/preview-flow';\nimport { apiInterceptors, getFlows } from '@/client/api';\nimport { IFlow } from '@/types/flow';\nimport { Select } from 'antd';\nimport Link from 'next/link';\nimport { t } from 'i18next';\n\ninterface IProps {\n  onFlowsChange: (data: any) => void;\n  teamContext: any;\n}\n\nexport default function DagLayout(props: IProps) {\n  const { onFlowsChange, teamContext } = props;\n  const [flows, setFlows] = useState<IFlow[]>();\n  const [flowsOptions, setFlowsOptions] = useState<any>();\n  const [curFlow, setCurFlow] = useState<IFlow>();\n  const fetchFlows = async () => {\n    const [_, data] = await apiInterceptors(getFlows());\n    if (data) {\n      setFlowsOptions(data?.items?.map((item: IFlow) => ({ label: item.name, value: item.name })));\n      setFlows(data.items);\n      onFlowsChange(data?.items[0]);\n    }\n  };\n\n  const handleFlowsChange = (value: string) => {\n    setCurFlow(flows?.find((item) => value === item.name));\n    onFlowsChange(flows?.find((item) => value === item.name));\n  };\n\n  useEffect(() => {\n    fetchFlows();\n  }, []);\n\n  useEffect(() => {\n    setCurFlow(flows?.find((item) => teamContext?.name === item.name) || flows?.[0]);\n  }, [teamContext, flows]);\n\n  return (\n    <div className=\"w-full h-[300px]\">\n      <div className=\"mr-24 mb-4 mt-2\">Flows:</div>\n      <div className=\"flex items-center mb-6\">\n        <Select onChange={handleFlowsChange} value={curFlow?.name || flowsOptions?.[0]?.value} className=\"w-1/4\" options={flowsOptions}></Select>\n        <Link href=\"/flow/canvas/\" className=\"ml-6\">\n          {t('edit_new_applications')}\n        </Link>\n        <div className=\"text-gray-500 ml-16\">{curFlow?.description}</div>\n      </div>\n      {curFlow && (\n        <div className=\"w-full h-full border-[0.5px] border-dark-gray\">\n          <PreviewFlow flowData={curFlow?.flow_data} />\n        </div>\n      )}\n    </div>\n  );\n}\n"
  },
  {
    "path": "components/app/resource-card.tsx",
    "content": "import { apiInterceptors, getResource } from '@/client/api';\nimport { DeleteFilled } from '@ant-design/icons';\nimport { Button, Card, ConfigProvider, Input, Select, Switch } from 'antd';\nimport React, { useEffect, useState } from 'react';\nimport { useTranslation } from 'react-i18next';\n\ninterface IProps {\n  resourceTypeOptions: any[];\n  updateResourcesByIndex: (data: any, index: number) => void;\n  index: number;\n  resource: any;\n}\n\nexport default function ResourceCard(props: IProps) {\n  const { resourceTypeOptions, updateResourcesByIndex, index, resource: editResource } = props;\n\n  const { t } = useTranslation();\n\n  const [resourceType, setResourceType] = useState<string>(editResource.type || resourceTypeOptions?.[0].label);\n  const [resourceValueOptions, setResourceValueOptions] = useState<any[]>([]);\n  const [resource, setResource] = useState<any>({\n    name: editResource.name,\n    type: editResource.type,\n    value: editResource.value,\n    is_dynamic: editResource.is_dynamic || false,\n  });\n\n  const fetchResource = async () => {\n    const [_, data] = await apiInterceptors(getResource({ type: resourceType }));\n\n    if (data) {\n      setResourceValueOptions(\n        data?.map((item) => {\n          return { label: item, value: item };\n        }),\n      );\n    } else {\n      setResourceValueOptions([]);\n    }\n  };\n\n  const handleChange = (value: string) => {\n    setResourceType(value);\n  };\n\n  const updateResource = (value: any, type: string) => {\n    const tempResource = resource;\n\n    tempResource[type] = value;\n    setResource(tempResource);\n    updateResourcesByIndex(tempResource, index);\n  };\n\n  const handleDeleteResource = () => {\n    updateResourcesByIndex(null, index);\n  };\n\n  useEffect(() => {\n    fetchResource();\n    updateResource(resource.type || resourceType, 'type');\n  }, [resourceType]);\n\n  useEffect(() => {\n    updateResource(resourceValueOptions[0]?.label || editResource.value, 'value');\n    setResource({ ...resource, value: resourceValueOptions[0]?.label || editResource.value });\n  }, [resourceValueOptions]);\n\n  return (\n    <Card\n      className=\"mb-3 dark:bg-[#232734] border-gray-200\"\n      title={`${t('resource')} ${index + 1}`}\n      extra={\n        <DeleteFilled\n          className=\"text-[#ff1b2e] !text-lg\"\n          onClick={() => {\n            handleDeleteResource();\n          }}\n        />\n      }\n    >\n      <div className=\"flex-1\">\n        <div className=\"flex items-center  mb-6\">\n          <div className=\"font-bold mr-4 w-32 text-center\">\n            <span className=\"text-[#ff4d4f] font-normal\">*</span>&nbsp;{t('resource_name')}:\n          </div>\n          <Input\n            className=\"w-1/3\"\n            required\n            value={resource.name}\n            onInput={(e: React.ChangeEvent<HTMLInputElement>) => {\n              updateResource(e.target.value, 'name');\n            }}\n          />\n          <div className=\"flex items-center\">\n            <div className=\"font-bold w-32 text-center\">{t('resource_dynamic')}</div>\n\n            <Switch\n              defaultChecked={editResource.is_dynamic || false}\n              style={{ background: resource.is_dynamic ? '#1677ff' : '#ccc' }}\n              onChange={(value) => {\n                updateResource(value, 'is_dynamic');\n              }}\n            />\n          </div>\n        </div>\n        <div className=\"flex mb-5  items-center\">\n          <div className=\"font-bold mr-4 w-32  text-center\">{t('resource_type')}: </div>\n          <Select\n            className=\"w-1/3\"\n            options={resourceTypeOptions}\n            value={resource.type || resourceTypeOptions?.[0]}\n            onChange={(value) => {\n              updateResource(value, 'type');\n              handleChange(value);\n            }}\n          />\n          <div className=\"font-bold w-32 text-center\">{t('resource_value')}:</div>\n          {resourceValueOptions?.length > 0 ? (\n            <Select\n              value={resource.value}\n              className=\"flex-1\"\n              options={resourceValueOptions}\n              onChange={(value) => {\n                updateResource(value, 'value');\n              }}\n            />\n          ) : (\n            <Input\n              className=\"flex-1\"\n              value={resource.value || editResource.value}\n              onInput={(e: React.ChangeEvent<HTMLInputElement>) => {\n                updateResource(e.target.value, 'value');\n              }}\n            />\n          )}\n        </div>\n      </div>\n    </Card>\n  );\n}\n"
  },
  {
    "path": "components/chart/autoChart/advisor/pipeline.ts",
    "content": "import { Advisor, CkbConfig } from '@antv/ava';\nimport type { Advice, AdviseParams, AdvisorConfig, ChartKnowledgeBase } from '@antv/ava';\nimport type { CustomAdvisorConfig, RuleConfig, Specification } from '../types';\n\nexport type CustomRecommendConfig = {\n  customCKB?: Partial<AdvisorConfig['ckbCfg']>;\n  customRule?: Partial<AdvisorConfig['ruleCfg']>;\n};\n\nexport const customizeAdvisor = (props: CustomAdvisorConfig): Advisor => {\n  const { charts, scopeOfCharts: CKBCfg, ruleConfig: ruleCfg } = props;\n\n  const customCKB: ChartKnowledgeBase = {};\n  charts?.forEach((chart) => {\n    /** 若用户自定义的图表 id 与内置图表 id 相同，内置图表将被覆盖 */\n    if (!chart.chartKnowledge.toSpec) {\n      chart.chartKnowledge.toSpec = (data: any, dataProps: any) => {\n        return { dataProps } as Specification;\n      };\n    } else {\n      const oriFunc = chart.chartKnowledge.toSpec;\n      chart.chartKnowledge.toSpec = (data: any, dataProps: any) => {\n        return {\n          ...oriFunc(data, dataProps),\n          dataProps: dataProps,\n        } as Specification;\n      };\n    }\n    customCKB[chart.chartType] = chart.chartKnowledge;\n  });\n\n  // 步骤一：如果有 exclude 项，先从给到的 CKB 中剔除部分选定的图表类型\n  if (CKBCfg?.exclude) {\n    CKBCfg.exclude.forEach((chartType: string) => {\n      if (Object.keys(customCKB).includes(chartType)) {\n        delete customCKB[chartType];\n      }\n    });\n  }\n  // 步骤二：如果有 include 项，则从当前（剔除后的）CKB中，只保留 include 中的图表类型。\n  if (CKBCfg?.include) {\n    const include = CKBCfg.include;\n    Object.keys(customCKB).forEach((chartType: string) => {\n      if (!include.includes(chartType)) {\n        delete customCKB[chartType];\n      }\n    });\n  }\n\n  const CKBConfig: CkbConfig = {\n    ...CKBCfg,\n    custom: customCKB,\n  };\n  const ruleConfig: RuleConfig = {\n    ...ruleCfg,\n  };\n\n  const myAdvisor = new Advisor({\n    ckbCfg: CKBConfig,\n    ruleCfg: ruleConfig,\n  });\n\n  return myAdvisor;\n};\n\n/** 主推荐流程 */\nexport const getVisAdvices = (props: any): Advice[] => {\n  const { data, dataMetaMap, myChartAdvisor } = props;\n  /**\n   * 若输入中有信息能够获取列的类型（ Interval, Nominal, Time ）,则将这个 信息传给 Advisor\n   * 主要是读取 levelOfMeasureMents 这个字段，即 dataMetaMap[item].levelOfMeasurements\n   */\n  const customDataProps = dataMetaMap\n    ? Object.keys(dataMetaMap).map((item) => {\n        return { name: item, ...dataMetaMap[item] };\n      })\n    : null;\n  const allAdvices = myChartAdvisor?.adviseWithLog({\n    data,\n    dataProps: customDataProps as AdviseParams['dataProps'],\n  });\n  return allAdvices?.advices ?? [];\n};\n"
  },
  {
    "path": "components/chart/autoChart/advisor/rule.ts",
    "content": "export const customRuleCfg = {};\n"
  },
  {
    "path": "components/chart/autoChart/advisor/utils.ts",
    "content": "import { isNull } from 'lodash';\nimport type { Advice } from '@antv/ava';\n\nexport function defaultAdvicesFilter(props: { advices: Advice[] }) {\n  const { advices } = props;\n  return advices;\n}\nexport const compare = (f1: any, f2: any) => {\n  if (isNull(f1.distinct) || isNull(f2.distinct)) {\n    if (f1.distinct! < f2!.distinct!) {\n      return 1;\n    }\n    if (f1.distinct! > f2.distinct!) {\n      return -1;\n    }\n    return 0;\n  }\n  return 0;\n};\n\nexport function hasSubset(array1: any[], array2: any[]): boolean {\n  return array2.every((e) => array1.includes(e));\n}\n\nexport function intersects(array1: any[], array2: any[]): boolean {\n  return array2.some((e) => array1.includes(e));\n}\n\nexport function LOM2EncodingType(lom: string) {\n  switch (lom) {\n    case 'Nominal':\n      return 'nominal';\n    case 'Ordinal':\n      return 'ordinal';\n    case 'Interval':\n      return 'quantitative';\n    case 'Time':\n      return 'temporal';\n    case 'Continuous':\n      return 'quantitative';\n    case 'Discrete':\n      return 'nominal';\n    default:\n      return 'nominal';\n  }\n}\n"
  },
  {
    "path": "components/chart/autoChart/charts/index.ts",
    "content": "import multi_line_chart from './multi-line-chart';\nimport multi_measure_column_chart from './multi-measure-column-chart';\nimport multi_measure_line_chart from './multi-measure-line-chart';\nimport type { CustomChart } from '../types';\n\nexport const customCharts: CustomChart[] = [multi_line_chart, multi_measure_column_chart, multi_measure_line_chart];\n\nexport type CustomChartsType = 'multi_line_chart' | 'multi_measure_column_chart' | 'multi_measure_line_chart';\n"
  },
  {
    "path": "components/chart/autoChart/charts/multi-line-chart.ts",
    "content": "import { hasSubset, intersects } from '../advisor/utils';\nimport { processDateEncode } from './util';\nimport type { ChartKnowledge, CustomChart, GetChartConfigProps, Specification } from '../types';\n\nconst getChartSpec = (data: GetChartConfigProps['data'], dataProps: GetChartConfigProps['dataProps']) => {\n  const field4X = dataProps.find((field) =>\n    // @ts-ignore\n    intersects(field.levelOfMeasurements, ['Time', 'Ordinal']),\n  );\n  // @ts-ignore\n  const field4Y = dataProps.filter((field) => hasSubset(field.levelOfMeasurements, ['Interval']));\n  const field4Nominal = dataProps.find((field) =>\n    // @ts-ignore\n    hasSubset(field.levelOfMeasurements, ['Nominal']),\n  );\n  if (!field4X || !field4Y) return null;\n\n  const spec: Specification = {\n    type: 'view',\n    autoFit: true,\n    data,\n    children: [],\n  };\n\n  field4Y.forEach((field) => {\n    const singleLine: Specification = {\n      type: 'line',\n      encode: {\n        x: processDateEncode(field4X.name as string, dataProps),\n        y: field.name,\n      },\n    };\n    if (field4Nominal) {\n      singleLine.encode.color = field4Nominal.name;\n    }\n    spec.children.push(singleLine);\n  });\n  return spec;\n};\n\nconst ckb: ChartKnowledge = {\n  id: 'multi_line_chart',\n  name: 'multi_line_chart',\n  alias: ['multi_line_chart'],\n  family: ['LineCharts'],\n  def: 'multi_line_chart uses lines with segments to show changes in data in a ordinal dimension',\n  purpose: ['Comparison', 'Trend'],\n  coord: ['Cartesian2D'],\n  category: ['Statistic'],\n  shape: ['Lines'],\n  dataPres: [\n    { minQty: 1, maxQty: 1, fieldConditions: ['Time', 'Ordinal'] },\n    { minQty: 1, maxQty: '*', fieldConditions: ['Interval'] },\n    { minQty: 0, maxQty: 1, fieldConditions: ['Nominal'] },\n  ],\n  channel: ['Color', 'Direction', 'Position'],\n  recRate: 'Recommended',\n  toSpec: getChartSpec,\n};\n\n/* 订制一个图表需要的所有参数 */\nexport const multi_line_chart: CustomChart = {\n  /* 图表唯一 Id */\n  chartType: 'multi_line_chart',\n  /* 图表知识 */\n  chartKnowledge: ckb as ChartKnowledge,\n  /** 图表中文名 */\n  chineseName: '折线图',\n};\n\nexport default multi_line_chart;\n"
  },
  {
    "path": "components/chart/autoChart/charts/multi-measure-column-chart.ts",
    "content": "import { hasSubset } from '../advisor/utils';\n\nimport type { ChartKnowledge, CustomChart, GetChartConfigProps, Specification } from '../types';\n\nconst getChartSpec = (data: GetChartConfigProps['data'], dataProps: GetChartConfigProps['dataProps']) => {\n  try {\n    // @ts-ignore\n    const field4Y = dataProps?.filter((field) => hasSubset(field.levelOfMeasurements, ['Interval']));\n    const field4Nominal = dataProps?.find((field) =>\n      // @ts-ignore\n      hasSubset(field.levelOfMeasurements, ['Nominal']),\n    );\n    if (!field4Nominal || !field4Y) return null;\n\n    const spec: Specification = {\n      type: 'view',\n      data,\n      children: [],\n    };\n\n    field4Y?.forEach((field) => {\n      const singleLine: Specification = {\n        type: 'interval',\n        encode: {\n          x: field4Nominal.name,\n          y: field.name,\n          color: () => field.name,\n          series: () => field.name,\n        },\n      };\n      spec.children.push(singleLine);\n    });\n    return spec;\n  } catch (err) {\n    console.log(err);\n    return null;\n  }\n};\n\nconst ckb: ChartKnowledge = {\n  id: 'multi_measure_column_chart',\n  name: 'multi_measure_column_chart',\n  alias: ['multi_measure_column_chart'],\n  family: ['ColumnCharts'],\n  def: 'multi_measure_column_chart uses lines with segments to show changes in data in a ordinal dimension',\n  purpose: ['Comparison', 'Distribution'],\n  coord: ['Cartesian2D'],\n  category: ['Statistic'],\n  shape: ['Lines'],\n  dataPres: [\n    { minQty: 1, maxQty: '*', fieldConditions: ['Interval'] },\n    { minQty: 1, maxQty: 1, fieldConditions: ['Nominal'] },\n  ],\n  channel: ['Color', 'Direction', 'Position'],\n  recRate: 'Recommended',\n  toSpec: getChartSpec,\n};\n\n/* 订制一个图表需要的所有参数 */\nexport const multi_measure_column_chart: CustomChart = {\n  /* 图表唯一 Id */\n  chartType: 'multi_measure_column_chart',\n  /* 图表知识 */\n  chartKnowledge: ckb as ChartKnowledge,\n  /** 图表中文名 */\n  chineseName: '折线图',\n};\n\nexport default multi_measure_column_chart;\n"
  },
  {
    "path": "components/chart/autoChart/charts/multi-measure-line-chart.ts",
    "content": "import { hasSubset, intersects } from '../advisor/utils';\nimport { processDateEncode } from './util';\nimport type { ChartKnowledge, CustomChart, GetChartConfigProps, Specification } from '../types';\n\nconst getChartSpec = (data: GetChartConfigProps['data'], dataProps: GetChartConfigProps['dataProps']) => {\n  try {\n    // @ts-ignore\n    const field4Y = dataProps?.filter((field) => hasSubset(field.levelOfMeasurements, ['Interval']));\n    const field4Nominal = dataProps?.find((field) =>\n      // @ts-ignore\n      hasSubset(field.levelOfMeasurements, ['Nominal']),\n    );\n    if (!field4Nominal || !field4Y) return null;\n\n    const spec: Specification = {\n      type: 'view',\n      data,\n      children: [],\n    };\n\n    field4Y?.forEach((field) => {\n      const singleLine: Specification = {\n        type: 'line',\n        encode: {\n          x: processDateEncode(field4Nominal.name as string, dataProps),\n          y: field.name,\n          color: () => field.name,\n          series: () => field.name,\n        },\n      };\n      spec.children.push(singleLine);\n    });\n    return spec;\n  } catch (err) {\n    console.log(err);\n    return null;\n  }\n};\n\nconst ckb: ChartKnowledge = {\n  id: 'multi_measure_line_chart',\n  name: 'multi_measure_line_chart',\n  alias: ['multi_measure_line_chart'],\n  family: ['LineCharts'],\n  def: 'multi_measure_line_chart uses lines with segments to show changes in data in a ordinal dimension',\n  purpose: ['Comparison', 'Distribution'],\n  coord: ['Cartesian2D'],\n  category: ['Statistic'],\n  shape: ['Lines'],\n  dataPres: [\n    { minQty: 1, maxQty: '*', fieldConditions: ['Interval'] },\n    { minQty: 1, maxQty: 1, fieldConditions: ['Nominal'] },\n  ],\n  channel: ['Color', 'Direction', 'Position'],\n  recRate: 'Recommended',\n  toSpec: getChartSpec,\n};\n\n/* 订制一个图表需要的所有参数 */\nexport const multi_measure_line_chart: CustomChart = {\n  /* 图表唯一 Id */\n  chartType: 'multi_measure_line_chart',\n  /* 图表知识 */\n  chartKnowledge: ckb as ChartKnowledge,\n  /** 图表中文名 */\n  chineseName: '折线图',\n};\n\nexport default multi_measure_line_chart;\n"
  },
  {
    "path": "components/chart/autoChart/charts/util.ts",
    "content": "type BasicDataPropertyForAdvice = any;\n\n/**\n * Process date column to new Date().\n * @param field\n * @param dataProps\n * @returns\n */\nexport function processDateEncode(field: string, dataProps: BasicDataPropertyForAdvice[]) {\n  const dp = dataProps.find((dataProp) => dataProp.name === field);\n\n  if (dp?.recommendation === 'date') {\n    return (d: any) => new Date(d[field]);\n  }\n  return field;\n}\n"
  },
  {
    "path": "components/chart/autoChart/helpers/index.ts",
    "content": "import { ChartId } from '@antv/ava';\nimport { CustomChartsType } from '../charts';\n\nexport type BackEndChartType =\n  | 'response_line_chart'\n  | 'response_bar_chart'\n  | 'response_pie_chart'\n  | 'response_scatter_chart'\n  | 'response_area_chart'\n  | 'response_heatmap_chart'\n  | 'response_table';\n\ntype ChartType = ChartId | CustomChartsType;\n\nexport const getChartType = (backendChartType: BackEndChartType): ChartType[] => {\n  if (backendChartType === 'response_line_chart') {\n    return ['multi_line_chart', 'multi_measure_line_chart'];\n  }\n  if (backendChartType === 'response_bar_chart') {\n    return ['multi_measure_column_chart'];\n  }\n  if (backendChartType === 'response_pie_chart') {\n    return ['pie_chart'];\n  }\n  if (backendChartType === 'response_scatter_chart') {\n    return ['scatter_plot'];\n  }\n  if (backendChartType === 'response_area_chart') {\n    return ['area_chart'];\n  }\n  if (backendChartType === 'response_heatmap_chart') {\n    return ['heatmap'];\n  }\n  return [];\n};\n"
  },
  {
    "path": "components/chart/autoChart/index.tsx",
    "content": "import { Empty, Row, Col, Select, Tooltip } from 'antd';\nimport { Advice, Advisor } from '@antv/ava';\nimport { Chart } from '@berryv/g2-react';\nimport i18n, { I18nKeys } from '@/app/i18n';\nimport { customizeAdvisor, getVisAdvices } from './advisor/pipeline';\nimport { useContext, useEffect, useMemo, useState } from 'react';\nimport { defaultAdvicesFilter } from './advisor/utils';\nimport { AutoChartProps, ChartType, CustomAdvisorConfig, CustomChart, Specification } from './types';\nimport { customCharts } from './charts';\nimport { ChatContext } from '@/app/chat-context';\n\nconst { Option } = Select;\n\nexport const AutoChart = (props: AutoChartProps) => {\n  const { data, chartType, scopeOfCharts, ruleConfig } = props;\n\n  const { mode } = useContext(ChatContext);\n\n  const [advisor, setAdvisor] = useState<Advisor>();\n  const [advices, setAdvices] = useState<Advice[]>([]);\n  const [renderChartType, setRenderChartType] = useState<ChartType>();\n\n  useEffect(() => {\n    const input_charts: CustomChart[] = customCharts;\n    const advisorConfig: CustomAdvisorConfig = {\n      charts: input_charts,\n      scopeOfCharts: undefined,\n      ruleConfig,\n    };\n    setAdvisor(customizeAdvisor(advisorConfig));\n  }, [ruleConfig, scopeOfCharts]);\n\n  useEffect(() => {\n    if (data && advisor) {\n      const avaAdvices = getVisAdvices({\n        data,\n        myChartAdvisor: advisor,\n      });\n      const filteredAdvices = defaultAdvicesFilter({\n        advices: avaAdvices,\n      });\n\n      filteredAdvices.sort((a, b) => {\n        return chartType.indexOf(b.type) - chartType?.indexOf(a.type);\n      });\n\n      setAdvices(filteredAdvices);\n\n      setRenderChartType(filteredAdvices[0]?.type as ChartType);\n    }\n  }, [data, advisor, chartType]);\n\n  const visComponent = useMemo(() => {\n    /* Advices exist, render the chart. */\n    if (advices?.length > 0) {\n      const chartTypeInput = renderChartType ?? advices[0].type;\n      const spec: Specification = advices?.find((item: Advice) => item.type === chartTypeInput)?.spec ?? undefined;\n      if (spec) {\n        return (\n          <Chart\n            key={chartTypeInput}\n            options={{\n              ...spec,\n              theme: mode,\n              autoFit: true,\n              height: 300,\n            }}\n          />\n        );\n      }\n    }\n  }, [advices, renderChartType]);\n\n  if (renderChartType) {\n    return (\n      <div>\n        <Row justify=\"start\" className=\"mb-2\">\n          <Col>{i18n.t('Advices')}</Col>\n          <Col style={{ marginLeft: 24 }}>\n            <Select\n              className=\"w-52\"\n              value={renderChartType}\n              placeholder={'Chart Switcher'}\n              onChange={(value) => setRenderChartType(value)}\n              size={'small'}\n            >\n              {advices?.map((item) => {\n                const name = i18n.t(item.type as I18nKeys);\n\n                return (\n                  <Option key={item.type} value={item.type}>\n                    <Tooltip title={name} placement={'right'}>\n                      <div>{name}</div>\n                    </Tooltip>\n                  </Option>\n                );\n              })}\n            </Select>\n          </Col>\n        </Row>\n        <div className=\"auto-chart-content\">{visComponent}</div>\n      </div>\n    );\n  }\n\n  return <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description={'暂无合适的可视化视图'} />;\n};\n\nexport * from './helpers';\n"
  },
  {
    "path": "components/chart/autoChart/types.ts",
    "content": "import { Advice, AdvisorConfig, ChartId, Datum, FieldInfo, PureChartKnowledge } from '@antv/ava';\n\nexport type ChartType = ChartId | string;\n\nexport type Specification = Advice['spec'] | any;\nexport type RuleConfig = AdvisorConfig['ruleCfg'];\n\nexport type AutoChartProps = {\n  data: Datum[];\n  /** Chart type which are suggestted. */\n  chartType: ChartType[];\n  /** Charts exclude or include. */\n  scopeOfCharts?: {\n    exclude?: string[];\n    include?: string[];\n  };\n  /** Customize rules. */\n  ruleConfig?: RuleConfig;\n};\n\nexport type ChartKnowledge = PureChartKnowledge & { toSpec?: any };\n\nexport type CustomChart = {\n  /** Chart type ID, unique. */\n  chartType: ChartType;\n  /** Chart knowledge. */\n  chartKnowledge: ChartKnowledge;\n  /** Chart name. */\n  chineseName?: string;\n};\n\nexport type GetChartConfigProps = {\n  data: Datum[];\n  spec: Specification;\n  dataProps: FieldInfo[];\n  chartType?: ChartType;\n};\n\nexport type CustomAdvisorConfig = {\n  charts?: CustomChart[];\n  scopeOfCharts?: {\n    exclude?: string[];\n    include?: string[];\n  };\n  ruleConfig?: RuleConfig;\n};\n"
  },
  {
    "path": "components/chart/bar-chart.tsx",
    "content": "import { ChatContext } from '@/app/chat-context';\nimport { ChartData } from '@/types/chat';\nimport { Chart } from '@berryv/g2-react';\nimport { useContext } from 'react';\n\nexport default function BarChart({ chart }: { key: string; chart: ChartData }) {\n  const { mode } = useContext(ChatContext);\n\n  return (\n    <div className=\"flex-1 min-w-0 p-4 bg-white dark:bg-theme-dark-container rounded\">\n      <div className=\"h-full\">\n        <div className=\"mb-2\">{chart.chart_name}</div>\n        <div className=\"opacity-80 text-sm mb-2\">{chart.chart_desc}</div>\n        <div className=\"h-[300px]\">\n          <Chart\n            style={{ height: '100%' }}\n            options={{\n              autoFit: true,\n              theme: mode,\n              type: 'interval',\n              data: chart.values,\n              encode: { x: 'name', y: 'value', color: 'type' },\n              axis: {\n                x: {\n                  labelAutoRotate: false,\n                },\n              },\n            }}\n          />\n        </div>\n      </div>\n    </div>\n  );\n}\n"
  },
  {
    "path": "components/chart/index.tsx",
    "content": "import { Card, CardContent, Typography } from '@mui/joy';\nimport BarChart from './bar-chart';\nimport LineChart from './line-chart';\nimport TableChart from './table-chart';\nimport { ChartData } from '@/types/chat';\nimport { useMemo } from 'react';\n\ntype Props = {\n  chartsData: Array<ChartData>;\n};\n\nfunction Chart({ chartsData }: Props) {\n  const chartRows = useMemo(() => {\n    if (chartsData) {\n      let res = [];\n      // 若是有类型为 IndicatorValue 的，提出去，独占一行\n      const chartCalc = chartsData?.filter((item) => item.chart_type === 'IndicatorValue');\n      if (chartCalc.length > 0) {\n        res.push({\n          charts: chartCalc,\n          type: 'IndicatorValue',\n        });\n      }\n      let otherCharts = chartsData?.filter((item) => item.chart_type !== 'IndicatorValue');\n      let otherLength = otherCharts.length;\n      let curIndex = 0;\n      // charts 数量 3～8个，暂定每行排序\n      let chartLengthMap = [[0], [1], [2], [1, 2], [1, 3], [2, 1, 2], [2, 1, 3], [3, 1, 3], [3, 2, 3]];\n      chartLengthMap[otherLength].forEach((item) => {\n        if (item > 0) {\n          const rowsItem = otherCharts.slice(curIndex, curIndex + item);\n          curIndex = curIndex + item;\n          res.push({\n            charts: rowsItem,\n          });\n        }\n      });\n      return res;\n    }\n    return undefined;\n  }, [chartsData]);\n\n  return (\n    <div className=\"flex flex-col gap-3\">\n      {chartRows?.map((chartRow, index) => (\n        <div key={`chart_row_${index}`} className={`${chartRow?.type !== 'IndicatorValue' ? 'flex gap-3' : ''}`}>\n          {chartRow.charts.map((chart) => {\n            if (chart.chart_type === 'IndicatorValue') {\n              return (\n                <div key={chart.chart_uid} className=\"flex flex-row gap-3\">\n                  {chart.values.map((item) => (\n                    <div key={item.name} className=\"flex-1\">\n                      <Card sx={{ background: 'transparent' }}>\n                        <CardContent className=\"justify-around\">\n                          <Typography gutterBottom component=\"div\">\n                            {item.name}\n                          </Typography>\n                          <Typography>{item.value}</Typography>\n                        </CardContent>\n                      </Card>\n                    </div>\n                  ))}\n                </div>\n              );\n            } else if (chart.chart_type === 'LineChart') {\n              return <LineChart key={chart.chart_uid} chart={chart} />;\n            } else if (chart.chart_type === 'BarChart') {\n              return <BarChart key={chart.chart_uid} chart={chart} />;\n            } else if (chart.chart_type === 'Table') {\n              return <TableChart key={chart.chart_uid} chart={chart} />;\n            }\n          })}\n        </div>\n      ))}\n    </div>\n  );\n}\n\nexport * from './autoChart';\nexport default Chart;\n"
  },
  {
    "path": "components/chart/line-chart.tsx",
    "content": "import { ChartData } from '@/types/chat';\nimport { Chart } from '@berryv/g2-react';\nimport { useContext } from 'react';\nimport { ChatContext } from '@/app/chat-context';\n\nexport default function LineChart({ chart }: { chart: ChartData }) {\n  const { mode } = useContext(ChatContext);\n\n  return (\n    <div className=\"flex-1 min-w-0 p-4 bg-white dark:bg-theme-dark-container rounded\">\n      <div className=\"h-full\">\n        <div className=\"mb-2\">{chart.chart_name}</div>\n        <div className=\"opacity-80 text-sm mb-2\">{chart.chart_desc}</div>\n        <div className=\"h-[300px]\">\n          <Chart\n            style={{ height: '100%' }}\n            options={{\n              autoFit: true,\n              theme: mode,\n              type: 'view',\n              data: chart.values,\n              children: [\n                {\n                  type: 'line',\n                  encode: {\n                    x: 'name',\n                    y: 'value',\n                    color: 'type',\n                    shape: 'smooth',\n                  },\n                },\n                {\n                  type: 'area',\n                  encode: {\n                    x: 'name',\n                    y: 'value',\n                    color: 'type',\n                    shape: 'smooth',\n                  },\n                  legend: false,\n                  style: {\n                    fillOpacity: 0.15,\n                  },\n                },\n              ],\n              axis: {\n                x: {\n                  labelAutoRotate: false,\n                },\n              },\n            }}\n          />\n        </div>\n      </div>\n    </div>\n  );\n}\n"
  },
  {
    "path": "components/chart/table-chart.tsx",
    "content": "import { ChartData } from '@/types/chat';\nimport { Table } from '@mui/joy';\nimport { groupBy } from 'lodash';\n\nexport default function TableChart({ chart }: { key: string; chart: ChartData }) {\n  const data = groupBy(chart.values, 'type');\n\n  return (\n    <div className=\"flex-1 min-w-0 p-4 bg-white dark:bg-theme-dark-container rounded\">\n      <div className=\"h-full\">\n        <div className=\"mb-2\">{chart.chart_name}</div>\n        <div className=\"opacity-80 text-sm mb-2\">{chart.chart_desc}</div>\n        <div className=\"flex-1\">\n          <Table aria-label=\"basic table\" stripe=\"odd\" hoverRow borderAxis=\"bothBetween\">\n            <thead>\n              <tr>\n                {Object.keys(data).map((key) => (\n                  <th key={key}>{key}</th>\n                ))}\n              </tr>\n            </thead>\n            <tbody>\n              {Object.values(data)?.[0]?.map((value, i) => (\n                <tr key={i}>\n                  {Object.keys(data)?.map((k) => (\n                    <td key={k}>{data?.[k]?.[i].value || ''}</td>\n                  ))}\n                </tr>\n              ))}\n            </tbody>\n          </Table>\n        </div>\n      </div>\n    </div>\n  );\n}\n"
  },
  {
    "path": "components/chat/agent-content.tsx",
    "content": "import { ChatContext } from '@/app/chat-context';\nimport { IChatDialogueMessageSchema } from '@/types/chat';\nimport classNames from 'classnames';\nimport { memo, useContext } from 'react';\nimport ReactMarkdown from 'react-markdown';\nimport markdownComponents from './chat-content/config';\nimport rehypeRaw from 'rehype-raw';\n\ninterface Props {\n  content: IChatDialogueMessageSchema;\n}\n\nfunction formatMarkdownVal(val: string) {\n  return val.replace(/<table(\\w*=[^>]+)>/gi, '<table $1>').replace(/<tr(\\w*=[^>]+)>/gi, '<tr $1>');\n}\n\nfunction AgentContent({ content }: Props) {\n  const { scene } = useContext(ChatContext);\n\n  const isView = content.role === 'view';\n\n  return (\n    <div\n      className={classNames('relative w-full p-2 md:p-4 rounded-xl break-words', {\n        'bg-white dark:bg-[#232734]': isView,\n        'lg:w-full xl:w-full pl-0': ['chat_with_db_execute', 'chat_dashboard'].includes(scene),\n      })}\n    >\n      {isView ? (\n        <ReactMarkdown components={markdownComponents} rehypePlugins={[rehypeRaw]}>\n          {formatMarkdownVal(content.context)}\n        </ReactMarkdown>\n      ) : (\n        <div className=\"\">{content.context}</div>\n      )}\n    </div>\n  );\n}\n\nexport default memo(AgentContent);\n"
  },
  {
    "path": "components/chat/chat-container.tsx",
    "content": "import React, { useCallback, useContext, useEffect, useState } from 'react';\nimport { useAsyncEffect } from 'ahooks';\nimport useChat from '@/hooks/use-chat';\nimport Completion from './completion';\nimport { ChartData, ChatHistoryResponse } from '@/types/chat';\nimport { apiInterceptors, getChatHistory } from '@/client/api';\nimport { ChatContext } from '@/app/chat-context';\nimport Header from './header';\nimport Chart from '../chart';\nimport classNames from 'classnames';\nimport MuiLoading from '../common/loading';\nimport { Empty } from 'antd';\nimport { useSearchParams } from 'next/navigation';\nimport { getInitMessage } from '@/utils';\n\nconst ChatContainer = () => {\n  const searchParams = useSearchParams();\n  const { scene, chatId, model, agent, setModel, history, setHistory } = useContext(ChatContext);\n  const chat = useChat({});\n  const initMessage = (searchParams && searchParams.get('initMessage')) ?? '';\n\n  const [loading, setLoading] = useState<boolean>(false);\n  const [chartsData, setChartsData] = useState<Array<ChartData>>();\n\n  const getHistory = async () => {\n    setLoading(true);\n    const [, res] = await apiInterceptors(getChatHistory(chatId));\n    setHistory(res ?? []);\n    setLoading(false);\n  };\n\n  const getChartsData = (list: ChatHistoryResponse) => {\n    const contextTemp = list[list.length - 1]?.context;\n    if (contextTemp) {\n      try {\n        const contextObj = JSON.parse(contextTemp);\n        setChartsData(contextObj?.template_name === 'report' ? contextObj?.charts : undefined);\n      } catch (e) {\n        setChartsData(undefined);\n      }\n    }\n  };\n\n  useAsyncEffect(async () => {\n    const initMessage = getInitMessage();\n    if (initMessage && initMessage.id === chatId) return;\n    await getHistory();\n  }, [initMessage, chatId]);\n\n  useEffect(() => {\n    if (!history.length) return;\n    /** use last view model_name as default model name */\n    const lastView = history.filter((i) => i.role === 'view')?.slice(-1)?.[0];\n    lastView?.model_name && setModel(lastView.model_name);\n\n    getChartsData(history);\n  }, [history.length]);\n\n  useEffect(() => {\n    return () => {\n      setHistory([]);\n    };\n  }, []);\n\n  const handleChat = useCallback(\n    (content: string, data?: Record<string, any>) => {\n      return new Promise<void>((resolve) => {\n        const tempHistory: ChatHistoryResponse = [\n          ...history,\n          { role: 'human', context: content, model_name: model, order: 0, time_stamp: 0 },\n          { role: 'view', context: '', model_name: model, order: 0, time_stamp: 0 },\n        ];\n        const index = tempHistory.length - 1;\n        setHistory([...tempHistory]);\n        chat({\n          data: { ...data, chat_mode: scene || 'chat_normal', model_name: model, user_input: content },\n          chatId,\n          onMessage: (message) => {\n            if (data?.incremental) {\n              tempHistory[index].context += message;\n            } else {\n              tempHistory[index].context = message;\n            }\n            setHistory([...tempHistory]);\n          },\n          onDone: () => {\n            getChartsData(tempHistory);\n            resolve();\n          },\n          onClose: () => {\n            getChartsData(tempHistory);\n            resolve();\n          },\n          onError: (message) => {\n            tempHistory[index].context = message;\n            setHistory([...tempHistory]);\n            resolve();\n          },\n        });\n      });\n    },\n    [history, chat, chatId, model, agent, scene],\n  );\n\n  return (\n    <>\n      <MuiLoading visible={loading} />\n      <Header\n        refreshHistory={getHistory}\n        modelChange={(newModel: string) => {\n          setModel(newModel);\n        }}\n      />\n      <div className=\"px-4 flex flex-1 flex-wrap overflow-hidden relative\">\n        {!!chartsData?.length && (\n          <div className=\"w-full pb-4 xl:w-3/4 h-3/5 xl:pr-4 xl:h-full overflow-y-auto\">\n            <Chart chartsData={chartsData} />\n          </div>\n        )}\n        {!chartsData?.length && scene === 'chat_dashboard' && (\n          <Empty\n            image=\"/empty.png\"\n            imageStyle={{ width: 320, height: 320, margin: '0 auto', maxWidth: '100%', maxHeight: '100%' }}\n            className=\"w-full xl:w-3/4 h-3/5 xl:h-full pt-0 md:pt-10\"\n          />\n        )}\n        {/** chat panel */}\n        <div\n          className={classNames('flex flex-1 flex-col overflow-hidden', {\n            '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',\n            'h-full lg:px-8': scene !== 'chat_dashboard',\n          })}\n        >\n          <Completion messages={history} onSubmit={handleChat} />\n        </div>\n      </div>\n    </>\n  );\n};\n\nexport default ChatContainer;\n"
  },
  {
    "path": "components/chat/chat-content/agent-messages.tsx",
    "content": "import ReactMarkdown from 'react-markdown';\nimport markdownComponents from './config';\nimport { renderModelIcon } from '../header/model-selector';\nimport { SwapRightOutlined } from '@ant-design/icons';\n\ninterface Props {\n  data: {\n    sender: string;\n    receiver: string;\n    model: string | null;\n    markdown: string;\n  }[];\n}\n\nfunction AgentMessages({ data }: Props) {\n  if (!data || !data.length) return null;\n\n  return (\n    <>\n      {data.map((item, index) => (\n        <div key={index} className=\"rounded my-4 md:my-6\">\n          <div className=\"flex items-center mb-3 text-sm\">\n            {item.model ? renderModelIcon(item.model) : <div className=\"rounded-full w-6 h-6 bg-gray-100\" />}\n            <div className=\"ml-2 opacity-70\">\n              {item.sender}\n              <SwapRightOutlined className=\"mx-2 text-base\" />\n              {item.receiver}\n            </div>\n          </div>\n          <div className=\"whitespace-normal text-sm\">\n            <ReactMarkdown components={markdownComponents}>{item.markdown}</ReactMarkdown>\n          </div>\n        </div>\n      ))}\n    </>\n  );\n}\n\nexport default AgentMessages;\n"
  },
  {
    "path": "components/chat/chat-content/agent-plans.tsx",
    "content": "import { CaretRightOutlined, CheckOutlined, ClockCircleOutlined } from '@ant-design/icons';\nimport { Collapse } from 'antd';\nimport ReactMarkdown from 'react-markdown';\nimport markdownComponents from './config';\n\ninterface Props {\n  data: {\n    name: string;\n    num: number;\n    status: 'complete' | 'todo';\n    agent: string;\n    markdown: string;\n  }[];\n}\n\nfunction AgentPlans({ data }: Props) {\n  if (!data || !data.length) return null;\n\n  return (\n    <Collapse\n      bordered\n      className=\"my-3\"\n      expandIcon={({ isActive }) => <CaretRightOutlined rotate={isActive ? 90 : 0} />}\n      items={data.map((item, index) => {\n        return {\n          key: index,\n          label: (\n            <div className=\"whitespace-normal\">\n              <span>\n                {item.name} - {item.agent}\n              </span>\n              {item.status === 'complete' ? (\n                <CheckOutlined className=\"!text-green-500 ml-2\" />\n              ) : (\n                <ClockCircleOutlined className=\"!text-gray-500 ml-2\" />\n              )}\n            </div>\n          ),\n          children: <ReactMarkdown components={markdownComponents}>{item.markdown}</ReactMarkdown>,\n        };\n      })}\n    />\n  );\n}\n\nexport default AgentPlans;\n"
  },
  {
    "path": "components/chat/chat-content/chart-view.tsx",
    "content": "import { Datum } from '@antv/ava';\nimport { Table, Tabs, TabsProps } from 'antd';\nimport React from 'react';\nimport { format } from 'sql-formatter';\nimport { AutoChart, BackEndChartType, getChartType } from '@/components/chart/autoChart';\nimport { CodePreview } from './code-preview';\n\nfunction ChartView({ data, type, sql }: { data: Datum[]; type: BackEndChartType; sql: string }) {\n  const columns = data?.[0]\n    ? Object.keys(data?.[0])?.map((item) => {\n        return {\n          title: item,\n          dataIndex: item,\n          key: item,\n        };\n      })\n    : [];\n  const ChartItem = {\n    key: 'chart',\n    label: 'Chart',\n    children: <AutoChart data={data} chartType={getChartType(type)} />,\n  };\n  const SqlItem = {\n    key: 'sql',\n    label: 'SQL',\n    children: <CodePreview language=\"sql\" code={format(sql ?? '', { language: 'mysql' }) as string} />,\n  };\n  const DataItem = {\n    key: 'data',\n    label: 'Data',\n    children: <Table dataSource={data} columns={columns} scroll={{ x: 'auto' }} />,\n  };\n  const TabItems: TabsProps['items'] = type === 'response_table' ? [DataItem, SqlItem] : [ChartItem, SqlItem, DataItem];\n\n  return <Tabs defaultActiveKey={type === 'response_table' ? 'data' : 'chart'} items={TabItems} size=\"small\" />;\n}\n\nexport default ChartView;\n"
  },
  {
    "path": "components/chat/chat-content/code-preview.tsx",
    "content": "import { Button, message } from 'antd';\nimport { CopyOutlined } from '@ant-design/icons';\nimport { oneDark, coldarkDark } from 'react-syntax-highlighter/dist/esm/styles/prism';\nimport { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';\nimport copy from 'copy-to-clipboard';\nimport { CSSProperties, useContext } from 'react';\nimport { ChatContext } from '@/app/chat-context';\n\ninterface Props {\n  code: string;\n  language: string;\n  customStyle?: CSSProperties;\n  light?: { [key: string]: CSSProperties };\n  dark?: { [key: string]: CSSProperties };\n}\n\nexport function CodePreview({ code, light, dark, language, customStyle }: Props) {\n  const { mode } = useContext(ChatContext);\n\n  return (\n    <div className=\"relative\">\n      <Button\n        className=\"absolute right-3 top-2 text-gray-300 hover:!text-gray-200 bg-gray-700\"\n        type=\"text\"\n        icon={<CopyOutlined />}\n        onClick={() => {\n          const success = copy(code);\n          message[success ? 'success' : 'error'](success ? 'Copy success' : 'Copy failed');\n        }}\n      />\n      <SyntaxHighlighter customStyle={customStyle} language={language} style={mode === 'dark' ? dark ?? coldarkDark : light ?? oneDark}>\n        {code}\n      </SyntaxHighlighter>\n    </div>\n  );\n}\n"
  },
  {
    "path": "components/chat/chat-content/config.tsx",
    "content": "import { LinkOutlined, ReadOutlined, SyncOutlined } from '@ant-design/icons';\nimport ReactMarkdown from 'react-markdown';\nimport { Table, Image, Tag, Tabs, TabsProps, Popover } from 'antd';\nimport { format } from 'sql-formatter';\nimport { Reference } from '@/types/chat';\nimport { AutoChart, BackEndChartType, getChartType } from '@/components/chart';\nimport { CodePreview } from './code-preview';\nimport { Datum } from '@antv/ava';\nimport rehypeRaw from 'rehype-raw';\nimport { IChunk } from '@/types/knowledge';\nimport AgentPlans from './agent-plans';\nimport AgentMessages from './agent-messages';\nimport VisConvertError from './vis-convert-error';\nimport VisChart from './vis-chart';\nimport VisDashboard from './vis-dashboard';\nimport VisPlugin from './vis-plugin';\nimport VisCode from './vis-code';\n\ntype MarkdownComponent = Parameters<typeof ReactMarkdown>['0']['components'];\n\nconst customeTags: (keyof JSX.IntrinsicElements)[] = ['custom-view', 'chart-view', 'references', 'summary'];\n\nfunction matchCustomeTagValues(context: string) {\n  const matchValues = customeTags.reduce<string[]>((acc, tagName) => {\n    const tagReg = new RegExp(`<${tagName}[^>]*\\/?>`, 'gi');\n    context = context.replace(tagReg, (matchVal) => {\n      acc.push(matchVal);\n      return '';\n    });\n    return acc;\n  }, []);\n  return { context, matchValues };\n}\n\nconst basicComponents: MarkdownComponent = {\n  code({ inline, node, className, children, style, ...props }) {\n    const content = String(children);\n    /**\n     * @description\n     * In some cases, tags are nested within code syntax,\n     * so it is necessary to extract the tags present in the code block and render them separately.\n     */\n    const { context, matchValues } = matchCustomeTagValues(content);\n    const lang = className?.replace('language-', '') || 'javascript';\n\n    if (lang === 'agent-plans') {\n      try {\n        const data = JSON.parse(content) as Parameters<typeof AgentPlans>[0]['data'];\n        return <AgentPlans data={data} />;\n      } catch (e) {\n        return <CodePreview language={lang} code={content} />;\n      }\n    }\n\n    if (lang === 'agent-messages') {\n      try {\n        const data = JSON.parse(content) as Parameters<typeof AgentMessages>[0]['data'];\n        return <AgentMessages data={data} />;\n      } catch (e) {\n        return <CodePreview language={lang} code={content} />;\n      }\n    }\n\n    if (lang === 'vis-convert-error') {\n      try {\n        const data = JSON.parse(content) as Parameters<typeof VisConvertError>[0]['data'];\n        return <VisConvertError data={data} />;\n      } catch (e) {\n        return <CodePreview language={lang} code={content} />;\n      }\n    }\n\n    if (lang === 'vis-dashboard') {\n      try {\n        const data = JSON.parse(content) as Parameters<typeof VisDashboard>[0]['data'];\n        return <VisDashboard data={data} />;\n      } catch (e) {\n        return <CodePreview language={lang} code={content} />;\n      }\n    }\n\n    if (lang === 'vis-chart') {\n      try {\n        const data = JSON.parse(content) as Parameters<typeof VisChart>[0]['data'];\n        return <VisChart data={data} />;\n      } catch (e) {\n        return <CodePreview language={lang} code={content} />;\n      }\n    }\n\n    if (lang === 'vis-plugin') {\n      try {\n        const data = JSON.parse(content) as Parameters<typeof VisPlugin>[0]['data'];\n        return <VisPlugin data={data} />;\n      } catch (e) {\n        return <CodePreview language={lang} code={content} />;\n      }\n    }\n\n    if (lang === 'vis-code') {\n      try {\n        const data = JSON.parse(content) as Parameters<typeof VisCode>[0]['data'];\n        return <VisCode data={data} />;\n      } catch (e) {\n        return <CodePreview language={lang} code={content} />;\n      }\n    }\n\n    return (\n      <>\n        {!inline ? (\n          <CodePreview code={context} language={lang} />\n        ) : (\n          <code {...props} style={style} className=\"p-1 mx-1 rounded bg-theme-light dark:bg-theme-dark text-sm\">\n            {children}\n          </code>\n        )}\n        <ReactMarkdown components={markdownComponents} rehypePlugins={[rehypeRaw]}>\n          {matchValues.join('\\n')}\n        </ReactMarkdown>\n      </>\n    );\n  },\n  ul({ children }) {\n    return <ul className=\"py-1\">{children}</ul>;\n  },\n  ol({ children }) {\n    return <ol className=\"py-1\">{children}</ol>;\n  },\n  li({ children, ordered }) {\n    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>;\n  },\n  table({ children }) {\n    return (\n      <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>\n    );\n  },\n  thead({ children }) {\n    return <thead className=\"bg-[#fafafa] dark:bg-black font-semibold\">{children}</thead>;\n  },\n  th({ children }) {\n    return <th className=\"!text-left p-4\">{children}</th>;\n  },\n  td({ children }) {\n    return <td className=\"p-4 border-t border-[#f0f0f0] dark:border-gray-700\">{children}</td>;\n  },\n  h1({ children }) {\n    return <h3 className=\"text-2xl font-bold my-4 border-b border-slate-300 pb-4\">{children}</h3>;\n  },\n  h2({ children }) {\n    return <h3 className=\"text-xl font-bold my-3\">{children}</h3>;\n  },\n  h3({ children }) {\n    return <h3 className=\"text-lg font-semibold my-2\">{children}</h3>;\n  },\n  h4({ children }) {\n    return <h3 className=\"text-base font-semibold my-1\">{children}</h3>;\n  },\n  a({ children, href }) {\n    return (\n      <div className=\"inline-block text-blue-600 dark:text-blue-400\">\n        <LinkOutlined className=\"mr-1\" />\n        <a href={href} target=\"_blank\">\n          {children}\n        </a>\n      </div>\n    );\n  },\n  img({ src, alt }) {\n    return (\n      <div>\n        <Image\n          className=\"min-h-[1rem] max-w-full max-h-full border rounded\"\n          src={src}\n          alt={alt}\n          placeholder={\n            <Tag icon={<SyncOutlined spin />} color=\"processing\">\n              Image Loading...\n            </Tag>\n          }\n          fallback=\"/images/fallback.png\"\n        />\n      </div>\n    );\n  },\n  blockquote({ children }) {\n    return (\n      <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\">\n        {children}\n      </blockquote>\n    );\n  },\n};\n\nconst extraComponents: MarkdownComponent = {\n  'chart-view': function ({ content, children }) {\n    let data: {\n      data: Datum[];\n      type: BackEndChartType;\n      sql: string;\n    };\n    try {\n      data = JSON.parse(content as string);\n    } catch (e) {\n      console.log(e, content);\n      data = {\n        type: 'response_table',\n        sql: '',\n        data: [],\n      };\n    }\n\n    const columns = data?.data?.[0]\n      ? Object.keys(data?.data?.[0])?.map((item) => {\n          return {\n            title: item,\n            dataIndex: item,\n            key: item,\n          };\n        })\n      : [];\n\n    const ChartItem = {\n      key: 'chart',\n      label: 'Chart',\n      children: <AutoChart data={data?.data} chartType={getChartType(data?.type)} />,\n    };\n    const SqlItem = {\n      key: 'sql',\n      label: 'SQL',\n      children: <CodePreview code={format(data?.sql, { language: 'mysql' }) as string} language={'sql'} />,\n    };\n    const DataItem = {\n      key: 'data',\n      label: 'Data',\n      children: <Table dataSource={data?.data} columns={columns} />,\n    };\n    const TabItems: TabsProps['items'] = data?.type === 'response_table' ? [DataItem, SqlItem] : [ChartItem, SqlItem, DataItem];\n\n    return (\n      <div>\n        <Tabs defaultActiveKey={data?.type === 'response_table' ? 'data' : 'chart'} items={TabItems} size=\"small\" />\n        {children}\n      </div>\n    );\n  },\n  references: function ({ title, references, children }) {\n    let referenceData;\n    // Low version compatibility, read data from children\n    if (children) {\n      try {\n        referenceData = JSON.parse(children as string);\n        title = referenceData.title;\n        references = referenceData.references;\n      } catch (error) {\n        console.log('parse references failed', error);\n        return <p className=\"text-sm text-red-500\">Render Reference Error!</p>;\n      }\n    } else {\n      // new version, read from tag props.\n      try {\n        references = JSON.parse(references as string);\n      } catch (error) {\n        console.log('parse references failed', error);\n        return <p className=\"text-sm text-red-500\">Render Reference Error!</p>;\n      }\n    }\n    if (!references || references?.length < 1) {\n      return null;\n    }\n    return (\n      <div className=\"border-t-[1px] border-gray-300 mt-3 py-2\">\n        <p className=\"text-sm text-gray-500 dark:text-gray-400 mb-2\">\n          <LinkOutlined className=\"mr-2\" />\n          <span className=\"font-semibold\">{title}</span>\n        </p>\n        {references.map((reference: Reference, index: number) => (\n          <div key={`file_${index}`} className=\"text-sm font-normal block ml-2 h-6 leading-6 overflow-hidden\">\n            <span className=\"inline-block w-6\">[{index + 1}]</span>\n            <span className=\"mr-2 lg:mr-4 text-blue-400\">{reference.name}</span>\n            {reference?.chunks?.map((chunk: IChunk | number, index) => (\n              <span key={`chunk_${index}`}>\n                {typeof chunk === 'object' ? (\n                  <Popover\n                    content={\n                      <div className=\"max-w-4xl\">\n                        <p className=\"mt-2 font-bold mr-2 border-t border-gray-500 pt-2\">Content:</p>\n                        <p>{chunk?.content || 'No Content'}</p>\n                        <p className=\"mt-2 font-bold mr-2 border-t border-gray-500 pt-2\">MetaData:</p>\n                        <p>{chunk?.meta_info || 'No MetaData'}</p>\n                        <p className=\"mt-2 font-bold mr-2 border-t border-gray-500 pt-2\">Score:</p>\n                        <p>{chunk?.recall_score || ''}</p>\n                      </div>\n                    }\n                    title=\"Chunk Information\"\n                  >\n                    <span className=\"cursor-pointer text-blue-500 ml-2\" key={`chunk_content_${chunk?.id}`}>\n                      {chunk?.id}\n                    </span>\n                  </Popover>\n                ) : (\n                  <span className=\"cursor-pointer text-blue-500 ml-2\" key={`chunk_id_${chunk}`}>\n                    {chunk}\n                  </span>\n                )}\n                {index < reference?.chunks.length - 1 && <span key={`chunk_comma_${index}`}>,</span>}\n              </span>\n            ))}\n          </div>\n        ))}\n      </div>\n    );\n  },\n  summary: function ({ children }) {\n    return (\n      <div>\n        <p className=\"mb-2\">\n          <ReadOutlined className=\"mr-2\" />\n          <span className=\"font-semibold\">Document Summary</span>\n        </p>\n        <div>{children}</div>\n      </div>\n    );\n  },\n};\n\nconst markdownComponents = {\n  ...basicComponents,\n  ...extraComponents,\n};\n\nexport default markdownComponents;\n"
  },
  {
    "path": "components/chat/chat-content/index.tsx",
    "content": "import { PropsWithChildren, ReactNode, memo, useContext, useMemo } from 'react';\nimport { CheckOutlined, ClockCircleOutlined, CloseOutlined, CodeOutlined, LoadingOutlined, RobotOutlined, UserOutlined } from '@ant-design/icons';\nimport ReactMarkdown from 'react-markdown';\nimport { IChatDialogueMessageSchema } from '@/types/chat';\nimport rehypeRaw from 'rehype-raw';\nimport classNames from 'classnames';\nimport { Tag } from 'antd';\nimport { renderModelIcon } from '../header/model-selector';\nimport { ChatContext } from '@/app/chat-context';\nimport markdownComponents from './config';\n\ninterface Props {\n  content: Omit<IChatDialogueMessageSchema, 'context'> & {\n    context:\n      | string\n      | {\n          template_name: string;\n          template_introduce: string;\n        };\n  };\n  isChartChat?: boolean;\n  onLinkClick?: () => void;\n}\n\ntype MarkdownComponent = Parameters<typeof ReactMarkdown>['0']['components'];\n\ntype DBGPTView = {\n  name: string;\n  status: 'todo' | 'runing' | 'failed' | 'completed' | (string & {});\n  result?: string;\n  err_msg?: string;\n};\n\nconst pluginViewStatusMapper: Record<DBGPTView['status'], { bgClass: string; icon: ReactNode }> = {\n  todo: {\n    bgClass: 'bg-gray-500',\n    icon: <ClockCircleOutlined className=\"ml-2\" />,\n  },\n  runing: {\n    bgClass: 'bg-blue-500',\n    icon: <LoadingOutlined className=\"ml-2\" />,\n  },\n  failed: {\n    bgClass: 'bg-red-500',\n    icon: <CloseOutlined className=\"ml-2\" />,\n  },\n  completed: {\n    bgClass: 'bg-green-500',\n    icon: <CheckOutlined className=\"ml-2\" />,\n  },\n};\n\nfunction formatMarkdownVal(val: string) {\n  return val\n    .replaceAll('\\\\n', '\\n')\n    .replace(/<table(\\w*=[^>]+)>/gi, '<table $1>')\n    .replace(/<tr(\\w*=[^>]+)>/gi, '<tr $1>');\n}\n\nfunction ChatContent({ children, content, isChartChat, onLinkClick }: PropsWithChildren<Props>) {\n  const { scene } = useContext(ChatContext);\n\n  const { context, model_name, role } = content;\n  const isRobot = role === 'view';\n\n  const { relations, value, cachePluginContext } = useMemo<{ relations: string[]; value: string; cachePluginContext: DBGPTView[] }>(() => {\n    if (typeof context !== 'string') {\n      return {\n        relations: [],\n        value: '',\n        cachePluginContext: [],\n      };\n    }\n    const [value, relation] = context.split('\\trelations:');\n    const relations = relation ? relation.split(',') : [];\n    const cachePluginContext: DBGPTView[] = [];\n\n    let cacheIndex = 0;\n    const result = value.replace(/<dbgpt-view[^>]*>[^<]*<\\/dbgpt-view>/gi, (matchVal) => {\n      try {\n        const pluginVal = matchVal.replaceAll('\\n', '\\\\n').replace(/<[^>]*>|<\\/[^>]*>/gm, '');\n        const pluginContext = JSON.parse(pluginVal) as DBGPTView;\n        const replacement = `<custom-view>${cacheIndex}</custom-view>`;\n\n        cachePluginContext.push({\n          ...pluginContext,\n          result: formatMarkdownVal(pluginContext.result ?? ''),\n        });\n        cacheIndex++;\n\n        return replacement;\n      } catch (e) {\n        console.log((e as any).message, e);\n        return matchVal;\n      }\n    });\n    return {\n      relations,\n      cachePluginContext,\n      value: result,\n    };\n  }, [context]);\n\n  const extraMarkdownComponents = useMemo<MarkdownComponent>(\n    () => ({\n      'custom-view'({ children }) {\n        const index = +children.toString();\n        if (!cachePluginContext[index]) {\n          return children;\n        }\n        const { name, status, err_msg, result } = cachePluginContext[index];\n        const { bgClass, icon } = pluginViewStatusMapper[status] ?? {};\n        return (\n          <div className=\"bg-white dark:bg-[#212121] rounded-lg overflow-hidden my-2 flex flex-col lg:max-w-[80%]\">\n            <div className={classNames('flex px-4 md:px-6 py-2 items-center text-white text-sm', bgClass)}>\n              {name}\n              {icon}\n            </div>\n            {result ? (\n              <div className=\"px-4 md:px-6 py-4 text-sm\">\n                <ReactMarkdown components={markdownComponents} rehypePlugins={[rehypeRaw]}>\n                  {result ?? ''}\n                </ReactMarkdown>\n              </div>\n            ) : (\n              <div className=\"px-4 md:px-6 py-4 text-sm\">{err_msg}</div>\n            )}\n          </div>\n        );\n      },\n    }),\n    [context, cachePluginContext],\n  );\n\n  if (!isRobot && !context) return <div className=\"h-12\"></div>;\n\n  return (\n    <div\n      className={classNames('relative flex flex-wrap w-full p-2 md:p-4 rounded-xl break-words', {\n        'bg-white dark:bg-[#232734]': isRobot,\n        'lg:w-full xl:w-full pl-0': ['chat_with_db_execute', 'chat_dashboard'].includes(scene),\n      })}\n    >\n      <div className=\"mr-2 flex flex-shrink-0 items-center justify-center h-7 w-7 rounded-full text-lg sm:mr-4\">\n        {isRobot ? renderModelIcon(model_name) || <RobotOutlined /> : <UserOutlined />}\n      </div>\n      <div className=\"flex-1 overflow-hidden items-center text-md leading-8 pb-2\">\n        {/* User Input */}\n        {!isRobot && typeof context === 'string' && context}\n        {/* Render Report */}\n        {isRobot && isChartChat && typeof context === 'object' && (\n          <div>\n            {`[${context.template_name}]: `}\n            <span className=\"text-theme-primary cursor-pointer\" onClick={onLinkClick}>\n              <CodeOutlined className=\"mr-1\" />\n              {context.template_introduce || 'More Details'}\n            </span>\n          </div>\n        )}\n        {/* Markdown */}\n        {isRobot && typeof context === 'string' && (\n          <ReactMarkdown components={{ ...markdownComponents, ...extraMarkdownComponents }} rehypePlugins={[rehypeRaw]}>\n            {formatMarkdownVal(value)}\n          </ReactMarkdown>\n        )}\n        {!!relations?.length && (\n          <div className=\"flex flex-wrap mt-2\">\n            {relations?.map((value, index) => (\n              <Tag color=\"#108ee9\" key={value + index}>\n                {value}\n              </Tag>\n            ))}\n          </div>\n        )}\n      </div>\n      {children}\n    </div>\n  );\n}\n\nexport default memo(ChatContent);\n"
  },
  {
    "path": "components/chat/chat-content/vis-chart.tsx",
    "content": "import { BackEndChartType } from '@/components/chart';\nimport ChartView from './chart-view';\nimport { Datum } from '@antv/ava';\n\ninterface Props {\n  data: {\n    data: Datum[];\n    describe: string;\n    title: string;\n    type: BackEndChartType;\n    sql: string;\n  };\n}\n\nfunction VisChart({ data }: Props) {\n  return <ChartView data={data.data} type={data.type} sql={data.sql} />;\n}\n\nexport default VisChart;\n"
  },
  {
    "path": "components/chat/chat-content/vis-code.tsx",
    "content": "import ReactMarkdown from 'react-markdown';\nimport remarkGfm from 'remark-gfm';\nimport markdownComponents from './config';\nimport { CodePreview } from './code-preview';\nimport classNames from 'classnames';\nimport { useState } from 'react';\nimport { CheckOutlined, CloseOutlined } from '@ant-design/icons';\nimport { useTranslation } from 'react-i18next';\nimport { oneLight, oneDark } from 'react-syntax-highlighter/dist/esm/styles/prism';\n\ninterface Props {\n  data: {\n    code: string[];\n    exit_success: true;\n    language: string;\n    log: string;\n  };\n}\n\nfunction VisCode({ data }: Props) {\n  const { t } = useTranslation();\n\n  const [show, setShow] = useState(0);\n\n  return (\n    <div className=\"bg-[#EAEAEB] rounded overflow-hidden border border-theme-primary dark:bg-theme-dark text-sm\">\n      <div>\n        <div className=\"flex\">\n          {data.code.map((item, index) => (\n            <div\n              key={index}\n              className={classNames('px-4 py-2 text-[#121417] dark:text-white cursor-pointer', {\n                'bg-white dark:bg-theme-dark-container': index === show,\n              })}\n              onClick={() => {\n                setShow(index);\n              }}\n            >\n              CODE {index + 1}: {item[0]}\n            </div>\n          ))}\n        </div>\n        {data.code.length && (\n          <CodePreview\n            language={data.code[show][0]}\n            code={data.code[show][1]}\n            customStyle={{ maxHeight: 300, margin: 0 }}\n            light={oneLight}\n            dark={oneDark}\n          />\n        )}\n      </div>\n      <div>\n        <div className=\"flex\">\n          <div className=\"bg-white dark:bg-theme-dark-container px-4 py-2 text-[#121417] dark:text-white\">\n            {t('Terminal')} {data.exit_success ? <CheckOutlined className=\"text-green-600\" /> : <CloseOutlined className=\"text-red-600\" />}\n          </div>\n        </div>\n        <div className=\"p-4 max-h-72 overflow-y-auto whitespace-normal bg-white dark:dark:bg-theme-dark\">\n          <ReactMarkdown components={markdownComponents} remarkPlugins={[remarkGfm]}>\n            {data.log}\n          </ReactMarkdown>\n        </div>\n      </div>\n    </div>\n  );\n}\n\nexport default VisCode;\n"
  },
  {
    "path": "components/chat/chat-content/vis-convert-error.tsx",
    "content": "import { format } from 'sql-formatter';\nimport { CodePreview } from './code-preview';\n\ninterface Props {\n  data: {\n    display_type: string;\n    sql: string;\n    thought: string;\n  };\n}\n\nfunction VisConvertError({ data }: Props) {\n  return (\n    <div className=\"rounded overflow-hidden\">\n      <div className=\"p-3 text-white bg-red-500 whitespace-normal\">{data.display_type}</div>\n      <div className=\"p-3 bg-red-50\">\n        <div className=\"mb-2 whitespace-normal\">{data.thought}</div>\n        <CodePreview code={format(data.sql)} language=\"sql\" />\n      </div>\n    </div>\n  );\n}\n\nexport default VisConvertError;\n"
  },
  {
    "path": "components/chat/chat-content/vis-dashboard.tsx",
    "content": "import { AutoChart, BackEndChartType, getChartType } from '@/components/chart';\nimport { Datum } from '@antv/ava';\nimport { useMemo } from 'react';\n\ninterface Props {\n  data: {\n    data: {\n      data: Datum[];\n      describe: string;\n      title: string;\n      type: BackEndChartType;\n      sql: string;\n    }[];\n    title: string | null;\n    display_strategy: string;\n    chart_count: number;\n  };\n}\n\nconst chartLayout = [[2], [1, 2], [1, 3], [2, 1, 2], [2, 1, 3], [3, 1, 3], [3, 2, 3]];\n\nfunction VisDashboard({ data }: Props) {\n  const charts = useMemo(() => {\n    if (data.chart_count > 1) {\n      const layout = chartLayout[data.chart_count - 2];\n      let prevIndex = 0;\n      return layout.map((item) => {\n        const items = data.data.slice(prevIndex, prevIndex + item);\n        prevIndex = item;\n        return items;\n      });\n    }\n    return [data.data];\n  }, [data.data, data.chart_count]);\n\n  return (\n    <div className=\"flex flex-col gap-3\">\n      {charts.map((row, index) => (\n        <div key={`row-${index}`} className=\"flex gap-3\">\n          {row.map((chart, subIndex) => (\n            <div\n              key={`chart-${subIndex}`}\n              className=\"flex flex-1 flex-col justify-between p-4 rounded border border-gray-200 dark:border-gray-500 whitespace-normal\"\n            >\n              <div>\n                {chart.title && <div className=\"mb-2 text-lg\">{chart.title}</div>}\n                {chart.describe && <div className=\"mb-4 text-sm text-gray-500\">{chart.describe}</div>}\n              </div>\n              <AutoChart data={chart.data} chartType={getChartType(chart.type)} />\n            </div>\n          ))}\n        </div>\n      ))}\n    </div>\n  );\n}\n\nexport default VisDashboard;\n"
  },
  {
    "path": "components/chat/chat-content/vis-plugin.tsx",
    "content": "import { CheckOutlined, ClockCircleOutlined, CloseOutlined, LoadingOutlined } from '@ant-design/icons';\nimport classNames from 'classnames';\nimport { ReactNode } from 'react';\nimport ReactMarkdown from 'react-markdown';\nimport markdownComponents from './config';\nimport rehypeRaw from 'rehype-raw';\n\ninterface IVisPlugin {\n  name: string;\n  args: {\n    query: string;\n  };\n  status: 'todo' | 'runing' | 'failed' | 'complete' | (string & {});\n  logo: string | null;\n  result: string;\n  err_msg: string | null;\n}\n\ninterface Props {\n  data: IVisPlugin;\n}\n\nconst pluginViewStatusMapper: Record<IVisPlugin['status'], { bgClass: string; icon: ReactNode }> = {\n  todo: {\n    bgClass: 'bg-gray-500',\n    icon: <ClockCircleOutlined className=\"ml-2\" />,\n  },\n  runing: {\n    bgClass: 'bg-blue-500',\n    icon: <LoadingOutlined className=\"ml-2\" />,\n  },\n  failed: {\n    bgClass: 'bg-red-500',\n    icon: <CloseOutlined className=\"ml-2\" />,\n  },\n  complete: {\n    bgClass: 'bg-green-500',\n    icon: <CheckOutlined className=\"ml-2\" />,\n  },\n};\n\nfunction VisPlugin({ data }: Props) {\n  const { bgClass, icon } = pluginViewStatusMapper[data.status] ?? {};\n\n  return (\n    <div className=\"bg-theme-light dark:bg-theme-dark-container rounded overflow-hidden my-2 flex flex-col lg:max-w-[80%]\">\n      <div className={classNames('flex px-4 md:px-6 py-2 items-center text-white text-sm', bgClass)}>\n        {data.name}\n        {icon}\n      </div>\n      {data.result ? (\n        <div className=\"px-4 md:px-6 py-4 text-sm whitespace-normal\">\n          <ReactMarkdown components={markdownComponents} rehypePlugins={[rehypeRaw]}>\n            {data.result ?? ''}\n          </ReactMarkdown>\n        </div>\n      ) : (\n        <div className=\"px-4 md:px-6 py-4 text-sm\">{data.err_msg}</div>\n      )}\n    </div>\n  );\n}\n\nexport default VisPlugin;\n"
  },
  {
    "path": "components/chat/chat-feedback.tsx",
    "content": "import React, { useState, useRef, useCallback, useEffect, useContext } from 'react';\nimport { MoreHoriz, CloseRounded } from '@mui/icons-material';\nimport {\n  MenuButton,\n  Button,\n  Menu,\n  MenuItem,\n  Dropdown,\n  Box,\n  Grid,\n  IconButton,\n  Slider,\n  Select,\n  Option,\n  Textarea,\n  Typography,\n  styled,\n  Sheet,\n} from '@mui/joy';\nimport { message, Tooltip } from 'antd';\nimport { apiInterceptors, getChatFeedBackItme, postChatFeedBackForm } from '@/client/api';\nimport { ChatContext } from '@/app/chat-context';\nimport { ChatFeedBackSchema } from '@/types/db';\nimport { useTranslation } from 'react-i18next';\nimport { FeedBack } from '@/types/chat';\n\ntype Props = {\n  conv_index: number;\n  question: any;\n  knowledge_space: string;\n  select_param?: FeedBack;\n};\n\nconst ChatFeedback = ({ conv_index, question, knowledge_space, select_param }: Props) => {\n  const { t } = useTranslation();\n  const { chatId } = useContext(ChatContext);\n  const [ques_type, setQuesType] = useState('');\n  const [score, setScore] = useState(4);\n  const [text, setText] = useState('');\n  const action = useRef(null);\n  const [messageApi, contextHolder] = message.useMessage();\n\n  const handleOpenChange = useCallback(\n    (event: any, isOpen: boolean) => {\n      if (isOpen) {\n        apiInterceptors(getChatFeedBackItme(chatId, conv_index))\n          .then((res) => {\n            const finddata = res[1] ?? {};\n            setQuesType(finddata.ques_type ?? '');\n            setScore(parseInt(finddata.score ?? '4'));\n            setText(finddata.messages ?? '');\n          })\n          .catch((err) => {\n            console.log(err);\n          });\n      } else {\n        setQuesType('');\n        setScore(4);\n        setText('');\n      }\n    },\n    [chatId, conv_index],\n  );\n\n  const marks = [\n    { value: 0, label: '0' },\n    { value: 1, label: '1' },\n    { value: 2, label: '2' },\n    { value: 3, label: '3' },\n    { value: 4, label: '4' },\n    { value: 5, label: '5' },\n  ];\n  function valueText(value: number) {\n    return {\n      0: t('Lowest'),\n      1: t('Missed'),\n      2: t('Lost'),\n      3: t('Incorrect'),\n      4: t('Verbose'),\n      5: t('Best'),\n    }[value];\n  }\n  const Item = styled(Sheet)(({ theme }) => ({\n    backgroundColor: theme.palette.mode === 'dark' ? '#FBFCFD' : '#0E0E10',\n    ...theme.typography['body-sm'],\n    padding: theme.spacing(1),\n    display: 'flex',\n    alignItems: 'center',\n    justifyContent: 'center',\n    borderRadius: 4,\n    width: '100%',\n    height: '100%',\n  }));\n  const handleSubmit = (event: any) => {\n    event.preventDefault();\n    const formData: ChatFeedBackSchema = {\n      conv_uid: chatId,\n      conv_index: conv_index,\n      question: question,\n      knowledge_space: knowledge_space,\n      score: score,\n      ques_type: ques_type,\n      messages: text,\n    };\n    console.log(formData);\n    apiInterceptors(\n      postChatFeedBackForm({\n        data: formData,\n      }),\n    )\n      .then((res) => {\n        messageApi.open({ type: 'success', content: 'save success' });\n      })\n      .catch((err) => {\n        messageApi.open({ type: 'error', content: 'save error' });\n      });\n  };\n  return (\n    <Dropdown onOpenChange={handleOpenChange}>\n      {contextHolder}\n      <Tooltip title={t('Rating')}>\n        <MenuButton slots={{ root: IconButton }} slotProps={{ root: { variant: 'plain', color: 'primary' } }} sx={{ borderRadius: 40 }}>\n          <MoreHoriz />\n        </MenuButton>\n      </Tooltip>\n      <Menu>\n        <MenuItem disabled sx={{ minHeight: 0 }} />\n        <Box\n          sx={{\n            width: '100%',\n            maxWidth: 350,\n            display: 'grid',\n            gap: 3,\n            padding: 1,\n          }}\n        >\n          <form onSubmit={handleSubmit}>\n            <Grid container spacing={0.5} columns={13} sx={{ flexGrow: 1 }}>\n              <Grid xs={3}>\n                <Item>{t('Q_A_Category')}</Item>\n              </Grid>\n              <Grid xs={10}>\n                <Select\n                  action={action}\n                  value={ques_type}\n                  placeholder=\"Choose one…\"\n                  onChange={(event, newValue) => setQuesType(newValue ?? '')}\n                  {...(ques_type && {\n                    // display the button and remove select indicator\n                    // when user has selected a value\n                    endDecorator: (\n                      <IconButton\n                        size=\"sm\"\n                        variant=\"plain\"\n                        color=\"neutral\"\n                        onMouseDown={(event) => {\n                          // don't open the popup when clicking on this button\n                          event.stopPropagation();\n                        }}\n                        onClick={() => {\n                          setQuesType('');\n                          action.current?.focusVisible();\n                        }}\n                      >\n                        <CloseRounded />\n                      </IconButton>\n                    ),\n                    indicator: null,\n                  })}\n                  sx={{ width: '100%' }}\n                >\n                  {select_param &&\n                    Object.keys(select_param)?.map((paramItem) => (\n                      <Option key={paramItem} value={paramItem}>\n                        {select_param[paramItem]}\n                      </Option>\n                    ))}\n                </Select>\n              </Grid>\n              <Grid xs={3}>\n                <Item>\n                  <Tooltip\n                    title={\n                      <Box>\n                        <div>{t('feed_back_desc')}</div>\n                      </Box>\n                    }\n                    variant=\"solid\"\n                    placement=\"left\"\n                  >\n                    {t('Q_A_Rating')}\n                  </Tooltip>\n                </Item>\n              </Grid>\n              <Grid xs={10} sx={{ pl: 0, ml: 0 }}>\n                <Slider\n                  aria-label=\"Custom\"\n                  step={1}\n                  min={0}\n                  max={5}\n                  valueLabelFormat={valueText}\n                  valueLabelDisplay=\"on\"\n                  marks={marks}\n                  sx={{ width: '90%', pt: 3, m: 2, ml: 1 }}\n                  onChange={(event) => setScore(event.target?.value)}\n                  value={score}\n                />\n              </Grid>\n              <Grid xs={13}>\n                <Textarea\n                  placeholder={t('Please_input_the_text')}\n                  value={text}\n                  onChange={(event) => setText(event.target.value)}\n                  minRows={2}\n                  maxRows={4}\n                  endDecorator={\n                    <Typography level=\"body-xs\" sx={{ ml: 'auto' }}>\n                      {t('input_count') + text.length + t('input_unit')}\n                    </Typography>\n                  }\n                  sx={{ width: '100%', fontSize: 14 }}\n                />\n              </Grid>\n              <Grid xs={13}>\n                <Button type=\"submit\" variant=\"outlined\" sx={{ width: '100%', height: '100%' }}>\n                  {t('submit')}\n                </Button>\n              </Grid>\n            </Grid>\n          </form>\n        </Box>\n      </Menu>\n    </Dropdown>\n  );\n};\nexport default ChatFeedback;\n"
  },
  {
    "path": "components/chat/completion.tsx",
    "content": "import { useState, useRef, useEffect, useMemo, useContext } from 'react';\nimport { useSearchParams } from 'next/navigation';\nimport MonacoEditor from './monaco-editor';\nimport ChatContent from './chat-content';\nimport ChatFeedback from './chat-feedback';\nimport { ChatContext } from '@/app/chat-context';\nimport { FeedBack, IChatDialogueMessageSchema } from '@/types/chat';\nimport classNames from 'classnames';\nimport { Empty, Modal, message, Tooltip } from 'antd';\nimport { renderModelIcon } from './header/model-selector';\nimport { cloneDeep } from 'lodash';\nimport copy from 'copy-to-clipboard';\nimport { useTranslation } from 'react-i18next';\nimport CompletionInput from '../common/completion-input';\nimport { useAsyncEffect } from 'ahooks';\nimport { STORAGE_INIT_MESSAGE_KET } from '@/utils';\nimport { Button, IconButton } from '@mui/joy';\nimport { CopyOutlined, RedoOutlined } from '@ant-design/icons';\nimport { getInitMessage } from '@/utils';\nimport { apiInterceptors, getChatFeedBackSelect } from '@/client/api';\nimport useSummary from '@/hooks/use-summary';\nimport AgentContent from './agent-content';\n\ntype Props = {\n  messages: IChatDialogueMessageSchema[];\n  onSubmit: (message: string, otherQueryBody?: Record<string, any>) => Promise<void>;\n};\n\nconst Completion = ({ messages, onSubmit }: Props) => {\n  const { dbParam, currentDialogue, scene, model, refreshDialogList, chatId, agent, docId } = useContext(ChatContext);\n  const { t } = useTranslation();\n  const searchParams = useSearchParams();\n\n  const flowSelectParam = (searchParams && searchParams.get('select_param')) ?? '';\n  const spaceNameOriginal = (searchParams && searchParams.get('spaceNameOriginal')) ?? '';\n\n  const [isLoading, setIsLoading] = useState(false);\n  const [jsonModalOpen, setJsonModalOpen] = useState(false);\n  const [showMessages, setShowMessages] = useState(messages);\n  const [jsonValue, setJsonValue] = useState<string>('');\n  const [select_param, setSelectParam] = useState<FeedBack>();\n\n  const scrollableRef = useRef<HTMLDivElement>(null);\n\n  // const incremental = useMemo(() => scene === 'chat_flow', [scene]);\n  const isChartChat = useMemo(() => scene === 'chat_dashboard', [scene]);\n\n  const summary = useSummary();\n\n  const selectParam = useMemo(() => {\n    switch (scene) {\n      case 'chat_agent':\n        return agent;\n      case 'chat_excel':\n        return currentDialogue?.select_param;\n      case 'chat_flow':\n        return flowSelectParam;\n      default:\n        return spaceNameOriginal || dbParam;\n    }\n  }, [scene, agent, currentDialogue, dbParam, spaceNameOriginal, flowSelectParam]);\n\n  const handleChat = async (content: string) => {\n    if (isLoading || !content.trim()) return;\n    if (scene === 'chat_agent' && !agent) {\n      message.warning(t('choice_agent_tip'));\n      return;\n    }\n    try {\n      setIsLoading(true);\n      await onSubmit(content, {\n        select_param: selectParam ?? '',\n        // incremental,\n      });\n    } finally {\n      setIsLoading(false);\n    }\n  };\n\n  const handleJson2Obj = (jsonStr: string) => {\n    try {\n      return JSON.parse(jsonStr);\n    } catch (e) {\n      return jsonStr;\n    }\n  };\n\n  const [messageApi, contextHolder] = message.useMessage();\n\n  const onCopyContext = async (context: any) => {\n    const pureStr = context?.replace(/\\trelations:.*/g, '');\n    const result = copy(pureStr);\n    if (result) {\n      if (pureStr) {\n        messageApi.open({ type: 'success', content: t('Copy_success') });\n      } else {\n        messageApi.open({ type: 'warning', content: t('Copy_nothing') });\n      }\n    } else {\n      messageApi.open({ type: 'error', content: t('Copry_error') });\n    }\n  };\n\n  const handleRetry = async () => {\n    if (isLoading || !docId) {\n      return;\n    }\n    setIsLoading(true);\n    await summary(docId);\n    setIsLoading(false);\n  };\n\n  useAsyncEffect(async () => {\n    const initMessage = getInitMessage();\n    if (initMessage && initMessage.id === chatId) {\n      await handleChat(initMessage.message);\n      refreshDialogList();\n      localStorage.removeItem(STORAGE_INIT_MESSAGE_KET);\n    }\n  }, [chatId]);\n\n  useEffect(() => {\n    let tempMessage: IChatDialogueMessageSchema[] = messages;\n    if (isChartChat) {\n      tempMessage = cloneDeep(messages).map((item) => {\n        if (item?.role === 'view' && typeof item?.context === 'string') {\n          item.context = handleJson2Obj(item?.context);\n        }\n        return item;\n      });\n    }\n    setShowMessages(tempMessage.filter((item) => ['view', 'human'].includes(item.role)));\n  }, [isChartChat, messages]);\n\n  useEffect(() => {\n    apiInterceptors(getChatFeedBackSelect())\n      .then((res) => {\n        setSelectParam(res[1] ?? {});\n      })\n      .catch((err) => {\n        console.log(err);\n      });\n  }, []);\n\n  useEffect(() => {\n    setTimeout(() => {\n      scrollableRef.current?.scrollTo(0, scrollableRef.current.scrollHeight);\n    }, 50);\n  }, [messages]);\n\n  return (\n    <>\n      {contextHolder}\n      <div ref={scrollableRef} className=\"flex flex-1 overflow-y-auto pb-8 w-full flex-col\">\n        <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\">\n          {showMessages.length ? (\n            showMessages.map((content, index) => {\n              if (scene === 'chat_agent') {\n                return <AgentContent key={index} content={content} />;\n              }\n              return (\n                <ChatContent\n                  key={index}\n                  content={content}\n                  isChartChat={isChartChat}\n                  onLinkClick={() => {\n                    setJsonModalOpen(true);\n                    setJsonValue(JSON.stringify(content?.context, null, 2));\n                  }}\n                >\n                  {content.role === 'view' && (\n                    <div className=\"flex w-full border-t border-gray-200 dark:border-theme-dark\">\n                      {scene === 'chat_knowledge' && content.retry ? (\n                        <Button onClick={handleRetry} slots={{ root: IconButton }} slotProps={{ root: { variant: 'plain', color: 'primary' } }}>\n                          <RedoOutlined />\n                          &nbsp;<span className=\"text-sm\">{t('Retry')}</span>\n                        </Button>\n                      ) : null}\n                      <div className=\"flex w-full flex-row-reverse\">\n                        <ChatFeedback\n                          select_param={select_param}\n                          conv_index={Math.ceil((index + 1) / 2)}\n                          question={showMessages?.filter((e) => e?.role === 'human' && e?.order === content.order)[0]?.context}\n                          knowledge_space={spaceNameOriginal || dbParam || ''}\n                        />\n                        <Tooltip title={t('Copy')}>\n                          <Button\n                            onClick={() => onCopyContext(content?.context)}\n                            slots={{ root: IconButton }}\n                            slotProps={{ root: { variant: 'plain', color: 'primary' } }}\n                            sx={{ borderRadius: 40 }}\n                          >\n                            <CopyOutlined />\n                          </Button>\n                        </Tooltip>\n                      </div>\n                    </div>\n                  )}\n                </ChatContent>\n              );\n            })\n          ) : (\n            <Empty\n              image=\"/empty.png\"\n              imageStyle={{ width: 320, height: 320, margin: '0 auto', maxWidth: '100%', maxHeight: '100%' }}\n              className=\"flex items-center justify-center flex-col h-full w-full\"\n              description=\"Start a conversation\"\n            />\n          )}\n        </div>\n      </div>\n      <div\n        className={classNames(\n          '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',\n          {\n            'cursor-not-allowed': scene === 'chat_excel' && !currentDialogue?.select_param,\n          },\n        )}\n      >\n        <div className=\"flex flex-wrap w-full py-2 sm:pt-6 sm:pb-10 items-center\">\n          {model && <div className=\"mr-2 flex\">{renderModelIcon(model)}</div>}\n          <CompletionInput loading={isLoading} onSubmit={handleChat} handleFinish={setIsLoading} />\n        </div>\n      </div>\n      <Modal\n        title=\"JSON Editor\"\n        open={jsonModalOpen}\n        width=\"60%\"\n        cancelButtonProps={{\n          hidden: true,\n        }}\n        onOk={() => {\n          setJsonModalOpen(false);\n        }}\n        onCancel={() => {\n          setJsonModalOpen(false);\n        }}\n      >\n        <MonacoEditor className=\"w-full h-[500px]\" language=\"json\" value={jsonValue} />\n      </Modal>\n    </>\n  );\n};\n\nexport default Completion;\n"
  },
  {
    "path": "components/chat/db-editor.tsx",
    "content": "import React from 'react';\nimport { useRequest } from 'ahooks';\nimport { Select, Option, Table, Box, Typography, Tooltip } from '@mui/joy';\nimport { Button } from 'antd';\nimport AutoAwesomeMotionIcon from '@mui/icons-material/AutoAwesomeMotion';\nimport { Input, Tree, Empty, Tabs } from 'antd';\nimport type { DataNode } from 'antd/es/tree';\nimport MonacoEditor from './monaco-editor';\nimport { sendGetRequest, sendSpacePostRequest } from '@/utils/request';\nimport { useSearchParams } from 'next/navigation';\nimport { OnChange } from '@monaco-editor/react';\nimport Header from './header';\nimport Chart from '../chart';\n\nconst { Search } = Input;\n\ninterface EditorValueProps {\n  sql?: string;\n  thoughts?: string;\n  title?: string;\n  showcase?: string;\n}\n\ninterface RoundProps {\n  db_name: string;\n  round: number;\n  round_name: string;\n}\n\ninterface IProps {\n  editorValue?: EditorValueProps;\n  chartData?: any;\n  tableData?: any;\n  handleChange: OnChange;\n}\n\ninterface ITableTreeItem {\n  title: string;\n  key: string;\n  type: string;\n  default_value: string | null;\n  can_null: string;\n  comment: string | null;\n  children: Array<ITableTreeItem>;\n}\n\nfunction DbEditorContent({ editorValue, chartData, tableData, handleChange }: IProps) {\n  const chartWrapper = React.useMemo(() => {\n    if (!chartData) return <div></div>;\n    return (\n      <div className=\"flex-1 overflow-auto p-3\" style={{ flexShrink: 0, overflow: 'hidden' }}>\n        <Chart chartsData={[chartData]} />\n      </div>\n    );\n  }, [chartData]);\n\n  return (\n    <>\n      <div className=\"flex-1 flex overflow-hidden\">\n        <div className=\"flex-1\" style={{ flexShrink: 0, overflow: 'auto' }}>\n          <MonacoEditor value={editorValue?.sql || ''} language=\"mysql\" onChange={handleChange} thoughts={editorValue?.thoughts || ''} />\n        </div>\n        {chartWrapper}\n      </div>\n      <div className=\"h-96 border-[var(--joy-palette-divider)] border-t border-solid overflow-auto\">\n        {tableData?.values?.length > 0 ? (\n          <Table aria-label=\"basic table\" stickyHeader>\n            <thead>\n              <tr>\n                {tableData?.columns?.map((column: any, i: number) => (\n                  <th key={column + i}>{column}</th>\n                ))}\n              </tr>\n            </thead>\n            <tbody>\n              {tableData?.values?.map((value: any, i: number) => (\n                <tr key={i}>\n                  {Object.keys(value)?.map((v) => (\n                    <td key={v}>{value[v]}</td>\n                  ))}\n                </tr>\n              ))}\n            </tbody>\n          </Table>\n        ) : (\n          <div className=\"h-full flex justify-center items-center\">\n            <Empty />\n          </div>\n        )}\n      </div>\n    </>\n  );\n}\n\nfunction DbEditor() {\n  const [expandedKeys, setExpandedKeys] = React.useState<React.Key[]>([]);\n  const [searchValue, setSearchValue] = React.useState('');\n  const [currentRound, setCurrentRound] = React.useState<null | string | number>();\n  const [autoExpandParent, setAutoExpandParent] = React.useState(true);\n  const [chartData, setChartData] = React.useState();\n  const [editorValue, setEditorValue] = React.useState<EditorValueProps | EditorValueProps[]>();\n  const [newEditorValue, setNewEditorValue] = React.useState<EditorValueProps>();\n  const [tableData, setTableData] = React.useState<{ columns: string[]; values: any }>();\n  const [currentTabIndex, setCurrentTabIndex] = React.useState<string>();\n\n  const searchParams = useSearchParams();\n  const id = searchParams?.get('id');\n  const scene = searchParams?.get('scene');\n\n  const { data: rounds, loading: roundsLoading } = useRequest(\n    async () =>\n      await sendGetRequest('/v1/editor/sql/rounds', {\n        con_uid: id,\n      }),\n    {\n      onSuccess: (res) => {\n        const lastItem = res?.data?.[res?.data?.length - 1];\n        if (lastItem) {\n          setCurrentRound(lastItem?.round);\n        }\n      },\n    },\n  );\n\n  const { run: runSql, loading: runLoading } = useRequest(\n    async () => {\n      const db_name = rounds?.data?.find((item) => item.round === currentRound)?.db_name;\n      return await sendSpacePostRequest(`/api/v1/editor/sql/run`, {\n        db_name,\n        sql: newEditorValue?.sql,\n      });\n    },\n    {\n      manual: true,\n      onSuccess: (res) => {\n        setTableData({\n          columns: res?.data?.colunms,\n          values: res?.data?.values,\n        });\n      },\n    },\n  );\n\n  const { run: runCharts, loading: runChartsLoading } = useRequest(\n    async () => {\n      const db_name = rounds?.data?.find((item) => item.round === currentRound)?.db_name;\n      const params: {\n        db_name: string;\n        sql?: string;\n        chart_type?: string;\n      } = {\n        db_name,\n        sql: newEditorValue?.sql,\n      };\n      if (scene === 'chat_dashboard') {\n        params['chart_type'] = newEditorValue?.showcase;\n      }\n      return await sendSpacePostRequest(`/api/v1/editor/chart/run`, params);\n    },\n    {\n      manual: true,\n      ready: !!newEditorValue?.sql,\n      onSuccess: (res) => {\n        if (res?.success) {\n          setTableData({\n            columns: res?.data?.sql_data?.colunms || [],\n            values: res?.data?.sql_data?.values || [],\n          });\n          if (!res?.data?.chart_values) {\n            setChartData(undefined);\n          } else {\n            setChartData({\n              type: res?.data?.chart_type,\n              values: res?.data?.chart_values,\n              title: newEditorValue?.title,\n              description: newEditorValue?.thoughts,\n            });\n          }\n        }\n      },\n    },\n  );\n\n  const { run: submitSql, loading: submitLoading } = useRequest(\n    async () => {\n      const db_name = rounds?.data?.find((item: RoundProps) => item.round === currentRound)?.db_name;\n      return await sendSpacePostRequest(`/api/v1/sql/editor/submit`, {\n        conv_uid: id,\n        db_name,\n        conv_round: currentRound,\n        old_sql: editorValue?.sql,\n        old_speak: editorValue?.thoughts,\n        new_sql: newEditorValue?.sql,\n        new_speak: newEditorValue?.thoughts?.match(/^\\n--(.*)\\n\\n$/)?.[1]?.trim() || newEditorValue?.thoughts,\n      });\n    },\n    {\n      manual: true,\n      onSuccess: (res) => {\n        if (res?.success) {\n          runSql();\n        }\n      },\n    },\n  );\n\n  const { run: submitChart, loading: submitChartLoading } = useRequest(\n    async () => {\n      const db_name = rounds?.data?.find((item) => item.round === currentRound)?.db_name;\n      return await sendSpacePostRequest(`/api/v1/chart/editor/submit`, {\n        conv_uid: id,\n        chart_title: newEditorValue?.title,\n        db_name,\n        old_sql: editorValue?.[currentTabIndex]?.sql,\n        new_chart_type: newEditorValue?.showcase,\n        new_sql: newEditorValue?.sql,\n        new_comment: newEditorValue?.thoughts?.match(/^\\n--(.*)\\n\\n$/)?.[1]?.trim() || newEditorValue?.thoughts,\n        gmt_create: new Date().getTime(),\n      });\n    },\n    {\n      manual: true,\n      onSuccess: (res) => {\n        if (res?.success) {\n          runCharts();\n        }\n      },\n    },\n  );\n\n  const { data: tables } = useRequest(\n    async () => {\n      const db_name = rounds?.data?.find((item: RoundProps) => item.round === currentRound)?.db_name;\n      return await sendGetRequest('/v1/editor/db/tables', {\n        db_name,\n        page_index: 1,\n        page_size: 200,\n      });\n    },\n    {\n      ready: !!rounds?.data?.find((item: RoundProps) => item.round === currentRound)?.db_name,\n      refreshDeps: [rounds?.data?.find((item: RoundProps) => item.round === currentRound)?.db_name],\n    },\n  );\n\n  const { run: handleGetEditorSql } = useRequest(\n    async (round) =>\n      await sendGetRequest('/v1/editor/sql', {\n        con_uid: id,\n        round,\n      }),\n    {\n      manual: true,\n      onSuccess: (res) => {\n        let sql = undefined;\n        try {\n          if (Array.isArray(res?.data)) {\n            sql = res?.data;\n            setCurrentTabIndex('0');\n          } else if (typeof res?.data === 'string') {\n            const d = JSON.parse(res?.data);\n            sql = d;\n          } else {\n            sql = res?.data;\n          }\n        } catch (e) {\n          console.log(e);\n        } finally {\n          setEditorValue(sql);\n          if (Array.isArray(sql)) {\n            setNewEditorValue(sql?.[Number(currentTabIndex || 0)]);\n          } else {\n            setNewEditorValue(sql);\n          }\n        }\n      },\n    },\n  );\n\n  const treeData = React.useMemo(() => {\n    const loop = (data: Array<ITableTreeItem>, parentKey?: string | number): DataNode[] =>\n      data.map((item: ITableTreeItem) => {\n        const strTitle = item.title;\n        const index = strTitle.indexOf(searchValue);\n        const beforeStr = strTitle.substring(0, index);\n        const afterStr = strTitle.slice(index + searchValue.length);\n        const showTitle =\n          index > -1 ? (\n            <Tooltip title={(item?.comment || item?.title) + (item?.can_null === 'YES' ? '(can null)' : `(can't null)`)}>\n              <span>\n                {beforeStr}\n                <span className=\"text-[#1677ff]\">{searchValue}</span>\n                {afterStr}\n                {item?.type && (\n                  <Typography gutterBottom level=\"body3\" className=\"pl-0.5\" style={{ display: 'inline' }}>\n                    {`[${item?.type}]`}\n                  </Typography>\n                )}\n              </span>\n            </Tooltip>\n          ) : (\n            <Tooltip title={(item?.comment || item?.title) + (item?.can_null === 'YES' ? '(can null)' : `(can't null)`)}>\n              <span>\n                {strTitle}\n                {item?.type && (\n                  <Typography gutterBottom level=\"body3\" className=\"pl-0.5\" style={{ display: 'inline' }}>\n                    {`[${item?.type}]`}\n                  </Typography>\n                )}\n              </span>\n            </Tooltip>\n          );\n        if (item.children) {\n          const itemKey = parentKey ? String(parentKey) + '_' + item.key : item.key;\n          return { title: strTitle, showTitle, key: itemKey, children: loop(item.children, itemKey) };\n        }\n\n        return {\n          title: strTitle,\n          showTitle,\n          key: item.key,\n        };\n      });\n    if (tables?.data) {\n      // default expand first node\n      setExpandedKeys([tables?.data.key]);\n      return loop([tables?.data]);\n    }\n    return [];\n  }, [searchValue, tables]);\n\n  const dataList = React.useMemo(() => {\n    let res: { key: string | number; title: string; parentKey?: string | number }[] = [];\n    const generateList = (data: DataNode[], parentKey?: string | number) => {\n      if (!data || data?.length <= 0) return;\n      for (let i = 0; i < data.length; i++) {\n        const node = data[i];\n        const { key, title } = node;\n        res.push({ key, title: title as string, parentKey });\n        if (node.children) {\n          generateList(node.children, key);\n        }\n      }\n    };\n    if (treeData) {\n      generateList(treeData);\n    }\n    return res;\n  }, [treeData]);\n\n  const getParentKey = (key: React.Key, tree: DataNode[]): React.Key => {\n    let parentKey: React.Key;\n    for (let i = 0; i < tree.length; i++) {\n      const node = tree[i];\n      if (node.children) {\n        if (node.children.some((item) => item.key === key)) {\n          parentKey = node.key;\n        } else if (getParentKey(key, node.children)) {\n          parentKey = getParentKey(key, node.children);\n        }\n      }\n    }\n    return parentKey!;\n  };\n\n  const onChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n    const { value } = e.target;\n    if (tables?.data) {\n      if (!value) {\n        setExpandedKeys([]);\n      } else {\n        const newExpandedKeys = dataList\n          .map((item) => {\n            if (item.title.indexOf(value) > -1) {\n              return getParentKey(item.key, treeData);\n            }\n            return null;\n          })\n          .filter((item, i, self) => item && self.indexOf(item) === i);\n        setExpandedKeys(newExpandedKeys as React.Key[]);\n      }\n      setSearchValue(value);\n      setAutoExpandParent(true);\n    }\n  };\n\n  React.useEffect(() => {\n    if (currentRound) {\n      handleGetEditorSql(currentRound);\n    }\n  }, [handleGetEditorSql, currentRound]);\n\n  React.useEffect(() => {\n    if (editorValue && scene === 'chat_dashboard' && currentTabIndex) {\n      runCharts();\n    }\n  }, [currentTabIndex, scene, editorValue, runCharts]);\n\n  React.useEffect(() => {\n    if (editorValue && scene !== 'chat_dashboard') {\n      runSql();\n    }\n  }, [scene, editorValue, runSql]);\n\n  function resolveSqlAndThoughts(value: string | undefined) {\n    if (!value) {\n      return { sql: '', thoughts: '' };\n    }\n    const match = value && value.match(/(--.*)\\n([\\s\\S]*)/);\n    let thoughts = '';\n    let sql;\n    if (match && match.length >= 3) {\n      thoughts = match[1];\n      sql = match[2];\n    }\n    return { sql, thoughts };\n  }\n\n  return (\n    <div className=\"flex flex-col w-full h-full\">\n      <Header />\n      <div className=\"relative flex flex-1 overflow-auto\">\n        <div\n          className=\"text h-full border-[var(--joy-palette-divider)] border-r border-solid p-3 max-h-full overflow-auto\"\n          style={{ width: '300px' }}\n        >\n          <div className=\"absolute right-4 top-2 z-10\">\n            <Button\n              className=\"mr-2\"\n              type=\"primary\"\n              loading={runLoading || runChartsLoading}\n              onClick={async () => {\n                if (scene === 'chat_dashboard') {\n                  runCharts();\n                } else {\n                  runSql();\n                }\n              }}\n            >\n              Run\n            </Button>\n            <Button\n              loading={submitLoading || submitChartLoading}\n              onClick={async () => {\n                if (scene === 'chat_dashboard') {\n                  await submitChart();\n                } else {\n                  await submitSql();\n                }\n              }}\n            >\n              Save\n            </Button>\n          </div>\n          <div className=\"flex items-center py-3\">\n            <Select\n              className=\"h-4 min-w-[240px]\"\n              size=\"sm\"\n              value={currentRound as string | null | undefined}\n              onChange={(e: React.SyntheticEvent | null, newValue: string | null) => {\n                setCurrentRound(newValue);\n              }}\n            >\n              {rounds?.data?.map((item: RoundProps) => (\n                <Option key={item?.round} value={item?.round}>\n                  {item?.round_name}\n                </Option>\n              ))}\n            </Select>\n            <AutoAwesomeMotionIcon className=\"ml-2\" />\n          </div>\n          <Search style={{ marginBottom: 8 }} placeholder=\"Search\" onChange={onChange} />\n          {treeData && treeData.length > 0 && (\n            <Tree\n              onExpand={(newExpandedKeys: React.Key[]) => {\n                setExpandedKeys(newExpandedKeys);\n                setAutoExpandParent(false);\n              }}\n              expandedKeys={expandedKeys}\n              autoExpandParent={autoExpandParent}\n              treeData={treeData}\n              fieldNames={{\n                title: 'showTitle',\n              }}\n            />\n          )}\n        </div>\n        <div className=\"flex flex-col flex-1 max-w-full overflow-hidden\">\n          {Array.isArray(editorValue) ? (\n            <>\n              <Box\n                className=\"h-full\"\n                sx={{\n                  '.ant-tabs-content, .ant-tabs-tabpane-active': {\n                    height: '100%',\n                  },\n                  '& .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': {\n                    borderRadius: '0',\n                  },\n                }}\n              >\n                <Tabs\n                  className=\"h-full dark:text-white px-2\"\n                  activeKey={currentTabIndex}\n                  onChange={(activeKey) => {\n                    setCurrentTabIndex(activeKey);\n                    setNewEditorValue(editorValue?.[Number(activeKey)]);\n                  }}\n                  items={editorValue?.map((item, i) => ({\n                    key: i + '',\n                    label: item?.title,\n                    children: (\n                      <div className=\"flex flex-col h-full\">\n                        <DbEditorContent\n                          editorValue={item}\n                          handleChange={(value) => {\n                            const { sql, thoughts } = resolveSqlAndThoughts(value);\n                            setNewEditorValue((old) => {\n                              return Object.assign({}, old, {\n                                sql,\n                                thoughts,\n                              });\n                            });\n                          }}\n                          tableData={tableData}\n                          chartData={chartData}\n                        />\n                      </div>\n                    ),\n                  }))}\n                />\n              </Box>\n            </>\n          ) : (\n            <DbEditorContent\n              editorValue={editorValue}\n              handleChange={(value) => {\n                const { sql, thoughts } = resolveSqlAndThoughts(value);\n                setNewEditorValue((old) => {\n                  return Object.assign({}, old, {\n                    sql,\n                    thoughts,\n                  });\n                });\n              }}\n              tableData={tableData}\n              chartData={undefined}\n            />\n          )}\n        </div>\n      </div>\n    </div>\n  );\n}\n\nexport default DbEditor;\n"
  },
  {
    "path": "components/chat/doc-list.tsx",
    "content": "import { IDocument } from '@/types/knowledge';\nimport { Button, Tooltip } from 'antd';\nimport { useRouter } from 'next/router';\nimport React from 'react';\nimport FileStatusIcon from '../common/FileStatusIcon';\n\ninterface IProps {\n  documents: IDocument[];\n  dbParam?: string;\n}\n\nexport default function DocList(props: IProps) {\n  const { documents, dbParam } = props;\n  const router = useRouter();\n\n  const handleClick = (id: number) => {\n    router.push(`/knowledge/chunk/?spaceName=${dbParam}&id=${id}`);\n  };\n\n  if (!documents?.length) return null;\n\n  return (\n    <div className=\"absolute flex overflow-scroll h-12 top-[-35px] w-full z-10\">\n      {documents.map((doc) => {\n        let color;\n        switch (doc.status) {\n          case 'RUNNING':\n            color = '#2db7f5';\n            break;\n          case 'FINISHED':\n            color = '#87d068';\n            break;\n          case 'FAILED':\n            color = '#f50';\n            break;\n          default:\n            color = '#87d068';\n            break;\n        }\n        return (\n          <Tooltip key={doc.id} title={doc.result}>\n            <Button\n              style={{ color }}\n              onClick={() => {\n                handleClick(doc.id);\n              }}\n              className=\"shrink flex items-center mr-3\"\n            >\n              <FileStatusIcon document={doc} />\n              {doc.doc_name}\n            </Button>\n          </Tooltip>\n        );\n      })}\n    </div>\n  );\n}\n"
  },
  {
    "path": "components/chat/doc-upload.tsx",
    "content": "import { ChatContext } from '@/app/chat-context';\nimport { apiInterceptors, uploadDocument } from '@/client/api';\n\nimport useSummary from '@/hooks/use-summary';\nimport { PaperClipOutlined } from '@ant-design/icons';\nimport { Button, Upload } from 'antd';\nimport React, { useContext, useState } from 'react';\n\ninterface IProps {\n  className?: string;\n  handleFinish?: (data: boolean) => void;\n  onUploadFinish: () => void;\n}\nexport default function DocUpload(props: IProps) {\n  const { dbParam, setDocId } = useContext(ChatContext);\n  const { onUploadFinish, handleFinish } = props;\n  const summary = useSummary();\n  const [loading, setLoading] = useState<boolean>(false);\n\n  const handleUpload = async (data: any) => {\n    setLoading(true);\n    const formData = new FormData();\n    formData.append('doc_name', data.file.name);\n    formData.append('doc_file', data.file);\n    formData.append('doc_type', 'DOCUMENT');\n\n    const res = await apiInterceptors(uploadDocument(dbParam || 'default', formData));\n\n    if (!res[1]) {\n      setLoading(false);\n      return;\n    }\n    setDocId(res[1]);\n    onUploadFinish();\n    setLoading(false);\n    handleFinish?.(true);\n    await summary(res[1]);\n    handleFinish?.(false);\n  };\n\n  return (\n    <Upload\n      customRequest={handleUpload}\n      showUploadList={false}\n      maxCount={1}\n      multiple={false}\n      className=\"absolute z-10 top-2 left-2\"\n      accept=\".pdf,.ppt,.pptx,.xls,.xlsx,.doc,.docx,.txt,.md\"\n    >\n      <Button loading={loading} size=\"small\" shape=\"circle\" icon={<PaperClipOutlined />}></Button>\n    </Upload>\n  );\n}\n"
  },
  {
    "path": "components/chat/header/agent-selector.tsx",
    "content": "import { ChatContext } from '@/app/chat-context';\nimport { apiInterceptors, getDbgptsList } from '@/client/api';\nimport { useRequest } from 'ahooks';\nimport { Select } from 'antd';\nimport { useContext } from 'react';\nimport { useTranslation } from 'react-i18next';\n\nfunction AgentSelector() {\n  const { t } = useTranslation();\n  const { agent, setAgent } = useContext(ChatContext);\n\n  const { data = [] } = useRequest(async () => {\n    const [, res] = await apiInterceptors(getDbgptsList());\n    return res ?? [];\n  });\n\n  return (\n    <Select\n      className=\"w-60\"\n      value={agent}\n      placeholder={t('Select_Plugins')}\n      options={data.map((item) => ({ label: item.app_name, value: item.app_code }))}\n      allowClear\n      onChange={(val) => {\n        setAgent?.(val);\n      }}\n    />\n  );\n}\n\nexport default AgentSelector;\n"
  },
  {
    "path": "components/chat/header/chat-excel.tsx",
    "content": "import ExcelUpload from './excel-upload';\nimport { LinkOutlined } from '@ant-design/icons';\nimport { useContext } from 'react';\nimport { ChatContext } from '@/app/chat-context';\n\ninterface Props {\n  onComplete?: () => void;\n}\n\nfunction ChatExcel({ onComplete }: Props) {\n  const { currentDialogue, scene, chatId } = useContext(ChatContext);\n\n  if (scene !== 'chat_excel') return null;\n\n  return (\n    <div className=\"max-w-md h-full relative\">\n      {currentDialogue ? (\n        <div className=\"flex h-8 overflow-hidden rounded\">\n          <div className=\"flex items-center justify-center px-2 bg-gray-600 text-lg\">\n            <LinkOutlined className=\"text-white\" />\n          </div>\n          <div className=\"flex items-center justify-center px-3 bg-gray-100 text-xs rounded-tr rounded-br dark:text-gray-800 truncate\">\n            {currentDialogue.select_param}\n          </div>\n        </div>\n      ) : (\n        <ExcelUpload convUid={chatId} chatMode={scene} onComplete={onComplete} />\n      )}\n    </div>\n  );\n}\n\nexport default ChatExcel;\n"
  },
  {
    "path": "components/chat/header/db-selector.tsx",
    "content": "import { ChatContext } from '@/app/chat-context';\nimport { apiInterceptors, postChatModeParamsList } from '@/client/api';\nimport { IDB } from '@/types/chat';\nimport { dbMapper } from '@/utils';\nimport { useAsyncEffect } from 'ahooks';\nimport { Select } from 'antd';\nimport { useContext, useEffect, useMemo, useState } from 'react';\nimport DBIcon from '@/components/common/db-icon';\n\nfunction DBSelector() {\n  const { scene, dbParam, setDbParam } = useContext(ChatContext);\n\n  const [dbs, setDbs] = useState<IDB[]>([]);\n\n  useAsyncEffect(async () => {\n    const [, res] = await apiInterceptors(postChatModeParamsList(scene as string));\n    setDbs(res ?? []);\n  }, [scene]);\n\n  const dbOpts = useMemo(\n    () =>\n      dbs.map?.((db: IDB) => {\n        return { name: db.param, ...dbMapper[db.type] };\n      }),\n    [dbs],\n  );\n\n  useEffect(() => {\n    if (dbOpts?.length && !dbParam) {\n      setDbParam(dbOpts[0].name);\n    }\n  }, [dbOpts, setDbParam, dbParam]);\n\n  if (!dbOpts?.length) return null;\n\n  return (\n    <Select\n      value={dbParam}\n      className=\"w-36\"\n      onChange={(val) => {\n        setDbParam(val);\n      }}\n    >\n      {dbOpts.map((item) => (\n        <Select.Option key={item.name}>\n          <DBIcon width={24} height={24} src={item.icon} label={item.label} className=\"w-[1.5em] h-[1.5em] mr-1 inline-block mt-[-4px]\" />\n          {item.name}\n        </Select.Option>\n      ))}\n    </Select>\n  );\n}\n\nexport default DBSelector;\n"
  },
  {
    "path": "components/chat/header/excel-upload.tsx",
    "content": "import { PropsWithChildren, useContext, useState } from 'react';\nimport { Upload, UploadProps, Button, message, UploadFile, Tooltip } from 'antd';\nimport { LinkOutlined, SelectOutlined, UploadOutlined } from '@ant-design/icons';\nimport { apiInterceptors, postChatModeParamsFileLoad } from '@/client/api';\nimport { ChatContext } from '@/app/chat-context';\n\ninterface Props {\n  convUid: string;\n  chatMode: string;\n  onComplete?: () => void;\n}\n\nfunction ExcelUpload({ convUid, chatMode, onComplete, ...props }: PropsWithChildren<Props & UploadProps>) {\n  const [loading, setLoading] = useState(false);\n  const [messageApi, contextHolder] = message.useMessage();\n  const [fileList, setFileList] = useState<UploadFile[]>([]);\n  const [percent, setPercent] = useState<number>();\n  const { model } = useContext(ChatContext);\n\n  const onChange: UploadProps['onChange'] = async (info) => {\n    if (!info) {\n      message.error('Please select the *.(csv|xlsx|xls) file');\n      return;\n    }\n    if (!/\\.(csv|xlsx|xls)$/.test(info.file.name ?? '')) {\n      message.error('File type must be csv, xlsx or xls');\n      return;\n    }\n\n    setFileList([info.file]);\n  };\n\n  const onUpload = async () => {\n    setLoading(true);\n    try {\n      const formData = new FormData();\n      formData.append('doc_file', fileList[0] as any);\n      messageApi.open({ content: `Uploading ${fileList[0].name}`, type: 'loading', duration: 0 });\n      const [err] = await apiInterceptors(\n        postChatModeParamsFileLoad({\n          convUid,\n          chatMode,\n          data: formData,\n          model,\n          config: {\n            /** timeout 1h */\n            timeout: 1000 * 60 * 60,\n            onUploadProgress: (progressEvent) => {\n              const progress = Math.ceil((progressEvent.loaded / (progressEvent.total || 0)) * 100);\n              setPercent(progress);\n            },\n          },\n        }),\n      );\n      if (err) return;\n      message.success('success');\n      onComplete?.();\n    } catch (e: any) {\n      message.error(e?.message || 'Upload Error');\n    } finally {\n      setLoading(false);\n      messageApi.destroy();\n    }\n  };\n\n  return (\n    <>\n      <div className=\"flex items-start gap-2\">\n        {contextHolder}\n        <Tooltip placement=\"bottom\" title=\"File cannot be changed after upload\">\n          <Upload\n            disabled={loading}\n            className=\"mr-1\"\n            beforeUpload={() => false}\n            fileList={fileList}\n            name=\"file\"\n            accept=\".csv,.xlsx,.xls\"\n            multiple={false}\n            onChange={onChange}\n            showUploadList={{\n              showDownloadIcon: false,\n              showPreviewIcon: false,\n              showRemoveIcon: false,\n            }}\n            itemRender={() => <></>}\n            {...props}\n          >\n            <Button className=\"flex justify-center items-center\" type=\"primary\" disabled={loading} icon={<SelectOutlined />}>\n              Select File\n            </Button>\n          </Upload>\n        </Tooltip>\n        <Button\n          type=\"primary\"\n          loading={loading}\n          className=\"flex justify-center items-center\"\n          disabled={!fileList.length}\n          icon={<UploadOutlined />}\n          onClick={onUpload}\n        >\n          {loading ? (percent === 100 ? 'Analysis' : 'Uploading') : 'Upload'}\n        </Button>\n        {!!fileList.length && (\n          <div className=\"mt-2 text-gray-500 text-sm flex items-center\">\n            <LinkOutlined className=\"mr-2\" />\n            <span>{fileList[0]?.name}</span>\n          </div>\n        )}\n      </div>\n    </>\n  );\n}\n\nexport default ExcelUpload;\n"
  },
  {
    "path": "components/chat/header/index.tsx",
    "content": "import { useContext } from 'react';\nimport ChatExcel from './chat-excel';\nimport { ChatContext } from '@/app/chat-context';\nimport ModeTab from '@/components/chat/mode-tab';\nimport ModelSelector from '@/components/chat/header/model-selector';\nimport DBSelector from './db-selector';\nimport AgentSelector from './agent-selector';\n\n/**\n * chat header\n */\ninterface Props {\n  refreshHistory?: () => Promise<void>;\n  modelChange?: (val: string) => void;\n}\n\nfunction Header({ refreshHistory, modelChange }: Props) {\n  const { scene, refreshDialogList } = useContext(ChatContext);\n\n  return (\n    <div className=\"w-full py-2 px-4 md:px-4 flex flex-wrap items-center justify-center gap-1 md:gap-4\">\n      {/* Models Selector */}\n      <ModelSelector onChange={modelChange} />\n      {/* DB Selector */}\n      <DBSelector />\n      {/* Excel Upload */}\n      {scene === 'chat_excel' && (\n        <ChatExcel\n          onComplete={() => {\n            refreshDialogList?.();\n            refreshHistory?.();\n          }}\n        />\n      )}\n      {/* Agent Selector */}\n      {scene === 'chat_agent' && <AgentSelector />}\n      <ModeTab />\n    </div>\n  );\n}\n\nexport default Header;\n"
  },
  {
    "path": "components/chat/header/model-selector.tsx",
    "content": "/**\n * multi-models selector\n */\n\nimport { ChatContext } from '@/app/chat-context';\nimport { Select } from 'antd';\nimport { MODEL_ICON_MAP } from '@/utils/constants';\nimport Image from 'next/image';\nimport { useContext } from 'react';\nimport { useTranslation } from 'react-i18next';\n\ninterface Props {\n  onChange?: (model: string) => void;\n}\n\nconst DEFAULT_ICON_URL = '/models/huggingface.svg';\n\nexport function renderModelIcon(model?: string, props?: { width: number; height: number }) {\n  const { width, height } = props || {};\n\n  if (!model) return null;\n\n  return (\n    <Image\n      className=\"rounded-full border border-gray-200 object-contain bg-white inline-block\"\n      width={width || 24}\n      height={height || 24}\n      src={MODEL_ICON_MAP[model]?.icon || DEFAULT_ICON_URL}\n      alt=\"llm\"\n    />\n  );\n}\n\nfunction ModelSelector({ onChange }: Props) {\n  const { t } = useTranslation();\n  const { modelList, model } = useContext(ChatContext);\n  if (!modelList || modelList.length <= 0) {\n    return null;\n  }\n  return (\n    <Select\n      value={model}\n      placeholder={t('choose_model')}\n      className=\"w-52\"\n      onChange={(val) => {\n        onChange?.(val);\n      }}\n    >\n      {modelList.map((item) => (\n        <Select.Option key={item}>\n          <div className=\"flex items-center\">\n            {renderModelIcon(item)}\n            <span className=\"ml-2\">{MODEL_ICON_MAP[item]?.label || item}</span>\n          </div>\n        </Select.Option>\n      ))}\n    </Select>\n  );\n}\n\nexport default ModelSelector;\n"
  },
  {
    "path": "components/chat/mode-tab/index.css",
    "content": ".model-tab::before {\n  content: \"\";\n  position: absolute;\n  left: 0.4rem;\n  width: 45%;\n  height: 80%;\n  background-color: #ffc800;\n  border-radius: 2rem;\n  transition: all .4s;\n}\n.editor-tab::before {\n  left: calc(50%);\n}"
  },
  {
    "path": "components/chat/mode-tab/index.tsx",
    "content": "import './index.css';\nimport { useContext } from 'react';\nimport { ChatContext } from '@/app/chat-context';\nimport { Radio } from 'antd';\nimport Icon, { AppstoreFilled } from '@ant-design/icons';\nimport { StarsSvg } from '@/components/icons';\n\nexport default function ModeTab() {\n  const { isContract, setIsContract, scene } = useContext(ChatContext);\n  const isShow = scene && ['chat_with_db_execute', 'chat_dashboard'].includes(scene as string);\n\n  if (!isShow) {\n    return null;\n  }\n\n  return (\n    <Radio.Group\n      value={isContract}\n      defaultValue={true}\n      buttonStyle=\"solid\"\n      onChange={() => {\n        setIsContract(!isContract);\n      }}\n    >\n      <Radio.Button value={false}>\n        <Icon component={StarsSvg} className=\"mr-1\" />\n        Preview\n      </Radio.Button>\n      <Radio.Button value={true}>\n        <AppstoreFilled className=\"mr-1\" />\n        Editor\n      </Radio.Button>\n    </Radio.Group>\n  );\n}\n"
  },
  {
    "path": "components/chat/monaco-editor.tsx",
    "content": "import * as monaco from 'monaco-editor/esm/vs/editor/editor.api.js';\nimport Editor, { OnChange, loader } from '@monaco-editor/react';\nimport classNames from 'classnames';\nimport { useMemo } from 'react';\nimport { format } from 'sql-formatter';\n\nloader.config({ monaco });\n\ninterface MonacoEditorProps {\n  className?: string;\n  value: string;\n  language: string;\n  onChange?: OnChange;\n  thoughts?: string;\n}\n\nexport default function MonacoEditor({ className, value, language = 'mysql', onChange, thoughts }: MonacoEditorProps) {\n  // merge value and thoughts\n  const editorValue = useMemo(() => {\n    if (language !== 'mysql') {\n      return value;\n    }\n    if (thoughts && thoughts.length > 0) {\n      return format(`-- ${thoughts} \\n${value}`);\n    }\n    return format(value);\n  }, [value, thoughts]);\n\n  return (\n    <Editor\n      className={classNames(className)}\n      value={editorValue}\n      language={language}\n      onChange={onChange}\n      theme=\"vs-dark\"\n      options={{\n        minimap: {\n          enabled: false,\n        },\n        wordWrap: 'on',\n      }}\n    />\n  );\n}\n"
  },
  {
    "path": "components/common/FileStatusIcon.tsx",
    "content": "import { IDocument } from '@/types/knowledge';\nimport React from 'react';\nimport { FileDone, FileSync } from '../icons';\nimport FileError from '../icons/file-error';\n\ninterface IProps {\n  document: IDocument;\n}\n\nexport default function FileStatusIcon({ document }: IProps) {\n  switch (document.status) {\n    case 'RUNNING':\n      return <FileSync />;\n    case 'FINISHED':\n      return <FileDone />;\n    case 'FAILED':\n      return <FileError />;\n    default:\n      return <FileDone />;\n  }\n}\n"
  },
  {
    "path": "components/common/MyEmpty.tsx",
    "content": "import { Button, Empty } from 'antd';\nimport { useTranslation } from 'react-i18next';\n\ninterface Props {\n  error?: boolean;\n  description?: string;\n  refresh?: () => void;\n}\n\nfunction MyEmpty({ error, description, refresh }: Props) {\n  const { t } = useTranslation();\n\n  return (\n    <Empty\n      image=\"/empty.png\"\n      imageStyle={{ width: 320, height: 320, margin: '0 auto', maxWidth: '100%', maxHeight: '100%' }}\n      className=\"flex items-center justify-center flex-col h-full w-full\"\n      description={\n        error ? (\n          <Button type=\"primary\" onClick={refresh}>\n            {t('try_again')}\n          </Button>\n        ) : (\n          description ?? t('no_data')\n        )\n      }\n    />\n  );\n}\n\nexport default MyEmpty;\n"
  },
  {
    "path": "components/common/chat-dialog.tsx",
    "content": "import useChat from '@/hooks/use-chat';\nimport CompletionInput from './completion-input';\nimport { useCallback, useState } from 'react';\nimport { IChatDialogueMessageSchema, IChatDialogueSchema } from '@/types/chat';\nimport AgentContent from '../chat/agent-content';\nimport { renderModelIcon } from '../chat/header/model-selector';\nimport MyEmpty from './MyEmpty';\nimport { CaretLeftOutlined } from '@ant-design/icons';\nimport classNames from 'classnames';\nimport { useRequest } from 'ahooks';\nimport { apiInterceptors, newDialogue } from '@/client/api';\nimport ChatContent from '../chat/chat-content';\n\ninterface Props {\n  title?: string;\n  completionApi?: string;\n  chatMode: IChatDialogueSchema['chat_mode'];\n  chatParams?: {\n    select_param?: string;\n  } & Record<string, string>;\n  model?: string;\n}\n\nfunction ChatDialog({ title, chatMode, completionApi, chatParams, model = '' }: Props) {\n  const chat = useChat({ queryAgentURL: completionApi });\n\n  const [loading, setLoading] = useState(false);\n  const [list, setList] = useState<IChatDialogueMessageSchema[]>([]);\n  const [open, setOpen] = useState(false);\n\n  const { data } = useRequest(\n    async () => {\n      const [, res] = await apiInterceptors(newDialogue({ chat_mode: chatMode }));\n      return res;\n    },\n    {\n      ready: !!chatMode,\n    },\n  );\n\n  const handleChat = useCallback(\n    (content: string) => {\n      if (!data) return;\n      return new Promise<void>((resolve) => {\n        const tempList: IChatDialogueMessageSchema[] = [\n          ...list,\n          { role: 'human', context: content, model_name: model, order: 0, time_stamp: 0 },\n          { role: 'view', context: '', model_name: model, order: 0, time_stamp: 0 },\n        ];\n        const index = tempList.length - 1;\n        setList([...tempList]);\n        setLoading(true);\n        chat({\n          chatId: data?.conv_uid,\n          data: { ...chatParams, chat_mode: chatMode, model_name: model, user_input: content },\n          onMessage: (message) => {\n            tempList[index].context = message;\n            setList([...tempList]);\n          },\n          onDone: () => {\n            resolve();\n          },\n          onClose: () => {\n            resolve();\n          },\n          onError: (message) => {\n            tempList[index].context = message;\n            setList([...tempList]);\n            resolve();\n          },\n        }).finally(() => {\n          setLoading(false);\n        });\n      });\n    },\n    [chat, list, data?.conv_uid],\n  );\n\n  return (\n    <div\n      className={classNames(\n        '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',\n        {\n          'translate-x-0': open,\n          'translate-x-full': !open,\n        },\n      )}\n    >\n      {title && <div className=\"p-4 border-b border-solid border-gray-100\">{title}</div>}\n      <div className=\"flex-1 overflow-y-auto px-2\">\n        {list.map((item, index) => (\n          <>{chatParams?.chat_mode === 'chat_agent' ? <AgentContent key={index} content={item} /> : <ChatContent key={index} content={item} />}</>\n        ))}\n        {!list.length && <MyEmpty description=\"\" />}\n      </div>\n      <div className=\"flex w-full p-4 border-t border-solid border-gray-100 items-center\">\n        {model && <div className=\"mr-2 flex\">{renderModelIcon(model)}</div>}\n        <CompletionInput loading={loading} onSubmit={handleChat} />\n      </div>\n      <div\n        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\"\n        onClick={() => {\n          setOpen(!open);\n        }}\n      >\n        <CaretLeftOutlined />\n      </div>\n    </div>\n  );\n}\n\nexport default ChatDialog;\n"
  },
  {
    "path": "components/common/completion-input.tsx",
    "content": "import { SendOutlined } from '@ant-design/icons';\nimport { Button, Input } from 'antd';\nimport { PropsWithChildren, useContext, useEffect, useMemo, useRef, useState } from 'react';\nimport PromptBot from './prompt-bot';\nimport DocUpload from '../chat/doc-upload';\nimport DocList from '../chat/doc-list';\nimport { IDocument } from '@/types/knowledge';\nimport { ChatContext } from '@/app/chat-context';\nimport { apiInterceptors, getDocumentList } from '@/client/api';\n\ntype TextAreaProps = Omit<Parameters<typeof Input.TextArea>[0], 'value' | 'onPressEnter' | 'onChange' | 'onSubmit'>;\n\ninterface Props {\n  loading?: boolean;\n  onSubmit: (val: string) => void;\n  handleFinish?: (val: boolean) => void;\n}\n\nfunction CompletionInput({ children, loading, onSubmit, handleFinish, ...props }: PropsWithChildren<Props & TextAreaProps>) {\n  const { dbParam, scene } = useContext(ChatContext);\n\n  const [userInput, setUserInput] = useState('');\n  const showUpload = useMemo(() => scene === 'chat_knowledge', [scene]);\n  const [documents, setDocuments] = useState<IDocument[]>([]);\n  const uploadCountRef = useRef(0);\n\n  useEffect(() => {\n    showUpload && fetchDocuments();\n  }, [dbParam]);\n\n  async function fetchDocuments() {\n    if (!dbParam) {\n      return null;\n    }\n    const [_, data] = await apiInterceptors(\n      getDocumentList(dbParam, {\n        page: 1,\n        page_size: uploadCountRef.current,\n      }),\n    );\n    setDocuments(data?.data!);\n  }\n\n  const onUploadFinish = async () => {\n    uploadCountRef.current += 1;\n    await fetchDocuments();\n  };\n\n  return (\n    <div className=\"flex-1 relative\">\n      <DocList documents={documents} dbParam={dbParam} />\n      {showUpload && <DocUpload handleFinish={handleFinish} onUploadFinish={onUploadFinish} className=\"absolute z-10 top-2 left-2\" />}\n      <Input.TextArea\n        className={`flex-1 ${showUpload ? 'pl-10' : ''} pr-10`}\n        size=\"large\"\n        value={userInput}\n        autoSize={{ minRows: 1, maxRows: 4 }}\n        {...props}\n        onPressEnter={(e) => {\n          if (!userInput.trim()) return;\n          if (e.keyCode === 13) {\n            if (e.shiftKey) {\n              setUserInput((state) => state + '\\n');\n              return;\n            }\n            onSubmit(userInput);\n            setTimeout(() => {\n              setUserInput('');\n            }, 0);\n          }\n        }}\n        onChange={(e) => {\n          if (typeof props.maxLength === 'number') {\n            setUserInput(e.target.value.substring(0, props.maxLength));\n            return;\n          }\n          setUserInput(e.target.value);\n        }}\n      />\n      <Button\n        className=\"ml-2 flex items-center justify-center absolute right-0 bottom-0\"\n        size=\"large\"\n        type=\"text\"\n        loading={loading}\n        icon={<SendOutlined />}\n        onClick={() => {\n          onSubmit(userInput);\n        }}\n      />\n      <PromptBot\n        submit={(prompt) => {\n          setUserInput(userInput + prompt);\n        }}\n      />\n      {children}\n    </div>\n  );\n}\n\nexport default CompletionInput;\n"
  },
  {
    "path": "components/common/db-icon.tsx",
    "content": "import Image from 'next/image';\n\ninterface IProps {\n  width?: number;\n  height?: number;\n  src: string;\n  label: string;\n  className?: string;\n}\n\nfunction DBIcon({ src, label, width, height, className }: IProps) {\n  return (\n    <Image\n      className={`w-11 h-11 rounded-full mr-4 border border-gray-200 object-contain bg-white ${className}`}\n      width={width || 44}\n      height={height || 44}\n      src={src}\n      alt={label || 'db-icon'}\n    />\n  );\n}\n\nexport default DBIcon;\n"
  },
  {
    "path": "components/common/gpt-card.tsx",
    "content": "import React, { HtmlHTMLAttributes, PropsWithChildren, ReactNode, memo, useCallback, useMemo } from 'react';\nimport { Tag, TagProps, Tooltip } from 'antd';\nimport classNames from 'classnames';\nimport Image from 'next/image';\n\ninterface Props {\n  title: string;\n  desc?: string;\n  disabled?: boolean;\n  tags?: (\n    | string\n    | {\n        text: ReactNode;\n        /** @default false */\n        border?: boolean;\n        /** @default default */\n        color?: TagProps['color'];\n      }\n  )[];\n  operations?: {\n    children: ReactNode;\n    label?: string;\n    onClick?: () => void;\n  }[];\n  icon?: ReactNode;\n  iconBorder?: boolean;\n  onClick?: () => void;\n}\n\nfunction GPTCard({\n  icon,\n  iconBorder = true,\n  title,\n  desc,\n  tags,\n  children,\n  disabled,\n  operations,\n  className,\n  ...props\n}: PropsWithChildren<HtmlHTMLAttributes<HTMLDivElement> & Props>) {\n  const iconNode = useMemo(() => {\n    if (!icon) return null;\n\n    if (typeof icon === 'string') {\n      return (\n        <Image\n          className={classNames('w-11 h-11 rounded-full mr-4 object-contain bg-white', {\n            'border border-gray-200': iconBorder,\n          })}\n          width={44}\n          height={44}\n          src={icon}\n          alt={title}\n        />\n      );\n    }\n\n    return icon;\n  }, [icon]);\n\n  const tagNode = useMemo(() => {\n    if (!tags || !tags.length) return null;\n    return (\n      <div className=\"flex items-center mt-1 flex-wrap\">\n        {tags.map((tag, index) => {\n          if (typeof tag === 'string') {\n            return (\n              <Tag key={index} className=\"text-xs\" bordered={false} color=\"default\">\n                {tag}\n              </Tag>\n            );\n          }\n          return (\n            <Tag key={index} className=\"text-xs\" bordered={tag.border ?? false} color={tag.color}>\n              {tag.text}\n            </Tag>\n          );\n        })}\n      </div>\n    );\n  }, [tags]);\n\n  return (\n    <div\n      className={classNames(\n        'group/card relative flex flex-col w-72 rounded justify-between text-black bg-white shadow-[0_8px_16px_-10px_rgba(100,100,100,.08)] hover:shadow-[0_14px_20px_-10px_rgba(100,100,100,.15)] dark:bg-[#232734] dark:text-white dark:hover:border-white transition-[transfrom_shadow] duration-300 hover:-translate-y-1 min-h-fit',\n        {\n          'grayscale cursor-no-drop': disabled,\n          'cursor-pointer': !disabled && !!props.onClick,\n        },\n        className,\n      )}\n      {...props}\n    >\n      <div className=\"p-4\">\n        <div className=\"flex items-center\">\n          {iconNode}\n          <div className=\"flex flex-col\">\n            <h2 className=\"text-sm font-semibold\">{title}</h2>\n            {tagNode}\n          </div>\n        </div>\n        {desc && (\n          <Tooltip title={desc}>\n            <p className=\"mt-2 text-sm text-gray-500 font-normal line-clamp-2\">{desc}</p>\n          </Tooltip>\n        )}\n      </div>\n      <div>\n        {children}\n        {operations && !!operations.length && (\n          <div className=\"flex flex-wrap items-center justify-center border-t border-solid border-gray-100 dark:border-theme-dark\">\n            {operations.map((item, index) => (\n              <Tooltip key={`operation-${index}`} title={item.label}>\n                <div\n                  className=\"relative flex flex-1 items-center justify-center h-11 text-gray-400 hover:text-blue-500 transition-colors duration-300 cursor-pointer\"\n                  onClick={(e) => {\n                    e.stopPropagation();\n                    item.onClick?.();\n                  }}\n                >\n                  {item.children}\n                  {index < operations.length - 1 && <div className=\"w-[1px] h-6 absolute top-2 right-0 bg-gray-100 rounded dark:bg-theme-dark\" />}\n                </div>\n              </Tooltip>\n            ))}\n          </div>\n        )}\n      </div>\n    </div>\n  );\n}\n\nexport default memo(GPTCard);\n"
  },
  {
    "path": "components/common/icon-wrapper.tsx",
    "content": "import classNames from 'classnames';\nimport React from 'react';\n\ninterface IconWrapperProps {\n  children: React.ReactNode;\n  className?: string;\n}\n\n// Icon wrapper, with background color and hover color. same width and height\nconst IconWrapper: React.FC<IconWrapperProps> = ({ children, className }) => {\n  return (\n    <div\n      className={classNames(\n        'flex justify-center items-center w-8 h-8 rounded-full dark:bg-zinc-700 hover:bg-stone-200 dark:hover:bg-zinc-900',\n        className,\n      )}\n    >\n      {children}\n    </div>\n  );\n};\n\nexport default IconWrapper;\n"
  },
  {
    "path": "components/common/loading.tsx",
    "content": "import { LoadingOutlined } from '@ant-design/icons';\n\nfunction MuiLoading({ visible }: { visible: boolean }) {\n  if (!visible) return null;\n\n  return (\n    <div className=\"absolute w-full h-full top-0 left-0 flex justify-center items-center z-10 bg-white dark:bg-black bg-opacity-50 dark:bg-opacity-50 backdrop-blur-sm text-3xl animate-fade animate-duration-200\">\n      <LoadingOutlined />\n    </div>\n  );\n}\n\nexport default MuiLoading;\n"
  },
  {
    "path": "components/common/prompt-bot.tsx",
    "content": "import { useState } from 'react';\nimport { List, FloatButton, Popover, Tooltip, Form, message, Select, ConfigProvider } from 'antd';\nimport { useRequest } from 'ahooks';\nimport { sendSpacePostRequest } from '@/utils/request';\nimport { useTranslation } from 'react-i18next';\n\ntype SelectTableProps = {\n  data: any;\n  loading: boolean;\n  submit: (prompt: string) => void;\n  close: () => void;\n};\n\nconst SelectTable: React.FC<SelectTableProps> = ({ data, loading, submit, close }) => {\n  const { t } = useTranslation();\n  const handleClick = (content: string) => () => {\n    submit(content);\n    close();\n  };\n\n  return (\n    <div\n      style={{\n        maxHeight: 400,\n        overflow: 'auto',\n      }}\n    >\n      <List\n        dataSource={data?.data}\n        loading={loading}\n        rowKey={(record: any) => record.prompt_name}\n        renderItem={(item) => (\n          <List.Item key={item.prompt_name} onClick={handleClick(item.content)}>\n            <Tooltip title={item.content}>\n              <List.Item.Meta\n                style={{ cursor: 'copy' }}\n                title={item.prompt_name}\n                description={t('Prompt_Info_Scene') + `：${item.chat_scene}，` + t('Prompt_Info_Sub_Scene') + `：${item.sub_chat_scene}`}\n              />\n            </Tooltip>\n          </List.Item>\n        )}\n      />\n    </div>\n  );\n};\n\ntype PromptBotProps = {\n  submit: (prompt: string) => void;\n};\n\nconst PromptBot: React.FC<PromptBotProps> = ({ submit }) => {\n  const { t } = useTranslation();\n  const [open, setOpen] = useState(false);\n  const [current, setCurrent] = useState('common');\n\n  const { data, loading } = useRequest(\n    () => {\n      const body = {\n        prompt_type: current,\n      };\n      return sendSpacePostRequest('/prompt/list', body);\n    },\n    {\n      refreshDeps: [current],\n      onError: (err) => {\n        message.error(err?.message);\n      },\n    },\n  );\n\n  const close = () => {\n    setOpen(false);\n  };\n\n  const handleOpenChange = (newOpen: boolean) => {\n    setOpen(newOpen);\n  };\n\n  const handleChange = (value: string) => {\n    setCurrent(value);\n  };\n\n  return (\n    <ConfigProvider\n      theme={{\n        components: {\n          Popover: {\n            minWidth: 250,\n          },\n        },\n      }}\n    >\n      <Popover\n        title={\n          <Form.Item label={'Prompt ' + t('Type')}>\n            <Select\n              style={{ width: 150 }}\n              value={current}\n              onChange={handleChange}\n              options={[\n                {\n                  label: t('Public') + ' Prompts',\n                  value: 'common',\n                },\n                {\n                  label: t('Private') + ' Prompts',\n                  value: 'private',\n                },\n              ]}\n            />\n          </Form.Item>\n        }\n        content={<SelectTable {...{ data, loading, submit, close }} />}\n        placement=\"topRight\"\n        trigger=\"click\"\n        open={open}\n        onOpenChange={handleOpenChange}\n      >\n        <Tooltip title={t('Click_Select') + ' Prompt'}>\n          <FloatButton className=\"bottom-[30%]\" />\n        </Tooltip>\n      </Popover>\n    </ConfigProvider>\n  );\n};\n\nexport default PromptBot;\n"
  },
  {
    "path": "components/database/form-dialog.tsx",
    "content": "/* eslint-disable react-hooks/exhaustive-deps */\nimport { Button, Form, Input, InputNumber, Modal, Select, message } from 'antd';\nimport { useEffect, useMemo, useState } from 'react';\nimport { apiInterceptors, postDbAdd, postDbEdit, postDbTestConnect } from '@/client/api';\nimport { DBOption, DBType, DbListResponse, PostDbParams } from '@/types/db';\nimport { isFileDb } from '@/pages/database';\nimport { useTranslation } from 'react-i18next';\n\ntype DBItem = DbListResponse[0];\n\ninterface Props {\n  dbTypeList: DBOption[];\n  open: boolean;\n  choiceDBType?: DBType;\n  editValue?: DBItem;\n  dbNames: string[];\n  onSuccess?: () => void;\n  onClose?: () => void;\n}\n\nfunction FormDialog({ open, choiceDBType, dbTypeList, editValue, dbNames, onClose, onSuccess }: Props) {\n  const [loading, setLoading] = useState(false);\n  const { t } = useTranslation();\n  const [form] = Form.useForm<DBItem>();\n  const dbType = Form.useWatch('db_type', form);\n\n  const fileDb = useMemo(() => isFileDb(dbTypeList, dbType), [dbTypeList, dbType]);\n\n  useEffect(() => {\n    if (choiceDBType) {\n      form.setFieldValue('db_type', choiceDBType);\n    }\n  }, [choiceDBType]);\n\n  useEffect(() => {\n    if (editValue) {\n      form.setFieldsValue({ ...editValue });\n    }\n  }, [editValue]);\n\n  useEffect(() => {\n    if (!open) {\n      form.resetFields();\n    }\n  }, [open]);\n\n  const onFinish = async (val: DBItem) => {\n    const { db_host, db_path, db_port, ...params } = val;\n    if (!editValue && dbNames.some((item) => item === params.db_name)) {\n      message.error('The database already exists!');\n      return;\n    }\n    const data: PostDbParams = {\n      db_host: fileDb ? undefined : db_host,\n      db_port: fileDb ? undefined : db_port,\n      file_path: fileDb ? db_path : undefined,\n      ...params,\n    };\n    setLoading(true);\n    try {\n      const [testErr] = await apiInterceptors(postDbTestConnect(data));\n      if (testErr) return;\n      const [err] = await apiInterceptors((editValue ? postDbEdit : postDbAdd)(data));\n      if (err) {\n        message.error(err.message);\n        return;\n      }\n      message.success('success');\n      onSuccess?.();\n    } catch (e: any) {\n      message.error(e.message);\n    } finally {\n      setLoading(false);\n    }\n  };\n\n  const lockDBType = useMemo(() => !!editValue || !!choiceDBType, [editValue, choiceDBType]);\n\n  return (\n    <Modal open={open} width={400} title={editValue ? t('Edit') : t('create_database')} maskClosable={false} footer={null} onCancel={onClose}>\n      <Form form={form} className=\"pt-2\" labelCol={{ span: 6 }} labelAlign=\"left\" onFinish={onFinish}>\n        <Form.Item name=\"db_type\" label=\"DB Type\" className=\"mb-3\" rules={[{ required: true }]}>\n          <Select aria-readonly={lockDBType} disabled={lockDBType} options={dbTypeList} />\n        </Form.Item>\n        <Form.Item name=\"db_name\" label=\"DB Name\" className=\"mb-3\" rules={[{ required: true }]}>\n          <Input readOnly={!!editValue} disabled={!!editValue} />\n        </Form.Item>\n        {fileDb === true && (\n          <Form.Item name=\"db_path\" label=\"Path\" className=\"mb-3\" rules={[{ required: true }]}>\n            <Input />\n          </Form.Item>\n        )}\n        {fileDb === false && (\n          <>\n            <Form.Item name=\"db_user\" label=\"Username\" className=\"mb-3\" rules={[{ required: true }]}>\n              <Input />\n            </Form.Item>\n            <Form.Item name=\"db_pwd\" label=\"Password\" className=\"mb-3\" rules={[{ required: true }]}>\n              <Input type=\"password\" />\n            </Form.Item>\n            <Form.Item name=\"db_host\" label=\"Host\" className=\"mb-3\" rules={[{ required: true }]}>\n              <Input />\n            </Form.Item>\n            <Form.Item name=\"db_port\" label=\"Port\" className=\"mb-3\" rules={[{ required: true }]}>\n              <InputNumber min={1} step={1} max={65535} />\n            </Form.Item>\n          </>\n        )}\n\n        <Form.Item name=\"comment\" label=\"Remark\" className=\"mb-3\">\n          <Input />\n        </Form.Item>\n        <Form.Item className=\"flex flex-row-reverse pt-1 mb-0\">\n          <Button htmlType=\"submit\" type=\"primary\" size=\"middle\" className=\"mr-1\" loading={loading}>\n            Save\n          </Button>\n          <Button size=\"middle\" onClick={onClose}>\n            Cancel\n          </Button>\n        </Form.Item>\n      </Form>\n    </Modal>\n  );\n}\n\nexport default FormDialog;\n"
  },
  {
    "path": "components/flow/add-nodes.tsx",
    "content": "import { apiInterceptors, getFlowNodes } from '@/client/api';\nimport { IFlowNode } from '@/types/flow';\nimport { PlusOutlined } from '@ant-design/icons';\nimport { Badge, Button, Collapse, CollapseProps, Input, Popover } from 'antd';\nimport React, { useEffect, useMemo, useState } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { FLOW_NODES_KEY } from '@/utils';\nimport StaticNodes from './static-nodes';\n\nconst { Search } = Input;\n\ntype GroupType = { category: string; categoryLabel: string; nodes: IFlowNode[] };\n\nconst AddNodes: React.FC = () => {\n  const { t } = useTranslation();\n  const [operators, setOperators] = useState<Array<IFlowNode>>([]);\n  const [resources, setResources] = useState<Array<IFlowNode>>([]);\n  const [operatorsGroup, setOperatorsGroup] = useState<GroupType[]>([]);\n  const [resourcesGroup, setResourcesGroup] = useState<GroupType[]>([]);\n  const [searchValue, setSearchValue] = useState<string>('');\n\n  useEffect(() => {\n    getNodes();\n  }, []);\n\n  async function getNodes() {\n    const [_, data] = await apiInterceptors(getFlowNodes());\n    if (data && data.length > 0) {\n      localStorage.setItem(FLOW_NODES_KEY, JSON.stringify(data));\n      const operatorNodes = data.filter((node) => node.flow_type === 'operator');\n      const resourceNodes = data.filter((node) => node.flow_type === 'resource');\n      setOperators(operatorNodes);\n      setResources(resourceNodes);\n      setOperatorsGroup(groupNodes(operatorNodes));\n      setResourcesGroup(groupNodes(resourceNodes));\n    }\n  }\n\n  function groupNodes(data: IFlowNode[]) {\n    const groups: GroupType[] = [];\n    const categoryMap: Record<string, { category: string; categoryLabel: string; nodes: IFlowNode[] }> = {};\n    data.forEach((item) => {\n      const { category, category_label } = item;\n      if (!categoryMap[category]) {\n        categoryMap[category] = { category, categoryLabel: category_label, nodes: [] };\n        groups.push(categoryMap[category]);\n      }\n      categoryMap[category].nodes.push(item);\n    });\n    return groups;\n  }\n\n  const operatorItems: CollapseProps['items'] = useMemo(() => {\n    if (!searchValue) {\n      return operatorsGroup.map(({ category, categoryLabel, nodes }) => ({\n        key: category,\n        label: categoryLabel,\n        children: <StaticNodes nodes={nodes} />,\n        extra: <Badge showZero count={nodes.length || 0} style={{ backgroundColor: nodes.length > 0 ? '#52c41a' : '#7f9474' }} />,\n      }));\n    } else {\n      const searchedNodes = operators.filter((node) => node.label.toLowerCase().includes(searchValue.toLowerCase()));\n      return groupNodes(searchedNodes).map(({ category, categoryLabel, nodes }) => ({\n        key: category,\n        label: categoryLabel,\n        children: <StaticNodes nodes={nodes} />,\n        extra: <Badge showZero count={nodes.length || 0} style={{ backgroundColor: nodes.length > 0 ? '#52c41a' : '#7f9474' }} />,\n      }));\n    }\n  }, [operatorsGroup, searchValue]);\n\n  const resourceItems: CollapseProps['items'] = useMemo(() => {\n    if (!searchValue) {\n      return resourcesGroup.map(({ category, categoryLabel, nodes }) => ({\n        key: category,\n        label: categoryLabel,\n        children: <StaticNodes nodes={nodes} />,\n        extra: <Badge showZero count={nodes.length || 0} style={{ backgroundColor: nodes.length > 0 ? '#52c41a' : '#7f9474' }} />,\n      }));\n    } else {\n      const searchedNodes = resources.filter((node) => node.label.toLowerCase().includes(searchValue.toLowerCase()));\n      return groupNodes(searchedNodes).map(({ category, categoryLabel, nodes }) => ({\n        key: category,\n        label: categoryLabel,\n        children: <StaticNodes nodes={nodes} />,\n        extra: <Badge showZero count={nodes.length || 0} style={{ backgroundColor: nodes.length > 0 ? '#52c41a' : '#7f9474' }} />,\n      }));\n    }\n  }, [resourcesGroup, searchValue]);\n\n  function searchNode(val: string) {\n    setSearchValue(val);\n  }\n\n  return (\n    <Popover\n      placement=\"bottom\"\n      trigger={['click']}\n      content={\n        <div className=\"w-[320px] overflow-hidden overflow-y-auto scrollbar-default\">\n          <p className=\"my-2 font-bold\">{t('add_node')}</p>\n          <Search placeholder=\"Search node\" onSearch={searchNode} />\n          <h2 className=\"my-2 ml-2 font-semibold\">{t('operators')}</h2>\n          <Collapse\n            className=\"max-h-[300px] overflow-hidden overflow-y-auto scrollbar-default\"\n            size=\"small\"\n            defaultActiveKey={['']}\n            items={operatorItems}\n          />\n          <h2 className=\"my-2 ml-2 font-semibold\">{t('resource')}</h2>\n          <Collapse\n            className=\"max-h-[300px] overflow-hidden overflow-y-auto scrollbar-default\"\n            size=\"small\"\n            defaultActiveKey={['']}\n            items={resourceItems}\n          />\n        </div>\n      }\n    >\n      <Button\n        type=\"primary\"\n        className=\"flex items-center justify-center rounded-full left-4 top-4\"\n        style={{ zIndex: 1050 }}\n        icon={<PlusOutlined />}\n      ></Button>\n    </Popover>\n  );\n};\n\nexport default AddNodes;\n"
  },
  {
    "path": "components/flow/button-edge.tsx",
    "content": "import React from 'react';\nimport { getBezierPath, EdgeProps, BaseEdge, useReactFlow } from 'reactflow';\n\nconst ButtonEdge: React.FC<EdgeProps> = ({ id, sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition, style = {}, data, markerEnd }) => {\n  const [edgePath, edgeCenterX, edgeCenterY] = getBezierPath({\n    sourceX,\n    sourceY,\n    sourcePosition,\n    targetX,\n    targetY,\n    targetPosition,\n  });\n  const reactFlow = useReactFlow();\n\n  function onEdgeClick(event: React.MouseEvent, id: string) {\n    event.stopPropagation();\n    reactFlow.setEdges(reactFlow.getEdges().filter((edge) => edge.id !== id));\n  }\n  return (\n    <>\n      <BaseEdge id={id} style={style} path={edgePath} markerEnd={markerEnd} />\n      <foreignObject\n        width={40}\n        height={40}\n        x={edgeCenterX - 40 / 2}\n        y={edgeCenterY - 40 / 2}\n        className=\"bg-transparent w-10 h-10 relative\"\n        requiredExtensions=\"http://www.w3.org/1999/xhtml\"\n      >\n        <button\n          className=\"absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 w-5 h-5 rounded-full bg-stone-400 dark:bg-zinc-700 cursor-pointer text-sm\"\n          onClick={(event) => onEdgeClick(event, id)}\n        >\n          ×\n        </button>\n      </foreignObject>\n    </>\n  );\n};\n\nexport default ButtonEdge;\n"
  },
  {
    "path": "components/flow/canvas-node.tsx",
    "content": "import { IFlowNode } from '@/types/flow';\nimport Image from 'next/image';\nimport NodeParamHandler from './node-param-handler';\nimport classNames from 'classnames';\nimport { useState } from 'react';\nimport NodeHandler from './node-handler';\nimport { Popover, Tooltip } from 'antd';\nimport { CopyOutlined, DeleteOutlined, InfoCircleOutlined } from '@ant-design/icons';\nimport { useReactFlow } from 'reactflow';\nimport IconWrapper from '../common/icon-wrapper';\nimport { getUniqueNodeId } from '@/utils/flow';\nimport { cloneDeep } from 'lodash';\n\ntype CanvasNodeProps = {\n  data: IFlowNode;\n};\n\nconst ICON_PATH_PREFIX = '/icons/node/';\n\nfunction TypeLabel({ label }: { label: string }) {\n  return <div className=\"w-full h-8 bg-stone-100 dark:bg-zinc-700 px-2 flex items-center justify-center\">{label}</div>;\n}\n\nconst CanvasNode: React.FC<CanvasNodeProps> = ({ data }) => {\n  const node = data;\n  const { inputs, outputs, parameters, flow_type: flowType } = node;\n  const [isHovered, setIsHovered] = useState(false);\n  const reactFlow = useReactFlow();\n\n  function onHover() {\n    setIsHovered(true);\n  }\n\n  function onLeave() {\n    setIsHovered(false);\n  }\n\n  function copyNode(e: React.MouseEvent<HTMLButtonElement, MouseEvent>) {\n    e.preventDefault();\n    e.stopPropagation();\n    const nodes = reactFlow.getNodes();\n    const originalNode = nodes.find((item) => item.id === node.id);\n    if (originalNode) {\n      const newNodeId = getUniqueNodeId(originalNode as IFlowNode, nodes);\n      const cloneNode = cloneDeep(originalNode);\n      const duplicatedNode = {\n        ...cloneNode,\n        id: newNodeId,\n        position: {\n          x: cloneNode.position.x + 400,\n          y: cloneNode.position.y,\n        },\n        positionAbsolute: {\n          x: cloneNode.positionAbsolute!.x + 400,\n          y: cloneNode.positionAbsolute!.y,\n        },\n        data: {\n          ...cloneNode.data,\n          id: newNodeId,\n        },\n        selected: false,\n      };\n      reactFlow.setNodes((nodes) => [...nodes, duplicatedNode]);\n    }\n  }\n\n  function deleteNode(e: React.MouseEvent<HTMLButtonElement, MouseEvent>) {\n    e.preventDefault();\n    e.stopPropagation();\n    reactFlow.setNodes((nodes) => nodes.filter((item) => item.id !== node.id));\n    reactFlow.setEdges((edges) => edges.filter((edge) => edge.source !== node.id && edge.target !== node.id));\n  }\n\n  function renderOutput(data: IFlowNode) {\n    if (flowType === 'operator' && outputs?.length > 0) {\n      return (\n        <>\n          <TypeLabel label=\"Outputs\" />\n          {(outputs || []).map((output, index) => (\n            <NodeHandler key={`${data.id}_input_${index}`} node={data} data={output} type=\"source\" label=\"outputs\" index={index} />\n          ))}\n        </>\n      );\n    } else if (flowType === 'resource') {\n      // resource nodes show output default\n      return (\n        <>\n          <TypeLabel label=\"Outputs\" />\n          <NodeHandler key={`${data.id}_input_0`} node={data} data={data} type=\"source\" label=\"outputs\" index={0} />\n        </>\n      );\n    }\n  }\n\n  return (\n    <Popover\n      placement=\"rightTop\"\n      trigger={['hover']}\n      content={\n        <>\n          <IconWrapper className=\"hover:text-blue-500\">\n            <CopyOutlined className=\"h-full text-lg cursor-pointer\" onClick={copyNode} />\n          </IconWrapper>\n          <IconWrapper className=\"mt-2 hover:text-red-500\">\n            <DeleteOutlined className=\"h-full text-lg cursor-pointer\" onClick={deleteNode} />\n          </IconWrapper>\n          <IconWrapper className=\"mt-2\">\n            <Tooltip title={node.description} placement=\"right\">\n              <InfoCircleOutlined className=\"h-full text-lg cursor-pointer\" />\n            </Tooltip>\n          </IconWrapper>\n        </>\n      }\n    >\n      <div\n        className={classNames('w-72 h-auto rounded-xl shadow-md p-0 border bg-white dark:bg-zinc-800 cursor-grab', {\n          'border-blue-500': node.selected || isHovered,\n          'border-stone-400 dark:border-white': !node.selected && !isHovered,\n          'border-dashed': flowType !== 'operator',\n          'border-red-600': node.invalid,\n        })}\n        onMouseEnter={onHover}\n        onMouseLeave={onLeave}\n      >\n        {/* icon and label */}\n        <div className=\"flex flex-row items-center p-2\">\n          <Image src={'/icons/node/vis.png'} width={24} height={24} alt=\"\" />\n          <p className=\"ml-2 text-lg font-bold text-ellipsis overflow-hidden whitespace-nowrap\">{node.label}</p>\n        </div>\n        {inputs && inputs.length > 0 && (\n          <>\n            <TypeLabel label=\"Inputs\" />\n            {(inputs || []).map((input, index) => (\n              <NodeHandler key={`${node.id}_input_${index}`} node={node} data={input} type=\"target\" label=\"inputs\" index={index} />\n            ))}\n          </>\n        )}\n        {parameters && parameters.length > 0 && (\n          <>\n            <TypeLabel label=\"Parameters\" />\n            {(parameters || []).map((parameter, index) => (\n              <NodeParamHandler key={`${node.id}_param_${index}`} node={node} data={parameter} label=\"parameters\" index={index} />\n            ))}\n          </>\n        )}\n        {renderOutput(node)}\n      </div>\n    </Popover>\n  );\n};\n\nexport default CanvasNode;\n"
  },
  {
    "path": "components/flow/flow-card.tsx",
    "content": "import { apiInterceptors, deleteFlowById, newDialogue } from '@/client/api';\nimport { IFlow } from '@/types/flow';\nimport {\n  CopyFilled,\n  DeleteFilled,\n  EditFilled,\n  ExclamationCircleFilled,\n  ExclamationCircleOutlined,\n  MessageFilled,\n  WarningOutlined,\n} from '@ant-design/icons';\nimport { Modal, Tooltip } from 'antd';\nimport React, { useContext } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport FlowPreview from './preview-flow';\nimport { useRouter } from 'next/router';\nimport GptCard from '../common/gpt-card';\nimport { ChatContext } from '@/app/chat-context';\nimport qs from 'querystring';\n\ninterface FlowCardProps {\n  flow: IFlow;\n  deleteCallback: (uid: string) => void;\n  onCopy: (flow: IFlow) => void;\n}\n\nconst FlowCard: React.FC<FlowCardProps> = ({ flow, onCopy, deleteCallback }) => {\n  const { model } = useContext(ChatContext);\n  const { t } = useTranslation();\n  const [modal, contextHolder] = Modal.useModal();\n  const router = useRouter();\n\n  async function deleteFlow() {\n    const [, , res] = await apiInterceptors(deleteFlowById(flow.uid));\n    if (res?.success) {\n      deleteCallback && deleteCallback(flow.uid);\n    }\n  }\n\n  function cardClick() {\n    router.push('/flow/canvas?id=' + flow.uid);\n  }\n\n  const handleChat = async () => {\n    const [, res] = await apiInterceptors(newDialogue({ chat_mode: 'chat_agent' }));\n    if (res) {\n      const queryStr = qs.stringify({\n        scene: 'chat_flow',\n        id: res.conv_uid,\n        model: model,\n        select_param: flow.uid,\n      });\n      router.push(`/chat?${queryStr}`);\n    }\n  };\n\n  const handleDel = () => {\n    modal.confirm({\n      title: t('Tips'),\n      icon: <WarningOutlined />,\n      content: t('delete_flow_confirm'),\n      okText: 'Yes',\n      okType: 'danger',\n      cancelText: 'No',\n      async onOk() {\n        deleteFlow();\n      },\n    });\n  };\n\n  return (\n    <>\n      {contextHolder}\n      <GptCard\n        className=\"w-[26rem] max-w-full\"\n        title={flow.name}\n        desc={flow.description}\n        tags={[\n          { text: flow.source, color: flow.source === 'DBGPT-WEB' ? 'green' : 'blue', border: true },\n          { text: flow.editable ? 'Editable' : 'Can not Edit', color: flow.editable ? 'green' : 'gray', border: true },\n          {\n            text: (\n              <>\n                {flow.error_message ? (\n                  <Tooltip placement=\"bottom\" title={flow.error_message}>\n                    {flow.state}\n                    <ExclamationCircleOutlined className=\"ml-1\" />\n                  </Tooltip>\n                ) : (\n                  flow.state\n                )}\n              </>\n            ),\n            color: flow.state === 'load_failed' ? 'red' : flow.state === 'running' ? 'green' : 'blue',\n            border: true,\n          },\n        ]}\n        operations={[\n          {\n            label: t('Chat'),\n            children: <MessageFilled />,\n            onClick: handleChat,\n          },\n          {\n            label: t('Edit'),\n            children: <EditFilled />,\n            onClick: cardClick,\n          },\n          {\n            label: t('Copy'),\n            children: <CopyFilled />,\n            onClick: () => {\n              onCopy(flow);\n            },\n          },\n          {\n            label: t('Delete'),\n            children: <DeleteFilled />,\n            onClick: handleDel,\n          },\n        ]}\n      >\n        <div className=\"w-full h-40 shadow-[inset_0_0_16px_rgba(50,50,50,.05)]\">\n          <FlowPreview flowData={flow.flow_data} />\n        </div>\n      </GptCard>\n    </>\n  );\n};\n\nexport default FlowCard;\n"
  },
  {
    "path": "components/flow/node-handler.tsx",
    "content": "import { Popconfirm, Tooltip, Typography, message } from 'antd';\nimport React from 'react';\nimport { Connection, Handle, Position, useReactFlow } from 'reactflow';\nimport RequiredIcon from './required-icon';\nimport { InfoCircleOutlined, PlusOutlined } from '@ant-design/icons';\nimport { IFlowNode, IFlowNodeInput, IFlowNodeOutput, IFlowNodeParameter } from '@/types/flow';\nimport { useTranslation } from 'react-i18next';\nimport classNames from 'classnames';\nimport { FLOW_NODES_KEY } from '@/utils';\nimport StaticNodes from './static-nodes';\n\ninterface NodeHandlerProps {\n  node: IFlowNode;\n  data: IFlowNodeInput | IFlowNodeParameter | IFlowNodeOutput;\n  type: 'source' | 'target';\n  label: 'inputs' | 'outputs' | 'parameters';\n  index: number;\n}\n\n// render react flow handle item\nconst NodeHandler: React.FC<NodeHandlerProps> = ({ node, data, type, label, index }) => {\n  const { t } = useTranslation();\n  const reactflow = useReactFlow();\n  const [relatedNodes, setRelatedNodes] = React.useState<IFlowNode[]>([]);\n\n  function isValidConnection(connection: Connection) {\n    const { sourceHandle, targetHandle, source, target } = connection;\n    const sourceNode = reactflow.getNode(source!);\n    const targetNode = reactflow.getNode(target!);\n    const { flow_type: sourceFlowType } = sourceNode?.data;\n    const { flow_type: targetFlowType } = targetNode?.data;\n    const sourceLabel = sourceHandle?.split('|')[1];\n    const targetLabel = targetHandle?.split('|')[1];\n    const sourceIndex = sourceHandle?.split('|')[2];\n    const targetIndex = targetHandle?.split('|')[2];\n    const targetTypeCls = targetNode?.data[targetLabel!][targetIndex!].type_cls;\n    if (sourceFlowType === targetFlowType && sourceFlowType === 'operator') {\n      // operator to operator, only type_cls and is_list matched can be connected\n      const sourceTypeCls = sourceNode?.data[sourceLabel!][sourceIndex!].type_cls;\n      const sourceIsList = sourceNode?.data[sourceLabel!][sourceIndex!].is_list;\n      const targetIsList = targetNode?.data[targetLabel!][targetIndex!].is_list;\n      return sourceTypeCls === targetTypeCls && sourceIsList === targetIsList;\n    } else if (sourceFlowType === 'resource' && (targetFlowType === 'operator' || targetFlowType === 'resource')) {\n      // resource to operator, check operator type_cls and resource parent_cls\n      const sourceParentCls = sourceNode?.data.parent_cls;\n      return sourceParentCls.includes(targetTypeCls);\n    }\n    message.warning(t('connect_warning'));\n    return false;\n  }\n\n  function showRelatedNodes() {\n    // find all nodes that can be connected to this node\n    const cache = localStorage.getItem(FLOW_NODES_KEY);\n    if (!cache) {\n      return;\n    }\n    const staticNodes = JSON.parse(cache);\n    const typeCls = data.type_cls;\n    let nodes: IFlowNode[] = [];\n    if (label === 'inputs') {\n      // find other operators and outputs matching this input type_cls\n      nodes = staticNodes\n        .filter((node: IFlowNode) => node.flow_type === 'operator')\n        .filter((node: IFlowNode) =>\n          node.outputs?.some((output: IFlowNodeOutput) => output.type_cls === typeCls && output.is_list === data?.is_list),\n        );\n    } else if (label === 'parameters') {\n      // fint other resources and parent_cls including this parameter type_cls\n      nodes = staticNodes.filter((node: IFlowNode) => node.flow_type === 'resource').filter((node: IFlowNode) => node.parent_cls?.includes(typeCls));\n    } else if (label === 'outputs') {\n      if (node.flow_type === 'operator') {\n        // find other operators and inputs matching this output type_cls\n        nodes = staticNodes\n          .filter((node: IFlowNode) => node.flow_type === 'operator')\n          .filter((node: IFlowNode) => node.inputs?.some((input: IFlowNodeInput) => input.type_cls === typeCls && input.is_list === data?.is_list));\n      } else if (node.flow_type === 'resource') {\n        // find other resources or operators that this output parent_cls includes their type_cls\n        nodes = staticNodes.filter(\n          (item: IFlowNode) =>\n            item.inputs?.some((input: IFlowNodeInput) => node.parent_cls?.includes(input.type_cls)) ||\n            item.parameters?.some((parameter: IFlowNodeParameter) => node.parent_cls?.includes(parameter.type_cls)),\n        );\n      }\n    }\n    setRelatedNodes(nodes);\n  }\n\n  return (\n    <div\n      className={classNames('relative flex items-center', {\n        'justify-start': label === 'parameters' || label === 'inputs',\n        'justify-end': label === 'outputs',\n      })}\n    >\n      <Handle\n        className=\"w-2 h-2\"\n        type={type}\n        position={type === 'source' ? Position.Right : Position.Left}\n        id={`${node.id}|${label}|${index}`}\n        isValidConnection={(connection) => isValidConnection(connection)}\n      />\n      <Typography\n        className={classNames('p-2', {\n          'pr-4': label === 'outputs',\n        })}\n      >\n        <Popconfirm\n          placement=\"left\"\n          icon={null}\n          showCancel={false}\n          okButtonProps={{ className: 'hidden' }}\n          title={t('related_nodes')}\n          description={\n            <div className=\"w-60\">\n              <StaticNodes nodes={relatedNodes} />\n            </div>\n          }\n        >\n          {['inputs', 'parameters'].includes(label) && <PlusOutlined className=\"mr-2 cursor-pointer\" onClick={showRelatedNodes} />}\n        </Popconfirm>\n        {data.type_name}:{label !== 'outputs' && <RequiredIcon optional={data.optional} />}\n        {data.description && (\n          <Tooltip title={data.description}>\n            <InfoCircleOutlined className=\"ml-2 cursor-pointer\" />\n          </Tooltip>\n        )}\n        <Popconfirm\n          placement=\"right\"\n          icon={null}\n          showCancel={false}\n          okButtonProps={{ className: 'hidden' }}\n          title={t('related_nodes')}\n          description={\n            <div className=\"w-60\">\n              <StaticNodes nodes={relatedNodes} />\n            </div>\n          }\n        >\n          {['outputs'].includes(label) && <PlusOutlined className=\"ml-2 cursor-pointer\" onClick={showRelatedNodes} />}\n        </Popconfirm>\n      </Typography>\n    </div>\n  );\n};\n\nexport default NodeHandler;\n"
  },
  {
    "path": "components/flow/node-param-handler.tsx",
    "content": "import { IFlowNode, IFlowNodeParameter } from '@/types/flow';\nimport { Checkbox, Input, InputNumber, Select, Tooltip } from 'antd';\nimport React from 'react';\nimport RequiredIcon from './required-icon';\nimport NodeHandler from './node-handler';\nimport { InfoCircleOutlined } from '@ant-design/icons';\n\ninterface NodeParamHandlerProps {\n  node: IFlowNode;\n  data: IFlowNodeParameter;\n  label: 'inputs' | 'outputs' | 'parameters';\n  index: number; // index of array\n}\n\n// render node parameters item\nconst NodeParamHandler: React.FC<NodeParamHandlerProps> = ({ node, data, label, index }) => {\n  function handleChange(value: any) {\n    data.value = value;\n  }\n\n  if (data.category === 'resource') {\n    return <NodeHandler node={node} data={data} type=\"target\" label={label} index={index} />;\n  } else if (data.category === 'common') {\n    let defaultValue = data.value !== null && data.value !== undefined ? data.value : data.default;\n    switch (data.type_name) {\n      case 'int':\n        return (\n          <div className=\"p-2 text-sm\">\n            <p>\n              {data.label}:<RequiredIcon optional={data.optional} />\n              {data.description && (\n                <Tooltip title={data.description}>\n                  <InfoCircleOutlined className=\"ml-2 cursor-pointer\" />\n                </Tooltip>\n              )}\n            </p>\n            <InputNumber\n              className=\"w-full\"\n              defaultValue={defaultValue}\n              onChange={(e) => {\n                handleChange(e.target.value);\n              }}\n            />\n          </div>\n        );\n      case 'str':\n        return (\n          <div className=\"p-2 text-sm\">\n            <p>\n              {data.label}:<RequiredIcon optional={data.optional} />\n              {data.description && (\n                <Tooltip title={data.description}>\n                  <InfoCircleOutlined className=\"ml-2 cursor-pointer\" />\n                </Tooltip>\n              )}\n            </p>\n            {data.options?.length > 0 ? (\n              <Select\n                className=\"w-full nodrag\"\n                defaultValue={defaultValue}\n                options={data.options.map((item: any) => ({ label: item.label, value: item.value }))}\n                onChange={handleChange}\n              />\n            ) : (\n              <Input\n                className=\"w-full\"\n                defaultValue={defaultValue}\n                onChange={(e) => {\n                  handleChange(e.target.value);\n                }}\n              />\n            )}\n          </div>\n        );\n      case 'bool':\n        defaultValue = defaultValue === 'False' ? false : defaultValue;\n        defaultValue = defaultValue === 'True' ? true : defaultValue;\n        return (\n          <div className=\"p-2 text-sm\">\n            <p>\n              {data.label}:<RequiredIcon optional={data.optional} />\n              {data.description && (\n                <Tooltip title={data.description}>\n                  <InfoCircleOutlined className=\"ml-2 cursor-pointer\" />\n                </Tooltip>\n              )}\n              <Checkbox\n                className=\"ml-2\"\n                defaultChecked={defaultValue}\n                onChange={(e) => {\n                  handleChange(e.target.checked);\n                }}\n              />\n            </p>\n          </div>\n        );\n    }\n  }\n};\n\nexport default NodeParamHandler;\n"
  },
  {
    "path": "components/flow/preview-flow.tsx",
    "content": "import { IFlowData } from '@/types/flow';\nimport React from 'react';\nimport ReactFlow, { Background } from 'reactflow';\nimport ButtonEdge from './button-edge';\nimport { mapUnderlineToHump } from '@/utils/flow';\nimport 'reactflow/dist/style.css';\n\nconst PreviewFlow: React.FC<{ flowData: IFlowData; minZoom?: number }> = ({ flowData, minZoom }) => {\n  const data = mapUnderlineToHump(flowData);\n\n  return (\n    <ReactFlow nodes={data.nodes} edges={data.edges} edgeTypes={{ buttonedge: ButtonEdge }} fitView minZoom={minZoom || 0.1}>\n      <Background color=\"#aaa\" gap={16} />\n    </ReactFlow>\n  );\n};\n\nexport default PreviewFlow;\n"
  },
  {
    "path": "components/flow/required-icon.tsx",
    "content": "import React from 'react';\n\nconst RequiredIcon: React.FC<{ optional?: boolean | undefined }> = ({ optional }) => {\n  if (optional) {\n    return null;\n  }\n  return <span className=\"text-red-600 align-middle inline-block\">&nbsp;*</span>;\n};\n\nexport default RequiredIcon;\n"
  },
  {
    "path": "components/flow/static-nodes.tsx",
    "content": "import { IFlowNode } from '@/types/flow';\nimport { Avatar, Empty, List } from 'antd';\nimport React, { DragEvent } from 'react';\nimport { useTranslation } from 'react-i18next';\n\nconst StaticNodes: React.FC<{ nodes: IFlowNode[] }> = ({ nodes }) => {\n  const { t } = useTranslation();\n\n  function onDragStart(event: DragEvent, node: IFlowNode) {\n    event.dataTransfer.setData('application/reactflow', JSON.stringify(node));\n    event.dataTransfer.effectAllowed = 'move';\n  }\n\n  if (nodes?.length > 0) {\n    return (\n      <List\n        className=\"overflow-hidden overflow-y-auto w-full\"\n        itemLayout=\"horizontal\"\n        dataSource={nodes}\n        renderItem={(node) => (\n          <List.Item\n            className=\"cursor-move hover:bg-[#F1F5F9] dark:hover:bg-theme-dark p-0 py-2\"\n            draggable\n            onDragStart={(event) => onDragStart(event, node)}\n          >\n            <List.Item.Meta\n              className=\"flex items-center justify-center\"\n              avatar={<Avatar src={'/icons/node/vis.png'} size={'large'} />}\n              title={<p className=\"line-clamp-1 font-medium\">{node.label}</p>}\n              description={<p className=\"line-clamp-2\">{node.description}</p>}\n            />\n          </List.Item>\n        )}\n      />\n    );\n  } else {\n    return <Empty className=\"px-2\" description={t('no_node')} />;\n  }\n};\n\nexport default StaticNodes;\n"
  },
  {
    "path": "components/icons/add-icon.tsx",
    "content": "import React from 'react';\n\nexport default function AddIcon() {\n  return (\n    <svg viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-id=\"5649\" width=\"1.5em\" height=\"1.5em\">\n      <path\n        d=\"M810.666667 554.666667h-256v256h-85.333334v-256H213.333333v-85.333334h256V213.333333h85.333334v256h256v85.333334z\"\n        p-id=\"5650\"\n        fill=\"#bfbfbf\"\n      ></path>\n    </svg>\n  );\n}\n"
  },
  {
    "path": "components/icons/collect.tsx",
    "content": "import React from 'react';\n\nexport default function CollectIcon() {\n  return (\n    <svg viewBox=\"0 0 1040 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-id=\"4212\" width=\"1.5em\" height=\"1.5em\">\n      <path\n        d=\"M177.812755 934.420799l46.095851-257.966283c2.695389-15.143908-2.527566-30.567179-14.003945-41.262776L14.733563 452.469162c-28.840862-26.952862-12.94687-73.967642 26.897603-79.524195l269.74863-37.622876c15.836687-2.223644 29.509081-11.753695 36.59343-25.507954l120.647801-234.709603c17.810644-34.650171 69.270666-34.650171 87.082334 0l120.647801 234.709603c7.086395 13.754259 20.75572 23.283286 36.5658 25.507954l269.775236 37.622876c39.819914 5.556553 55.711859 52.571333 26.926256 79.524195L814.393121 635.19174c-11.44875 10.695596-16.671705 26.118867-13.978363 41.262776l46.098921 257.966283c6.807033 38.09462-34.845623 67.131957-70.465888 49.152467l-241.295602-121.81437c-14.141068-7.111978-31.035854-7.111978-45.179992 0l-241.295602 121.81437C212.655308 1001.551732 171.002652 972.514395 177.812755 934.420799L177.812755 934.420799zM534.75219 812.217572l238.684124 120.478955c11.864212 6.001691 25.75764-3.695159 23.50739-16.365736L751.34518 661.141762c-2.694365-15.115256 2.529613-30.56411 13.948687-41.289382l193.115276-180.692339c9.613962-9.003048 4.306072-24.676006-8.974395-26.5374l-266.858814-37.232996c-15.810081-2.196015-29.482475-11.727089-36.5658-25.481348L526.667048 117.729332c-5.947456-11.532661-23.090904-11.532661-29.037337 0L378.316302 349.909321c-7.086395 13.754259-20.756743 23.285333-36.566824 25.481348L74.891687 412.623665c-13.280468 1.861394-18.560728 17.534352-8.974395 26.507724l193.0856 180.720991c11.447726 10.724249 16.670681 26.174126 13.975293 41.289382l-45.569872 255.18903c-2.276856 12.670577 11.587919 22.367427 23.480784 16.365736l238.682078-120.478955C503.716336 805.074895 520.610098 805.074895 534.75219 812.217572L534.75219 812.217572zM534.75219 812.217572\"\n        p-id=\"4213\"\n        fill=\"#707070\"\n      ></path>\n    </svg>\n  );\n}\n"
  },
  {
    "path": "components/icons/collected.tsx",
    "content": "import React from 'react';\n\nexport default function CollectedIcon() {\n  return (\n    <svg  viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-id=\"4396\" width=\"1.5em\" height=\"1.5em\"><path d=\"M485.173861 869.842745l-229.813553 120.819332a53.974999 53.974999 0 0 1-78.316666-56.898821l43.8912-255.899353a53.974999 53.974999 0 0 0-15.522222-47.777399L19.490266 448.857506a53.974999 53.974999 0 0 1 29.915556-92.06371l256.935108-37.338a53.974999 53.974999 0 0 0 40.639999-29.526088l114.909599-232.824864a53.974999 53.974999 0 0 1 96.802221 0l114.906776 232.824864a53.974999 53.974999 0 0 0 40.64 29.52891l256.93793 37.335178a53.974999 53.974999 0 0 1 29.915555 92.06371L815.170657 630.089326a53.974999 53.974999 0 0 0-15.522222 47.777399l43.888377 255.899353a53.974999 53.974999 0 0 1-78.316666 56.898821l-229.813552-120.819332a53.974999 53.974999 0 0 0-50.232733 0z\" fill=\"#f4ea2a\" p-id=\"4397\"></path></svg>\n  );\n}\n"
  },
  {
    "path": "components/icons/colorful-chat.tsx",
    "content": "function ColorfulChat() {\n  return (\n    <svg className=\"w-full h-full\" viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\">\n      <path\n        d=\"M416.9549913 314.32347826h297.42302609a119.56758261 119.56758261 0 0 1 119.56758261 119.56758261v179.19109565a196.71485217 196.71485217 0 0 1-196.71485217 196.71485218H416.9549913a119.56758261 119.56758261 0 0 1-119.5675826-119.56758261v-256.44521739A119.56758261 119.56758261 0 0 1 416.9549913 314.32347826z\"\n        fill=\"#F5384A\"\n        p-id=\"1186\"\n      ></path>\n      <path\n        d=\"M716.24793043 314.32347826H415.03165217a117.5373913 117.5373913 0 0 0-117.5373913 117.53739131v260.18504347c0 3.84667826 0 7.69335652 0.58768696 11.43318261a345.7202087 345.7202087 0 0 0 502.9531826-353.19986087A117.1634087 117.1634087 0 0 0 716.24793043 314.32347826z\"\n        fill=\"#F54F5C\"\n        p-id=\"1187\"\n      ></path>\n      <path\n        d=\"M318.91812174 594.54330435a345.7202087 345.7202087 0 0 0 420.73043478-249.07241739c2.35074783-9.18928696 4.22066087-18.432 5.82344348-27.67471305a117.10998261 117.10998261 0 0 0-29.22406957-3.63297391H415.03165217a117.5373913 117.5373913 0 0 0-117.5373913 117.5373913v156.43158261c6.9453913 2.35074783 14.10448696 4.54121739 21.42386087 6.41113044z\"\n        fill=\"#F66C73\"\n        p-id=\"1188\"\n      ></path>\n      <path\n        d=\"M630.17850435 314.32347826H415.03165217a117.5373913 117.5373913 0 0 0-117.5373913 117.53739131v48.08347826a346.14761739 346.14761739 0 0 0 332.68424348-165.62086957z\"\n        fill=\"#F78989\"\n        p-id=\"1189\"\n      ></path>\n      <path\n        d=\"M859.85725217 354.76702609h-25.53766956C802.26393043 200.52591304 669.92751304 84.59130435 512 84.59130435S221.73606957 200.52591304 189.68041739 354.76702609h-25.53766956a139.6557913 139.6557913 0 0 0-139.44208696 139.49551304v79.872a139.6557913 139.6557913 0 0 0 139.44208696 139.49551304h27.62128695a54.65488696 54.65488696 0 0 0 54.60146087-54.60146087V427.10594783C246.36549565 273.6128 365.50566957 148.7026087 512 148.7026087s265.63450435 124.9101913 265.63450435 278.40333913v159.3165913c0 116.09488696-74.79652174 219.47436522-181.38156522 251.42316522a30.23916522 30.23916522 0 0 0-3.09871304 1.06852174 60.15777391 60.15777391 0 1 0 18.05801739 61.06601739 23.50747826 23.50747826 0 0 0 3.36584348-0.69453913c93.12166957-27.88841739 166.63596522-98.67798261 203.01913043-187.79269565a54.92201739 54.92201739 0 0 0 14.90587826 2.13704347h27.62128696a139.6557913 139.6557913 0 0 0 139.44208696-139.49551304V494.26253913a139.6557913 139.6557913 0 0 0-139.7092174-139.49551304zM182.2541913 649.51874783h-18.11144347a75.43763478 75.43763478 0 0 1-75.33078261-75.3842087V494.26253913a75.43763478 75.43763478 0 0 1 75.33078261-75.3842087h18.11144347v230.6404174z m752.93384348-75.3842087a75.43763478 75.43763478 0 0 1-75.33078261 75.3842087h-18.11144347V418.87833043h18.11144347a75.43763478 75.43763478 0 0 1 75.33078261 75.3842087z\"\n        fill=\"#444444\"\n        p-id=\"1190\"\n      ></path>\n    </svg>\n  );\n}\n\nexport default ColorfulChat;\n"
  },
  {
    "path": "components/icons/colorful-dashboard.tsx",
    "content": "function ColorfulDashboard() {\n  return (\n    <svg viewBox=\"0 0 1116 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-id=\"1581\" className=\"w-full h-full\">\n      <path\n        d=\"M80.75 80.75m67.14674945 0l805.76099677 0q67.14674945 0 67.14674947 67.14674945l0 604.32074759q0 67.14674945-67.14674947 67.14674945l-805.76099677 0q-67.14674945 0-67.14674945-67.14674945l0-604.32074759q0-67.14674945 67.14674945-67.14674945Z\"\n        fill=\"#36CFC9\"\n        p-id=\"1582\"\n      ></path>\n      <path\n        d=\"M1020.80449568 685.07074759v67.14674945a67.14674945 67.14674945 0 0 1-67.14674946 67.14674945h-308.20358111l91.3195796 100.72012459-24.84429735 22.49416172L600.46584251 819.36424649h-100.72012459L389.62504831 943.25 364.78075097 920.08437108l91.31957961-100.72012459H147.89674945a67.14674945 67.14674945 0 0 1-67.14674945-67.14674945v-67.14674946z\"\n        fill=\"#08979C\"\n        p-id=\"1583\"\n      ></path>\n      <path\n        d=\"M416.48374894 282.19024919v335.7337481H315.76362434V282.19024919z m167.86687404 134.29349975v201.44024834h-100.72012459v-201.44024834z m167.86687406 67.14674945v134.2934989h-100.7201246v-134.2934989z m-225.94881252-302.16037379v141.34390829h201.4402492V272.11823698L819.36424649 341.27938889l-91.3195796 63.45367858V356.38740719h-239.71389641V215.04349975H315.76362434V181.4701246z\"\n        fill=\"#B5F5EC\"\n        p-id=\"1584\"\n      ></path>\n      <path\n        d=\"M550.77724783 752.21749704m-33.57337513 0a33.57337515 33.57337515 0 1 0 67.14675028 0 33.57337515 33.57337515 0 1 0-67.14675028 0Z\"\n        fill=\"#FFFFFF\"\n        p-id=\"1585\"\n      ></path>\n    </svg>\n  );\n}\n\nexport default ColorfulDashboard;\n"
  },
  {
    "path": "components/icons/colorful-data.tsx",
    "content": "function ColorfulData() {\n  return (\n    <svg viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-id=\"1722\" className=\"w-full h-full\">\n      <path\n        d=\"M207.83 962c-5.4 0-10.88-1.17-16.08-3.67-18.55-8.89-26.39-31.13-17.5-49.69l77.22-161.26c8.9-18.58 31.14-26.41 49.7-17.51 18.55 8.89 26.39 31.13 17.5 49.69l-77.22 161.26c-6.4 13.38-19.74 21.18-33.62 21.18zM821.57 962c-13.88 0-27.21-7.8-33.62-21.17l-77.24-161.26c-8.9-18.55-1.06-40.8 17.5-49.69 18.57-8.87 40.8-1.07 49.7 17.51l77.24 161.26c8.9 18.55 1.06 40.8-17.5 49.69a37.266 37.266 0 0 1-16.08 3.66z\"\n        fill=\"#12926E\"\n        p-id=\"1723\"\n      ></path>\n      <path\n        d=\"M156.74 105.14h710.51c50.7 0 91.8 41.1 91.8 91.8v525.82c0 50.7-41.1 91.8-91.8 91.8H156.74c-50.7 0-91.8-41.1-91.8-91.8V196.93c0.01-50.69 41.11-91.79 91.8-91.79z\"\n        fill=\"#39E2A0\"\n        p-id=\"1724\"\n      ></path>\n      <path\n        d=\"M835.65 686.01h-614.7c-5.14 0-9.31-4.17-9.31-9.31 0-5.14 4.17-9.31 9.31-9.31h614.7c5.14 0 9.31 4.17 9.31 9.31 0 5.14-4.17 9.31-9.31 9.31z\"\n        fill=\"#D3F8EA\"\n        p-id=\"1725\"\n      ></path>\n      <path\n        d=\"M699.31 631.94H624.8V454.95c0-11.28 9.14-20.42 20.42-20.42h33.67c11.28 0 20.42 9.14 20.42 20.42v176.99zM846.22 631.94h-74.51V346.76c0-11.28 9.14-20.42 20.42-20.42h33.67c11.28 0 20.42 9.14 20.42 20.42v285.18zM289.51 631.94H215V417.69c0-11.28 9.14-20.42 20.42-20.42h33.67c11.28 0 20.42 9.14 20.42 20.42v214.25zM436.42 631.94h-74.51V495.77c0-11.28 9.14-20.42 20.42-20.42H416c11.28 0 20.42 9.14 20.42 20.42v136.17z\"\n        fill=\"#FFFFFF\"\n        p-id=\"1726\"\n      ></path>\n      <path\n        d=\"M715.4 173.76H308.6c-11.11 0-20.12-9.01-20.12-20.12V82.12c0-11.11 9.01-20.12 20.12-20.12h406.8c11.11 0 20.12 9.01 20.12 20.12v71.52c0.01 11.11-9 20.12-20.12 20.12z\"\n        fill=\"#12926E\"\n        p-id=\"1727\"\n      ></path>\n    </svg>\n  );\n}\n\nexport default ColorfulData;\n"
  },
  {
    "path": "components/icons/colorful-db.tsx",
    "content": "function ColorfulDB() {\n  return (\n    <svg viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-id=\"1129\" className=\"w-full h-full\">\n      <path d=\"M226.3 70.4C151.1 91.6 91.6 151.1 70.4 226.3L226.3 70.4z\" fill=\"#FFA65A\" p-id=\"1130\"></path>\n      <path d=\"M277.9 62.2c-116.5 4.7-211 99.1-215.7 215.7L277.9 62.2z\" fill=\"#FFA659\" p-id=\"1131\"></path>\n      <path d=\"M321.5 62H287C163.3 62 62 163.3 62 287v34.5L321.5 62z\" fill=\"#FFA558\" p-id=\"1132\"></path>\n      <path d=\"M365 62h-78C163.3 62 62 163.3 62 287v78L365 62z\" fill=\"#FFA557\" p-id=\"1133\"></path>\n      <path d=\"M408.4 62H287C163.3 62 62 163.3 62 287v121.4L408.4 62z\" fill=\"#FFA556\" p-id=\"1134\"></path>\n      <path d=\"M451.8 62H287c-35.9 0-69.8 8.5-100 23.6L85.6 187C70.5 217.2 62 251.1 62 287v164.8L451.8 62z\" fill=\"#FFA555\" p-id=\"1135\"></path>\n      <path d=\"M495.3 62H287c-12.2 0-24.2 1-35.9 2.9L64.9 251.1C63 262.8 62 274.8 62 287v208.3L495.3 62z\" fill=\"#FFA454\" p-id=\"1136\"></path>\n      <path d=\"M62 538.7L538.7 62H297.5L62 297.5z\" fill=\"#FFA453\" p-id=\"1137\"></path>\n      <path d=\"M62 582.1L582.1 62H340.9L62 340.9z\" fill=\"#FFA452\" p-id=\"1138\"></path>\n      <path d=\"M62 625.6L625.6 62H384.3L62 384.3z\" fill=\"#FFA451\" p-id=\"1139\"></path>\n      <path d=\"M62 427.8V669L669 62H427.8z\" fill=\"#FFA450\" p-id=\"1140\"></path>\n      <path d=\"M62 471.2v241.2L712.4 62H471.2z\" fill=\"#FFA34F\" p-id=\"1141\"></path>\n      <path d=\"M737 62H514.6L62 514.6V737c0 6.1 0.3 12.1 0.7 18.1L755.1 62.7c-6-0.4-12-0.7-18.1-0.7z\" fill=\"#FFA34E\" p-id=\"1142\"></path>\n      <path d=\"M737 62H558.1L62 558.1V737c0 19.1 2.4 37.6 6.9 55.4L792.4 68.9C774.6 64.4 756.1 62 737 62z\" fill=\"#FFA34D\" p-id=\"1143\"></path>\n      <path d=\"M737 62H601.5L62 601.5V737c0 31.1 6.4 60.8 17.9 87.8L824.8 79.9C797.8 68.4 768.1 62 737 62z\" fill=\"#FFA34C\" p-id=\"1144\"></path>\n      <path d=\"M853.5 94.7C819.4 74 779.5 62 737 62h-92.1L62 644.9V737c0 42.5 12 82.4 32.7 116.5L853.5 94.7z\" fill=\"#FFA24B\" p-id=\"1145\"></path>\n      <path\n        d=\"M878.9 112.7C840.1 81.1 790.7 62 737 62h-48.6L62 688.4V737c0 53.7 19.1 103.1 50.7 141.9l766.2-766.2z\"\n        fill=\"#FFA24A\"\n        p-id=\"1146\"\n      ></path>\n      <path d=\"M737 62h-5.2L62 731.8v5.2c0 64.7 27.7 123.2 71.7 164.3l767.6-767.6C860.2 89.7 801.7 62 737 62z\" fill=\"#FFA249\" p-id=\"1147\"></path>\n      <path d=\"M64.8 772.4c9.8 61 44.3 114.1 92.8 148.4l763.2-763.2c-34.3-48.6-87.4-83.1-148.4-92.8L64.8 772.4z\" fill=\"#FFA248\" p-id=\"1148\"></path>\n      <path d=\"M73.3 807.3c18.7 56.4 59.2 103 111.3 129.9l752.6-752.6C910.4 132.5 863.7 92 807.3 73.3l-734 734z\" fill=\"#FFA247\" p-id=\"1149\"></path>\n      <path d=\"M86.1 838c26.5 52.3 72.9 93.1 129.1 112.2l735-735C931.1 159 890.3 112.6 838 86.1L86.1 838z\" fill=\"#FFA147\" p-id=\"1150\"></path>\n      <path d=\"M102.4 865.2c34 48.7 86.7 83.5 147.5 93.7l709-709c-10.2-60.8-45-113.5-93.7-147.5L102.4 865.2z\" fill=\"#FFA146\" p-id=\"1151\"></path>\n      <path d=\"M962 287c0-65.2-28.1-124.1-72.7-165.3L121.7 889.3C162.9 933.9 221.8 962 287 962h3.2L962 290.2V287z\" fill=\"#FFA145\" p-id=\"1152\"></path>\n      <path d=\"M962 287c0-54.2-19.4-104-51.6-143L144 910.4c39 32.2 88.8 51.6 143 51.6h46.6L962 333.6V287z\" fill=\"#FFA144\" p-id=\"1153\"></path>\n      <path d=\"M962 287c0-43.1-12.3-83.4-33.5-117.7L169.3 928.5C203.6 949.7 243.9 962 287 962h90.1L962 377.1V287z\" fill=\"#FFA143\" p-id=\"1154\"></path>\n      <path d=\"M287 962h133.5L962 420.5V287c0-31.6-6.6-61.8-18.5-89.2L197.8 943.4c27.4 12 57.6 18.6 89.2 18.6z\" fill=\"#FFA042\" p-id=\"1155\"></path>\n      <path d=\"M287 962h176.9L962 463.9V287c0-19.7-2.6-38.7-7.4-56.9L230.1 954.6c18.2 4.8 37.2 7.4 56.9 7.4z\" fill=\"#FFA041\" p-id=\"1156\"></path>\n      <path d=\"M287 962h220.4L962 507.4V287c0-6.7-0.3-13.4-0.9-20L267 961.1c6.6 0.6 13.3 0.9 20 0.9z\" fill=\"#FFA040\" p-id=\"1157\"></path>\n      <path d=\"M550.8 962L962 550.8V309.6L309.6 962z\" fill=\"#FFA03F\" p-id=\"1158\"></path>\n      <path d=\"M594.2 962L962 594.2V353L353 962z\" fill=\"#FF9F3E\" p-id=\"1159\"></path>\n      <path d=\"M637.7 962L962 637.7V396.4L396.4 962z\" fill=\"#FF9F3D\" p-id=\"1160\"></path>\n      <path d=\"M681.1 962L962 681.1V439.9L439.9 962z\" fill=\"#FF9F3C\" p-id=\"1161\"></path>\n      <path d=\"M724.5 962L962 724.5V483.3L483.3 962z\" fill=\"#FF9F3B\" p-id=\"1162\"></path>\n      <path d=\"M962 737V526.7L526.7 962H737c11.4 0 22.5-0.9 33.5-2.5l189-189c1.6-11 2.5-22.1 2.5-33.5z\" fill=\"#FF9F3A\" p-id=\"1163\"></path>\n      <path d=\"M962 737V570.2L570.2 962H737c34.3 0 66.9-7.8 96.1-21.7l107.2-107.2c13.9-29.2 21.7-61.8 21.7-96.1z\" fill=\"#FF9E39\" p-id=\"1164\"></path>\n      <path d=\"M962 613.6L613.6 962H737c123.8 0 225-101.3 225-225V613.6z\" fill=\"#FF9E38\" p-id=\"1165\"></path>\n      <path d=\"M962 657L657 962h80c123.8 0 225-101.3 225-225v-80z\" fill=\"#FF9E37\" p-id=\"1166\"></path>\n      <path d=\"M962 700.5L700.5 962H737c123.8 0 225-101.3 225-225v-36.5z\" fill=\"#FF9E36\" p-id=\"1167\"></path>\n      <path d=\"M961.9 744L744 961.9c118.2-3.7 214.2-99.7 217.9-217.9z\" fill=\"#FF9D35\" p-id=\"1168\"></path>\n      <path d=\"M954.4 795L795 954.4c77.4-20.8 138.6-82 159.4-159.4z\" fill=\"#FF9D34\" p-id=\"1169\"></path>\n      <path\n        d=\"M736.3 622.9L523.5 747.3c-5.6 3.3-12.4 3.3-18 0.1L287.8 622.6c-12.2-7-12-24.6 0.3-31.4l212.8-116.7c5.3-2.9 11.8-3 17.2-0.1l217.7 117c12.3 6.7 12.6 24.4 0.5 31.5z\"\n        fill=\"#FFD9C0\"\n        p-id=\"1170\"\n      ></path>\n      <path\n        d=\"M736.3 523.9L523.5 648.3c-5.6 3.3-12.4 3.3-18 0.1L287.8 523.6c-12.2-7-12-24.6 0.3-31.4l212.8-116.7c5.3-2.9 11.8-3 17.2-0.1l217.7 117c12.3 6.7 12.6 24.4 0.5 31.5z\"\n        fill=\"#FFE8D9\"\n        p-id=\"1171\"\n      ></path>\n      <path\n        d=\"M736.3 424.9L523.5 549.3c-5.6 3.3-12.4 3.3-18 0.1L287.8 424.6c-12.2-7-12-24.6 0.3-31.4l212.8-116.7c5.3-2.9 11.8-3 17.2-0.1l217.7 117c12.3 6.7 12.6 24.4 0.5 31.5z\"\n        fill=\"#FFF6F0\"\n        p-id=\"1172\"\n      ></path>\n    </svg>\n  );\n}\n\nexport default ColorfulDB;\n"
  },
  {
    "path": "components/icons/colorful-doc.tsx",
    "content": "function ColorfulDoc() {\n  return (\n    <svg viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-id=\"1300\" className=\"w-full h-full\">\n      <path\n        d=\"M197.99492187 62v900h-34.18066406C124.57285156 962 92.76171875 930.18886719 92.76171875 890.94746094V133.05253906C92.76171875 93.81113281 124.57285156 62 163.81425781 62h34.18066406z m662.19082032 0C899.42714844 62 931.23828125 93.81113281 931.23828125 133.05253906v757.89492188c0 39.24140625-31.81113281 71.05253906-71.05253906 71.05253906H276.92070312V62h583.26503907z\"\n        fill=\"#19A05F\"\n        p-id=\"1301\"\n      ></path>\n      <path\n        d=\"M577.0390625 62l0.33222656 220.3875 111.2475586-108.80771484L800.19951172 284.36328125V62zM425.40224609 508.18554688h377.05078125v50.94404296h-377.05078125V508.18554688z m0 101.88720703h377.05078125v50.94316406h-377.05078125v-50.94316406z\"\n        fill=\"#FFFFFF\"\n        p-id=\"1302\"\n      ></path>\n    </svg>\n  );\n}\n\nexport default ColorfulDoc;\n"
  },
  {
    "path": "components/icons/colorful-excel.tsx",
    "content": "function ColorfulExcel() {\n  return (\n    <svg viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-id=\"2006\" className=\"w-full h-full\">\n      <path\n        d=\"M701.95942066 37.1014489H250.80579673a142.46956521 142.46956521 0 0 0-142.46956522 142.46956523v664.85797174a142.46956521 142.46956521 0 0 0 142.46956522 142.46956523h522.38840654a142.46956521 142.46956521 0 0 0 142.46956522-142.46956523V274.55072501L701.95942066 37.1014489z\"\n        fill=\"#53D39C\"\n        p-id=\"2007\"\n      ></path>\n      <path\n        d=\"M444.2794663 392.18309566l69.64387283 117.72735109h2.70692174l69.97630108-117.70360654h82.4661337l-105.40373371 172.67311305 107.77822609 172.6968587h-83.98580869l-70.83111847-117.89356521h-2.70692174L443.09222066 737.57681196h-83.65338045l108.11065544-172.6968587-106.09233586-172.6968576h82.82230651z\"\n        fill=\"#25BF79\"\n        p-id=\"2008\"\n      ></path>\n      <path\n        d=\"M444.2794663 380.31063151l69.64387283 117.7273511h2.70692174l69.97630108-117.70360543h82.4661337l-105.40373371 172.67311305L671.44718803 725.70434783h-83.98580869l-70.83111847-117.89356522h-2.70692174L443.09222066 725.70434783h-83.65338045l108.11065544-172.6968576-106.09233586-172.69685872h82.82230651z\"\n        fill=\"#FFFFFF\"\n        p-id=\"2009\"\n      ></path>\n      <path\n        d=\"M701.95942066 37.1014489l160.27826087 178.08695653L915.66376849 274.55072501h-142.46956522a71.23478261 71.23478261 0 0 1-71.23478261-71.23478261V37.1014489z\"\n        fill=\"#25BF79\"\n        p-id=\"2010\"\n      ></path>\n    </svg>\n  );\n}\n\nexport default ColorfulExcel;\n"
  },
  {
    "path": "components/icons/colorful-plugin.tsx",
    "content": "function ColorfulPlugin() {\n  return (\n    <svg viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-id=\"2147\" className=\"w-full h-full\">\n      <path\n        d=\"M688.51536688 447.75428656l-2.39993719 1.25996719a200.75473031 200.75473031 0 0 1-7.19981156 38.03900156l-47.33875688 166.43563031 110.45710031-59.63843437-47.03876531-114.41699625a108.2971575 108.2971575 0 0 1-6.47982937-31.67916844z m194.87488406-200.99472375l-96.35747063-58.55846344-354.77068687 217.43429251a70.01816156 70.01816156 0 0 0-32.51914688 59.57843624v193.97490844l-158.99582625-98.09742562V362.67651969a69.4181775 69.4181775 0 0 1 33.95910844-60.41841375l358.67058469-206.99456625 13.55964469 7.97979L544.75914031 41.26495719a62.75835281 62.75835281 0 0 0-65.63827687 0L140.54975094 246.75956281a69.89816531 69.89816531 0 0 0-32.81913844 59.75843063v410.98921218c-0.11999719 24.47935781 12.2996775 47.1587625 32.81913844 59.81842969l338.5711125 205.49460563c20.21946937 12.23967844 45.35880937 12.23967844 65.63827687 0l338.69110875-205.49460563c20.33946563-12.41967375 32.87913656-35.09907844 32.8791375-59.81842968v-410.98921219a69.77816813 69.77816813 0 0 0-32.93913562-59.75843063z m-89.51764969 477.88745532l-31.01918625-75.65801438-150.53604844 81.35786438-30.47919937 108.95713968-95.81748563 51.7186425 151.61602032-485.20726312 103.79727562-56.09852719 148.73609531 322.97152219-96.29747156 51.95863594z m0-1e-8\"\n        fill=\"#0F6CF9\"\n        p-id=\"2148\"\n      ></path>\n    </svg>\n  );\n}\n\nexport default ColorfulPlugin;\n"
  },
  {
    "path": "components/icons/dark-svg.tsx",
    "content": "function DarkSvg() {\n  return (\n    <svg width=\"1em\" height=\"1em\" fill=\"currentColor\" viewBox=\"0 0 1024 1024\">\n      <path\n        d=\"M593.054 120.217C483.656 148.739 402.91 248.212 402.91 366.546c0 140.582 113.962 254.544 254.544 254.544 118.334 0 217.808-80.746 246.328-190.144C909.17 457.12 912 484.23 912 512c0 220.914-179.086 400-400 400S112 732.914 112 512s179.086-400 400-400c27.77 0 54.88 2.83 81.054 8.217z\"\n        p-id=\"5941\"\n      ></path>\n    </svg>\n  );\n}\n\nexport default DarkSvg;\n"
  },
  {
    "path": "components/icons/db-svg.tsx",
    "content": "function DBSvg() {\n  return (\n    <svg width=\"1em\" height=\"1em\" fill=\"currentColor\" viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-id=\"4870\">\n      <path\n        d=\"M511.875 128.125c-211.875 0-383.75 76.25-383.75 170.625 0 94.375 171.875 170.625 383.75 170.625 211.875 0 383.75-76.25 383.75-170.625C896.25 204.375 724.375 128.125 511.875 128.125M128.125 383.75l0 128.125c0 94.375 171.875 170.625 383.75 170.625 211.875 0 383.75-76.25 383.75-170.625l0-128.125c0 94.375-171.875 170.625-383.75 170.625C300 555 128.125 478.125 128.125 383.75M128.125 597.5l0 128.125c0 94.375 171.875 170.625 383.75 170.625 211.875 0 383.75-76.25 383.75-170.625l0-128.125c0 94.375-171.875 170.625-383.75 170.625C300 768.125 128.125 691.875 128.125 597.5z\"\n        p-id=\"4871\"\n      ></path>\n    </svg>\n  );\n}\n\nexport default DBSvg;\n"
  },
  {
    "path": "components/icons/done-icon.tsx",
    "content": "export default function DoneIcon() {\n  return (\n    <svg viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-id=\"3205\" width=\"1.5em\" height=\"1.5em\">\n      <path\n        d=\"M688 312v-48c0-4.4-3.6-8-8-8H296c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8h384c4.4 0 8-3.6 8-8zM296 400c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8h184c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8H296zM672 516c-119.3 0-216 96.7-216 216s96.7 216 216 216 216-96.7 216-216-96.7-216-216-216z m107.5 323.5C750.8 868.2 712.6 884 672 884s-78.8-15.8-107.5-44.5C535.8 810.8 520 772.6 520 732s15.8-78.8 44.5-107.5C593.2 595.8 631.4 580 672 580s78.8 15.8 107.5 44.5C808.2 653.2 824 691.4 824 732s-15.8 78.8-44.5 107.5z\"\n        p-id=\"3206\"\n        fill=\"#1afa29\"\n      ></path>\n      <path\n        d=\"M761 656h-44.3c-2.6 0-5 1.2-6.5 3.3l-63.5 87.8-23.1-31.9c-1.5-2.1-3.9-3.3-6.5-3.3H573c-6.5 0-10.3 7.4-6.5 12.7l73.8 102.1c3.2 4.4 9.7 4.4 12.9 0l114.2-158c3.9-5.3 0.1-12.7-6.4-12.7z\"\n        p-id=\"3207\"\n        fill=\"#1afa29\"\n      ></path>\n      <path\n        d=\"M440 852H208V148h560v344c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8V108c0-17.7-14.3-32-32-32H168c-17.7 0-32 14.3-32 32v784c0 17.7 14.3 32 32 32h272c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8z\"\n        p-id=\"3208\"\n        fill=\"#1afa29\"\n      ></path>\n    </svg>\n  );\n}\n"
  },
  {
    "path": "components/icons/file-done.tsx",
    "content": "import React from 'react';\n\nexport default function FileDone() {\n  return (\n    <svg className=\"mr-1\" viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-id=\"4602\" width=\"1.5em\" height=\"1.5em\">\n      <path\n        d=\"M688 312v-48c0-4.4-3.6-8-8-8H296c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8h384c4.4 0 8-3.6 8-8zM296 400c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8h184c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8H296zM672 516c-119.3 0-216 96.7-216 216s96.7 216 216 216 216-96.7 216-216-96.7-216-216-216z m107.5 323.5C750.8 868.2 712.6 884 672 884s-78.8-15.8-107.5-44.5C535.8 810.8 520 772.6 520 732s15.8-78.8 44.5-107.5C593.2 595.8 631.4 580 672 580s78.8 15.8 107.5 44.5C808.2 653.2 824 691.4 824 732s-15.8 78.8-44.5 107.5z\"\n        p-id=\"4603\"\n        fill=\"#87d068\"\n      ></path>\n      <path\n        d=\"M761 656h-44.3c-2.6 0-5 1.2-6.5 3.3l-63.5 87.8-23.1-31.9c-1.5-2.1-3.9-3.3-6.5-3.3H573c-6.5 0-10.3 7.4-6.5 12.7l73.8 102.1c3.2 4.4 9.7 4.4 12.9 0l114.2-158c3.9-5.3 0.1-12.7-6.4-12.7z\"\n        p-id=\"4604\"\n        fill=\"#87d068\"\n      ></path>\n      <path\n        d=\"M440 852H208V148h560v344c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8V108c0-17.7-14.3-32-32-32H168c-17.7 0-32 14.3-32 32v784c0 17.7 14.3 32 32 32h272c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8z\"\n        p-id=\"4605\"\n        fill=\"#87d068\"\n      ></path>\n    </svg>\n  );\n}\n"
  },
  {
    "path": "components/icons/file-error.tsx",
    "content": "import React from 'react';\n\nexport default function FileError() {\n  return (\n    <svg className=\"mr-1\" viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-id=\"6058\" width=\"1.5em\" height=\"1.5em\">\n      <path\n        d=\"M688 312c0 4.4-3.6 8-8 8H296c-4.4 0-8-3.6-8-8v-48c0-4.4 3.6-8 8-8h384c4.4 0 8 3.6 8 8v48z m-392 88h184c4.4 0 8 3.6 8 8v48c0 4.4-3.6 8-8 8H296c-4.4 0-8-3.6-8-8v-48c0-4.4 3.6-8 8-8z m376 116c119.3 0 216 96.7 216 216s-96.7 216-216 216-216-96.7-216-216 96.7-216 216-216z m107.5 323.5C808.2 810.8 824 772.6 824 732s-15.8-78.8-44.5-107.5S712.6 580 672 580s-78.8 15.8-107.5 44.5S520 691.4 520 732s15.8 78.8 44.5 107.5S631.4 884 672 884s78.8-15.8 107.5-44.5zM440 852c4.4 0 8 3.6 8 8v56c0 4.4-3.6 8-8 8H168c-17.7 0-32-14.3-32-32V108c0-17.7 14.3-32 32-32h640c17.7 0 32 14.3 32 32v384c0 4.4-3.6 8-8 8h-56c-4.4 0-8-3.6-8-8V148H208v704h232z m232-76.06l-20.56 28.43c-1.5 2.1-3.9 3.3-6.5 3.3h-44.3c-6.5 0-10.3-7.4-6.4-12.7l45.75-63.3-45.75-63.3c-3.9-5.3-0.1-12.7 6.4-12.7h44.3c2.6 0 5 1.2 6.5 3.3L672 687.4l20.56-28.43c1.5-2.1 3.9-3.3 6.5-3.3h44.3c6.5 0 10.3 7.4 6.4 12.7l-45.75 63.3 45.75 63.3c3.9 5.3 0.1 12.7-6.4 12.7h-44.3c-2.6 0-5-1.2-6.5-3.3L672 775.94z\"\n        fill=\"#d81e06\"\n        p-id=\"6059\"\n      ></path>\n    </svg>\n  );\n}\n"
  },
  {
    "path": "components/icons/file-sync.tsx",
    "content": "import React from 'react';\n\nexport default function FileSync() {\n  return (\n    <svg className=\"mr-1\" viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-id=\"4838\" width=\"1.5em\" height=\"1.5em\">\n      <path\n        d=\"M296 256c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8h384c4.4 0 8-3.6 8-8v-48c0-4.4-3.6-8-8-8H296zM488 456v-48c0-4.4-3.6-8-8-8H296c-4.4 0-8 3.6-8 8v48c0 4.4 3.6 8 8 8h184c4.4 0 8-3.6 8-8z\"\n        p-id=\"4839\"\n        fill=\"#2db7f5\"\n      ></path>\n      <path\n        d=\"M440 852H208V148h560v344c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8V108c0-17.7-14.3-32-32-32H168c-17.7 0-32 14.3-32 32v784c0 17.7 14.3 32 32 32h272c4.4 0 8-3.6 8-8v-56c0-4.4-3.6-8-8-8z\"\n        p-id=\"4840\"\n        fill=\"#2db7f5\"\n      ></path>\n      <path\n        d=\"M544.1 736.4c1.8-34.5 16.2-66.8 40.8-91.4 26.2-26.2 62-41 99.1-41 37.4 0 72.6 14.6 99.1 41 3.2 3.2 6.3 6.6 9.2 10.1L769.2 673c-5.3 4.1-3.5 12.5 3 14.1l93.3 22.5c5 1.2 9.8-2.6 9.9-7.7l0.6-95.4c0-6.7-7.6-10.5-12.9-6.4l-20.3 15.8C805.4 569.6 748.1 540 684 540c-109.9 0-199.6 86.9-204 195.7-0.2 4.5 3.5 8.3 8 8.3h48.1c4.3 0 7.8-3.3 8-7.6zM880 744h-48.1c-4.3 0-7.8 3.3-8 7.6-1.8 34.5-16.2 66.8-40.8 91.4-26.2 26.2-62 41-99.1 41-37.4 0-72.6-14.6-99.1-41-3.2-3.2-6.3-6.6-9.2-10.1l23.1-17.9c5.3-4.1 3.5-12.5-3-14.1l-93.3-22.5c-5-1.2-9.8 2.6-9.9 7.7l-0.6 95.4c0 6.7 7.6 10.5 12.9 6.4l20.3-15.8C562.6 918.4 619.9 948 684 948c109.9 0 199.6-86.9 204-195.7 0.2-4.5-3.5-8.3-8-8.3z\"\n        p-id=\"4841\"\n        fill=\"#2db7f5\"\n      ></path>\n    </svg>\n  );\n}\n"
  },
  {
    "path": "components/icons/index.tsx",
    "content": "import ColorfulDashboard from './colorful-dashboard';\nimport ColorfulData from './colorful-data';\nimport ColorfulDB from './colorful-db';\nimport ColorfulDoc from './colorful-doc';\nimport ColorfulExcel from './colorful-excel';\nimport ColorfulPlugin from './colorful-plugin';\nimport ColorfulChat from './colorful-chat';\nimport DarkSvg from './dark-svg';\nimport DBSvg from './db-svg';\nimport ModelSvg from './model-svg';\nimport StarsSvg from './stars-svg';\nimport SunnySvg from './sunny-svg';\nimport Knowledge from './knowledge';\nimport FileDone from './file-done';\nimport FileSync from './file-sync';\nimport PendingIcon from './pending-icon';\nimport SyncIcon from './sync-icon';\nimport DoneIcon from './done-icon';\nimport FileError from './file-error';\n\nexport {\n  ColorfulDashboard,\n  ColorfulData,\n  ColorfulDB,\n  ColorfulDoc,\n  ColorfulExcel,\n  ColorfulPlugin,\n  ColorfulChat,\n  DarkSvg,\n  DBSvg,\n  ModelSvg,\n  StarsSvg,\n  SunnySvg,\n  Knowledge,\n  FileDone,\n  FileSync,\n  PendingIcon,\n  SyncIcon,\n  DoneIcon,\n  FileError,\n};\n"
  },
  {
    "path": "components/icons/knowledge.tsx",
    "content": "import React from 'react';\n\nexport default function knowledge() {\n  return (\n    <svg viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-id=\"4164\" width=\"1em\" height=\"1em\">\n      <path\n        d=\"M544.32 648a64 64 0 1 1-64-0.64V172.8a128 128 0 0 1-23.936-9.024l-44.928-22.4A128 128 0 0 0 354.496 128H192a64 64 0 0 0-64 64v575.68a64 64 0 0 0 64 64h161.792a128 128 0 0 1 58.176 14.016l71.04 36.224a64 64 0 0 0 58.176 0l71.168-36.288a128 128 0 0 1 58.112-13.952h161.984a64 64 0 0 0 64-64V192a64 64 0 0 0-64-64h-158.208a128 128 0 0 0-55.04 12.416l-50.88 24.256a128 128 0 0 1-24 8.576v474.752zM674.24 64h158.208a128 128 0 0 1 128 128V767.68a128 128 0 0 1-128 128h-161.92a64 64 0 0 0-29.12 7.04l-71.168 36.224a128 128 0 0 1-116.288 0l-71.04-36.224a64 64 0 0 0-29.12-7.04H192a128 128 0 0 1-128-128V192a128 128 0 0 1 128-128h162.496a192 192 0 0 1 85.44 20.032l44.928 22.4a64 64 0 0 0 55.936 0.448l50.944-24.256A192 192 0 0 1 674.24 64z\"\n        fill=\"#000000\"\n        p-id=\"4165\"\n      ></path>\n    </svg>\n  );\n}\n"
  },
  {
    "path": "components/icons/model-svg.tsx",
    "content": "function ModelSvg() {\n  return (\n    <svg width=\"1em\" height=\"1em\" fill=\"currentColor\" viewBox=\"0 0 1024 1024\" version=\"1.1\">\n      <path d=\"M513.89 950.72c-5.5 0-11-1.4-15.99-4.2L143.84 743c-9.85-5.73-15.99-16.17-15.99-27.64V308.58c0-11.33 6.14-21.91 15.99-27.64L497.9 77.43c9.85-5.73 22.14-5.73 31.99 0l354.06 203.52c9.85 5.73 15.99 16.17 15.99 27.64V715.5c0 11.33-6.14 21.91-15.99 27.64L529.89 946.52c-4.99 2.8-10.49 4.2-16 4.2zM191.83 697.15L513.89 882.2l322.07-185.05V326.92L513.89 141.87 191.83 326.92v370.23z m322.06-153.34c-5.37 0-10.88-1.4-15.99-4.33L244.29 393.91c-15.35-8.79-20.6-28.27-11.77-43.56 8.83-15.28 28.41-20.5 43.76-11.72l253.61 145.7c15.35 8.79 20.6 28.27 11.77 43.56-6.01 10.32-16.76 15.92-27.77 15.92z m0 291.52c-17.66 0-31.99-14.26-31.99-31.84V530.44L244.55 393.91s-0.13 0-0.13-0.13l-100.45-57.69c-15.35-8.79-20.6-28.27-11.77-43.56s28.41-20.5 43.76-11.72l354.06 203.52c9.85 5.73 15.99 16.17 15.99 27.64v291.39c-0.13 17.71-14.46 31.97-32.12 31.97z m0 115.39c-17.66 0-31.99-14.26-31.99-31.84V511.97c0-17.58 14.33-31.84 31.99-31.84s31.99 14.26 31.99 31.84v406.91c0 17.7-14.33 31.84-31.99 31.84z m0-406.91c-11 0-21.75-5.73-27.77-15.92-8.83-15.28-3.58-34.64 11.77-43.56l354.06-203.52c15.35-8.79 34.8-3.57 43.76 11.72 8.83 15.28 3.58 34.64-11.77 43.56L529.89 539.61c-4.99 2.93-10.49 4.2-16 4.2z\"></path>\n    </svg>\n  );\n}\n\nexport default ModelSvg;\n"
  },
  {
    "path": "components/icons/pending-icon.tsx",
    "content": "export default function PendingIcon() {\n  return (\n    <svg viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-id=\"4260\" width=\"1.5em\" height=\"1.5em\">\n      <path d=\"M114.5856 951.04h298.24v-71.68H186.2656v-747.52h593.92v271.36h71.68v-343.04h-737.28v890.88z\" fill=\"#747690\" p-id=\"4261\"></path>\n      <path\n        d=\"M662.4256 311.04h-358.4v-71.68h358.4v71.68zM508.8256 490.24h-204.8v-71.68h204.8v71.68zM668.8256 554.24a168.96 168.96 0 1 0 0 337.92 168.96 168.96 0 0 0 0-337.92z m-240.64 168.96a240.64 240.64 0 1 1 481.28 0 240.64 240.64 0 0 1-481.28 0z\"\n        fill=\"#747690\"\n        p-id=\"4262\"\n      ></path>\n      <path d=\"M629.76 588.8h71.68v131.4304l82.5856 41.3184-32.0512 64.1024-122.2144-61.0816V588.8z\" fill=\"#747690\" p-id=\"4263\"></path>\n    </svg>\n  );\n}\n"
  },
  {
    "path": "components/icons/stars-svg.tsx",
    "content": "function StarsSvg() {\n  return (\n    <svg width=\"1em\" height=\"1em\" fill=\"currentColor\" viewBox=\"0 0 1024 1024\" version=\"1.1\">\n      <path\n        d=\"M602.24 246.72a17.28 17.28 0 0 0-11.84-16.32l-42.88-14.4A90.56 90.56 0 0 1 490.24 160l-14.4-42.88a17.28 17.28 0 0 0-32 0L428.8 160a90.56 90.56 0 0 1-57.28 57.28l-42.88 14.4a17.28 17.28 0 0 0 0 32l42.88 14.4a90.56 90.56 0 0 1 57.28 57.28l14.4 42.88a17.28 17.28 0 0 0 32 0l14.4-42.88a90.56 90.56 0 0 1 57.28-57.28l42.88-14.4a17.28 17.28 0 0 0 12.48-16.96z m301.12 221.76l-48.32-16a101.44 101.44 0 0 1-64-64l-16-48.32a19.2 19.2 0 0 0-36.8 0l-16 48.32a101.44 101.44 0 0 1-64 64l-48.32 16a19.2 19.2 0 0 0 0 36.8l48.32 16a101.44 101.44 0 0 1 64 64l16 48.32a19.2 19.2 0 0 0 36.8 0l16-48.32a101.44 101.44 0 0 1 64-64l48.32-16a19.2 19.2 0 0 0 0-36.8z m-376.64 195.52l-64-20.8a131.84 131.84 0 0 1-83.52-83.52l-20.8-64a25.28 25.28 0 0 0-47.68 0l-20.8 64a131.84 131.84 0 0 1-82.24 83.52l-64 20.8a25.28 25.28 0 0 0 0 47.68l64 20.8a131.84 131.84 0 0 1 83.52 83.84l20.8 64a25.28 25.28 0 0 0 47.68 0l20.8-64a131.84 131.84 0 0 1 83.52-83.52l64-20.8a25.28 25.28 0 0 0 0-47.68z\"\n        p-id=\"3992\"\n      ></path>\n    </svg>\n  );\n}\n\nexport default StarsSvg;\n"
  },
  {
    "path": "components/icons/sunny-svg.tsx",
    "content": "function SunnySvg() {\n  return (\n    <svg width=\"1em\" height=\"1em\" fill=\"currentColor\" viewBox=\"0 0 1024 1024\">\n      <path\n        d=\"M554.6 64h-85.4v128h85.4V64z m258.2 87.4L736 228.2l59.8 59.8 76.8-76.8-59.8-59.8z m-601.6 0l-59.8 59.8 76.8 76.8 59.8-59.8-76.8-76.8zM512 256c-140.8 0-256 115.2-256 256s115.2 256 256 256 256-115.2 256-256-115.2-256-256-256z m448 213.4h-128v85.4h128v-85.4z m-768 0H64v85.4h128v-85.4zM795.8 736L736 795.8l76.8 76.8 59.8-59.8-76.8-76.8z m-567.6 0l-76.8 76.8 59.8 59.8 76.8-76.8-59.8-59.8z m326.4 96h-85.4v128h85.4v-128z\"\n        p-id=\"7802\"\n      ></path>\n    </svg>\n  );\n}\n\nexport default SunnySvg;\n"
  },
  {
    "path": "components/icons/sync-icon.tsx",
    "content": "export default function SyncIcon() {\n  return (\n    <svg viewBox=\"0 0 1024 1024\" version=\"1.1\" xmlns=\"http://www.w3.org/2000/svg\" p-id=\"9211\" width=\"1.5em\" height=\"1.5em\">\n      <path\n        d=\"M151.5 586.2c-5-24.2-7.5-49.2-7.5-74.2s2.5-50 7.5-74.2c4.8-23.6 12-46.8 21.4-69 9.2-21.8 20.6-42.8 33.9-62.5 13.2-19.5 28.3-37.8 45-54.5s35-31.8 54.5-45c19.7-13.3 40.7-24.7 62.5-33.9 22.2-9.4 45.4-16.6 69-21.4 48.5-9.9 99.9-9.9 148.4 0 23.6 4.8 46.8 12 69 21.4 21.8 9.2 42.8 20.6 62.5 33.9 19.5 13.2 37.8 28.3 54.5 45 1.4 1.4 2.8 2.8 4.1 4.2H688c-17.7 0-32 14.3-32 32s14.3 32 32 32h160c17.7 0 32-14.3 32-32V128c0-17.7-14.3-32-32-32s-32 14.3-32 32v77.1c-19.2-19-40.1-36.2-62.4-51.3-23.1-15.6-47.8-29-73.4-39.8-26.1-11-53.4-19.5-81.1-25.2-56.9-11.6-117.1-11.6-174.1 0-27.8 5.7-55.1 14.2-81.1 25.2-25.6 10.8-50.3 24.2-73.4 39.8-22.9 15.4-44.4 33.2-63.9 52.7s-37.3 41-52.7 63.9c-15.6 23.1-29 47.8-39.8 73.4-11 26.1-19.5 53.4-25.2 81.1C83 453.4 80 482.7 80 512s3 58.6 8.8 87c3.1 15.2 16.4 25.6 31.3 25.6 2.1 0 4.3-0.2 6.4-0.7 17.4-3.5 28.5-20.4 25-37.7zM935.2 425c-3.5-17.3-20.5-28.5-37.8-24.9-17.3 3.5-28.5 20.5-24.9 37.8 5 24.2 7.5 49.2 7.5 74.2s-2.5 50-7.5 74.2c-4.8 23.6-12 46.8-21.4 69-9.2 21.8-20.6 42.8-33.9 62.5-13.2 19.5-28.3 37.8-45 54.5s-35 31.8-54.5 45C698 830.6 677 842 655.2 851.2c-22.2 9.4-45.4 16.6-69 21.4-48.5 9.9-99.9 9.9-148.4 0-23.6-4.8-46.8-12-69-21.4-21.8-9.2-42.8-20.6-62.5-33.9-19.5-13.2-37.8-28.3-54.5-45-1.4-1.4-2.8-2.8-4.1-4.2H336c17.7 0 32-14.3 32-32s-14.3-32-32-32H176c-17.7 0-32 14.3-32 32v160c0 17.7 14.3 32 32 32s32-14.3 32-32V819c19.2 19 40.1 36.2 62.4 51.3 23.1 15.6 47.8 29 73.4 39.8 26.1 11 53.4 19.5 81.1 25.2 28.5 5.8 57.7 8.8 87 8.8s58.6-3 87-8.8c27.8-5.7 55-14.2 81.1-25.2 25.6-10.8 50.3-24.2 73.4-39.8 22.9-15.5 44.4-33.2 63.9-52.7s37.3-41 52.7-63.9c15.6-23.1 29-47.8 39.8-73.4 11-26.1 19.5-53.4 25.2-81.1 5.8-28.5 8.8-57.7 8.8-87 0.2-29.5-2.8-58.8-8.6-87.2z\"\n        fill=\"#1875F0\"\n        p-id=\"9212\"\n      ></path>\n    </svg>\n  );\n}\n"
  },
  {
    "path": "components/knowledge/arguments-modal.tsx",
    "content": "import React, { useEffect, useState } from 'react';\nimport { Modal, Tabs, Button, Input, Form, Col, Row, Spin } from 'antd';\nimport { useTranslation } from 'react-i18next';\n\nimport { AlertFilled, BookOutlined, FileSearchOutlined } from '@ant-design/icons';\nimport { apiInterceptors, getArguments, saveArguments } from '@/client/api';\nimport { IArguments, ISpace } from '@/types/knowledge';\n\nconst { TextArea } = Input;\n\ninterface IProps {\n  space: ISpace;\n  argumentsShow: boolean;\n  setArgumentsShow: (argumentsShow: boolean) => void;\n}\n\nexport default function ArgumentsModal({ space, argumentsShow, setArgumentsShow }: IProps) {\n  const { t } = useTranslation();\n  const [newSpaceArguments, setNewSpaceArguments] = useState<IArguments | null>();\n  const [spinning, setSpinning] = useState<boolean>(false);\n\n  const fetchArguments = async () => {\n    const [_, data] = await apiInterceptors(getArguments(space.name));\n    setNewSpaceArguments(data);\n  };\n\n  useEffect(() => {\n    fetchArguments();\n  }, [space.name]);\n\n  const renderEmbeddingForm = () => {\n    return (\n      <Row gutter={24}>\n        <Col span={12} offset={0}>\n          <Form.Item<IArguments> tooltip={t(`the_top_k_vectors`)} rules={[{ required: true }]} label={t('topk')} name={['embedding', 'topk']}>\n            <Input className=\"mb-5 h-12\" />\n          </Form.Item>\n        </Col>\n        <Col span={12}>\n          <Form.Item<IArguments>\n            tooltip={t(`Set_a_threshold_score`)}\n            rules={[{ required: true }]}\n            label={t('recall_score')}\n            name={['embedding', 'recall_score']}\n          >\n            <Input className=\"mb-5  h-12\" placeholder={t('Please_input_the_owner')} />\n          </Form.Item>\n        </Col>\n        <Col span={12}>\n          <Form.Item<IArguments> tooltip={t(`recall_type`)} rules={[{ required: true }]} label={t('recall_type')} name={['embedding', 'recall_type']}>\n            <Input className=\"mb-5  h-12\" />\n          </Form.Item>\n        </Col>\n        <Col span={12}>\n          <Form.Item<IArguments> tooltip={t(`A_model_used`)} rules={[{ required: true }]} label={t('model')} name={['embedding', 'model']}>\n            <Input className=\"mb-5  h-12\" />\n          </Form.Item>\n        </Col>\n        <Col span={12}>\n          <Form.Item<IArguments>\n            tooltip={t(`The_size_of_the_data_chunks`)}\n            rules={[{ required: true }]}\n            label={t('chunk_size')}\n            name={['embedding', 'chunk_size']}\n          >\n            <Input className=\"mb-5  h-12\" />\n          </Form.Item>\n        </Col>\n        <Col span={12}>\n          <Form.Item<IArguments>\n            tooltip={t(`The_amount_of_overlap`)}\n            rules={[{ required: true }]}\n            label={t('chunk_overlap')}\n            name={['embedding', 'chunk_overlap']}\n          >\n            <Input className=\"mb-5  h-12\" placeholder={t('Please_input_the_description')} />\n          </Form.Item>\n        </Col>\n      </Row>\n    );\n  };\n\n  const renderPromptForm = () => {\n    return (\n      <>\n        <Form.Item<IArguments> tooltip={t(`A_contextual_parameter`)} label={t('scene')} name={['prompt', 'scene']}>\n          <TextArea rows={4} className=\"mb-2\" />\n        </Form.Item>\n        <Form.Item<IArguments> tooltip={t(`structure_or_format`)} label={t('template')} name={['prompt', 'template']}>\n          <TextArea rows={7} className=\"mb-2\" />\n        </Form.Item>\n        <Form.Item<IArguments> tooltip={t(`The_maximum_number_of_tokens`)} label={t('max_token')} name={['prompt', 'max_token']}>\n          <Input className=\"mb-2\" />\n        </Form.Item>\n      </>\n    );\n  };\n\n  const renderSummary = () => {\n    return (\n      <>\n        <Form.Item<IArguments> rules={[{ required: true }]} label={t('max_iteration')} name={['summary', 'max_iteration']}>\n          <Input className=\"mb-2\" />\n        </Form.Item>\n        <Form.Item<IArguments> rules={[{ required: true }]} label={t('concurrency_limit')} name={['summary', 'concurrency_limit']}>\n          <Input className=\"mb-2\" />\n        </Form.Item>\n      </>\n    );\n  };\n\n  const items = [\n    {\n      key: 'Embedding',\n      label: (\n        <div>\n          <FileSearchOutlined />\n          {t('Embedding')}\n        </div>\n      ),\n      children: renderEmbeddingForm(),\n    },\n    {\n      key: 'Prompt',\n      label: (\n        <div>\n          <AlertFilled />\n          {t('Prompt')}\n        </div>\n      ),\n      children: renderPromptForm(),\n    },\n    {\n      key: 'Summary',\n      label: (\n        <div>\n          <BookOutlined />\n          {t('Summary')}\n        </div>\n      ),\n      children: renderSummary(),\n    },\n  ];\n\n  const handleSubmit = async (fieldsValue: IArguments) => {\n    setSpinning(true);\n    const [_, data, res] = await apiInterceptors(\n      saveArguments(space.name, {\n        argument: JSON.stringify(fieldsValue),\n      }),\n    );\n    setSpinning(false);\n    res?.success && setArgumentsShow(false);\n  };\n\n  return (\n    <Modal\n      width={850}\n      open={argumentsShow}\n      onCancel={() => {\n        setArgumentsShow(false);\n      }}\n      footer={null}\n    >\n      <Spin spinning={spinning}>\n        <Form\n          size=\"large\"\n          className=\"mt-4\"\n          layout=\"vertical\"\n          name=\"basic\"\n          initialValues={{ ...newSpaceArguments }}\n          autoComplete=\"off\"\n          onFinish={handleSubmit}\n        >\n          <Tabs items={items}></Tabs>\n          <div className=\"mt-3 mb-3\">\n            <Button htmlType=\"submit\" type=\"primary\" className=\"mr-6\">\n              {t('Submit')}\n            </Button>\n            <Button\n              onClick={() => {\n                setArgumentsShow(false);\n              }}\n            >\n              {t('close')}\n            </Button>\n          </div>\n        </Form>\n      </Spin>\n    </Modal>\n  );\n}\n"
  },
  {
    "path": "components/knowledge/doc-icon.tsx",
    "content": "import { FileTextFilled, FileWordTwoTone, IeCircleFilled } from '@ant-design/icons';\n\nexport default function DocIcon({ type }: { type: string }) {\n  if (type === 'TEXT') {\n    return <FileTextFilled className=\"text-[#2AA3FF] mr-2 !text-lg\" />;\n  } else if (type === 'DOCUMENT') {\n    return <FileWordTwoTone className=\"text-[#2AA3FF] mr-2 !text-lg\" />;\n  } else {\n    return <IeCircleFilled className=\"text-[#2AA3FF] mr-2 !text-lg\" />;\n  }\n}\n"
  },
  {
    "path": "components/knowledge/doc-panel.tsx",
    "content": "import React, { useEffect, useMemo, useRef, useState } from 'react';\nimport { Button, Card, Space, Divider, Empty, Spin, Tag, Tooltip, Modal } from 'antd';\nimport { DeleteFilled, InteractionFilled, PlusOutlined, ToolFilled, EyeFilled, WarningOutlined } from '@ant-design/icons';\nimport { apiInterceptors, delDocument, getDocumentList, syncDocument } from '@/client/api';\nimport { IDocument, ISpace } from '@/types/knowledge';\nimport moment from 'moment';\nimport ArgumentsModal from './arguments-modal';\nimport { useTranslation } from 'react-i18next';\nimport { useRouter } from 'next/router';\nimport DocIcon from './doc-icon';\n\ninterface IProps {\n  space: ISpace;\n  onAddDoc: (spaceName: string) => void;\n  onDeleteDoc: () => void;\n}\n\nconst { confirm } = Modal;\n\nexport default function DocPanel(props: IProps) {\n  const { space } = props;\n  const { t } = useTranslation();\n  const router = useRouter();\n  const page_size = 18;\n\n  const [isLoading, setIsLoading] = useState<boolean>(false);\n  const [documents, setDocuments] = useState<any>([]);\n  const [argumentsShow, setArgumentsShow] = useState<boolean>(false);\n  const [total, setTotal] = useState<number>(0);\n  const currentPageRef = useRef(1);\n\n  const hasMore = useMemo(() => {\n    return documents.length < total;\n  }, [documents.length, total]);\n\n  const showDeleteConfirm = (row: any) => {\n    confirm({\n      title: t('Tips'),\n      icon: <WarningOutlined />,\n      content: `${t('Del_Document_Tips')}?`,\n      okText: 'Yes',\n      okType: 'danger',\n      cancelText: 'No',\n      async onOk() {\n        await handleDelete(row);\n      },\n    });\n  };\n\n  async function fetchDocuments() {\n    setIsLoading(true);\n    const [_, data] = await apiInterceptors(\n      getDocumentList(space.name, {\n        page: currentPageRef.current,\n        page_size,\n      }),\n    );\n    setDocuments(data?.data);\n    setTotal(data?.total || 0);\n    setIsLoading(false);\n  }\n\n  const loadMoreDocuments = async () => {\n    if (!hasMore) {\n      return;\n    }\n    setIsLoading(true);\n    currentPageRef.current += 1;\n    const [_, data] = await apiInterceptors(\n      getDocumentList(space.name, {\n        page: currentPageRef.current,\n        page_size,\n      }),\n    );\n    setDocuments([...documents, ...data!.data]);\n    setIsLoading(false);\n  };\n\n  const handleSync = async (spaceName: string, id: number) => {\n    await apiInterceptors(syncDocument(spaceName, { doc_ids: [id] }));\n  };\n\n  const handleDelete = async (row: any) => {\n    await apiInterceptors(delDocument(space.name, { doc_name: row.doc_name }));\n    fetchDocuments();\n    props.onDeleteDoc();\n  };\n\n  const handleAddDocument = () => {\n    props.onAddDoc(space.name);\n  };\n\n  const handleArguments = () => {\n    setArgumentsShow(true);\n  };\n\n  const renderResultTag = (status: string, result: string) => {\n    let color;\n    switch (status) {\n      case 'TODO':\n        color = 'gold';\n        break;\n      case 'RUNNING':\n        color = '#2db7f5';\n        break;\n      case 'FINISHED':\n        color = 'cyan';\n        break;\n      case 'FAILED':\n        color = 'red';\n        break;\n      default:\n        color = 'red';\n        break;\n    }\n    return (\n      <Tooltip title={result}>\n        <Tag color={color}>{status}</Tag>\n      </Tooltip>\n    );\n  };\n\n  useEffect(() => {\n    fetchDocuments();\n  }, [space]);\n\n  const renderDocumentCard = () => {\n    if (documents?.length > 0) {\n      return (\n        <div className=\"max-h-96 overflow-auto max-w-3/4\">\n          <div className=\"mt-3 grid grid-cols-1 gap-x-6 gap-y-5 sm:grid-cols-2 lg:grid-cols-3 xl:gap-x-5\">\n            {documents.map((document: IDocument) => {\n              return (\n                <Card\n                  key={document.id}\n                  className=\" dark:bg-[#484848] relative  shrink-0 grow-0 cursor-pointer rounded-[10px] border border-gray-200 border-solid w-full\"\n                  title={\n                    <Tooltip title={document.doc_name}>\n                      <div className=\"truncate \">\n                        <DocIcon type={document.doc_type} />\n                        <span>{document.doc_name}</span>\n                      </div>\n                    </Tooltip>\n                  }\n                  extra={\n                    <div className=\"mx-3\">\n                      <Tooltip title={'detail'}>\n                        <EyeFilled\n                          className=\"mr-2 !text-lg\"\n                          style={{ color: '#1b7eff', fontSize: '20px' }}\n                          onClick={() => {\n                            router.push(`/knowledge/chunk/?spaceName=${space.name}&id=${document.id}`);\n                          }}\n                        />\n                      </Tooltip>\n                      <Tooltip title={'Sync'}>\n                        <InteractionFilled\n                          className=\"mr-2 !text-lg\"\n                          style={{ color: '#1b7eff', fontSize: '20px' }}\n                          onClick={() => {\n                            handleSync(space.name, document.id);\n                          }}\n                        />\n                      </Tooltip>\n                      <Tooltip title={'Delete'}>\n                        <DeleteFilled\n                          className=\"text-[#ff1b2e] !text-lg\"\n                          onClick={() => {\n                            showDeleteConfirm(document);\n                          }}\n                        />\n                      </Tooltip>\n                    </div>\n                  }\n                >\n                  <p className=\"mt-2 font-semibold \">{t('Size')}:</p>\n                  <p>{document.chunk_size} chunks</p>\n                  <p className=\"mt-2 font-semibold \">{t('Last_Sync')}:</p>\n                  <p>{moment(document.last_sync).format('YYYY-MM-DD HH:MM:SS')}</p>\n                  <p className=\"mt-2 mb-2\">{renderResultTag(document.status, document.result)}</p>\n                </Card>\n              );\n            })}\n          </div>\n          {hasMore && (\n            <Divider>\n              <span className=\"cursor-pointer\" onClick={loadMoreDocuments}>\n                {t('Load_more')}\n              </span>\n            </Divider>\n          )}\n        </div>\n      );\n    }\n    return (\n      <Empty image={Empty.PRESENTED_IMAGE_DEFAULT}>\n        <Button type=\"primary\" className=\"flex items-center mx-auto\" icon={<PlusOutlined />} onClick={handleAddDocument}>\n          Create Now\n        </Button>\n      </Empty>\n    );\n  };\n\n  return (\n    <div className=\"collapse-container pt-2 px-4\">\n      <Space>\n        <Button size=\"middle\" type=\"primary\" className=\"flex items-center\" icon={<PlusOutlined />} onClick={handleAddDocument}>\n          {t('Add_Datasource')}\n        </Button>\n        <Button size=\"middle\" className=\"flex items-center mx-2\" icon={<ToolFilled />} onClick={handleArguments}>\n          Arguments\n        </Button>\n      </Space>\n      <Divider />\n      <Spin spinning={isLoading}>{renderDocumentCard()}</Spin>\n      <ArgumentsModal space={space} argumentsShow={argumentsShow} setArgumentsShow={setArgumentsShow} />\n    </div>\n  );\n}\n"
  },
  {
    "path": "components/knowledge/doc-type-form.tsx",
    "content": "import { StepChangeParams } from '@/types/knowledge';\nimport { Card } from 'antd';\nimport { useTranslation } from 'react-i18next';\nimport DocIcon from './doc-icon';\n\ntype IProps = {\n  handleStepChange: (params: StepChangeParams) => void;\n};\n\nexport default function DocTypeForm(props: IProps) {\n  const { t } = useTranslation();\n  const { handleStepChange } = props;\n  const docTypeList = [\n    {\n      type: 'TEXT',\n      title: t('Text'),\n      subTitle: t('Fill your raw text'),\n      iconType: 'TEXT',\n    },\n    {\n      type: 'URL',\n      title: t('URL'),\n      subTitle: t('Fetch_the_content_of_a_URL'),\n      iconType: 'WEBPAGE',\n    },\n    {\n      type: 'DOCUMENT',\n      title: t('Document'),\n      subTitle: t('Upload_a_document'),\n      iconType: 'DOCUMENT',\n    },\n  ];\n\n  return (\n    <>\n      {docTypeList.map((type, index) => (\n        <Card\n          key={index}\n          className=\"mt-4 mb-4 cursor-pointer\"\n          onClick={() => {\n            handleStepChange({ label: 'forward', docType: type.type });\n          }}\n        >\n          <div className=\"font-semibold\">\n            <DocIcon type={type.iconType} />\n            {type.title}\n          </div>\n          <div>{type.subTitle}</div>\n        </Card>\n      ))}\n    </>\n  );\n}\n"
  },
  {
    "path": "components/knowledge/doc-upload-form.tsx",
    "content": "import { Button, Form, Input, Upload, Spin, message } from 'antd';\nimport React, { useState } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { InboxOutlined } from '@ant-design/icons';\nimport { apiInterceptors, addDocument, uploadDocument } from '@/client/api';\nimport { RcFile, UploadChangeParam } from 'antd/es/upload';\nimport { File, StepChangeParams } from '@/types/knowledge';\nimport { UploadRequestOption as RcCustomRequestOptions } from 'rc-upload/lib/interface';\nimport classNames from 'classnames';\n\ntype FileParams = {\n  file: RcFile;\n  fileList: FileList;\n};\n\ntype IProps = {\n  className: string;\n  handleStepChange: (params: StepChangeParams) => void;\n  spaceName: string;\n  docType: string;\n};\n\ntype FieldType = {\n  docName: string;\n  textSource: string;\n  originFileObj: FileParams;\n  text: string;\n  webPageUrl: string;\n};\n\nconst { Dragger } = Upload;\nconst { TextArea } = Input;\n\nexport default function DocUploadForm(props: IProps) {\n  const { className, handleStepChange, spaceName, docType } = props;\n  const { t } = useTranslation();\n  const [form] = Form.useForm();\n  const [spinning, setSpinning] = useState<boolean>(false);\n  const [files, setFiles] = useState<Array<File>>([]);\n\n  const upload = async (data: FieldType) => {\n    const { docName, textSource, text, webPageUrl } = data;\n    let docId;\n    setSpinning(true);\n    switch (docType) {\n      case 'URL':\n        [, docId] = await apiInterceptors(\n          addDocument(spaceName as string, {\n            doc_name: docName,\n            content: webPageUrl,\n            doc_type: 'URL',\n          }),\n        );\n        break;\n      case 'TEXT':\n        [, docId] = await apiInterceptors(\n          addDocument(spaceName as string, {\n            doc_name: docName,\n            source: textSource,\n            content: text,\n            doc_type: 'TEXT',\n          }),\n        );\n        break;\n    }\n    setSpinning(false);\n    if (docType === 'DOCUMENT' && files.length < 1) {\n      return message.error('Upload failed, please re-upload.');\n    } else if (docType !== 'DOCUMENT' && !docId) {\n      return message.error('Upload failed, please re-upload.');\n    }\n    handleStepChange({\n      label: 'forward',\n      files:\n        docType === 'DOCUMENT'\n          ? files\n          : [\n              {\n                name: docName,\n                doc_id: docId || -1,\n              },\n            ],\n    });\n  };\n\n  const handleFileChange = ({ file, fileList }: UploadChangeParam) => {\n    if (fileList.length === 0) {\n      form.setFieldValue('originFileObj', null);\n    }\n  };\n\n  const uploadFile = async (options: RcCustomRequestOptions) => {\n    const { onSuccess, onError, file } = options;\n    const formData = new FormData();\n    const filename = file?.name;\n    formData.append('doc_name', filename);\n    formData.append('doc_file', file);\n    formData.append('doc_type', 'DOCUMENT');\n    const [, docId] = await apiInterceptors(uploadDocument(spaceName, formData));\n    if (Number.isInteger(docId)) {\n      onSuccess && onSuccess(docId || 0);\n      setFiles((files) => {\n        files.push({\n          name: filename,\n          doc_id: docId || -1,\n        });\n        return files;\n      });\n    } else {\n      onError && onError({ name: '', message: '' });\n    }\n  };\n\n  const renderText = () => {\n    return (\n      <>\n        <Form.Item<FieldType> label={`${t('Name')}:`} name=\"docName\" rules={[{ required: true, message: t('Please_input_the_name') }]}>\n          <Input className=\"mb-5 h-12\" placeholder={t('Please_input_the_name')} />\n        </Form.Item>\n        <Form.Item<FieldType>\n          label={`${t('Text_Source')}:`}\n          name=\"textSource\"\n          rules={[{ required: true, message: t('Please_input_the_text_source') }]}\n        >\n          <Input className=\"mb-5  h-12\" placeholder={t('Please_input_the_text_source')} />\n        </Form.Item>\n        <Form.Item<FieldType> label={`${t('Text')}:`} name=\"text\" rules={[{ required: true, message: t('Please_input_the_description') }]}>\n          <TextArea rows={4} />\n        </Form.Item>\n      </>\n    );\n  };\n\n  const renderWebPage = () => {\n    return (\n      <>\n        <Form.Item<FieldType> label={`${t('Name')}:`} name=\"docName\" rules={[{ required: true, message: t('Please_input_the_name') }]}>\n          <Input className=\"mb-5 h-12\" placeholder={t('Please_input_the_name')} />\n        </Form.Item>\n        <Form.Item<FieldType>\n          label={`${t('Web_Page_URL')}:`}\n          name=\"webPageUrl\"\n          rules={[{ required: true, message: t('Please_input_the_Web_Page_URL') }]}\n        >\n          <Input className=\"mb-5  h-12\" placeholder={t('Please_input_the_Web_Page_URL')} />\n        </Form.Item>\n      </>\n    );\n  };\n\n  const renderDocument = () => {\n    return (\n      <>\n        <Form.Item<FieldType> name=\"originFileObj\" rules={[{ required: true, message: t('Please_select_file') }]}>\n          <Dragger\n            multiple\n            onChange={handleFileChange}\n            maxCount={10}\n            accept=\".pdf,.ppt,.pptx,.xls,.xlsx,.doc,.docx,.txt,.md\"\n            customRequest={uploadFile}\n          >\n            <p className=\"ant-upload-drag-icon\">\n              <InboxOutlined />\n            </p>\n            <p style={{ color: 'rgb(22, 108, 255)', fontSize: '20px' }}>{t('Select_or_Drop_file')}</p>\n            <p className=\"ant-upload-hint\" style={{ color: 'rgb(22, 108, 255)' }}>\n              PDF, PowerPoint, Excel, Word, Text, Markdown,\n            </p>\n          </Dragger>\n        </Form.Item>\n      </>\n    );\n  };\n\n  const renderFormContainer = () => {\n    switch (docType) {\n      case 'URL':\n        return renderWebPage();\n      case 'DOCUMENT':\n        return renderDocument();\n      default:\n        return renderText();\n    }\n  };\n\n  return (\n    <Spin spinning={spinning}>\n      <Form\n        form={form}\n        size=\"large\"\n        className={classNames('mt-4', className)}\n        layout=\"vertical\"\n        name=\"basic\"\n        initialValues={{ remember: true }}\n        autoComplete=\"off\"\n        onFinish={upload}\n      >\n        {renderFormContainer()}\n        <Form.Item>\n          <Button\n            onClick={() => {\n              handleStepChange({ label: 'back' });\n            }}\n            className=\"mr-4\"\n          >{`${t('Back')}`}</Button>\n          <Button type=\"primary\" loading={spinning} htmlType=\"submit\">\n            {t('Next')}\n          </Button>\n        </Form.Item>\n      </Form>\n    </Spin>\n  );\n}\n"
  },
  {
    "path": "components/knowledge/segmentation.tsx",
    "content": "import { apiInterceptors, getChunkStrategies, getDocumentList, syncBatchDocument } from '@/client/api';\nimport { File, IChunkStrategyResponse, ISyncBatchParameter, StepChangeParams } from '@/types/knowledge';\nimport { Alert, Button, Collapse, Form, Spin, message } from 'antd';\nimport Icon from '@ant-design/icons';\nimport { useEffect, useState } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport StrategyForm from './strategy-form';\nimport { DoneIcon, PendingIcon, SyncIcon, FileError } from '@/components/icons';\n\ntype IProps = {\n  spaceName: string;\n  docType: string;\n  handleStepChange: (params: StepChangeParams) => void;\n  uploadFiles: Array<File>;\n};\n\ntype FieldType = {\n  fileStrategies: Array<ISyncBatchParameter>;\n};\n\nlet intervalId: string | number | NodeJS.Timeout | undefined;\n\nexport default function Segmentation(props: IProps) {\n  const { spaceName, docType, uploadFiles, handleStepChange } = props;\n  const { t } = useTranslation();\n  const [form] = Form.useForm();\n  const [files, setFiles] = useState(uploadFiles);\n  const [loading, setLoading] = useState<boolean>();\n  const [strategies, setStrategies] = useState<Array<IChunkStrategyResponse>>([]);\n  const [syncStatus, setSyncStatus] = useState<string>('');\n\n  async function getStrategies() {\n    setLoading(true);\n    const [, allStrategies] = await apiInterceptors(getChunkStrategies());\n    setLoading(false);\n    setStrategies((allStrategies || [])?.filter((i) => i.type.indexOf(docType) > -1));\n  }\n\n  useEffect(() => {\n    getStrategies();\n    return () => {\n      intervalId && clearInterval(intervalId);\n    };\n  }, []);\n\n  const handleFinish = async (data: FieldType) => {\n    if (checkParameter(data)) {\n      setLoading(true);\n      const [, result] = await apiInterceptors(syncBatchDocument(spaceName, data.fileStrategies));\n      setLoading(false);\n      if (result?.tasks && result?.tasks?.length > 0) {\n        message.success(`Segemation task start successfully. task id: ${result?.tasks.join(',')}`);\n        setSyncStatus('RUNNING');\n        const docIds = data.fileStrategies.map((i) => i.doc_id);\n        intervalId = setInterval(async () => {\n          const status = await updateSyncStatus(docIds);\n          if (status === 'FINISHED') {\n            clearInterval(intervalId);\n            setSyncStatus('FINISHED');\n            message.success('Congratulation, All files sync successfully.');\n            handleStepChange({\n              label: 'finish',\n            });\n          }\n        }, 3000);\n      }\n    }\n  };\n\n  function checkParameter(data: FieldType) {\n    let checked = true;\n    if (syncStatus === 'RUNNING') {\n      checked = false;\n      message.warning('The task is still running, do not submit it again.');\n    }\n    const { fileStrategies } = data;\n    fileStrategies.map((item) => {\n      const name = item?.chunk_parameters?.chunk_strategy;\n      if (!name) {\n        // set default strategy\n        item.chunk_parameters = { chunk_strategy: 'Automatic' };\n      }\n      const strategy = strategies.filter((item) => item.strategy === name)[0];\n      const newParam: any = {\n        chunk_strategy: item?.chunk_parameters?.chunk_strategy,\n      };\n      if (strategy && strategy.parameters) {\n        // remove unused parameter, otherwise api will failed.\n        strategy.parameters.forEach((param) => {\n          const paramName = param.param_name;\n          newParam[paramName] = (item?.chunk_parameters as any)[paramName];\n        });\n      }\n      item.chunk_parameters = newParam;\n    });\n    return checked;\n  }\n\n  async function updateSyncStatus(docIds: Array<number>) {\n    const [, docs] = await apiInterceptors(\n      getDocumentList(spaceName, {\n        doc_ids: docIds,\n      }),\n    );\n    if (docs?.data && docs?.data.length > 0) {\n      const copy = [...files!];\n      // set file status one by one\n      docs?.data.map((doc) => {\n        const file = copy?.filter((file) => file.doc_id === doc.id)?.[0];\n        if (file) {\n          file.status = doc.status;\n        }\n      });\n      setFiles(copy);\n      // all doc sync finished\n      if (docs?.data.every((item) => item.status === 'FINISHED' || item.status === 'FAILED')) {\n        return 'FINISHED';\n      }\n    }\n  }\n\n  function renderStrategy() {\n    if (!strategies || !strategies.length) {\n      return <Alert message={`Cannot find one strategy for ${docType} type knowledge.`} type=\"warning\" />;\n    }\n    return (\n      <Form.List name=\"fileStrategies\">\n        {(fields) => {\n          switch (docType) {\n            case 'TEXT':\n            case 'URL':\n              return fields?.map((field) => (\n                <StrategyForm strategies={strategies} docType={docType} fileName={files![field.name].name} field={field} />\n              ));\n            case 'DOCUMENT':\n              return (\n                <Collapse defaultActiveKey={0} size={files.length > 5 ? 'small' : 'middle'}>\n                  {fields?.map((field) => (\n                    // field [{name: 0, key: 0, isListField: true, fieldKey: 0}, {name: 1, key: 1, isListField: true, fieldKey: 1}]\n                    <Collapse.Panel header={`${field.name + 1}. ${files![field.name].name}`} key={field.key} extra={renderSyncStatus(field.name)}>\n                      <StrategyForm strategies={strategies} docType={docType} fileName={files![field.name].name} field={field} />\n                    </Collapse.Panel>\n                  ))}\n                </Collapse>\n              );\n          }\n        }}\n      </Form.List>\n    );\n  }\n\n  function renderSyncStatus(index: number) {\n    const status = files![index].status;\n    switch (status) {\n      case 'FINISHED':\n        return <Icon component={DoneIcon} />;\n      case 'RUNNING':\n        return <Icon className=\"animate-spin animate-infinite\" component={SyncIcon} />;\n      case 'FAILED':\n        return <Icon component={FileError} />;\n      default:\n        return <Icon component={PendingIcon} />;\n    }\n  }\n\n  return (\n    <Spin spinning={loading}>\n      <Form\n        labelCol={{ span: 6 }}\n        wrapperCol={{ span: 18 }}\n        labelAlign=\"right\"\n        form={form}\n        size=\"large\"\n        className=\"mt-4\"\n        layout=\"horizontal\"\n        name=\"basic\"\n        autoComplete=\"off\"\n        initialValues={{\n          fileStrategies: files,\n        }}\n        onFinish={handleFinish}\n      >\n        {renderStrategy()}\n        <Form.Item className=\"mt-4\">\n          <Button\n            onClick={() => {\n              handleStepChange({ label: 'back' });\n            }}\n            className=\"mr-4\"\n          >{`${t('Back')}`}</Button>\n          <Button type=\"primary\" htmlType=\"submit\" loading={loading || syncStatus === 'RUNNING'}>\n            {t('Process')}\n          </Button>\n        </Form.Item>\n      </Form>\n    </Spin>\n  );\n}\n"
  },
  {
    "path": "components/knowledge/space-card.tsx",
    "content": "import { Popover, ConfigProvider, Modal, Badge } from 'antd';\nimport { useRouter } from 'next/router';\nimport Image from 'next/image';\nimport { ClockCircleOutlined, DeleteFilled, MessageFilled, UserOutlined, WarningOutlined } from '@ant-design/icons';\nimport { ISpace } from '@/types/knowledge';\nimport DocPanel from './doc-panel';\nimport moment from 'moment';\nimport { apiInterceptors, delSpace, newDialogue } from '@/client/api';\nimport { useTranslation } from 'react-i18next';\nimport GptCard from '../common/gpt-card';\n\ninterface IProps {\n  space: ISpace;\n  onAddDoc: (spaceName: string) => void;\n  getSpaces: () => void;\n}\n\nconst { confirm } = Modal;\n\nexport default function SpaceCard(props: IProps) {\n  const router = useRouter();\n  const { t } = useTranslation();\n  const { space, getSpaces } = props;\n\n  const showDeleteConfirm = () => {\n    confirm({\n      title: t('Tips'),\n      icon: <WarningOutlined />,\n      content: `${t('Del_Knowledge_Tips')}?`,\n      okText: 'Yes',\n      okType: 'danger',\n      cancelText: 'No',\n      async onOk() {\n        await apiInterceptors(delSpace({ name: space?.name }));\n        getSpaces();\n      },\n    });\n  };\n\n  function onDeleteDoc() {\n    getSpaces();\n  }\n\n  const handleChat = async () => {\n    const [_, data] = await apiInterceptors(\n      newDialogue({\n        chat_mode: 'chat_knowledge',\n      }),\n    );\n    if (data?.conv_uid) {\n      router.push(`/chat?scene=chat_knowledge&id=${data?.conv_uid}&db_param=${space.name}`);\n    }\n  };\n\n  return (\n    <ConfigProvider\n      theme={{\n        components: {\n          Popover: {\n            zIndexPopup: 90,\n          },\n        },\n      }}\n    >\n      <Popover\n        className=\"cursor-pointer\"\n        placement=\"bottom\"\n        trigger=\"click\"\n        content={<DocPanel space={space} onAddDoc={props.onAddDoc} onDeleteDoc={onDeleteDoc} />}\n      >\n        <Badge className=\"mb-4 min-w-[200px] sm:w-60 lg:w-72\" count={space.docs || 0}>\n          <GptCard\n            title={space.name}\n            desc={space.desc}\n            icon=\"/models/knowledge-default.jpg\"\n            iconBorder={false}\n            tags={[\n              {\n                text: (\n                  <>\n                    <UserOutlined className=\"mr-1\" />\n                    {space?.owner}\n                  </>\n                ),\n              },\n              {\n                text: (\n                  <>\n                    <ClockCircleOutlined className=\"mr-1\" />\n                    {moment(space.gmt_modified).format('YYYY-MM-DD')}\n                  </>\n                ),\n              },\n            ]}\n            operations={[\n              {\n                label: t('Chat'),\n                children: <MessageFilled />,\n                onClick: handleChat,\n              },\n              {\n                label: t('Delete'),\n                children: <DeleteFilled />,\n                onClick: () => {\n                  showDeleteConfirm();\n                },\n              },\n            ]}\n          />\n        </Badge>\n      </Popover>\n    </ConfigProvider>\n  );\n}\n"
  },
  {
    "path": "components/knowledge/space-form.tsx",
    "content": "import { addSpace, apiInterceptors } from '@/client/api';\nimport { StepChangeParams } from '@/types/knowledge';\nimport { Button, Form, Input, Spin } from 'antd';\nimport { useState } from 'react';\nimport { useTranslation } from 'react-i18next';\n\ntype FieldType = {\n  spaceName: string;\n  owner: string;\n  description: string;\n};\n\ntype IProps = {\n  handleStepChange: (params: StepChangeParams) => void;\n};\n\nexport default function SpaceForm(props: IProps) {\n  const { t } = useTranslation();\n  const { handleStepChange } = props;\n  const [spinning, setSpinning] = useState<boolean>(false);\n\n  const handleFinish = async (fieldsValue: FieldType) => {\n    const { spaceName, owner, description } = fieldsValue;\n    setSpinning(true);\n    const [_, data, res] = await apiInterceptors(\n      addSpace({\n        name: spaceName,\n        vector_type: 'Chroma',\n        owner,\n        desc: description,\n      }),\n    );\n    setSpinning(false);\n    res?.success && handleStepChange({ label: 'forward', spaceName });\n  };\n  return (\n    <Spin spinning={spinning}>\n      <Form\n        size=\"large\"\n        className=\"mt-4\"\n        layout=\"vertical\"\n        name=\"basic\"\n        initialValues={{ remember: true }}\n        autoComplete=\"off\"\n        onFinish={handleFinish}\n      >\n        <Form.Item<FieldType>\n          label={t('Knowledge_Space_Name')}\n          name=\"spaceName\"\n          rules={[\n            { required: true, message: t('Please_input_the_name') },\n            () => ({\n              validator(_, value) {\n                if (/[^\\u4e00-\\u9fa50-9a-zA-Z_-]/.test(value)) {\n                  return Promise.reject(new Error(t('the_name_can_only_contain')));\n                }\n                return Promise.resolve();\n              },\n            }),\n          ]}\n        >\n          <Input className=\"mb-5 h-12\" placeholder={t('Please_input_the_name')} />\n        </Form.Item>\n        <Form.Item<FieldType> label={t('Owner')} name=\"owner\" rules={[{ required: true, message: t('Please_input_the_owner') }]}>\n          <Input className=\"mb-5  h-12\" placeholder={t('Please_input_the_owner')} />\n        </Form.Item>\n        <Form.Item<FieldType> label={t('Description')} name=\"description\" rules={[{ required: true, message: t('Please_input_the_description') }]}>\n          <Input className=\"mb-5  h-12\" placeholder={t('Please_input_the_description')} />\n        </Form.Item>\n        <Form.Item>\n          <Button type=\"primary\" htmlType=\"submit\">\n            {t('Next')}\n          </Button>\n        </Form.Item>\n      </Form>\n    </Spin>\n  );\n}\n"
  },
  {
    "path": "components/knowledge/strategy-form.tsx",
    "content": "import { IChunkStrategyResponse } from '@/types/knowledge';\nimport { Alert, Checkbox, Form, FormListFieldData, Input, InputNumber, Radio, RadioChangeEvent } from 'antd';\nimport { useState } from 'react';\nimport { useTranslation } from 'react-i18next';\nconst { TextArea } = Input;\n\ntype IProps = {\n  strategies: Array<IChunkStrategyResponse>;\n  docType: string;\n  field: FormListFieldData;\n  fileName: string;\n};\n\n/**\n * render strategies by doc type and file suffix\n */\nexport default function StrategyForm({ strategies, docType, fileName, field }: IProps) {\n  const [selectedStrategy, setSelectedStrategy] = useState<string>();\n  let filleSuffix = '';\n  if (docType === 'DOCUMENT') {\n    // filter strategy by file suffix\n    const arr = fileName.split('.');\n    filleSuffix = arr[arr.length - 1];\n  }\n  const ableStrategies = filleSuffix ? strategies.filter((i) => i.suffix.indexOf(filleSuffix) > -1) : strategies;\n  const { t } = useTranslation();\n  const DEFAULT_STRATEGY = {\n    strategy: 'Automatic',\n    name: t('Automatic'),\n    desc: t('Automatic_desc'),\n  };\n\n  function radioChange(e: RadioChangeEvent) {\n    setSelectedStrategy(e.target.value);\n  }\n\n  function renderStrategyParamForm() {\n    if (!selectedStrategy) {\n      return null;\n    }\n    if (selectedStrategy === DEFAULT_STRATEGY.strategy) {\n      return <p className=\"my-4\">{DEFAULT_STRATEGY.desc}</p>;\n    }\n    const parameters = ableStrategies?.filter((i) => i.strategy === selectedStrategy)[0].parameters;\n    if (!parameters || !parameters.length) {\n      return <Alert className=\"my-2\" type=\"warning\" message={t('No_parameter')} />;\n    }\n    return (\n      <div className=\"mt-2\">\n        {parameters?.map((param) => (\n          <Form.Item\n            key={`param_${param.param_name}`}\n            label={param.param_name}\n            name={[field!.name, 'chunk_parameters', param.param_name]}\n            rules={[{ required: true, message: t('Please_input_the_name') }]}\n            initialValue={param.default_value}\n            valuePropName={param.param_type === 'boolean' ? 'checked' : 'value'}\n            tooltip={param.description}\n          >\n            {renderParamByType(param.param_type)}\n          </Form.Item>\n        ))}\n      </div>\n    );\n  }\n\n  function renderParamByType(type: string) {\n    switch (type) {\n      case 'int':\n        return <InputNumber className=\"w-full\" min={1} />;\n      case 'string':\n        return <TextArea className=\"w-full\" rows={2} />;\n      case 'boolean':\n        return <Checkbox />;\n    }\n  }\n  return (\n    <>\n      <Form.Item name={[field!.name, 'chunk_parameters', 'chunk_strategy']} initialValue={DEFAULT_STRATEGY.strategy}>\n        <Radio.Group style={{ marginTop: 16 }} onChange={radioChange}>\n          <Radio value={DEFAULT_STRATEGY.strategy}>{DEFAULT_STRATEGY.name}</Radio>\n          {ableStrategies.map((strategy) => (\n            <Radio key={`strategy_radio_${strategy.strategy}`} value={strategy.strategy}>\n              {strategy.name}\n            </Radio>\n          ))}\n        </Radio.Group>\n      </Form.Item>\n      {renderStrategyParamForm()}\n    </>\n  );\n}\n"
  },
  {
    "path": "components/layout/side-bar.tsx",
    "content": "import { ChatContext } from '@/app/chat-context';\nimport { apiInterceptors, delDialogue } from '@/client/api';\nimport { STORAGE_LANG_KEY, STORAGE_THEME_KEY } from '@/utils';\nimport { DarkSvg, SunnySvg, ModelSvg } from '@/components/icons';\nimport { IChatDialogueSchema } from '@/types/chat';\nimport Icon, {\n  ConsoleSqlOutlined,\n  PartitionOutlined,\n  DeleteOutlined,\n  MessageOutlined,\n  GlobalOutlined,\n  MenuFoldOutlined,\n  MenuUnfoldOutlined,\n  PlusOutlined,\n  ShareAltOutlined,\n  MenuOutlined,\n  SettingOutlined,\n  BuildOutlined,\n  ForkOutlined,\n  AppstoreOutlined,\n} from '@ant-design/icons';\nimport { Modal, message, Tooltip, Dropdown } from 'antd';\nimport { ItemType } from 'antd/es/menu/hooks/useItems';\nimport copy from 'copy-to-clipboard';\nimport Image from 'next/image';\nimport Link from 'next/link';\nimport { useRouter } from 'next/router';\nimport { ReactNode, useCallback, useContext, useEffect, useMemo, useState } from 'react';\nimport { useTranslation } from 'react-i18next';\n\ntype SettingItem = {\n  key: string;\n  name: string;\n  icon: ReactNode;\n  noDropdownItem?: boolean;\n  onClick: () => void;\n};\n\ntype RouteItem = {\n  key: string;\n  name: string;\n  icon: ReactNode;\n  path: string;\n};\n\nfunction menuItemStyle(active?: boolean) {\n  return `flex items-center h-10 hover:bg-[#F1F5F9] dark:hover:bg-theme-dark text-base w-full transition-colors whitespace-nowrap px-4 ${\n    active ? 'bg-[#F1F5F9] dark:bg-theme-dark' : ''\n  }`;\n}\n\nfunction smallMenuItemStyle(active?: boolean) {\n  return `flex items-center justify-center mx-auto rounded w-14 h-14 text-xl hover:bg-[#F1F5F9] dark:hover:bg-theme-dark transition-colors cursor-pointer ${\n    active ? 'bg-[#F1F5F9] dark:bg-theme-dark' : ''\n  }`;\n}\n\nfunction SideBar() {\n  const { chatId, scene, isMenuExpand, dialogueList, queryDialogueList, refreshDialogList, setIsMenuExpand, setAgent, mode, setMode } =\n    useContext(ChatContext);\n  const { pathname, replace } = useRouter();\n  const { t, i18n } = useTranslation();\n\n  const [logo, setLogo] = useState<string>('/LOGO_1.png');\n\n  const routes = useMemo(() => {\n    const items: RouteItem[] = [\n      {\n        key: 'app',\n        name: t('App'),\n        path: '/app',\n        icon: <AppstoreOutlined />,\n      },\n      {\n        key: 'flow',\n        name: t('awel_flow'),\n        icon: <ForkOutlined />,\n        path: '/flow',\n      },\n      {\n        key: 'models',\n        name: t('model_manage'),\n        path: '/models',\n        icon: <Icon component={ModelSvg} />,\n      },\n      {\n        key: 'database',\n        name: t('Database'),\n        icon: <ConsoleSqlOutlined />,\n        path: '/database',\n      },\n      {\n        key: 'knowledge',\n        name: t('Knowledge_Space'),\n        icon: <PartitionOutlined />,\n        path: '/knowledge',\n      },\n      {\n        key: 'agent',\n        name: t('Plugins'),\n        path: '/agent',\n        icon: <BuildOutlined />,\n      },\n      {\n        key: 'prompt',\n        name: t('Prompt'),\n        icon: <MessageOutlined />,\n        path: '/prompt',\n      },\n    ];\n    return items;\n  }, [i18n.language]);\n\n  const handleToggleMenu = () => {\n    setIsMenuExpand(!isMenuExpand);\n  };\n\n  const handleToggleTheme = useCallback(() => {\n    const theme = mode === 'light' ? 'dark' : 'light';\n    setMode(theme);\n    localStorage.setItem(STORAGE_THEME_KEY, theme);\n  }, [mode]);\n\n  const handleChangeLang = useCallback(() => {\n    const language = i18n.language === 'en' ? 'zh' : 'en';\n    i18n.changeLanguage(language);\n    localStorage.setItem(STORAGE_LANG_KEY, language);\n  }, [i18n.language, i18n.changeLanguage]);\n\n  const settings = useMemo(() => {\n    const items: SettingItem[] = [\n      {\n        key: 'theme',\n        name: t('Theme'),\n        icon: mode === 'dark' ? <Icon component={DarkSvg} /> : <Icon component={SunnySvg} />,\n        onClick: handleToggleTheme,\n      },\n      {\n        key: 'language',\n        name: t('language'),\n        icon: <GlobalOutlined />,\n        onClick: handleChangeLang,\n      },\n      {\n        key: 'fold',\n        name: t(isMenuExpand ? 'Close_Sidebar' : 'Show_Sidebar'),\n        icon: isMenuExpand ? <MenuFoldOutlined /> : <MenuUnfoldOutlined />,\n        onClick: handleToggleMenu,\n        noDropdownItem: true,\n      },\n    ];\n    return items;\n  }, [mode, handleChangeLang, handleToggleMenu, handleChangeLang]);\n\n  const dropDownRoutes: ItemType[] = useMemo(() => {\n    return routes.map<ItemType>((item) => ({\n      key: item.key,\n      label: (\n        <Link href={item.path} className=\"text-base\">\n          {item.icon}\n          <span className=\"ml-2 text-sm\">{item.name}</span>\n        </Link>\n      ),\n    }));\n  }, [routes]);\n\n  const dropDownSettings: ItemType[] = useMemo(() => {\n    return settings\n      .filter((item) => !item.noDropdownItem)\n      .map<ItemType>((item) => ({\n        key: item.key,\n        label: (\n          <div className=\"text-base\" onClick={item.onClick}>\n            {item.icon}\n            <span className=\"ml-2 text-sm\">{item.name}</span>\n          </div>\n        ),\n      }));\n  }, [settings]);\n\n  const handleDelChat = useCallback(\n    (dialogue: IChatDialogueSchema) => {\n      Modal.confirm({\n        title: 'Delete Chat',\n        content: 'Are you sure delete this chat?',\n        width: '276px',\n        centered: true,\n        onOk() {\n          return new Promise<void>(async (resolve, reject) => {\n            try {\n              const [err] = await apiInterceptors(delDialogue(dialogue.conv_uid));\n              if (err) {\n                reject();\n                return;\n              }\n              message.success('success');\n              refreshDialogList();\n              dialogue.chat_mode === scene && dialogue.conv_uid === chatId && replace('/');\n              resolve();\n            } catch (e) {\n              reject();\n            }\n          });\n        },\n      });\n    },\n    [refreshDialogList],\n  );\n\n  const handleClickChatItem = (item: IChatDialogueSchema) => {\n    if (item.chat_mode === 'chat_agent' && item.select_param) {\n      setAgent?.(item.select_param);\n    }\n  };\n\n  const copyLink = useCallback((item: IChatDialogueSchema) => {\n    const success = copy(`${location.origin}/chat?scene=${item.chat_mode}&id=${item.conv_uid}`);\n    message[success ? 'success' : 'error'](success ? 'Copy success' : 'Copy failed');\n  }, []);\n\n  useEffect(() => {\n    queryDialogueList();\n  }, []);\n\n  useEffect(() => {\n    setLogo(mode === 'dark' ? '/WHITE_LOGO.png' : '/LOGO_1.png');\n  }, [mode]);\n\n  if (!isMenuExpand) {\n    return (\n      <div className=\"flex flex-col justify-between h-screen bg-white dark:bg-[#232734] animate-fade animate-duration-300\">\n        <Link href=\"/\" className=\"px-2 py-3\">\n          <Image src=\"/LOGO_SMALL.png\" alt=\"DB-GPT\" width={63} height={46} className=\"w-[63px] h-[46px]\" />\n        </Link>\n        <div>\n          <Link href=\"/\" className=\"flex items-center justify-center my-4 mx-auto w-12 h-12 bg-theme-primary rounded-full text-white\">\n            <PlusOutlined className=\"text-lg\" />\n          </Link>\n        </div>\n        {/* Chat List */}\n        <div className=\"flex-1 overflow-y-scroll py-4 space-y-2\">\n          {dialogueList?.map((item) => {\n            const active = item.conv_uid === chatId && item.chat_mode === scene;\n\n            return (\n              <Tooltip key={item.conv_uid} title={item.user_name || item.user_input} placement=\"right\">\n                <Link\n                  href={`/chat?scene=${item.chat_mode}&id=${item.conv_uid}`}\n                  className={smallMenuItemStyle(active)}\n                  onClick={() => {\n                    handleClickChatItem(item);\n                  }}\n                >\n                  <MessageOutlined />\n                </Link>\n              </Tooltip>\n            );\n          })}\n        </div>\n        <div className=\"py-4\">\n          <Dropdown menu={{ items: dropDownRoutes }} placement=\"topRight\">\n            <div className={smallMenuItemStyle()}>\n              <MenuOutlined />\n            </div>\n          </Dropdown>\n          <Dropdown menu={{ items: dropDownSettings }} placement=\"topRight\">\n            <div className={smallMenuItemStyle()}>\n              <SettingOutlined />\n            </div>\n          </Dropdown>\n          {settings\n            .filter((item) => item.noDropdownItem)\n            .map((item) => (\n              <Tooltip key={item.key} title={item.name} placement=\"right\">\n                <div className={smallMenuItemStyle()} onClick={item.onClick}>\n                  {item.icon}\n                </div>\n              </Tooltip>\n            ))}\n        </div>\n      </div>\n    );\n  }\n\n  return (\n    <div className=\"flex flex-col h-screen bg-white dark:bg-[#232734]\">\n      {/* LOGO */}\n      <Link href=\"/\" className=\"p-2\">\n        <Image src={logo} alt=\"DB-GPT\" width={239} height={60} className=\"w-full h-full\" />\n      </Link>\n      <Link href=\"/\" className=\"flex items-center justify-center mb-4 mx-4 h-11 bg-theme-primary rounded text-white\">\n        <PlusOutlined className=\"mr-2\" />\n        <span>{t('new_chat')}</span>\n      </Link>\n      {/* Chat List */}\n      <div className=\"flex-1 overflow-y-scroll\">\n        {dialogueList?.map((item) => {\n          const active = item.conv_uid === chatId && item.chat_mode === scene;\n\n          return (\n            <Link\n              key={item.conv_uid}\n              href={`/chat?scene=${item.chat_mode}&id=${item.conv_uid}`}\n              className={`group/item ${menuItemStyle(active)}`}\n              onClick={() => {\n                handleClickChatItem(item);\n              }}\n            >\n              <MessageOutlined className=\"text-base\" />\n              <div className=\"flex-1 line-clamp-1 mx-2 text-sm\">{item.user_name || item.user_input}</div>\n              <div\n                className=\"group-hover/item:opacity-100 cursor-pointer opacity-0 mr-1\"\n                onClick={(e) => {\n                  e.preventDefault();\n                  copyLink(item);\n                }}\n              >\n                <ShareAltOutlined />\n              </div>\n              <div\n                className=\"group-hover/item:opacity-100 cursor-pointer opacity-0\"\n                onClick={(e) => {\n                  e.preventDefault();\n                  handleDelChat(item);\n                }}\n              >\n                <DeleteOutlined />\n              </div>\n            </Link>\n          );\n        })}\n      </div>\n      {/* Settings */}\n      <div className=\"pt-4\">\n        <div className=\"max-h-52 overflow-y-auto scrollbar-default\">\n          {routes.map((item) => (\n            <Link key={item.key} href={item.path} className={`${menuItemStyle(pathname === item.path)} overflow-hidden`}>\n              <>\n                {item.icon}\n                <span className=\"ml-3 text-sm\">{item.name}</span>\n              </>\n            </Link>\n          ))}\n        </div>\n        <div className=\"flex items-center justify-around py-4 mt-2\">\n          {settings.map((item) => (\n            <Tooltip key={item.key} title={item.name}>\n              <div className=\"flex-1 flex items-center justify-center cursor-pointer text-xl\" onClick={item.onClick}>\n                {item.icon}\n              </div>\n            </Tooltip>\n          ))}\n        </div>\n      </div>\n    </div>\n  );\n}\n\nexport default SideBar;\n"
  },
  {
    "path": "components/layout/top-progress-bar.tsx",
    "content": "import Router from 'next/router';\nimport NProgress from 'nprogress';\n\nlet timer: any;\nlet state: any;\nlet activeRequests = 0;\nconst delay = 250;\n\nfunction load() {\n  if (state === 'loading') {\n    return;\n  }\n\n  state = 'loading';\n\n  timer = setTimeout(function () {\n    NProgress.start();\n  }, delay); // only show progress bar if it takes longer than the delay\n}\n\nfunction stop() {\n  if (activeRequests > 0) {\n    return;\n  }\n\n  state = 'stop';\n\n  clearTimeout(timer);\n  NProgress.done();\n}\n\nRouter.events.on('routeChangeStart', load);\nRouter.events.on('routeChangeComplete', stop);\nRouter.events.on('routeChangeError', stop);\n\nif (typeof window !== 'undefined' && typeof window?.fetch === 'function') {\n  const originalFetch = window.fetch;\n  window.fetch = async function (...args) {\n    if (activeRequests === 0) {\n      load();\n    }\n  \n    activeRequests++;\n  \n    try {\n      const response = await originalFetch(...args);\n      return response;\n    } catch (error) {\n      return Promise.reject(error);\n    } finally {\n      activeRequests -= 1;\n      if (activeRequests === 0) {\n        stop();\n      }\n    }\n  };\n}\n\nexport default function TopProgressBar() {\n  return null;\n}\n"
  },
  {
    "path": "components/model/model-card.tsx",
    "content": "import React, { useState } from 'react';\nimport { IModelData } from '@/types/model';\nimport { useTranslation } from 'react-i18next';\nimport { message } from 'antd';\nimport moment from 'moment';\nimport { apiInterceptors, stopModel } from '@/client/api';\nimport GptCard from '../common/gpt-card';\nimport { PauseCircleOutlined } from '@ant-design/icons';\nimport { MODEL_ICON_MAP } from '@/utils';\n\ninterface Props {\n  info: IModelData;\n}\n\nfunction ModelCard({ info }: Props) {\n  const { t } = useTranslation();\n  const [loading, setLoading] = useState<boolean>(false);\n\n  async function stopTheModel(info: IModelData) {\n    if (loading) {\n      return;\n    }\n    setLoading(true);\n    const [, res] = await apiInterceptors(\n      stopModel({\n        host: info.host,\n        port: info.port,\n        model: info.model_name,\n        worker_type: info.model_type,\n        params: {},\n      }),\n    );\n    setLoading(false);\n    if (res === true) {\n      message.success(t('stop_model_success'));\n    }\n  }\n  return (\n    <GptCard\n      className=\"w-96\"\n      title={info.model_name}\n      tags={[\n        {\n          text: info.healthy ? 'Healthy' : 'Unhealthy',\n          color: info.healthy ? 'green' : 'red',\n          border: true,\n        },\n        info.model_type,\n      ]}\n      icon={MODEL_ICON_MAP[info.model_name]?.icon || '/models/huggingface.svg'}\n      operations={[\n        {\n          children: (\n            <div>\n              <PauseCircleOutlined className=\"mr-2\" />\n              <span className=\"text-sm\">Stop Model</span>\n            </div>\n          ),\n          onClick: () => {\n            stopTheModel(info);\n          },\n        },\n      ]}\n    >\n      <div className=\"flex flex-col gap-1 px-4 pb-4 text-xs\">\n        <div className=\"flex overflow-hidden\">\n          <p className=\"w-28 text-gray-500 mr-2\">Host:</p>\n          <p className=\"flex-1 text-ellipsis\">{info.host}</p>\n        </div>\n        <div className=\"flex overflow-hidden\">\n          <p className=\"w-28 text-gray-500 mr-2\">Manage Host:</p>\n          <p className=\"flex-1 text-ellipsis\">\n            {info.manager_host}:{info.manager_port}\n          </p>\n        </div>\n        <div className=\"flex overflow-hidden\">\n          <p className=\"w-28 text-gray-500 mr-2\">Last Heart Beat:</p>\n          <p className=\"flex-1 text-ellipsis\">{moment(info.last_heartbeat).format('YYYY-MM-DD')}</p>\n        </div>\n      </div>\n    </GptCard>\n  );\n}\n\nexport default ModelCard;\n"
  },
  {
    "path": "components/model/model-form.tsx",
    "content": "import { apiInterceptors, getSupportModels, startModel } from '@/client/api';\nimport { SupportModel, SupportModelParams } from '@/types/model';\nimport { Button, Form, Select, Tooltip, message } from 'antd';\nimport { useEffect, useState } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { renderModelIcon } from '@/components/chat/header/model-selector';\nimport ModelParams from './model-params';\nconst { Option } = Select;\n\nfunction ModelForm({ onCancel, onSuccess }: { onCancel: () => void; onSuccess: () => void }) {\n  const { t } = useTranslation();\n  const [models, setModels] = useState<Array<SupportModel> | null>([]);\n  const [selectedModel, setSelectedModel] = useState<SupportModel>();\n  const [params, setParams] = useState<Array<SupportModelParams> | null>(null);\n  const [loading, setLoading] = useState<boolean>(false);\n  const [form] = Form.useForm();\n\n  async function getModels() {\n    const [, res] = await apiInterceptors(getSupportModels());\n    if (res && res.length) {\n      setModels(\n        res.sort((a: SupportModel, b: SupportModel) => {\n          if (a.enabled && !b.enabled) {\n            return -1;\n          } else if (!a.enabled && b.enabled) {\n            return 1;\n          } else {\n            return a.model.localeCompare(b.model);\n          }\n        }),\n      );\n    }\n    setModels(res);\n  }\n\n  useEffect(() => {\n    getModels();\n  }, []);\n\n  function handleChange(value: string, option: any) {\n    setSelectedModel(option.model);\n    setParams(option.model.params);\n  }\n\n  async function onFinish(values: any) {\n    if (!selectedModel) {\n      return;\n    }\n    delete values.model;\n    setLoading(true);\n    const [, , data] = await apiInterceptors(\n      startModel({\n        host: selectedModel.host,\n        port: selectedModel.port,\n        model: selectedModel.model,\n        worker_type: selectedModel?.worker_type,\n        params: values,\n      }),\n    );\n    setLoading(false);\n    if (data?.success === true) {\n      onSuccess && onSuccess();\n      return message.success(t('start_model_success'));\n    }\n  }\n\n  return (\n    <Form labelCol={{ span: 8 }} wrapperCol={{ span: 16 }} onFinish={onFinish} form={form}>\n      <Form.Item label=\"Model\" name=\"model\" rules={[{ required: true, message: t('model_select_tips') }]}>\n        <Select showSearch onChange={handleChange}>\n          {models?.map((model) => (\n            <Option key={model.model} value={model.model} label={model.model} model={model} disabled={!model.enabled}>\n              {renderModelIcon(model.model)}\n              <Tooltip title={model.enabled ? model.model : t('download_model_tip')}>\n                <span className=\"ml-2\">{model.model}</span>\n              </Tooltip>\n              <Tooltip title={model.enabled ? `${model.host}:${model.port}` : t('download_model_tip')}>\n                <p className=\"inline-block absolute right-4\">\n                  <span>{model.host}:</span>\n                  <span>{model.port}</span>\n                </p>\n              </Tooltip>\n            </Option>\n          ))}\n        </Select>\n      </Form.Item>\n      <ModelParams params={params} form={form} />\n      <div className=\"flex justify-center\">\n        <Button type=\"primary\" htmlType=\"submit\" loading={loading}>\n          {t('submit')}\n        </Button>\n        <Button className=\"ml-10\" onClick={onCancel}>\n          Cancel\n        </Button>\n      </div>\n    </Form>\n  );\n}\n\nexport default ModelForm;\n"
  },
  {
    "path": "components/model/model-params.tsx",
    "content": "import { SupportModelParams } from '@/types/model';\nimport { Checkbox, Form, FormInstance, Input, InputNumber } from 'antd';\nimport { useEffect } from 'react';\n\ninterface ParamValues {\n  [key: string]: string | number | boolean;\n}\n\nfunction ModelParams({ params, form }: { params: Array<SupportModelParams> | null; form: FormInstance<any> }) {\n  useEffect(() => {\n    if (params) {\n      const initialValues: ParamValues = {};\n      params.forEach((param) => {\n        initialValues[param.param_name] = param.default_value;\n      });\n      form.setFieldsValue(initialValues); // 设置表单字段的初始值\n    }\n  }, [params, form]);\n  if (!params || params?.length < 1) {\n    return null;\n  }\n\n  function renderItem(param: SupportModelParams) {\n    switch (param.param_type) {\n      case 'str':\n        return <Input />;\n      case 'int':\n        return <InputNumber />;\n      case 'bool':\n        return <Checkbox />;\n    }\n  }\n  return (\n    <>\n      {params?.map((param: SupportModelParams) => (\n        <Form.Item\n          key={param.param_name}\n          label={\n            <p className=\"whitespace-normal overflow-wrap-break-word\">{param.description?.length > 20 ? param.param_name : param.description}</p>\n          }\n          name={param.param_name}\n          initialValue={param.default_value}\n          valuePropName={param.param_type === 'bool' ? 'checked' : 'value'}\n          tooltip={param.description}\n          rules={[{ required: param.required, message: `Please input ${param.description}` }]}\n        >\n          {renderItem(param)}\n        </Form.Item>\n      ))}\n    </>\n  );\n}\n\nexport default ModelParams;\n"
  },
  {
    "path": "components/prompt/prompt-form.tsx",
    "content": "import React, { Ref, forwardRef, useEffect, useState } from 'react';\nimport { Form, Input, Spin, Select, FormInstance } from 'antd';\nimport { useTranslation } from 'react-i18next';\nimport { IPrompt } from '@/types/prompt';\n\ninterface IProps {\n  prompt?: IPrompt;\n  onFinish: (prompt: IPrompt) => void;\n  scenes?: Array<Record<string, string>>;\n}\n\nexport default forwardRef(function PromptForm(props: IProps, ref: Ref<FormInstance<any>> | undefined) {\n  const { t } = useTranslation();\n  const [form] = Form.useForm();\n  const { prompt, onFinish, scenes } = props;\n  const [loading, setLoading] = useState<boolean>(false);\n\n  useEffect(() => {\n    if (prompt) {\n      form.setFieldsValue(prompt);\n    }\n  }, []);\n\n  const submit = async () => {\n    const values = form.getFieldsValue();\n    setLoading(true);\n    await onFinish(values);\n    setLoading(false);\n  };\n\n  return (\n    <Spin spinning={loading}>\n      <Form form={form} ref={ref} name={`prompt-item-${prompt?.prompt_name || 'new'}`} layout=\"vertical\" className=\"mt-4\" onFinish={submit}>\n        <Form.Item name=\"chat_scene\" label={t('Prompt_Info_Scene')} rules={[{ required: true, message: t('Please_Input') + t('Prompt_Info_Scene') }]}>\n          <Select options={scenes}></Select>\n        </Form.Item>\n        <Form.Item\n          name=\"sub_chat_scene\"\n          label={t('Prompt_Info_Sub_Scene')}\n          rules={[{ required: true, message: t('Please_Input') + t('Prompt_Info_Sub_Scene') }]}\n        >\n          <Input />\n        </Form.Item>\n        <Form.Item name=\"prompt_name\" label={t('Prompt_Info_Name')} rules={[{ required: true, message: t('Please_Input') + t('Prompt_Info_Name') }]}>\n          <Input disabled={!!prompt} />\n        </Form.Item>\n        <Form.Item\n          name=\"content\"\n          label={t('Prompt_Info_Content')}\n          rules={[{ required: true, message: t('Please_Input') + t('Prompt_Info_Content') }]}\n        >\n          <Input.TextArea rows={6} />\n        </Form.Item>\n      </Form>\n    </Spin>\n  );\n});\n"
  },
  {
    "path": "defaultTheme.ts",
    "content": "import { extendTheme } from '@mui/joy/styles';\nimport colors from '@mui/joy/colors';\n\nexport const joyTheme = extendTheme({\n  colorSchemes: {\n    light: {\n      palette: {\n        mode: 'dark',\n        primary: {\n          ...colors.grey,\n          solidBg: '#e6f4ff',\n          solidColor: '#1677ff',\n          solidHoverBg: '#e6f4ff',\n        },\n        neutral: {\n          plainColor: '#4d4d4d',\n          plainHoverColor: '#131318',\n          plainHoverBg: '#EBEBEF',\n          plainActiveBg: '#D8D8DF',\n          plainDisabledColor: '#B9B9C6',\n        },\n        background: {\n          body: '#F7F7F7',\n          surface: '#fff',\n        },\n        text: {\n          primary: '#505050',\n        },\n      },\n    },\n    dark: {\n      palette: {\n        mode: 'light',\n        primary: {\n          ...colors.grey,\n          softBg: '#353539',\n          softHoverBg: '#35353978',\n          softDisabledBg: '#353539',\n          solidBg: '#51525beb',\n          solidHoverBg: '#51525beb',\n        },\n        neutral: {\n          plainColor: '#D8D8DF',\n          plainHoverColor: '#F7F7F8',\n          plainHoverBg: '#353539',\n          plainActiveBg: '#434356',\n          plainDisabledColor: '#434356',\n          outlinedBorder: '#353539',\n          outlinedHoverBorder: '#454651',\n        },\n        text: {\n          primary: '#FDFDFC',\n        },\n        background: {\n          body: '#151622',\n          surface: '#51525beb',\n        },\n      },\n    },\n  },\n  fontFamily: {\n    body: 'Josefin Sans, sans-serif',\n    display: 'Josefin Sans, sans-serif',\n  },\n  zIndex: {\n    modal: 1001,\n  },\n});\n"
  },
  {
    "path": "genAntdCss.ts",
    "content": "import { createHash } from 'crypto';\nimport fs from 'fs';\nimport path from 'path';\nimport { extractStyle } from '@ant-design/cssinjs';\nimport type Entity from '@ant-design/cssinjs/lib/Cache';\n\nexport type DoExtraStyleOptions = {\n  cache: Entity;\n  dir?: string;\n  baseFileName?: string;\n};\nexport function doExtraStyle({ cache, dir = 'antd-output', baseFileName = 'antd.min' }: DoExtraStyleOptions) {\n  const baseDir = path.resolve(__dirname, '../../static/css');\n\n  const outputCssPath = path.join(baseDir, dir);\n\n  if (!fs.existsSync(outputCssPath)) {\n    fs.mkdirSync(outputCssPath, { recursive: true });\n  }\n\n  const css = extractStyle(cache, true);\n  if (!css) return '';\n\n  const md5 = createHash('md5');\n  const hash = md5.update(css).digest('hex');\n  const fileName = `${baseFileName}.${hash.substring(0, 8)}.css`;\n  const fullpath = path.join(outputCssPath, fileName);\n\n  const res = `_next/static/css/${dir}/${fileName}`;\n\n  if (fs.existsSync(fullpath)) return res;\n\n  fs.writeFileSync(fullpath, css);\n\n  return res;\n}\n"
  },
  {
    "path": "global.d.ts",
    "content": "declare namespace JSX {\n  interface IntrinsicElements {\n    summary: React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>;\n    'custom-view': React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>;\n    references: React.DetailedHTMLProps<\n      React.HTMLAttributes<HTMLElement> & {\n        title: string;\n        references: any;\n      },\n      HTMLElement\n    >;\n    'chart-view': React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement>;\n  }\n}\n"
  },
  {
    "path": "hooks/use-chat.ts",
    "content": "import { EventStreamContentType, fetchEventSource } from '@microsoft/fetch-event-source';\nimport { message } from 'antd';\nimport { useCallback, useEffect, useMemo } from 'react';\nimport i18n from '@/app/i18n';\n\ntype Props = {\n  queryAgentURL?: string;\n};\n\ntype ChatParams = {\n  chatId: string;\n  data?: Record<string, any>;\n  query?: Record<string, string>;\n  onMessage: (message: string) => void;\n  onClose?: () => void;\n  onDone?: () => void;\n  onError?: (content: string, error?: Error) => void;\n};\n\nconst useChat = ({ queryAgentURL = '/api/v1/chat/completions' }: Props) => {\n  const ctrl = useMemo(() => new AbortController(), []);\n\n  const chat = useCallback(\n    async ({ data, chatId, onMessage, onClose, onDone, onError }: ChatParams) => {\n      if (!data?.user_input && !data?.doc_id) {\n        message.warning(i18n.t('no_context_tip'));\n        return;\n      }\n\n      const parmas = {\n        ...data,\n        conv_uid: chatId,\n      };\n\n      if (!parmas.conv_uid) {\n        message.error('conv_uid 不存在，请刷新后重试');\n        return;\n      }\n\n      try {\n        await fetchEventSource(`${process.env.API_BASE_URL ?? ''}${queryAgentURL}`, {\n          method: 'POST',\n          headers: {\n            'Content-Type': 'application/json',\n          },\n          body: JSON.stringify(parmas),\n          signal: ctrl.signal,\n          openWhenHidden: true,\n          async onopen(response) {\n            if (response.ok && response.headers.get('content-type') === EventStreamContentType) {\n              return;\n            }\n          },\n          onclose() {\n            ctrl.abort();\n            onClose?.();\n          },\n          onerror(err) {\n            throw new Error(err);\n          },\n          onmessage: (event) => {\n            let message = event.data;\n            try {\n              message = JSON.parse(message).vis;\n            } catch (e) {\n              message.replaceAll('\\\\n', '\\n');\n            }\n            if (message === '[DONE]') {\n              onDone?.();\n            } else if (message?.startsWith('[ERROR]')) {\n              onError?.(message?.replace('[ERROR]', ''));\n            } else {\n              onMessage?.(message);\n            }\n          },\n        });\n      } catch (err) {\n        ctrl.abort();\n        onError?.('Sorry, We meet some error, please try agin later.', err as Error);\n      }\n    },\n    [queryAgentURL],\n  );\n\n  useEffect(() => {\n    return () => {\n      ctrl.abort();\n    };\n  }, []);\n\n  return chat;\n};\n\nexport default useChat;\n"
  },
  {
    "path": "hooks/use-summary.ts",
    "content": "import { ChatContext } from '@/app/chat-context';\nimport { ChatHistoryResponse } from '@/types/chat';\nimport { useCallback, useContext } from 'react';\nimport useChat from './use-chat';\nimport { apiInterceptors, getChatHistory } from '@/client/api';\n\nconst useSummary = () => {\n  const { history, setHistory, chatId, model, docId } = useContext(ChatContext);\n  const chat = useChat({ queryAgentURL: '/knowledge/document/summary' });\n\n  const summary = useCallback(\n    async (curDocId?: number) => {\n      const [, res] = await apiInterceptors(getChatHistory(chatId));\n      const tempHistory: ChatHistoryResponse = [\n        ...res!,\n        { role: 'human', context: '', model_name: model, order: 0, time_stamp: 0 },\n        { role: 'view', context: '', model_name: model, order: 0, time_stamp: 0, retry: true },\n      ];\n      const index = tempHistory.length - 1;\n      setHistory([...tempHistory]);\n      await chat({\n        data: {\n          doc_id: curDocId || docId,\n          model_name: model,\n        },\n        chatId,\n        onMessage: (message) => {\n          tempHistory[index].context = message;\n          setHistory([...tempHistory]);\n        },\n      });\n    },\n    [history, model, docId, chatId],\n  );\n  return summary;\n};\n\nexport default useSummary;\n"
  },
  {
    "path": "next.config.js",
    "content": "/** @type {import('next').NextConfig} */\nconst nextConfig = {\n  output: 'export',\n  experimental: {\n    esmExternals: 'loose',\n  },\n  typescript: {\n    ignoreBuildErrors: true,\n  },\n  env: {\n    API_BASE_URL: process.env.API_BASE_URL,\n  },\n  trailingSlash: true,\n  images: { unoptimized: true },\n};\n\nmodule.exports = nextConfig;\n"
  },
  {
    "path": "nprogress.css",
    "content": "/* Make clicks pass-through */\n#nprogress {\n  pointer-events: none;\n}\n\n#nprogress .bar {\n  background: var(--joy-palette-primary-500, #096BDE);\n\n  position: fixed;\n  z-index: 10031;\n  top: 0;\n  left: 0;\n\n  width: 100%;\n  height: 3px;\n}\n\n/* Fancy blur effect */\n#nprogress .peg {\n  display: block;\n  position: absolute;\n  right: 0px;\n  width: 100px;\n  height: 100%;\n  box-shadow: 0 0 10px var(--joy-palette-primary-500, #096BDE), 0 0 5px var(--joy-palette-primary-500, #096BDE);\n  opacity: 1;\n\n  -webkit-transform: rotate(3deg) translate(0px, -4px);\n  -ms-transform: rotate(3deg) translate(0px, -4px);\n  transform: rotate(3deg) translate(0px, -4px);\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"db-gpt-web\",\n  \"version\": \"0.1.0\",\n  \"private\": true,\n  \"scripts\": {\n    \"start\": \"next start\",\n    \"dev\": \"next dev\",\n    \"build\": \"next build\",\n    \"build:prod\": \"APP_ENV=prod next build\",\n    \"lint\": \"next lint\",\n    \"export\": \"next export\",\n    \"compile\": \"next build && next export\"\n  },\n  \"dependencies\": {\n    \"@ant-design/icons\": \"^5.2.5\",\n    \"@antv/ava\": \"3.2.1-beta.1\",\n    \"@antv/g2\": \"^5.1.8\",\n    \"@antv/s2\": \"^1.51.2\",\n    \"@berryv/g2-react\": \"^0.1.0\",\n    \"@microsoft/fetch-event-source\": \"^2.0.1\",\n    \"@monaco-editor/react\": \"^4.5.2\",\n    \"@mui/icons-material\": \"^5.11.16\",\n    \"@mui/joy\": \"5.0.0-beta.5\",\n    \"ahooks\": \"^3.7.8\",\n    \"antd\": \"^5.6.2\",\n    \"axios\": \"^1.3.4\",\n    \"classnames\": \"^2.3.2\",\n    \"copy-to-clipboard\": \"^3.3.3\",\n    \"i18next\": \"^23.4.5\",\n    \"lodash\": \"^4.17.21\",\n    \"moment\": \"^2.29.4\",\n    \"next\": \"13.4.7\",\n    \"next-auth\": \"^4.20.1\",\n    \"nprogress\": \"^0.2.0\",\n    \"react\": \"18.2.0\",\n    \"react-dom\": \"^18.2.0\",\n    \"react-i18next\": \"^13.2.0\",\n    \"react-markdown\": \"^8.0.7\",\n    \"react-syntax-highlighter\": \"^15.5.0\",\n    \"reactflow\": \"^11.10.2\",\n    \"rehype-raw\": \"6.1.1\",\n    \"remark-gfm\": \"^3.0.1\",\n    \"sql-formatter\": \"^12.2.4\",\n    \"tailwindcss\": \"3.3.2\",\n    \"tailwindcss-animated\": \"^1.0.1\",\n    \"typescript\": \"5.1.3\"\n  },\n  \"devDependencies\": {\n    \"@types/lodash\": \"^4.14.195\",\n    \"@types/node\": \"20.5.7\",\n    \"@types/nprogress\": \"^0.2.0\",\n    \"@types/react\": \"18.2.14\",\n    \"@types/react-dom\": \"18.2.6\",\n    \"@types/react-syntax-highlighter\": \"^15.5.7\",\n    \"autoprefixer\": \"10.4.14\",\n    \"eslint\": \"8.43.0\",\n    \"eslint-config-next\": \"13.4.7\"\n  },\n  \"description\": \"DB-GPT WebUI\",\n  \"main\": \"next.config.js\",\n  \"directories\": {\n    \"lib\": \"lib\"\n  },\n  \"author\": \"\",\n  \"license\": \"ISC\",\n  \"repository\": \"https://github.com/eosphoros-ai/DB-GPT-Web.git\"\n}\n"
  },
  {
    "path": "pages/_app.tsx",
    "content": "import type { AppProps } from 'next/app';\nimport React, { useContext, useEffect, useRef } from 'react';\nimport SideBar from '@/components/layout/side-bar';\nimport TopProgressBar from '@/components/layout/top-progress-bar';\nimport { useTranslation } from 'react-i18next';\nimport { ChatContext, ChatContextProvider } from '@/app/chat-context';\nimport classNames from 'classnames';\nimport '../styles/globals.css';\nimport '../nprogress.css';\nimport '../app/i18n';\nimport { STORAGE_LANG_KEY } from '@/utils';\nimport { ConfigProvider, MappingAlgorithm, theme } from 'antd';\nimport zhCN from 'antd/locale/zh_CN';\nimport enUS from 'antd/locale/en_US';\nimport { CssVarsProvider, ThemeProvider, useColorScheme } from '@mui/joy';\nimport { joyTheme } from '@/defaultTheme';\n\nconst antdDarkTheme: MappingAlgorithm = (seedToken, mapToken) => {\n  return {\n    ...theme.darkAlgorithm(seedToken, mapToken),\n    colorBgBase: '#232734',\n    colorBorder: '#828282',\n    colorBgContainer: '#232734',\n  };\n};\n\nfunction CssWrapper({ children }: { children: React.ReactElement }) {\n  const { mode } = useContext(ChatContext);\n  const { i18n } = useTranslation();\n  const { setMode: setMuiMode } = useColorScheme();\n\n  useEffect(() => {\n    setMuiMode(mode);\n  }, [mode]);\n\n  useEffect(() => {\n    if (mode) {\n      document.body?.classList?.add(mode);\n      if (mode === 'light') {\n        document.body?.classList?.remove('dark');\n      } else {\n        document.body?.classList?.remove('light');\n      }\n    }\n  }, [mode]);\n\n  useEffect(() => {\n    i18n.changeLanguage && i18n.changeLanguage(window.localStorage.getItem(STORAGE_LANG_KEY) || 'en');\n  }, [i18n]);\n\n  return (\n    <div>\n      <TopProgressBar />\n      {children}\n    </div>\n  );\n}\n\nfunction LayoutWrapper({ children }: { children: React.ReactNode }) {\n  const { isMenuExpand, mode } = useContext(ChatContext);\n  const { i18n } = useTranslation();\n\n  return (\n    <ConfigProvider\n      locale={i18n.language === 'en' ? enUS : zhCN}\n      theme={{\n        token: {\n          colorPrimary: '#0069FE',\n          borderRadius: 4,\n        },\n        algorithm: mode === 'dark' ? antdDarkTheme : undefined,\n      }}\n    >\n      <div className=\"flex w-screen h-screen overflow-hidden\">\n        <div className={classNames('transition-[width]', isMenuExpand ? 'w-60' : 'w-20', 'hidden', 'md:block')}>\n          <SideBar />\n        </div>\n        <div className=\"flex flex-col flex-1 relative overflow-hidden\">{children}</div>\n      </div>\n    </ConfigProvider>\n  );\n}\n\nfunction MyApp({ Component, pageProps }: AppProps) {\n  return (\n    <ChatContextProvider>\n      <ThemeProvider theme={joyTheme}>\n        <CssVarsProvider theme={joyTheme} defaultMode=\"light\">\n          <CssWrapper>\n            <LayoutWrapper>\n              <Component {...pageProps} />\n            </LayoutWrapper>\n          </CssWrapper>\n        </CssVarsProvider>\n      </ThemeProvider>\n    </ChatContextProvider>\n  );\n}\n\nexport default MyApp;\n"
  },
  {
    "path": "pages/_document.tsx",
    "content": "import { createCache, StyleProvider } from '@ant-design/cssinjs';\nimport Document, { DocumentContext, Head, Html, Main, NextScript } from 'next/document';\nimport { doExtraStyle } from '../genAntdCss';\n\nclass MyDocument extends Document {\n  static async getInitialProps(ctx: DocumentContext) {\n    const cache = createCache();\n    let fileName = '';\n    const originalRenderPage = ctx.renderPage;\n    ctx.renderPage = () =>\n      originalRenderPage({\n        enhanceApp: (App) => (props) =>\n          (\n            <StyleProvider cache={cache} hashPriority=\"high\">\n              <App {...props} />\n            </StyleProvider>\n          ),\n      });\n    const initialProps = await Document.getInitialProps(ctx);\n\n    fileName = doExtraStyle({\n      cache,\n    });\n\n    return {\n      ...initialProps,\n      styles: (\n        <>\n          {initialProps.styles}\n          {/* 1.2 inject css */}\n          {fileName && <link rel=\"stylesheet\" href={`/${fileName}`} />}\n        </>\n      ),\n    };\n  }\n\n  render() {\n    return (\n      <Html lang=\"en\">\n        <Head>\n          <link rel=\"icon\" href=\"/favicon.ico\" />\n          <meta name=\"description\" content=\"Revolutionizing Database Interactions with Private LLM Technology\" />\n          <meta property=\"og:site_name\" content=\"dbgpt.site\" />\n          <meta property=\"og:description\" content=\"eosphoros-ai\" />\n          <meta property=\"og:title\" content=\"DB-GPT\" />\n        </Head>\n        <body>\n          <Main />\n          <NextScript />\n        </body>\n      </Html>\n    );\n  }\n}\n\nexport default MyDocument;\n"
  },
  {
    "path": "pages/agent/index.tsx",
    "content": "import MarketPlugins from '@/components/agent/market-plugins';\nimport MyPlugins from '@/components/agent/my-plugins';\nimport { Tabs } from 'antd';\nimport { useMemo, useState } from 'react';\nimport { useTranslation } from 'react-i18next';\n\nfunction Agent() {\n  const { t } = useTranslation();\n  const [activeKey, setActiveKey] = useState('market');\n\n  const items: Required<Parameters<typeof Tabs>[0]['items']> = useMemo(\n    () => [\n      {\n        key: 'market',\n        label: t('Market_Plugins'),\n        children: <MarketPlugins />,\n      },\n      {\n        key: 'my',\n        label: t('My_Plugins'),\n        children: activeKey === 'market' ? null : <MyPlugins />,\n      },\n    ],\n    [t, activeKey],\n  );\n\n  return (\n    <div className=\"h-screen p-4 md:p-6 overflow-y-auto\">\n      <Tabs activeKey={activeKey} items={items} onChange={setActiveKey} />\n    </div>\n  );\n}\n\nexport default Agent;\n"
  },
  {
    "path": "pages/app/index.tsx",
    "content": "import AppModal from '@/components/app/app-modal';\nimport AppCard from '@/components/app/app-card';\nimport { Button, Spin, Tabs, TabsProps } from 'antd';\nimport React, { useEffect, useState } from 'react';\nimport { apiInterceptors, getAppList } from '@/client/api';\nimport { IApp } from '@/types/app';\nimport { PlusOutlined } from '@ant-design/icons';\nimport { useTranslation } from 'react-i18next';\nimport MyEmpty from '@/components/common/MyEmpty';\n\ntype TabKey = 'app' | 'collected';\n\ntype ModalType = 'edit' | 'add';\n\nexport default function App() {\n  const { t } = useTranslation();\n\n  const [open, setOpen] = useState<boolean>(false);\n  const [spinning, setSpinning] = useState<boolean>(false);\n  const [activeKey, setActiveKey] = useState<TabKey>('app');\n  const [apps, setApps] = useState<IApp[]>([]);\n  const [curApp, setCurApp] = useState<IApp>();\n  const [modalType, setModalType] = useState<ModalType>('add');\n\n  const handleCreate = () => {\n    setModalType('add');\n    setOpen(true);\n  };\n\n  const handleCancel = () => {\n    setOpen(false);\n  };\n\n  const handleEdit = (app: any) => {\n    setModalType('edit');\n    setCurApp(app);\n    setOpen(true);\n  };\n\n  const handleTabChange = (activeKey: string) => {\n    setActiveKey(activeKey as TabKey);\n    if (activeKey === 'collected') {\n      initData({ is_collected: true });\n    } else {\n      initData();\n    }\n  };\n\n  const initData = async (params = {}) => {\n    setSpinning(true);\n    const [error, data] = await apiInterceptors(getAppList(params));\n    if (error) {\n      setSpinning(false);\n      return;\n    }\n    if (!data) return;\n\n    setApps(data.app_list || []);\n    setSpinning(false);\n  };\n\n  useEffect(() => {\n    initData();\n  }, []);\n\n  const renderAppList = (data: { isCollected: boolean }) => {\n    const isNull = data.isCollected ? apps.every((item) => !item.is_collected) : apps.length === 0;\n\n    return (\n      <div>\n        {!data.isCollected && (\n          <Button onClick={handleCreate} type=\"primary\" className=\"mb-4\" icon={<PlusOutlined />}>\n            {t('create')}\n          </Button>\n        )}\n        {!isNull ? (\n          <div className=\" w-full flex flex-wrap pb-0 gap-4\">\n            {apps.map((app, index) => {\n              return <AppCard handleEdit={handleEdit} key={index} app={app} updateApps={initData} isCollected={activeKey === 'collected'} />;\n            })}\n          </div>\n        ) : (\n          <MyEmpty />\n        )}\n      </div>\n    );\n  };\n\n  const items: TabsProps['items'] = [\n    {\n      key: 'app',\n      label: t('App'),\n      children: renderAppList({ isCollected: false }),\n    },\n    {\n      key: 'collected',\n      label: t('collected'),\n      children: renderAppList({ isCollected: true }),\n    },\n  ];\n\n  return (\n    <>\n      <Spin spinning={spinning}>\n        <div className=\"h-screen w-full p-4 md:p-6 overflow-y-auto\">\n          <Tabs defaultActiveKey=\"app\" items={items} onChange={handleTabChange} />\n          {open && (\n            <AppModal app={modalType === 'edit' ? curApp : {}} type={modalType} updateApps={initData} open={open} handleCancel={handleCancel} />\n          )}\n        </div>\n      </Spin>\n    </>\n  );\n}\n"
  },
  {
    "path": "pages/chat/index.tsx",
    "content": "import React, { useContext, useEffect } from 'react';\nimport { useRouter } from 'next/router';\nimport { ChatContext } from '@/app/chat-context';\nimport dynamic from 'next/dynamic';\n\nconst DbEditor = dynamic(() => import('@/components/chat/db-editor'), { ssr: false });\nconst ChatContainer = dynamic(() => import('@/components/chat/chat-container'), { ssr: false });\n\nfunction Chat() {\n  const {\n    query: { id, scene },\n  } = useRouter();\n  const { isContract, setIsContract, setIsMenuExpand } = useContext(ChatContext);\n\n  useEffect(() => {\n    // 仅初始化执行，防止dashboard页面无法切换状态\n    setIsMenuExpand(scene !== 'chat_dashboard');\n    // 路由变了要取消Editor模式，再进来是默认的Preview模式\n    if (id && scene) {\n      setIsContract(false);\n    }\n  }, [id, scene]);\n\n  return <>{isContract ? <DbEditor /> : <ChatContainer />}</>;\n}\n\nexport default Chat;\n"
  },
  {
    "path": "pages/database/index.tsx",
    "content": "import React, { useMemo, useState } from 'react';\nimport { useAsyncEffect } from 'ahooks';\nimport { Badge, Button, Card, Drawer, Empty, Modal, message } from 'antd';\nimport FormDialog from '@/components/database/form-dialog';\nimport { apiInterceptors, getDbList, getDbSupportType, postDbDelete } from '@/client/api';\nimport { DeleteFilled, EditFilled, PlusOutlined } from '@ant-design/icons';\nimport { DBOption, DBType, DbListResponse, DbSupportTypeResponse } from '@/types/db';\nimport MuiLoading from '@/components/common/loading';\nimport { dbMapper } from '@/utils';\nimport GPTCard from '@/components/common/gpt-card';\nimport { useTranslation } from 'react-i18next';\n\ntype DBItem = DbListResponse[0];\n\nexport function isFileDb(dbTypeList: DBOption[], dbType: DBType) {\n  return dbTypeList.find((item) => item.value === dbType)?.isFileDb;\n}\n\nfunction Database() {\n  const { t } = useTranslation();\n\n  const [dbList, setDbList] = useState<DbListResponse>([]);\n  const [dbSupportList, setDbSupportList] = useState<DbSupportTypeResponse>([]);\n  const [loading, setLoading] = useState(false);\n  const [modal, setModal] = useState<{ open: boolean; info?: DBItem; dbType?: DBType }>({ open: false });\n  const [draw, setDraw] = useState<{ open: boolean; dbList?: DbListResponse; name?: string; type?: DBType }>({ open: false });\n\n  const getDbSupportList = async () => {\n    const [, data] = await apiInterceptors(getDbSupportType());\n    setDbSupportList(data ?? []);\n  };\n\n  const refreshDbList = async () => {\n    setLoading(true);\n    const [, data] = await apiInterceptors(getDbList());\n    setDbList(data ?? []);\n    setLoading(false);\n  };\n\n  const dbTypeList = useMemo(() => {\n    const supportDbList = dbSupportList.map((item) => {\n      const { db_type, is_file_db } = item;\n\n      return { ...dbMapper[db_type], value: db_type, isFileDb: is_file_db };\n    }) as DBOption[];\n    const unSupportDbList = Object.keys(dbMapper)\n      .filter((item) => !supportDbList.some((db) => db.value === item))\n      .map((item) => ({ ...dbMapper[item], value: dbMapper[item].label, disabled: true })) as DBOption[];\n    return [...supportDbList, ...unSupportDbList];\n  }, [dbSupportList]);\n\n  const onModify = (item: DBItem) => {\n    setModal({ open: true, info: item });\n  };\n\n  const onDelete = (item: DBItem) => {\n    Modal.confirm({\n      title: 'Tips',\n      content: `Do you Want to delete the ${item.db_name}?`,\n      onOk() {\n        return new Promise<void>(async (resolve, reject) => {\n          try {\n            const [err] = await apiInterceptors(postDbDelete(item.db_name));\n            if (err) {\n              message.error(err.message);\n              reject();\n              return;\n            }\n            message.success('success');\n            refreshDbList();\n            resolve();\n          } catch (e: any) {\n            reject();\n          }\n        });\n      },\n    });\n  };\n\n  const dbListByType = useMemo(() => {\n    const mapper = dbTypeList.reduce((acc, item) => {\n      acc[item.value] = dbList.filter((dbConn) => dbConn.db_type === item.value);\n      return acc;\n    }, {} as Record<DBType, DbListResponse>);\n    return mapper;\n  }, [dbList, dbTypeList]);\n\n  useAsyncEffect(async () => {\n    await refreshDbList();\n    await getDbSupportList();\n  }, []);\n\n  const handleDbTypeClick = (info: DBOption) => {\n    const dbItems = dbList.filter((item) => item.db_type === info.value);\n    setDraw({ open: true, dbList: dbItems, name: info.label, type: info.value });\n  };\n\n  return (\n    <div className=\"relative p-4 md:p-6 min-h-full overflow-y-auto\">\n      <MuiLoading visible={loading} />\n      <div className=\"mb-4\">\n        <Button\n          type=\"primary\"\n          className=\"flex items-center\"\n          icon={<PlusOutlined />}\n          onClick={() => {\n            setModal({ open: true });\n          }}\n        >\n          {t('create')}\n        </Button>\n      </div>\n      <div className=\"flex flex-wrap gap-2 md:gap-4\">\n        {dbTypeList.map((item) => (\n          <Badge key={item.value} count={dbListByType[item.value].length} className=\"min-h-fit\">\n            <GPTCard\n              className=\"h-full\"\n              title={item.label}\n              desc={item.desc ?? ''}\n              disabled={item.disabled}\n              icon={item.icon}\n              onClick={() => {\n                if (item.disabled) return;\n                handleDbTypeClick(item);\n              }}\n            />\n          </Badge>\n        ))}\n      </div>\n      <FormDialog\n        open={modal.open}\n        dbTypeList={dbTypeList}\n        choiceDBType={modal.dbType}\n        editValue={modal.info}\n        dbNames={dbList.map((item) => item.db_name)}\n        onSuccess={() => {\n          setModal({ open: false });\n          refreshDbList();\n        }}\n        onClose={() => {\n          setModal({ open: false });\n        }}\n      />\n      <Drawer\n        title={draw.name}\n        placement=\"right\"\n        onClose={() => {\n          setDraw({ open: false });\n        }}\n        open={draw.open}\n      >\n        {draw.type && dbListByType[draw.type] && dbListByType[draw.type].length ? (\n          <>\n            <Button\n              type=\"primary\"\n              className=\"mb-4 flex items-center\"\n              icon={<PlusOutlined />}\n              onClick={() => {\n                setModal({ open: true, dbType: draw.type });\n              }}\n            >\n              Create\n            </Button>\n            {dbListByType[draw.type].map((item) => (\n              <Card\n                key={item.db_name}\n                title={item.db_name}\n                extra={\n                  <>\n                    <EditFilled\n                      className=\"mr-2\"\n                      style={{ color: '#1b7eff' }}\n                      onClick={() => {\n                        onModify(item);\n                      }}\n                    />\n                    <DeleteFilled\n                      style={{ color: '#ff1b2e' }}\n                      onClick={() => {\n                        onDelete(item);\n                      }}\n                    />\n                  </>\n                }\n                className=\"mb-4\"\n              >\n                {item.db_path ? (\n                  <p>path: {item.db_path}</p>\n                ) : (\n                  <>\n                    <p>host: {item.db_host}</p>\n                    <p>username: {item.db_user}</p>\n                    <p>port: {item.db_port}</p>\n                  </>\n                )}\n                <p>remark: {item.comment}</p>\n              </Card>\n            ))}\n          </>\n        ) : (\n          <Empty image={Empty.PRESENTED_IMAGE_DEFAULT}>\n            <Button\n              type=\"primary\"\n              className=\"flex items-center mx-auto\"\n              icon={<PlusOutlined />}\n              onClick={() => {\n                setModal({ open: true, dbType: draw.type });\n              }}\n            >\n              Create Now\n            </Button>\n          </Empty>\n        )}\n      </Drawer>\n    </div>\n  );\n}\n\nexport default Database;\n"
  },
  {
    "path": "pages/flow/canvas/index.tsx",
    "content": "import { addFlow, apiInterceptors, getFlowById, updateFlowById } from '@/client/api';\nimport MuiLoading from '@/components/common/loading';\nimport AddNodes from '@/components/flow/add-nodes';\nimport ButtonEdge from '@/components/flow/button-edge';\nimport CanvasNode from '@/components/flow/canvas-node';\nimport { IFlowData, IFlowUpdateParam } from '@/types/flow';\nimport { checkFlowDataRequied, getUniqueNodeId, mapHumpToUnderline, mapUnderlineToHump } from '@/utils/flow';\nimport { FrownOutlined, SaveOutlined } from '@ant-design/icons';\nimport { Button, Checkbox, Divider, Form, Input, Modal, Space, message, notification } from 'antd';\nimport { useSearchParams } from 'next/navigation';\nimport React, { DragEvent, useCallback, useEffect, useRef, useState } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport ReactFlow, { Background, Connection, Controls, ReactFlowProvider, addEdge, useEdgesState, useNodesState, useReactFlow, Node } from 'reactflow';\nimport 'reactflow/dist/style.css';\n\nconst { TextArea } = Input;\n\ninterface Props {\n  // Define your component props here\n}\nconst nodeTypes = { customNode: CanvasNode };\nconst edgeTypes = { buttonedge: ButtonEdge };\n\nconst Canvas: React.FC<Props> = () => {\n  const { t } = useTranslation();\n  const [messageApi, contextHolder] = message.useMessage();\n  const [form] = Form.useForm<IFlowUpdateParam>();\n  const searchParams = useSearchParams();\n  const id = searchParams?.get('id') || '';\n  const reactFlow = useReactFlow();\n\n  const [loading, setLoading] = useState(false);\n  const [nodes, setNodes, onNodesChange] = useNodesState([]);\n  const [edges, setEdges, onEdgesChange] = useEdgesState([]);\n  const reactFlowWrapper = useRef<HTMLDivElement>(null);\n  const [isModalVisible, setIsModalVisible] = useState(false);\n  const [flowInfo, setFlowInfo] = useState<IFlowUpdateParam>();\n  const [deploy, setDeploy] = useState(true);\n\n  async function getFlowData() {\n    setLoading(true);\n    const [_, data] = await apiInterceptors(getFlowById(id));\n    if (data) {\n      const flowData = mapUnderlineToHump(data.flow_data);\n      setFlowInfo(data);\n      setNodes(flowData.nodes);\n      setEdges(flowData.edges);\n    }\n    setLoading(false);\n  }\n\n  useEffect(() => {\n    id && getFlowData();\n  }, [id]);\n\n  useEffect(() => {\n    const handleBeforeUnload = (event: BeforeUnloadEvent) => {\n      event.returnValue = message;\n    };\n\n    window.addEventListener('beforeunload', handleBeforeUnload);\n\n    return () => {\n      window.removeEventListener('beforeunload', handleBeforeUnload);\n    };\n  }, []);\n\n  function onNodesClick(event: any, clickedNode: Node) {\n    reactFlow.setNodes((nds) =>\n      nds.map((node) => {\n        if (node.id === clickedNode.id) {\n          node.data = {\n            ...node.data,\n            selected: true,\n          };\n        } else {\n          node.data = {\n            ...node.data,\n            selected: false,\n          };\n        }\n        return node;\n      }),\n    );\n  }\n\n  function onConnect(connection: Connection) {\n    const newEdge = {\n      ...connection,\n      type: 'buttonedge',\n      id: `${connection.source}|${connection.target}`,\n    };\n    setEdges((eds) => addEdge(newEdge, eds));\n  }\n\n  const onDrop = useCallback(\n    (event: DragEvent) => {\n      event.preventDefault();\n      const reactFlowBounds = reactFlowWrapper.current!.getBoundingClientRect();\n      let nodeStr = event.dataTransfer.getData('application/reactflow');\n      if (!nodeStr || typeof nodeStr === 'undefined') {\n        return;\n      }\n      const nodeData = JSON.parse(nodeStr);\n      const position = reactFlow.screenToFlowPosition({\n        x: event.clientX - reactFlowBounds.left,\n        y: event.clientY - reactFlowBounds.top,\n      });\n      const nodeId = getUniqueNodeId(nodeData, reactFlow.getNodes());\n      nodeData.id = nodeId;\n      const newNode = {\n        id: nodeId,\n        position,\n        type: 'customNode',\n        data: nodeData,\n      };\n      setNodes((nds) =>\n        nds.concat(newNode).map((node) => {\n          if (node.id === newNode.id) {\n            node.data = {\n              ...node.data,\n              selected: true,\n            };\n          } else {\n            node.data = {\n              ...node.data,\n              selected: false,\n            };\n          }\n          return node;\n        }),\n      );\n    },\n    [reactFlow],\n  );\n\n  const onDragOver = useCallback((event: DragEvent) => {\n    event.preventDefault();\n    event.dataTransfer.dropEffect = 'move';\n  }, []);\n\n  function labelChange(e: React.ChangeEvent<HTMLInputElement>) {\n    const label = e.target.value;\n    // replace spaces with underscores, convert uppercase letters to lowercase, remove characters other than digits, letters, _, and -.\n    let result = label\n      .replace(/\\s+/g, '_')\n      .replace(/[^a-z0-9_-]/g, '')\n      .toLowerCase();\n    result = result;\n    form.setFieldsValue({ name: result });\n  }\n\n  function clickSave() {\n    const flowData = reactFlow.toObject() as IFlowData;\n    const [check, node, message] = checkFlowDataRequied(flowData);\n    if (!check && message) {\n      setNodes((nds) =>\n        nds.map((item) => {\n          if (item.id === node?.id) {\n            item.data = {\n              ...item.data,\n              invalid: true,\n            };\n          } else {\n            item.data = {\n              ...item.data,\n              invalid: false,\n            };\n          }\n          return item;\n        }),\n      );\n      return notification.error({ message: 'Error', description: message, icon: <FrownOutlined className=\"text-red-600\" /> });\n    }\n    setIsModalVisible(true);\n  }\n\n  async function handleSaveFlow() {\n    const { name, label, description = '', editable = false, state = 'deployed' } = form.getFieldsValue();\n    console.log(form.getFieldsValue());\n    const reactFlowObject = mapHumpToUnderline(reactFlow.toObject() as IFlowData);\n    if (id) {\n      const [, , res] = await apiInterceptors(updateFlowById(id, { name, label, description, editable, uid: id, flow_data: reactFlowObject, state }));\n      setIsModalVisible(false);\n      if (res?.success) {\n        messageApi.success(t('save_flow_success'));\n      } else if (res?.err_msg) {\n        messageApi.error(res?.err_msg);\n      }\n    } else {\n      const [_, res] = await apiInterceptors(addFlow({ name, label, description, editable, flow_data: reactFlowObject, state }));\n      if (res?.uid) {\n        messageApi.success(t('save_flow_success'));\n        const history = window.history;\n        history.pushState(null, '', `/flow/canvas?id=${res.uid}`);\n      }\n      setIsModalVisible(false);\n    }\n  }\n\n  return (\n    <>\n      <MuiLoading visible={loading} />\n      <div className=\"my-2 mx-4 flex flex-row justify-end items-center\">\n        <div className=\"w-8 h-8 rounded-md bg-stone-300 dark:bg-zinc-700 dark:text-zinc-200 flext justify-center items-center hover:text-blue-500 dark:hover:text-zinc-100\">\n          <SaveOutlined className=\"block text-xl\" onClick={clickSave} />\n        </div>\n      </div>\n      <Divider className=\"mt-0 mb-0\" />\n      <div className=\"h-[calc(100vh-60px)] w-full\" ref={reactFlowWrapper}>\n        <ReactFlow\n          nodes={nodes}\n          edges={edges}\n          nodeTypes={nodeTypes}\n          edgeTypes={edgeTypes}\n          onNodesChange={onNodesChange}\n          onEdgesChange={onEdgesChange}\n          onNodeClick={onNodesClick}\n          onConnect={onConnect}\n          onDrop={onDrop}\n          onDragOver={onDragOver}\n          minZoom={0.1}\n          fitView\n          deleteKeyCode={['Backspace', 'Delete']}\n        >\n          <Controls className=\"flex flex-row items-center\" position=\"bottom-center\" />\n          <Background color=\"#aaa\" gap={16} />\n          <AddNodes />\n        </ReactFlow>\n      </div>\n      <Modal\n        title={t('flow_modal_title')}\n        open={isModalVisible}\n        onCancel={() => {\n          setIsModalVisible(false);\n        }}\n        cancelButtonProps={{ className: 'hidden' }}\n        okButtonProps={{ className: 'hidden' }}\n      >\n        <Form\n          name=\"flow_form\"\n          form={form}\n          labelCol={{ span: 8 }}\n          wrapperCol={{ span: 16 }}\n          style={{ maxWidth: 600 }}\n          initialValues={{ remember: true }}\n          onFinish={handleSaveFlow}\n          autoComplete=\"off\"\n        >\n          <Form.Item label=\"Title\" name=\"label\" initialValue={flowInfo?.label} rules={[{ required: true, message: 'Please input flow title!' }]}>\n            <Input onChange={labelChange} />\n          </Form.Item>\n          <Form.Item\n            label=\"Name\"\n            name=\"name\"\n            initialValue={flowInfo?.name}\n            rules={[\n              { required: true, message: 'Please input flow name!' },\n              () => ({\n                validator(_, value) {\n                  const regex = /^[a-zA-Z0-9_\\-]+$/;\n                  if (!regex.test(value)) {\n                    return Promise.reject('Can only contain numbers, letters, underscores, and dashes');\n                  }\n                  return Promise.resolve();\n                },\n              }),\n            ]}\n          >\n            <Input />\n          </Form.Item>\n          <Form.Item label=\"Description\" initialValue={flowInfo?.description} name=\"description\">\n            <TextArea rows={3} />\n          </Form.Item>\n          <Form.Item label=\"Editable\" name=\"editable\" initialValue={flowInfo?.editable} valuePropName=\"checked\">\n            <Checkbox />\n          </Form.Item>\n          <Form.Item hidden name=\"state\">\n            <Input />\n          </Form.Item>\n          <Form.Item label=\"Deploy\">\n            <Checkbox\n              defaultChecked={flowInfo?.state === 'deployed' || flowInfo?.state === 'running'}\n              value={deploy}\n              onChange={(e) => {\n                const val = e.target.checked;\n                form.setFieldValue('state', val ? 'deployed' : 'developing');\n                setDeploy(val);\n              }}\n            />\n          </Form.Item>\n          <Form.Item wrapperCol={{ offset: 8, span: 16 }}>\n            <Space>\n              <Button\n                htmlType=\"button\"\n                onClick={() => {\n                  setIsModalVisible(false);\n                }}\n              >\n                Cancel\n              </Button>\n              <Button type=\"primary\" htmlType=\"submit\">\n                Submit\n              </Button>\n            </Space>\n          </Form.Item>\n        </Form>\n      </Modal>\n      {contextHolder}\n    </>\n  );\n};\n\nexport default function CanvasWrapper() {\n  return (\n    <ReactFlowProvider>\n      <Canvas />\n    </ReactFlowProvider>\n  );\n}\n"
  },
  {
    "path": "pages/flow/index.tsx",
    "content": "import { addFlow, apiInterceptors, getFlows } from '@/client/api';\nimport MyEmpty from '@/components/common/MyEmpty';\nimport MuiLoading from '@/components/common/loading';\nimport FlowCard from '@/components/flow/flow-card';\nimport { IFlow, IFlowUpdateParam } from '@/types/flow';\nimport { PlusOutlined } from '@ant-design/icons';\nimport { Button, Checkbox, Form, Input, Modal, message } from 'antd';\nimport Link from 'next/link';\nimport React, { useEffect, useRef, useState } from 'react';\nimport { useTranslation } from 'react-i18next';\n\nfunction Flow() {\n  const { t } = useTranslation();\n\n  const [showModal, setShowModal] = useState(false);\n  const [loading, setLoading] = useState(false);\n  const [flowList, setFlowList] = useState<Array<IFlow>>([]);\n  const [deploy, setDeploy] = useState(false);\n\n  const [messageApi, contextHolder] = message.useMessage();\n  const [form] = Form.useForm<Pick<IFlow, 'label' | 'name'>>();\n\n  const copyFlowTemp = useRef<IFlow>();\n\n  async function getFlowList() {\n    setLoading(true);\n    const [_, data] = await apiInterceptors(getFlows());\n    setLoading(false);\n    setFlowList(data?.items ?? []);\n  }\n\n  useEffect(() => {\n    getFlowList();\n  }, []);\n\n  function updateFlowList(uid: string) {\n    setFlowList((flows) => flows.filter((flow) => flow.uid !== uid));\n  }\n\n  const handleCopy = (flow: IFlow) => {\n    copyFlowTemp.current = flow;\n    form.setFieldValue('label', `${flow.label} Copy`);\n    form.setFieldValue('name', `${flow.name}_copy`);\n    setDeploy(false);\n    setShowModal(true);\n  };\n\n  const onFinish = async (val: { name: string; label: string }) => {\n    if (!copyFlowTemp.current) return;\n    const { source, uid, dag_id, gmt_created, gmt_modified, state, ...params } = copyFlowTemp.current;\n    const data: IFlowUpdateParam = {\n      ...params,\n      editable: true,\n      state: deploy ? 'deployed' : 'developing',\n      ...val,\n    };\n    const [err] = await apiInterceptors(addFlow(data));\n    if (!err) {\n      messageApi.success(t('save_flow_success'));\n      setShowModal(false);\n      getFlowList();\n    }\n  };\n\n  return (\n    <div className=\"relative p-4 md:p-6 min-h-full overflow-y-auto\">\n      {contextHolder}\n      <MuiLoading visible={loading} />\n      <div className=\"mb-4\">\n        <Link href=\"/flow/canvas\">\n          <Button type=\"primary\" className=\"flex items-center\" icon={<PlusOutlined />}>\n            New AWEL Flow\n          </Button>\n        </Link>\n      </div>\n      <div className=\"flex flex-wrap gap-2 md:gap-4 justify-start items-stretch\">\n        {flowList.map((flow) => (\n          <FlowCard key={flow.uid} flow={flow} deleteCallback={updateFlowList} onCopy={handleCopy} />\n        ))}\n        {flowList.length === 0 && <MyEmpty description=\"No flow found\" />}\n      </div>\n      <Modal\n        open={showModal}\n        title=\"Copy AWEL Flow\"\n        onCancel={() => {\n          setShowModal(false);\n        }}\n        footer={false}\n      >\n        <Form form={form} onFinish={onFinish} className=\"mt-6\">\n          <Form.Item name=\"name\" label=\"Name\" rules={[{ required: true }]}>\n            <Input />\n          </Form.Item>\n          <Form.Item name=\"label\" label=\"Label\" rules={[{ required: true }]}>\n            <Input />\n          </Form.Item>\n          <Form.Item label=\"Deploy\">\n            <Checkbox\n              value={deploy}\n              onChange={(e) => {\n                const val = e.target.checked;\n                setDeploy(val);\n              }}\n            />\n          </Form.Item>\n          <div className=\"flex justify-end\">\n            <Button type=\"primary\" htmlType=\"submit\">\n              {t('Submit')}\n            </Button>\n          </div>\n        </Form>\n      </Modal>\n    </div>\n  );\n}\n\nexport default Flow;\n"
  },
  {
    "path": "pages/index.tsx",
    "content": "import { useRequest } from 'ahooks';\nimport { useContext, useState } from 'react';\nimport { Divider, Spin, Tag } from 'antd';\nimport { useRouter } from 'next/navigation';\nimport Image from 'next/image';\nimport { NextPage } from 'next';\nimport { apiInterceptors, newDialogue, postScenes } from '@/client/api';\nimport ModelSelector from '@/components/chat/header/model-selector';\nimport { ChatContext } from '@/app/chat-context';\nimport { SceneResponse } from '@/types/chat';\nimport CompletionInput from '@/components/common/completion-input';\nimport { useTranslation } from 'react-i18next';\nimport { STORAGE_INIT_MESSAGE_KET } from '@/utils';\nimport Icon from '@ant-design/icons/lib/components/Icon';\nimport { ColorfulDB, ColorfulPlugin, ColorfulDashboard, ColorfulData, ColorfulExcel, ColorfulDoc, ColorfulChat } from '@/components/icons';\nimport classNames from 'classnames';\n\nconst Home: NextPage = () => {\n  const router = useRouter();\n  const { model, setModel } = useContext(ChatContext);\n  const { t } = useTranslation();\n\n  const [loading, setLoading] = useState(false);\n  const [chatSceneLoading, setChatSceneLoading] = useState<boolean>(false);\n\n  const { data: scenesList = [] } = useRequest(async () => {\n    setChatSceneLoading(true);\n    const [, res] = await apiInterceptors(postScenes());\n    setChatSceneLoading(false);\n    return res ?? [];\n  });\n\n  const submit = async (message: string) => {\n    setLoading(true);\n    const [, res] = await apiInterceptors(newDialogue({ chat_mode: 'chat_normal' }));\n    if (res) {\n      localStorage.setItem(STORAGE_INIT_MESSAGE_KET, JSON.stringify({ id: res.conv_uid, message }));\n      router.push(`/chat/?scene=chat_normal&id=${res.conv_uid}${model ? `&model=${model}` : ''}`);\n    }\n    setLoading(false);\n  };\n\n  const handleNewChat = async (scene: SceneResponse) => {\n    if (scene.show_disable) return;\n    const [, res] = await apiInterceptors(newDialogue({ chat_mode: 'chat_normal' }));\n    if (res) {\n      router.push(`/chat?scene=${scene.chat_scene}&id=${res.conv_uid}${model ? `&model=${model}` : ''}`);\n    }\n  };\n\n  function renderSceneIcon(scene: string) {\n    switch (scene) {\n      case 'chat_knowledge':\n        return <Icon className=\"w-10 h-10 mr-4 p-1\" component={ColorfulDoc} />;\n      case 'chat_with_db_execute':\n        return <Icon className=\"w-10 h-10 mr-4 p-1\" component={ColorfulData} />;\n      case 'chat_excel':\n        return <Icon className=\"w-10 h-10 mr-4 p-1\" component={ColorfulExcel} />;\n      case 'chat_with_db_qa':\n        return <Icon className=\"w-10 h-10 mr-4 p-1\" component={ColorfulDB} />;\n      case 'chat_dashboard':\n        return <Icon className=\"w-10 h-10 mr-4 p-1\" component={ColorfulDashboard} />;\n      case 'chat_agent':\n        return <Icon className=\"w-10 h-10 mr-4 p-1\" component={ColorfulPlugin} />;\n      case 'dbgpt_chat':\n        return <Icon className=\"w-10 h-10 mr-4 p-1\" component={ColorfulChat} />;\n      default:\n        return null;\n    }\n  }\n\n  return (\n    <div className=\"px-4 h-screen flex flex-col justify-center items-center overflow-hidden\">\n      <div className=\"max-w-3xl max-h-screen overflow-y-auto\">\n        <Image\n          src=\"/LOGO.png\"\n          alt=\"Revolutionizing Database Interactions with Private LLM Technology\"\n          width={856}\n          height={160}\n          className=\"w-full mt-4\"\n          unoptimized\n        />\n        <Divider className=\"!text-[#878c93] !my-6\" plain>\n          {t('Quick_Start')}\n        </Divider>\n        <Spin spinning={chatSceneLoading}>\n          <div className=\"flex flex-wrap -m-1 md:-m-2\">\n            {scenesList.map((scene) => (\n              <div\n                key={scene.chat_scene}\n                className=\"w-full sm:w-1/2 p-1 md:p-2\"\n                onClick={() => {\n                  handleNewChat(scene);\n                }}\n              >\n                <div\n                  className={classNames(\n                    'flex flex-row justify-center h-[102px] min-h-min bg-white dark:bg-[#232734] dark:text-white rounded p-4 cursor-pointer hover:-translate-y-1 transition-[transform_shadow] duration-300 hover:shadow-[0_14px_20px_-10px_rgba(100,100,100,.1)]',\n                    { 'grayscale !cursor-no-drop': scene.show_disable },\n                  )}\n                >\n                  {renderSceneIcon(scene.chat_scene)}\n                  <div className=\"flex flex-col flex-1\">\n                    <h2 className=\"flex items-center text-lg font-sans font-semibold\">\n                      {scene.scene_name}\n                      {scene.show_disable && <Tag className=\"ml-2\">Comming soon</Tag>}\n                    </h2>\n                    <p className=\"opacity-80 line-clamp-2\">{scene.scene_describe}</p>\n                  </div>\n                </div>\n              </div>\n            ))}\n          </div>\n        </Spin>\n        <div className=\"mt-8 mb-2\">\n          <ModelSelector\n            onChange={(newModel: string) => {\n              setModel(newModel);\n            }}\n          />\n        </div>\n        <div className=\"flex flex-1 w-full mb-4\">\n          <CompletionInput loading={loading} onSubmit={submit} />\n        </div>\n      </div>\n    </div>\n  );\n};\n\nexport default Home;\n"
  },
  {
    "path": "pages/knowledge/chunk/index.tsx",
    "content": "import React, { useEffect, useState } from 'react';\nimport { useRouter } from 'next/router';\nimport { Breadcrumb, Card, Empty, Pagination, Spin } from 'antd';\nimport { useTranslation } from 'react-i18next';\nimport { apiInterceptors, getChunkList } from '@/client/api';\nimport DocIcon from '@/components/knowledge/doc-icon';\n\nconst DEDAULT_PAGE_SIZE = 10;\n\nfunction ChunkList() {\n  const router = useRouter();\n  const { t } = useTranslation();\n  const [chunkList, setChunkList] = useState<any>([]);\n  const [total, setTotal] = useState<number>(0);\n  const [loading, setLoading] = useState<boolean>(false);\n  const {\n    query: { id, spaceName },\n  } = useRouter();\n\n  const fetchChunks = async () => {\n    const [_, data] = await apiInterceptors(\n      getChunkList(spaceName as string, {\n        document_id: id as string,\n        page: 1,\n        page_size: DEDAULT_PAGE_SIZE,\n      }),\n    );\n\n    setChunkList(data?.data);\n    setTotal(data?.total!);\n  };\n\n  const loaderMore = async (page: number, page_size: number) => {\n    setLoading(true);\n    const [_, data] = await apiInterceptors(\n      getChunkList(spaceName as string, {\n        document_id: id as string,\n        page,\n        page_size,\n      }),\n    );\n    setChunkList(data?.data || []);\n    setLoading(false);\n  };\n\n  useEffect(() => {\n    spaceName && id && fetchChunks();\n  }, [id, spaceName]);\n\n  return (\n    <div className=\"h-full overflow-y-scroll relative px-2\">\n      <Breadcrumb\n        className=\"m-6\"\n        items={[\n          {\n            title: 'Knowledge',\n            onClick() {\n              router.back();\n            },\n            path: '/knowledge',\n          },\n          {\n            title: spaceName,\n          },\n        ]}\n      />\n      <Spin spinning={loading}>\n        <div className=\"flex justify-center flex-col\">\n          {chunkList?.length > 0 ? (\n            chunkList?.map((chunk: any) => {\n              return (\n                <Card\n                  key={chunk.id}\n                  className=\"mt-2\"\n                  title={\n                    <>\n                      <DocIcon type={chunk.doc_type} />\n                      <span>{chunk.doc_name}</span>\n                    </>\n                  }\n                >\n                  <p className=\"font-semibold\">{t('Content')}:</p>\n                  <p>{chunk?.content}</p>\n                  <p className=\"font-semibold\">{t('Meta_Data')}: </p>\n                  <p>{chunk?.meta_info}</p>\n                </Card>\n              );\n            })\n          ) : (\n            <Empty image={Empty.PRESENTED_IMAGE_DEFAULT}></Empty>\n          )}\n        </div>\n      </Spin>\n      <Pagination\n        className=\"mx-2 my-4 float-right right-6 bottom-4\"\n        defaultCurrent={1}\n        defaultPageSize={DEDAULT_PAGE_SIZE}\n        total={total}\n        showTotal={(total) => `Total ${total} items`}\n        onChange={loaderMore}\n      />\n    </div>\n  );\n}\n\nexport default ChunkList;\n"
  },
  {
    "path": "pages/knowledge/index.tsx",
    "content": "import React, { useState, useEffect } from 'react';\nimport { PlusOutlined } from '@ant-design/icons';\nimport { Button, Modal, Steps } from 'antd';\nimport SpaceCard from '@/components/knowledge/space-card';\nimport { File, ISpace, StepChangeParams } from '@/types/knowledge';\nimport { apiInterceptors, getSpaceList } from '@/client/api';\nimport { useTranslation } from 'react-i18next';\nimport DocUploadForm from '@/components/knowledge/doc-upload-form';\nimport SpaceForm from '@/components/knowledge/space-form';\nimport DocTypeForm from '@/components/knowledge/doc-type-form';\nimport Segmentation from '@/components/knowledge/segmentation';\nimport classNames from 'classnames';\n\nconst Knowledge = () => {\n  const [spaceList, setSpaceList] = useState<Array<ISpace> | null>([]);\n  const [isAddShow, setIsAddShow] = useState<boolean>(false);\n  const [activeStep, setActiveStep] = useState<number>(0);\n  const [spaceName, setSpaceName] = useState<string>('');\n  const [files, setFiles] = useState<Array<File>>([]);\n  const [docType, setDocType] = useState<string>('');\n  const { t } = useTranslation();\n  const addKnowledgeSteps = [\n    { title: t('Knowledge_Space_Config') },\n    { title: t('Choose_a_Datasource_type') },\n    { title: t('Upload') },\n    { title: t('Segmentation') },\n  ];\n\n  async function getSpaces() {\n    const [_, data] = await apiInterceptors(getSpaceList());\n    setSpaceList(data);\n  }\n  useEffect(() => {\n    getSpaces();\n  }, []);\n\n  const handleStepChange = ({ label, spaceName, docType, files }: StepChangeParams) => {\n    if (label === 'finish') {\n      setIsAddShow(false);\n      getSpaces();\n      setSpaceName('');\n      setDocType('');\n      getSpaces();\n    } else if (label === 'forward') {\n      activeStep === 0 && getSpaces();\n      setActiveStep((step) => step + 1);\n    } else {\n      setActiveStep((step) => step - 1);\n    }\n    files && setFiles(files);\n    spaceName && setSpaceName(spaceName);\n    docType && setDocType(docType);\n  };\n\n  function onAddDoc(spaceName: string) {\n    setSpaceName(spaceName);\n    setActiveStep(1);\n    setIsAddShow(true);\n  }\n\n  return (\n    <div className=\"bg-[#FAFAFA] dark:bg-transparent w-full h-full\">\n      <div className=\"page-body p-4 md:p-6 h-full overflow-auto\">\n        <Button\n          type=\"primary\"\n          className=\"flex items-center\"\n          icon={<PlusOutlined />}\n          onClick={() => {\n            setIsAddShow(true);\n          }}\n        >\n          Create\n        </Button>\n        <div className=\"flex flex-wrap mt-4 gap-2 md:gap-4\">\n          {spaceList?.map((space: ISpace) => (\n            <SpaceCard key={space.id} space={space} onAddDoc={onAddDoc} getSpaces={getSpaces} />\n          ))}\n        </div>\n      </div>\n      <Modal\n        title=\"Add Knowledge\"\n        centered\n        open={isAddShow}\n        destroyOnClose={true}\n        onCancel={() => {\n          setIsAddShow(false);\n        }}\n        width={1000}\n        afterClose={() => {\n          setActiveStep(0);\n          getSpaces();\n        }}\n        footer={null}\n      >\n        <Steps current={activeStep} items={addKnowledgeSteps} />\n        {activeStep === 0 && <SpaceForm handleStepChange={handleStepChange} />}\n        {activeStep === 1 && <DocTypeForm handleStepChange={handleStepChange} />}\n        <DocUploadForm\n          className={classNames({ hidden: activeStep !== 2 })}\n          spaceName={spaceName}\n          docType={docType}\n          handleStepChange={handleStepChange}\n        />\n        {activeStep === 3 && <Segmentation spaceName={spaceName} docType={docType} uploadFiles={files} handleStepChange={handleStepChange} />}\n      </Modal>\n    </div>\n  );\n};\n\nexport default Knowledge;\n"
  },
  {
    "path": "pages/models/index.tsx",
    "content": "import { apiInterceptors, getModelList } from '@/client/api';\nimport ModelCard from '@/components/model/model-card';\nimport ModelForm from '@/components/model/model-form';\nimport { IModelData } from '@/types/model';\nimport { Button, Modal } from 'antd';\nimport { useEffect, useState } from 'react';\nimport { useTranslation } from 'react-i18next';\n\nfunction Models() {\n  const { t } = useTranslation();\n  const [models, setModels] = useState<Array<IModelData>>([]);\n  const [isModalOpen, setIsModalOpen] = useState(false);\n\n  async function getModels() {\n    const [, res] = await apiInterceptors(getModelList());\n    setModels(res ?? []);\n  }\n\n  useEffect(() => {\n    getModels();\n  }, []);\n\n  return (\n    <div className=\"p-4 md:p-6 overflow-y-auto\">\n      <Button\n        className=\"mb-4\"\n        type=\"primary\"\n        onClick={() => {\n          setIsModalOpen(true);\n        }}\n      >\n        {t('create_model')}\n      </Button>\n      <div className=\"flex flex-wrap gap-2 md:gap-4\">\n        {models.map((item) => (\n          <ModelCard info={item} key={item.model_name} />\n        ))}\n      </div>\n      <Modal\n        width={800}\n        open={isModalOpen}\n        title={t('create_model')}\n        onCancel={() => {\n          setIsModalOpen(false);\n        }}\n        footer={null}\n      >\n        <ModelForm\n          onCancel={() => {\n            setIsModalOpen(false);\n          }}\n          onSuccess={() => {\n            setIsModalOpen(false);\n            getModels();\n          }}\n        />\n      </Modal>\n    </div>\n  );\n}\n\nexport default Models;\n"
  },
  {
    "path": "pages/prompt/index.tsx",
    "content": "import { useState, useEffect, useRef, Ref } from 'react';\nimport type { ColumnsType } from 'antd/es/table';\nimport type { FormInstance, MenuProps } from 'antd';\nimport { Menu, Table, Button, Tooltip, Modal } from 'antd';\nimport { PlusOutlined } from '@ant-design/icons';\nimport GroupsIcon from '@mui/icons-material/Groups';\nimport PersonIcon from '@mui/icons-material/Person';\nimport { useTranslation } from 'react-i18next';\nimport { addPrompt, apiInterceptors, getPromptList, postScenes, updatePrompt } from '@/client/api';\nimport { IPrompt } from '@/types/prompt';\nimport PromptForm from '@/components/prompt/prompt-form';\nimport { TFunction } from 'i18next';\n\nconst getItems = (t: TFunction) => [\n  {\n    label: t('Public') + ' Prompts',\n    key: 'common',\n    icon: <GroupsIcon />,\n  },\n  {\n    label: t('Private') + ' Prompts',\n    key: 'private',\n    icon: <PersonIcon />,\n  },\n];\n\nconst getColumns = (t: TFunction, handleEdit: (prompt: IPrompt) => void): ColumnsType<IPrompt> => [\n  {\n    title: t('Prompt_Info_Name'),\n    dataIndex: 'prompt_name',\n    key: 'prompt_name',\n  },\n  {\n    title: t('Prompt_Info_Scene'),\n    dataIndex: 'chat_scene',\n    key: 'chat_scene',\n  },\n  {\n    title: t('Prompt_Info_Sub_Scene'),\n    dataIndex: 'sub_chat_scene',\n    key: 'sub_chat_scene',\n  },\n  {\n    title: t('Prompt_Info_Content'),\n    dataIndex: 'content',\n    key: 'content',\n    render: (content) => (\n      <Tooltip placement=\"topLeft\" title={content}>\n        {content}\n      </Tooltip>\n    ),\n  },\n  {\n    title: t('Operation'),\n    dataIndex: 'operate',\n    key: 'operate',\n    render: (_, record) => (\n      <Button\n        onClick={() => {\n          handleEdit(record);\n        }}\n        type=\"primary\"\n      >\n        {t('Edit')}\n      </Button>\n    ),\n  },\n];\n\ntype FormType = Ref<FormInstance<any>> | undefined;\n\nconst Prompt = () => {\n  const { t } = useTranslation();\n\n  const [promptType, setPromptType] = useState<string>('common');\n  const [promptList, setPromptList] = useState<Array<IPrompt>>();\n  const [loading, setLoading] = useState<boolean>(false);\n  const [prompt, setPrompt] = useState<IPrompt>();\n  const [showModal, setShowModal] = useState<boolean>(false);\n  const [scenes, setScenes] = useState<Array<Record<string, string>>>();\n  const formRef = useRef<FormType>();\n\n  const getPrompts = async () => {\n    setLoading(true);\n    const body = {\n      prompt_type: promptType,\n      current: 1,\n      pageSize: 1000,\n      hideOnSinglePage: true,\n      showQuickJumper: true,\n    };\n    const [_, data] = await apiInterceptors(getPromptList(body));\n    setPromptList(data!);\n    setLoading(false);\n  };\n\n  const getScenes = async () => {\n    const [, res] = await apiInterceptors(postScenes());\n    setScenes(res?.map((scene) => ({ value: scene.chat_scene, label: scene.scene_name })));\n  };\n\n  const onFinish = async (newPrompt: IPrompt) => {\n    if (prompt) {\n      await apiInterceptors(updatePrompt({ ...newPrompt, prompt_type: promptType }));\n    } else {\n      await apiInterceptors(addPrompt({ ...newPrompt, prompt_type: promptType }));\n    }\n    getPrompts();\n    handleClose();\n  };\n\n  const handleEditBtn = (prompt: IPrompt) => {\n    setPrompt(prompt);\n    setShowModal(true);\n  };\n\n  const handleAddBtn = () => {\n    setShowModal(true);\n    setPrompt(undefined);\n  };\n\n  const handleClose = () => {\n    setShowModal(false);\n  };\n\n  const handleMenuChange: MenuProps['onClick'] = (e) => {\n    const type = e.key;\n    setPromptType(type);\n  };\n\n  useEffect(() => {\n    getPrompts();\n  }, [promptType]);\n\n  useEffect(() => {\n    getScenes();\n  }, []);\n\n  return (\n    <div>\n      <Menu onClick={handleMenuChange} selectedKeys={[promptType]} mode=\"horizontal\" items={getItems(t)} />\n      <div className=\"px-6 py-4\">\n        <div className=\"flex flex-row-reverse mb-4\">\n          <Button className=\"flex items-center\" onClick={handleAddBtn}>\n            <PlusOutlined />\n            {t('Add')} Prompts\n          </Button>\n          {promptType === 'common' && (\n            <Button className=\"mr-2 flex items-center\" disabled>\n              <PlusOutlined />\n              {t('Add')} Prompts {t('template')}\n            </Button>\n          )}\n        </div>\n        <Table\n          columns={getColumns(t, handleEditBtn)}\n          dataSource={promptList}\n          loading={loading}\n          rowKey={(record) => record.prompt_name}\n          scroll={{ y: 600 }}\n        />\n      </div>\n      <Modal\n        title={`${prompt ? t('Edit') : t('Add')} Prompts`}\n        destroyOnClose\n        open={showModal}\n        onCancel={handleClose}\n        cancelText={t('cancel')}\n        okText={t('submit')}\n        onOk={() => {\n          // @ts-ignore\n          formRef.current?.submit();\n        }}\n      >\n        <PromptForm scenes={scenes} ref={formRef as FormType} prompt={prompt} onFinish={onFinish} />\n      </Modal>\n    </div>\n  );\n};\n\nexport default Prompt;\n"
  },
  {
    "path": "postcss.config.js",
    "content": "module.exports = {\n  plugins: {\n    tailwindcss: {},\n    autoprefixer: {},\n  },\n}\n"
  },
  {
    "path": "styles/globals.css",
    "content": "@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\nbody {\n  margin: 0;\n  font-family: var(--joy-fontFamily-body, var(--joy-Josefin Sans, sans-serif));\n  line-height: var(--joy-lineHeight-md, 1.5);\n  --antd-primary-color: #0069fe;\n}\n\n.light {\n  color: #333;\n  background-color: #f7f7f7;\n}\n\n.dark {\n  color: #f7f7f7;\n  background-color: #151622;\n}\n.dark-sub-bg {\n  background-color: rgb(35, 38, 44);\n}\n\n.ant-btn-primary {\n  background-color: var(--antd-primary-color);\n}\n\n.ant-pagination .ant-pagination-prev * {\n  color: var(--antd-primary-color) !important;\n}\n\n.ant-pagination .ant-pagination-next * {\n  color: var(--antd-primary-color) !important;\n}\n\n.ant-pagination .ant-pagination-item a {\n  color: rgb(176, 176, 191);\n}\n\n.ant-pagination .ant-pagination-item.ant-pagination-item-active {\n  background-color: var(--antd-primary-color) !important;\n}\n\n.ant-pagination .ant-pagination-item.ant-pagination-item-active a {\n  color: white !important;\n}\n\ntable tr td {\n  overflow: hidden;\n  white-space: nowrap;\n  text-overflow: ellipsis;\n}\n.scrollbar-default::-webkit-scrollbar {\n  display: block;\n  width: 6px;\n}\n/* 自定义滚动条样式 */\n::-webkit-scrollbar {\n  display: none;\n}\n\n::-webkit-scrollbar-track {\n  background: #f1f1f1;\n}\n\n::-webkit-scrollbar-thumb {\n  background: #888;\n}\n\n::-webkit-scrollbar-thumb:hover {\n  background: #555;\n}\n\n.dark :where(.css-dev-only-do-not-override-18iikkb).ant-tabs .ant-tabs-tab-btn {\n  color: white;\n}\n\n:where(.css-dev-only-do-not-override-18iikkb).ant-form-item .ant-form-item-label > label {\n  height: 36px;\n}\n\n@keyframes rotate {\n  to {\n    transform: rotate(360deg);\n  }\n}\n.react-flow__panel {\n  display: none !important;\n}\n"
  },
  {
    "path": "tailwind.config.js",
    "content": "const defaultTheme = require('tailwindcss/defaultTheme');\n\n/** @type {import('tailwindcss').Config} */\nmodule.exports = {\n  content: ['./pages/**/*.{js,ts,jsx,tsx,mdx}', './components/**/*.{js,ts,jsx,tsx,mdx}', './app/**/*.{js,ts,jsx,tsx,mdx}'],\n  theme: {\n    extend: {\n      fontFamily: {\n        sans: ['\"Josefin Sans\"', ...defaultTheme.fontFamily.sans],\n      },\n      colors: {\n        theme: {\n          primary: '#0069fe',\n          light: '#f7f7f7',\n          dark: '#151622',\n          'dark-container': '#232734',\n        },\n      },\n    },\n  },\n  important: true,\n  darkMode: 'class',\n  /**\n   * @see https://www.tailwindcss-animated.com/configurator.html\n   */\n  plugins: [require('tailwindcss-animated')],\n};\n"
  },
  {
    "path": "tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"es5\",\n    \"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n    \"allowJs\": true,\n    \"skipLibCheck\": true,\n    \"strict\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"noEmit\": true,\n    \"esModuleInterop\": true,\n    \"module\": \"esnext\",\n    \"moduleResolution\": \"node\",\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"jsx\": \"preserve\",\n    \"incremental\": true,\n    \"plugins\": [\n      {\n        \"name\": \"next\"\n      }\n    ],\n    \"paths\": {\n      \"@/*\": [\"./*\"]\n    }\n  },\n  \"include\": [\"next-env.d.ts\", \"**/*.ts\", \"**/*.tsx\", \".next/types/**/*.ts\"],\n  \"exclude\": [\"node_modules\"]\n}\n"
  },
  {
    "path": "types/agent.ts",
    "content": "export type PostAgentHubUpdateParams = {\n  channel: string;\n  url: string;\n  branch: string;\n  authorization: string;\n};\n\nexport type PostAgentQueryParams = {\n  page_index: number;\n  page_size: number;\n  filter?: {\n    name?: string;\n    description?: string;\n    author?: string;\n    email?: string;\n    type?: string;\n    version?: string;\n    storage_channel?: string;\n    storage_url?: string;\n  };\n};\n\nexport type IAgentPlugin = {\n  name: string;\n  description: string;\n  email: string;\n  version: string;\n  storage_url: string;\n  download_param: string;\n  installed: number;\n  id: number;\n  author: string;\n  type: string;\n  storage_channel: string;\n  created_at: string;\n};\n\nexport type PostAgentPluginResponse = {\n  page_index: number;\n  page_size: number;\n  total_page: number;\n  total_row_count: number;\n  datas: IAgentPlugin[];\n};\n\nexport type IMyPlugin = {\n  user_name: null | string;\n  id: number;\n  file_name: string;\n  version: string;\n  succ_count: number;\n  name: string;\n  tenant: null | string;\n  user_code: string;\n  type: string;\n  use_count: number;\n  created_at: string;\n  description: string;\n};\n\nexport type PostAgentMyPluginResponse = IMyPlugin[];\n\nexport type GetDBGPTsListResponse = {\n  app_code: string;\n  app_describe: string;\n  app_name: string;\n\n  language: string;\n  sys_code: string;\n  updated_at: string;\n  team_mode: string;\n  id: number;\n  user_code: string;\n  created_at: string;\n}[];\n"
  },
  {
    "path": "types/app.ts",
    "content": "// app\nexport type IApp = {\n  app_code: string;\n  /**\n   * 应用名\n   */\n  app_name: string;\n  /**\n   * 应用描述信息/简介\n   */\n  app_describe: string;\n  /**\n   * 语言/prompt关联\n   */\n  language: 'en' | 'zh';\n  /**\n   * 组织模式（AutoPlan/LayOut）\n   */\n  team_mode: string;\n  /**\n   * 组织上下文/ None\n   */\n  team_context: string;\n  /**\n   * 应用节点信息\n   */\n  details?: IDetail[];\n  /**\n   * 是否已收藏\n   */\n  is_collected: string;\n};\n\nexport type IAppData = {\n  app_list: IApp[];\n  current_page: number;\n  total_count: number;\n  total_page: number;\n};\n\n// agent\nexport type AgentParams = {\n  agent_name: string;\n  node_id: string;\n  /**\n   * Agent绑定的资源\n   */\n  resources: string;\n  /**\n   * Agent的绑定模板\n   */\n  prompt_template: string;\n  /**\n   * llm的使用策略，默认是priority\n   */\n  llm_strategy: string;\n  /**\n   * 策略包含的参数\n   */\n  llm_strategy_value: string;\n};\n\nexport type IAgent = {\n  describe?: string;\n  name: string;\n  system_message?: string;\n};\n\nexport type ITeamModal = {\n  auto_plan: string;\n  awel_layout: string;\n  singe_agent: string;\n};\n\nexport type IResource = {\n  is_dynamic: boolean;\n  name: string;\n  type: string;\n  value: string;\n};\n\nexport type IDetail = {\n  agent_name?: string;\n  app_code?: string;\n  llm_strategy?: string;\n  llm_strategy_value?: string;\n  resources?: IResource[];\n  key?: string;\n};\n"
  },
  {
    "path": "types/chat.ts",
    "content": "type ChartValue = {\n  name: string;\n  type: string;\n  value: number;\n};\n\n/**\n * dashboard chart type\n */\nexport type ChartData = {\n  chart_desc: string;\n  chart_name: string;\n  chart_sql: string;\n  chart_type: string;\n  chart_uid: string;\n  column_name: Array<string>;\n  values: Array<ChartValue>;\n};\n\nexport type SceneResponse = {\n  chat_scene: string;\n  param_title: string;\n  scene_describe: string;\n  scene_name: string;\n  show_disable: boolean;\n};\n\nexport type NewDialogueParam = {\n  chat_mode: string;\n  model?: string;\n};\n\nexport type ChatHistoryResponse = IChatDialogueMessageSchema[];\n\nexport type IChatDialogueSchema = {\n  conv_uid: string;\n  user_input: string;\n  user_name: string;\n  chat_mode:\n    | 'chat_with_db_execute'\n    | 'chat_excel'\n    | 'chat_with_db_qa'\n    | 'chat_knowledge'\n    | 'chat_dashboard'\n    | 'chat_execution'\n    | 'chat_agent'\n    | 'chat_flow'\n    | (string & {});\n  select_param: string;\n};\n\nexport type DialogueListResponse = IChatDialogueSchema[];\n\nexport type IChatDialogueMessageSchema = {\n  role: 'human' | 'view' | 'system' | 'ai';\n  context: string;\n  order: number;\n  time_stamp: number | string | null;\n  model_name: string;\n  retry?: boolean;\n};\n\nexport type ModelType =\n  | 'proxyllm'\n  | 'flan-t5-base'\n  | 'vicuna-13b'\n  | 'vicuna-7b'\n  | 'vicuna-13b-v1.5'\n  | 'vicuna-7b-v1.5'\n  | 'codegen2-1b'\n  | 'codet5p-2b'\n  | 'chatglm-6b-int4'\n  | 'chatglm-6b'\n  | 'chatglm2-6b'\n  | 'chatglm2-6b-int4'\n  | 'guanaco-33b-merged'\n  | 'falcon-40b'\n  | 'gorilla-7b'\n  | 'gptj-6b'\n  | 'proxyllm'\n  | 'chatgpt_proxyllm'\n  | 'bard_proxyllm'\n  | 'claude_proxyllm'\n  | 'wenxin_proxyllm'\n  | 'tongyi_proxyllm'\n  | 'zhipu_proxyllm'\n  | 'llama-2-7b'\n  | 'llama-2-13b'\n  | 'llama-2-70b'\n  | 'baichuan-7b'\n  | 'baichuan-13b'\n  | 'baichuan2-7b'\n  | 'baichuan2-13b'\n  | 'wizardlm-13b'\n  | 'llama-cpp'\n  | (string & {});\n\nexport type LLMOption = { label: string; icon: string };\n\nexport type FeedBack = {\n  information?: string;\n  just_fun?: string;\n  others?: string;\n  work_study?: string;\n};\n\nexport type Reference = {\n  name: string;\n  chunks: Array<number>;\n};\n\nexport type IDB = {\n  param: string;\n  type: string;\n};\n"
  },
  {
    "path": "types/db.ts",
    "content": "export type DBOption = { label: string; value: DBType; disabled?: boolean; isFileDb?: boolean; icon: string; desc?: string };\n\nexport type DBType =\n  | 'mysql'\n  | 'duckdb'\n  | 'sqlite'\n  | 'mssql'\n  | 'clickhouse'\n  | 'oracle'\n  | 'postgresql'\n  | 'db2'\n  | 'access'\n  | 'mongodb'\n  | 'starrocks'\n  | 'hbase'\n  | 'redis'\n  | 'cassandra'\n  | 'couchbase'\n  | (string & {});\n\nexport type IChatDbSchema = {\n  comment: string;\n  db_host: string;\n  db_name: string;\n  db_path: string;\n  db_port: number;\n  db_pwd: string;\n  db_type: DBType;\n  db_user: string;\n};\n\nexport type DbListResponse = IChatDbSchema[];\n\nexport type IChatDbSupportTypeSchema = {\n  db_type: DBType;\n  is_file_db: boolean;\n};\n\nexport type DbSupportTypeResponse = IChatDbSupportTypeSchema[];\n\nexport type PostDbParams = Partial<DbListResponse[0] & { file_path: string }>;\n\nexport type ChatFeedBackSchema = {\n  conv_uid: string;\n  conv_index: number;\n  question: string;\n  knowledge_space: string;\n  score: number;\n  ques_type: string;\n  messages: string;\n};\n\nexport type PromptProps = {\n  id: number;\n  chat_scene: string;\n  sub_chat_scene: string;\n  prompt_type: string;\n  content: string;\n  user_name: string;\n  prompt_name: string;\n  gmt_created: string;\n  gmt_modified: string;\n};\n"
  },
  {
    "path": "types/editor.ts",
    "content": "export type IEditorSQLRound = {\n  db_name: string;\n  round: number;\n  round_name: string;\n};\n\nexport type GetEditorSQLRoundRequest = IEditorSQLRound[];\n\nexport type PostEditorSQLRunParams = {\n  db_name: string;\n  sql: string;\n};\n\nexport type PostEditorChartRunParams = {\n  db_name: string;\n  sql?: string;\n  chart_type?: string;\n};\n\nexport type PostEditorChartRunResponse = {\n  sql_data: {\n    result_info: string;\n    run_cost: string;\n    colunms: string[];\n    values: Record<string, any>[];\n  };\n  chart_values: Record<string, any>[];\n  chart_type: string;\n};\n\nexport type PostSQLEditorSubmitParams = {\n  conv_uid: string;\n  db_name: string;\n  conv_round?: string | number | null;\n  old_sql?: string;\n  old_speak?: string;\n  new_sql?: string;\n  new_speak?: string;\n};\n\nexport type PostEditorSqlParams = {\n  con_uid: string;\n  round: string | number;\n};\n\nexport type PostEditorSqlRequest = {};\n\nexport type GetEditorySqlParams = { con_uid: string; round: string | number };\n"
  },
  {
    "path": "types/flow.ts",
    "content": "import { Node } from 'reactflow';\n\nexport type FlowState = 'deployed' | 'developing' | 'initializing' | 'testing' | 'disabled' | 'running' | 'load_failed';\n\nexport type IFlowUpdateParam = {\n  name: string;\n  label: string;\n  editable: boolean;\n  description: string;\n  uid?: string;\n  flow_data?: IFlowData;\n  state?: FlowState;\n};\n\nexport type IFlow = {\n  dag_id: string;\n  gmt_created: string;\n  gmt_modified: string;\n  uid: string;\n  name: string;\n  label: string;\n  editable: boolean;\n  description: string;\n  flow_data: IFlowData;\n  source: string;\n  state?: FlowState;\n  error_message?: string;\n};\n\nexport type IFlowResponse = {\n  items: Array<IFlow>;\n  total_count: number;\n  total_pages: number;\n  page: number;\n  page_size: number;\n};\n\nexport type IFlowNodeParameter = {\n  id: string;\n  type_name: string;\n  type_cls: string;\n  label: string;\n  name: string;\n  category: string;\n  optional: boolean;\n  default?: any;\n  placeholder?: any;\n  description: string;\n  options?: any;\n  value: any;\n  is_list?: boolean;\n};\n\nexport type IFlowNodeInput = {\n  type_name: string;\n  type_cls: string;\n  label: string;\n  name: string;\n  description: string;\n  id: string;\n  optional?: boolean | undefined;\n  value: any;\n  is_list?: boolean;\n};\n\nexport type IFlowNodeOutput = {\n  type_name: string;\n  type_cls: string;\n  label: string;\n  name: string;\n  description: string;\n  id: string;\n  optional?: boolean | undefined;\n  is_list?: boolean;\n};\n\nexport type IFlowNode = Node & {\n  type_name: string;\n  type_cls: string;\n  parent_cls?: string; // resource have this key\n  label: string;\n  name: string;\n  description: string;\n  category: string;\n  category_label: string;\n  flow_type: 'resource' | 'operator';\n  icon?: string;\n  documentation_url?: null;\n  id: string;\n  tags?: any;\n  parameters: Array<IFlowNodeParameter>;\n  inputs: Array<IFlowNodeInput>;\n  outputs: Array<IFlowNodeOutput>;\n  version: string;\n  invalid?: boolean;\n};\n\ninterface Position {\n  x: number;\n  y: number;\n  zoom: number;\n}\n\n// flodata, the data of the flow\nexport type IFlowDataNode = {\n  width: number;\n  height: number;\n  id: string;\n  position: Position;\n  position_absolute?: Position;\n  positionAbsolute?: Position;\n  data: IFlowNode;\n  type: string;\n};\n\nexport type IFlowDataEdge = {\n  source: string;\n  target: string;\n  source_handle?: string;\n  sourceHandle?: string;\n  target_handle?: string;\n  targetHandle?: string;\n  id: string;\n  type: string;\n};\n\nexport type IFlowDataViewport = {\n  x: number;\n  y: number;\n  zoom: number;\n};\n\nexport type IFlowData = {\n  nodes: Array<IFlowDataNode>;\n  edges: Array<IFlowDataEdge>;\n  viewport: IFlowDataViewport;\n};\n"
  },
  {
    "path": "types/knowledge.ts",
    "content": "import { type } from 'os';\n\nexport interface ISpace {\n  context?: any;\n  desc: string;\n  docs: string | number;\n  gmt_created: string;\n  gmt_modified: string;\n  id: string | number;\n  name: string;\n  owner: string;\n  vector_type: string;\n}\nexport type AddKnowledgeParams = {\n  name: string;\n  vector_type: string;\n  owner: string;\n  desc: string;\n};\n\nexport type BaseDocumentParams = {\n  doc_name: string;\n  content: string;\n  doc_type: string;\n};\n\nexport type Embedding = {\n  chunk_overlap: string | number;\n  chunk_size: string | number;\n  model: string;\n  recall_score: string | number;\n  recall_type: string;\n  topk: string;\n};\n\nexport type Prompt = {\n  max_token: string | number;\n  scene: string;\n  template: string;\n};\n\nexport type Summary = {\n  max_iteration: number;\n  concurrency_limit: number;\n};\nexport type IArguments = {\n  embedding: Embedding;\n  prompt: Prompt;\n  summary: Summary;\n};\n\nexport type DocumentParams = {\n  doc_name: string;\n  source?: string;\n  content: string;\n  doc_type: string;\n};\n\nexport type IDocument = {\n  doc_name: string;\n  source?: string;\n  content: string;\n  doc_type: string;\n  chunk_size: string | number;\n  gmt_created: string;\n  gmt_modified: string;\n  id: number;\n  last_sync: string;\n  result: string;\n  space: string;\n  status: string;\n  vector_ids: string;\n};\n\nexport type IDocumentResponse = {\n  data: Array<IDocument>;\n  page: number;\n  total: number;\n};\n\nexport type IStrategyParameter = {\n  param_name: string;\n  param_type: string;\n  default_value?: string | number;\n  description: string;\n};\n\nexport type IChunkStrategyResponse = {\n  strategy: string;\n  name: string;\n  parameters: Array<IStrategyParameter>;\n  suffix: Array<string>;\n  type: Array<string>;\n};\n\nexport type IStrategyProps = {\n  chunk_strategy: string;\n  chunk_size?: number;\n  chunk_overlap?: number;\n};\n\nexport type ISyncBatchParameter = {\n  doc_id: number;\n  name?: string;\n  chunk_parameters: IStrategyProps;\n};\n\nexport type ISyncBatchResponse = {\n  tasks: Array<number>;\n};\n\nexport type ChunkListParams = {\n  document_id?: string | number;\n  page: number;\n  page_size: number;\n};\n\nexport type IChunk = {\n  content: string;\n  doc_name: string;\n  doc_type: string;\n  document_id: string | number;\n  gmt_created: string;\n  gmt_modified: string;\n  id: string | number;\n  meta_info: string;\n  recall_score?: string | number;\n};\nexport type IChunkList = {\n  data: Array<IChunk>;\n  page: number;\n  total: number;\n};\n\nexport type ArgumentsParams = {\n  argument: string;\n};\n\nexport type StepChangeParams = {\n  label: 'forward' | 'back' | 'finish';\n  spaceName?: string;\n  docType?: string;\n  files?: Array<File>;\n};\n\nexport type File = {\n  name: string;\n  doc_id: number;\n  status?: string;\n};\n\nexport type SummaryParams = {\n  doc_id: number;\n  model_name: string;\n  conv_uid: string;\n};\n"
  },
  {
    "path": "types/model.ts",
    "content": "export type IModelData = {\n  chat_scene: string;\n  model_name: string;\n  model_type: string;\n  host: string;\n  port: number;\n  manager_host: string;\n  manager_port: number;\n  healthy: boolean;\n  check_healthy: boolean;\n  prompt_template: string;\n  last_heartbeat: string;\n  stream_api: string;\n  nostream_api: string;\n};\n\nexport type BaseModelParams = {\n  host: string;\n  port: number;\n  model: string;\n  worker_type: string;\n  params: any;\n};\n\nexport type ModelParams = {\n  model_name: string;\n  model_path: string;\n  proxy_api_key: string;\n  proxy_server_url: string;\n  model_type: string;\n  max_context_size: number;\n};\n\nexport type StartModelParams = {\n  host: string;\n  port: number;\n  model: string;\n  worker_type: string;\n  params: ModelParams;\n};\n\ninterface ExtMetadata {\n  tags: string;\n}\n\nexport type SupportModelParams = {\n  param_class: string;\n  param_name: string;\n  param_type: string;\n  default_value: string | boolean | number;\n  description: string;\n  required: boolean;\n  valid_values: null;\n  ext_metadata: ExtMetadata;\n};\n\nexport type SupportModel = {\n  model: string;\n  path: string;\n  worker_type: string;\n  path_exist: boolean;\n  proxy: boolean;\n  enabled: boolean;\n  host: string;\n  port: number;\n  params: SupportModelParams;\n};\n"
  },
  {
    "path": "types/prompt.ts",
    "content": "export type PromptParams = {\n  prompt_type: string;\n  current: number;\n  pageSize: number;\n  hideOnSinglePage: boolean;\n  showQuickJumper: boolean;\n};\n\nexport interface UpdatePromptParams extends IPrompt {\n  prompt_type: string;\n}\n\nexport interface IPrompt {\n  chat_scene: string;\n  content: string;\n  gmt_created: string;\n  gmt_modified: string;\n  id: number;\n  prompt_name: string;\n  prompt_type: string;\n  sub_chat_scene: string;\n  user_name?: string;\n}\n"
  },
  {
    "path": "utils/constants.ts",
    "content": "import { ModelType } from '@/types/chat';\nimport { DBType } from '@/types/db';\n\nexport const MODEL_ICON_MAP: Record<ModelType, { label: string; icon: string }> = {\n  proxyllm: { label: 'Proxy LLM', icon: '/models/chatgpt.png' },\n  'flan-t5-base': { label: 'flan-t5-base', icon: '/models/google.png' },\n  'vicuna-13b': { label: 'vicuna-13b', icon: '/models/vicuna.jpeg' },\n  'vicuna-7b': { label: 'vicuna-7b', icon: '/models/vicuna.jpeg' },\n  'vicuna-13b-v1.5': { label: 'vicuna-13b-v1.5', icon: '/models/vicuna.jpeg' },\n  'vicuna-7b-v1.5': { label: 'vicuna-7b-v1.5', icon: '/models/vicuna.jpeg' },\n  'codegen2-1b': { label: 'codegen2-1B', icon: '/models/vicuna.jpeg' },\n  'codet5p-2b': { label: 'codet5p-2b', icon: '/models/vicuna.jpeg' },\n  'chatglm-6b-int4': { label: 'chatglm-6b-int4', icon: '/models/chatglm.png' },\n  'chatglm-6b': { label: 'chatglm-6b', icon: '/models/chatglm.png' },\n  'chatglm2-6b': { label: 'chatglm2-6b', icon: '/models/chatglm.png' },\n  'chatglm2-6b-int4': { label: 'chatglm2-6b-int4', icon: '/models/chatglm.png' },\n  'guanaco-33b-merged': { label: 'guanaco-33b-merged', icon: '/models/huggingface.svg' },\n  'falcon-40b': { label: 'falcon-40b', icon: '/models/falcon.jpeg' },\n  'gorilla-7b': { label: 'gorilla-7b', icon: '/models/gorilla.png' },\n  'gptj-6b': { label: 'ggml-gpt4all-j-v1.3-groovy.bin', icon: '' },\n  chatgpt_proxyllm: { label: 'chatgpt_proxyllm', icon: '/models/chatgpt.png' },\n  bard_proxyllm: { label: 'bard_proxyllm', icon: '/models/bard.gif' },\n  claude_proxyllm: { label: 'claude_proxyllm', icon: '/models/claude.png' },\n  wenxin_proxyllm: { label: 'wenxin_proxyllm', icon: '' },\n  tongyi_proxyllm: { label: 'tongyi_proxyllm', icon: '/models/qwen2.png' },\n  zhipu_proxyllm: { label: 'zhipu_proxyllm', icon: '/models/zhipu.png' },\n  'llama-2-7b': { label: 'Llama-2-7b-chat-hf', icon: '/models/llama.jpg' },\n  'llama-2-13b': { label: 'Llama-2-13b-chat-hf', icon: '/models/llama.jpg' },\n  'llama-2-70b': { label: 'Llama-2-70b-chat-hf', icon: '/models/llama.jpg' },\n  'baichuan-13b': { label: 'Baichuan-13B-Chat', icon: '/models/baichuan.png' },\n  'baichuan-7b': { label: 'baichuan-7b', icon: '/models/baichuan.png' },\n  'baichuan2-7b': { label: 'Baichuan2-7B-Chat', icon: '/models/baichuan.png' },\n  'baichuan2-13b': { label: 'Baichuan2-13B-Chat', icon: '/models/baichuan.png' },\n  'wizardlm-13b': { label: 'WizardLM-13B-V1.2', icon: '/models/wizardlm.png' },\n  'llama-cpp': { label: 'ggml-model-q4_0.bin', icon: '/models/huggingface.svg' },\n  'internlm-7b': { label: 'internlm-chat-7b-v1_1', icon: '/models/internlm.png' },\n  'internlm-7b-8k': { label: 'internlm-chat-7b-8k', icon: '/models/internlm.png' },\n  'solar-10.7b-instruct-v1.0': { label: 'solar-10.7b-instruct-v1.0', icon: '/models/solar_logo.png' },\n};\n\nexport const dbMapper: Record<DBType, { label: string; icon: string; desc: string }> = {\n  mysql: { label: 'MySQL', icon: '/icons/mysql.png', desc: 'Fast, reliable, scalable open-source relational database management system.' },\n  mssql: { label: 'MSSQL', icon: '/icons/mssql.png', desc: 'Powerful, scalable, secure relational database system by Microsoft.' },\n  duckdb: { label: 'DuckDB', icon: '/icons/duckdb.png', desc: 'In-memory analytical database with efficient query processing.' },\n  sqlite: { label: 'Sqlite', icon: '/icons/sqlite.png', desc: 'Lightweight embedded relational database with simplicity and portability.' },\n  clickhouse: { label: 'ClickHouse', icon: '/icons/clickhouse.png', desc: 'Columnar database for high-performance analytics and real-time queries.' },\n  oracle: { label: 'Oracle', icon: '/icons/oracle.png', desc: 'Robust, scalable, secure relational database widely used in enterprises.' },\n  access: { label: 'Access', icon: '/icons/access.png', desc: 'Easy-to-use relational database for small-scale applications by Microsoft.' },\n  mongodb: { label: 'MongoDB', icon: '/icons/mongodb.png', desc: 'Flexible, scalable NoSQL document database for web and mobile apps.' },\n  doris: { label: 'ApacheDoris', icon: '/icons/doris.png', desc: 'A new-generation open-source real-time data warehouse.' },\n  starrocks: { label: 'StarRocks', icon: '/icons/starrocks.png', desc: 'An Open-Source, High-Performance Analytical Database.' },\n  db2: { label: 'DB2', icon: '/icons/db2.png', desc: 'Scalable, secure relational database system developed by IBM.' },\n  hbase: { label: 'HBase', icon: '/icons/hbase.png', desc: 'Distributed, scalable NoSQL database for large structured/semi-structured data.' },\n  redis: { label: 'Redis', icon: '/icons/redis.png', desc: 'Fast, versatile in-memory data structure store as cache, DB, or broker.' },\n  cassandra: { label: 'Cassandra', icon: '/icons/cassandra.png', desc: 'Scalable, fault-tolerant distributed NoSQL database for large data.' },\n  couchbase: { label: 'Couchbase', icon: '/icons/couchbase.png', desc: 'High-performance NoSQL document database with distributed architecture.' },\n  postgresql: {\n    label: 'PostgreSQL',\n    icon: '/icons/postgresql.png',\n    desc: 'Powerful open-source relational database with extensibility and SQL standards.',\n  },\n  spark: { label: 'Spark', icon: '/icons/spark.png', desc: 'Unified engine for large-scale data analytics.' },\n  hive: { label: 'Hive', icon: '/icons/hive.png', desc: 'A distributed fault-tolerant data warehouse system.' },\n  space: { label: 'Space', icon: '/icons/knowledge.png', desc: 'knowledge analytics.' },\n};\n"
  },
  {
    "path": "utils/ctx-axios.ts",
    "content": "import axios from 'axios';\n\nconst api = axios.create({\n  baseURL: process.env.API_BASE_URL,\n});\n\napi.defaults.timeout = 10000;\n\napi.interceptors.response.use(\n  response => response.data,\n\terr => Promise.reject(err)\n);\n\nexport default api;"
  },
  {
    "path": "utils/flow.ts",
    "content": "import { IFlowData, IFlowDataNode, IFlowNode } from '@/types/flow';\nimport { Node } from 'reactflow';\n\nexport const getUniqueNodeId = (nodeData: IFlowNode, nodes: Node[]) => {\n  let count = 0;\n  nodes.forEach((node) => {\n    if (node.data.name === nodeData.name) {\n      count++;\n    }\n  });\n  return `${nodeData.id}_${count}`;\n};\n\n// 驼峰转下划线，接口协议字段命名规范\nexport const mapHumpToUnderline = (flowData: IFlowData) => {\n  /**\n   * sourceHandle -> source_handle,\n   * targetHandle -> target_handle,\n   * positionAbsolute -> position_absolute\n   */\n  const { nodes, edges, ...rest } = flowData;\n  const newNodes = nodes.map((node) => {\n    const { positionAbsolute, ...rest } = node;\n    return {\n      position_absolute: positionAbsolute,\n      ...rest,\n    };\n  });\n  const newEdges = edges.map((edge) => {\n    const { sourceHandle, targetHandle, ...rest } = edge;\n    return {\n      source_handle: sourceHandle,\n      target_handle: targetHandle,\n      ...rest,\n    };\n  });\n  return {\n    nodes: newNodes,\n    edges: newEdges,\n    ...rest,\n  };\n};\n\nexport const mapUnderlineToHump = (flowData: IFlowData) => {\n  /**\n   * source_handle -> sourceHandle,\n   * target_handle -> targetHandle,\n   * position_absolute -> positionAbsolute\n   */\n  const { nodes, edges, ...rest } = flowData;\n  const newNodes = nodes.map((node) => {\n    const { position_absolute, ...rest } = node;\n    return {\n      positionAbsolute: position_absolute,\n      ...rest,\n    };\n  });\n  const newEdges = edges.map((edge) => {\n    const { source_handle, target_handle, ...rest } = edge;\n    return {\n      sourceHandle: source_handle,\n      targetHandle: target_handle,\n      ...rest,\n    };\n  });\n  return {\n    nodes: newNodes,\n    edges: newEdges,\n    ...rest,\n  };\n};\n\nexport const checkFlowDataRequied = (flowData: IFlowData) => {\n  const { nodes, edges } = flowData;\n  // check the input, parameters that are required\n  let result: [boolean, IFlowDataNode, string] = [true, nodes[0], ''];\n  outerLoop: for (let i = 0; i < nodes.length; i++) {\n    const node = nodes[i].data;\n    const { inputs = [], parameters = [] } = node;\n    // check inputs\n    for (let j = 0; j < inputs.length; j++) {\n      if (!edges.some((edge) => edge.targetHandle === `${nodes[i].id}|inputs|${j}`)) {\n        result = [false, nodes[i], `The input ${inputs[j].type_name} of node ${node.label} is required`];\n        break outerLoop;\n      }\n    }\n    // check parameters\n    for (let k = 0; k < parameters.length; k++) {\n      const parameter = parameters[k];\n      if (!parameter.optional && parameter.category === 'resource' && !edges.some((edge) => edge.targetHandle === `${nodes[i].id}|parameters|${k}`)) {\n        result = [false, nodes[i], `The parameter ${parameter.type_name} of node ${node.label} is required`];\n        break outerLoop;\n      } else if (!parameter.optional && parameter.category === 'common' && (parameter.value === undefined || parameter.value === null)) {\n        result = [false, nodes[i], `The parameter ${parameter.type_name} of node ${node.label} is required`];\n        break outerLoop;\n      }\n    }\n  }\n  return result;\n};\n"
  },
  {
    "path": "utils/index.ts",
    "content": "/** Theme */\nexport const STORAGE_THEME_KEY = '__db_gpt_theme_key';\n/** Language */\nexport const STORAGE_LANG_KEY = '__db_gpt_lng_key';\n/** Init Message */\nexport const STORAGE_INIT_MESSAGE_KET = '__db_gpt_im_key';\n/** Flow nodes */\nexport const FLOW_NODES_KEY = '__db_gpt_static_flow_nodes_key';\n\nexport * from './storage';\nexport * from './constants';\n"
  },
  {
    "path": "utils/request.ts",
    "content": "import { message } from 'antd';\nimport axios from './ctx-axios';\nimport { isPlainObject } from 'lodash';\n\nconst DEFAULT_HEADERS = {\n  'content-type': 'application/json',\n};\n\n// body 字段 trim\nconst sanitizeBody = (obj: Record<string, any>): string => {\n  // simple shallow copy to avoid changing original obj\n  if (!isPlainObject(obj)) return JSON.stringify(obj);\n  const resObj = { ...obj };\n  for (const key in resObj) {\n    const val = resObj[key];\n    if (typeof val === 'string') {\n      resObj[key] = val.trim();\n    }\n  }\n  return JSON.stringify(resObj);\n};\n\nexport const sendGetRequest = (url: string, qs?: { [key: string]: any }) => {\n  if (qs) {\n    const str = Object.keys(qs)\n      .filter((k) => qs[k] !== undefined && qs[k] !== '')\n      .map((k) => `${k}=${qs[k]}`)\n      .join('&');\n    if (str) {\n      url += `?${str}`;\n    }\n  }\n  return axios\n    .get<null, any>('/api' + url, {\n      headers: DEFAULT_HEADERS,\n    })\n    .then((res) => res)\n    .catch((err) => {\n      message.error(err);\n      Promise.reject(err);\n    });\n};\n\nexport const sendSpaceGetRequest = (url: string, qs?: { [key: string]: any }) => {\n  if (qs) {\n    const str = Object.keys(qs)\n      .filter((k) => qs[k] !== undefined && qs[k] !== '')\n      .map((k) => `${k}=${qs[k]}`)\n      .join('&');\n    if (str) {\n      url += `?${str}`;\n    }\n  }\n  return axios\n    .get<null, any>(url, {\n      headers: DEFAULT_HEADERS,\n    })\n    .then((res) => res)\n    .catch((err) => {\n      message.error(err);\n      Promise.reject(err);\n    });\n};\n\nexport const sendPostRequest = (url: string, body?: any) => {\n  const reqBody = sanitizeBody(body);\n  return axios\n    .post<null, any>('/api' + url, {\n      body: reqBody,\n      headers: DEFAULT_HEADERS,\n    })\n    .then((res) => res)\n    .catch((err) => {\n      message.error(err);\n      Promise.reject(err);\n    });\n};\n\nexport const sendSpacePostRequest = (url: string, body?: any) => {\n  return axios\n    .post<null, any>(url, body, {\n      headers: DEFAULT_HEADERS,\n    })\n    .then((res) => res)\n    .catch((err) => {\n      message.error(err);\n      Promise.reject(err);\n    });\n};\n\nexport const sendSpaceUploadPostRequest = (url: string, body?: any) => {\n  return axios\n    .post<null, any>(url, body)\n    .then((res) => res)\n    .catch((err) => {\n      message.error(err);\n      Promise.reject(err);\n    });\n};\n"
  },
  {
    "path": "utils/storage.ts",
    "content": "import { STORAGE_INIT_MESSAGE_KET } from '@/utils';\n\nexport function getInitMessage() {\n  const value = localStorage.getItem(STORAGE_INIT_MESSAGE_KET) ?? '';\n  try {\n    const initData = JSON.parse(value) as { id: string; message: string };\n    return initData;\n  } catch (e) {\n    return null;\n  }\n}\n"
  }
]