app-modal.tsx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396
  1. import { addApp, apiInterceptors, getAgents, getResourceType, getTeamMode, updateApp } from '@/client/api';
  2. import { AgentParams, CreateAppParams, IAgent as IAgentParams, IDetail } from '@/types/app';
  3. import { IFlow } from '@/types/flow';
  4. import type { TabsProps } from 'antd';
  5. import { Dropdown, Form, Input, Modal, Select, Space, Spin, Tabs } from 'antd';
  6. import React, { useEffect, useState } from 'react';
  7. import { useTranslation } from 'react-i18next';
  8. import AddIcon from '../icons/add-icon';
  9. import AgentPanel from './agent-panel';
  10. import DagLayout from './dag-layout';
  11. type TargetKey = string;
  12. type FieldType = {
  13. app_name: string;
  14. app_describe: string;
  15. language: string;
  16. team_mode: string;
  17. };
  18. type IAgent = {
  19. label: string;
  20. children?: React.ReactNode;
  21. onClick?: () => void;
  22. key: number | string;
  23. };
  24. interface IProps {
  25. handleCancel: () => void;
  26. open: boolean;
  27. updateApps: () => void;
  28. type: string;
  29. app?: any;
  30. }
  31. type TeamModals = 'awel_layout' | 'singe_agent' | 'auto_plan';
  32. export default function AppModal(props: IProps) {
  33. const { handleCancel, open, updateApps, type, app } = props;
  34. const { t } = useTranslation();
  35. const [spinning, setSpinning] = useState<boolean>(false);
  36. const [activeKey, setActiveKey] = useState<string>();
  37. const [teamModal, setTeamModal] = useState<{ label: string; value: string }[]>();
  38. const [agents, setAgents] = useState<TabsProps['items']>([]);
  39. const [dropItems, setDropItems] = useState<IAgent[]>([]);
  40. const [details, setDetails] = useState<IDetail[]>([...(app?.details || [])]);
  41. const [flow, setFlow] = useState<IFlow>();
  42. const [resourceTypes, setResourceTypes] = useState<string[]>();
  43. const [curTeamModal, setCurTeamModal] = useState<TeamModals>(app.team_modal || 'auto_plan');
  44. const [form] = Form.useForm();
  45. const languageOptions = [
  46. { value: 'zh', label: t('Chinese') },
  47. { value: 'en', label: t('English') },
  48. ];
  49. const onChange = (newActiveKey: string) => {
  50. setActiveKey(newActiveKey);
  51. };
  52. const createApp = async (app: CreateAppParams) => {
  53. await apiInterceptors(type === 'add' ? addApp(app) : updateApp(app));
  54. await updateApps();
  55. };
  56. const initApp = async () => {
  57. const appDetails = app.details;
  58. const [_, resourceType] = await apiInterceptors(getResourceType());
  59. if (appDetails?.length > 0) {
  60. setAgents(
  61. appDetails?.map((item: AgentParams) => {
  62. return {
  63. label: item?.agent_name,
  64. children: (
  65. <AgentPanel
  66. editResources={type === 'edit' && item.resources}
  67. detail={{
  68. key: item?.agent_name,
  69. llm_strategy: item?.llm_strategy,
  70. agent_name: item?.agent_name,
  71. prompt_template: item?.prompt_template,
  72. llm_strategy_value: item?.llm_strategy_value,
  73. }}
  74. updateDetailsByAgentKey={updateDetailsByAgentKey}
  75. resourceTypes={resourceType}
  76. />
  77. ),
  78. key: item?.agent_name,
  79. };
  80. }),
  81. );
  82. }
  83. };
  84. const fetchTeamModal = async () => {
  85. const [_, data] = await apiInterceptors(getTeamMode());
  86. if (!data) return null;
  87. const teamModalOptions = data.map(item => {
  88. return { value: item, label: item };
  89. });
  90. setTeamModal(teamModalOptions);
  91. };
  92. const fetchAgent = async () => {
  93. const [_, data] = await apiInterceptors(getAgents());
  94. if (!data) {
  95. return null;
  96. }
  97. setDropItems(
  98. data
  99. .map(agent => {
  100. return {
  101. label: agent.name,
  102. key: agent.name,
  103. onClick: () => {
  104. add(agent);
  105. },
  106. agent,
  107. };
  108. })
  109. .filter(item => {
  110. if (!app.details || app.details?.length === 0) {
  111. return item;
  112. }
  113. return app?.details?.every((detail: AgentParams) => detail.agent_name !== item.label);
  114. }),
  115. );
  116. };
  117. const handleFlowsChange = (data: IFlow) => {
  118. setFlow(data);
  119. };
  120. const fetchResourceType = async () => {
  121. const [_, data] = await apiInterceptors(getResourceType());
  122. if (data) {
  123. setResourceTypes(data);
  124. }
  125. };
  126. useEffect(() => {
  127. fetchTeamModal();
  128. fetchAgent();
  129. fetchResourceType();
  130. }, []);
  131. useEffect(() => {
  132. type === 'edit' && initApp();
  133. }, [resourceTypes]);
  134. useEffect(() => {
  135. setCurTeamModal(app.team_mode || 'auto_plan');
  136. }, [app]);
  137. const updateDetailsByAgentKey = (key: string, data: IDetail) => {
  138. setDetails((details: IDetail[]) => {
  139. return details.map((detail: IDetail) => {
  140. return key === (detail.agent_name || detail.key) ? data : detail;
  141. });
  142. });
  143. };
  144. const add = async (tabBar: IAgentParams) => {
  145. const newActiveKey = tabBar.name;
  146. const [_, data] = await apiInterceptors(getResourceType());
  147. setActiveKey(newActiveKey);
  148. setDetails((details: IDetail[]) => {
  149. return [...details, { key: newActiveKey, name: '', llm_strategy: 'priority' }];
  150. });
  151. setAgents((items: any) => {
  152. return [
  153. ...items,
  154. {
  155. label: newActiveKey,
  156. children: (
  157. <AgentPanel
  158. detail={{
  159. key: newActiveKey,
  160. llm_strategy: 'default',
  161. agent_name: newActiveKey,
  162. prompt_template: '',
  163. llm_strategy_value: null,
  164. }}
  165. updateDetailsByAgentKey={updateDetailsByAgentKey}
  166. resourceTypes={data}
  167. />
  168. ),
  169. key: newActiveKey,
  170. },
  171. ];
  172. });
  173. setDropItems(items => {
  174. return items.filter(item => item.key !== tabBar.name);
  175. });
  176. };
  177. const remove = (targetKey: TargetKey) => {
  178. let newActiveKey = activeKey;
  179. let lastIndex = -1;
  180. if (!agents) {
  181. return null;
  182. }
  183. agents.forEach((item, i) => {
  184. if (item.key === targetKey) {
  185. lastIndex = i - 1;
  186. }
  187. });
  188. const newPanes = agents.filter(item => item.key !== targetKey);
  189. if (newPanes.length && newActiveKey === targetKey) {
  190. if (lastIndex >= 0) {
  191. newActiveKey = newPanes[lastIndex].key;
  192. } else {
  193. newActiveKey = newPanes[0].key;
  194. }
  195. }
  196. setDetails((details: IDetail[]) => {
  197. return details?.filter((detail: any) => {
  198. return (detail.agent_name || detail.key) !== targetKey;
  199. });
  200. });
  201. setAgents(newPanes);
  202. setActiveKey(newActiveKey);
  203. setDropItems((items: any) => {
  204. return [
  205. ...items,
  206. {
  207. label: targetKey,
  208. key: targetKey,
  209. onClick: () => {
  210. add({ name: targetKey, describe: '', system_message: '' });
  211. },
  212. },
  213. ];
  214. });
  215. };
  216. const onEdit = (targetKey: any, action: 'add' | 'remove') => {
  217. if (action === 'add') {
  218. // add();
  219. } else {
  220. remove(targetKey);
  221. }
  222. };
  223. const handleSubmit = async () => {
  224. const isValidate = await form.validateFields();
  225. if (!isValidate) {
  226. return;
  227. }
  228. setSpinning(true);
  229. const data = {
  230. ...form.getFieldsValue(),
  231. };
  232. if (type === 'edit') {
  233. data.app_code = app.app_code;
  234. }
  235. if (data.team_mode !== 'awel_layout') {
  236. data.details = details;
  237. } else {
  238. const tempFlow = { ...flow };
  239. delete tempFlow.flow_data;
  240. data.team_context = tempFlow;
  241. }
  242. try {
  243. await createApp(data);
  244. } catch {
  245. return;
  246. }
  247. setSpinning(false);
  248. handleCancel();
  249. };
  250. const handleTeamModalChange = (value: TeamModals) => {
  251. setCurTeamModal(value);
  252. };
  253. const renderAddIcon = () => {
  254. return (
  255. <Dropdown menu={{ items: dropItems }} trigger={['click']}>
  256. <a className='h-8 flex items-center' onClick={e => e.preventDefault()}>
  257. <Space>
  258. <AddIcon />
  259. </Space>
  260. </a>
  261. </Dropdown>
  262. );
  263. };
  264. return (
  265. <div>
  266. <Modal
  267. okText={t('Submit')}
  268. title={type === 'edit' ? 'edit application' : 'add application'}
  269. open={open}
  270. width={'65%'}
  271. onCancel={handleCancel}
  272. onOk={handleSubmit}
  273. destroyOnClose={true}
  274. >
  275. <Spin spinning={spinning}>
  276. <Form
  277. form={form}
  278. preserve={false}
  279. size='large'
  280. className='mt-4 max-h-[70vh] overflow-auto h-[90vh]'
  281. layout='horizontal'
  282. labelAlign='left'
  283. labelCol={{ span: 4 }}
  284. initialValues={{
  285. app_name: app.app_name,
  286. app_describe: app.app_describe,
  287. language: app.language || languageOptions[0].value,
  288. team_mode: app.team_mode || 'auto_plan',
  289. }}
  290. autoComplete='off'
  291. onFinish={handleSubmit}
  292. >
  293. <Form.Item<FieldType>
  294. label={'App Name'}
  295. name='app_name'
  296. rules={[{ required: true, message: t('Please_input_the_name') }]}
  297. >
  298. <Input placeholder={t('Please_input_the_name')} />
  299. </Form.Item>
  300. <Form.Item<FieldType>
  301. label={t('Description')}
  302. name='app_describe'
  303. rules={[{ required: true, message: t('Please_input_the_description') }]}
  304. >
  305. <Input.TextArea rows={3} placeholder={t('Please_input_the_description')} />
  306. </Form.Item>
  307. <div className='flex w-full'>
  308. <Form.Item<FieldType>
  309. labelCol={{ span: 7 }}
  310. label={t('language')}
  311. name='language'
  312. className='w-1/2'
  313. rules={[{ required: true }]}
  314. >
  315. <Select className='w-2/3 ml-4' placeholder={t('language_select_tips')} options={languageOptions} />
  316. </Form.Item>
  317. <Form.Item<FieldType>
  318. label={t('team_modal')}
  319. name='team_mode'
  320. className='w-1/2'
  321. labelCol={{ span: 6 }}
  322. rules={[{ required: true }]}
  323. >
  324. <Select
  325. defaultValue={app.team_mode || 'auto_plan'}
  326. className='ml-4 w-72'
  327. onChange={handleTeamModalChange}
  328. placeholder={t('Please_input_the_work_modal')}
  329. options={teamModal}
  330. />
  331. </Form.Item>
  332. </div>
  333. {curTeamModal !== 'awel_layout' ? (
  334. <>
  335. <div className='mb-5'>Agents</div>
  336. <Tabs
  337. addIcon={renderAddIcon()}
  338. type='editable-card'
  339. onChange={onChange}
  340. activeKey={activeKey}
  341. onEdit={onEdit}
  342. items={agents}
  343. />
  344. </>
  345. ) : (
  346. <DagLayout onFlowsChange={handleFlowsChange} teamContext={app.team_context} />
  347. )}
  348. </Form>
  349. </Spin>
  350. </Modal>
  351. </div>
  352. );
  353. }