ai.service.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. const { ChatZhipuAI } = require('@langchain/community/chat_models/zhipuai');
  2. const { HumanMessage } = require('@langchain/core/messages');
  3. const { GlmModelProvider } = require('./provider/model/glm');
  4. const { RunnableConfig } = require('@langchain/core/runnables');
  5. const { extractCodeBlocks } = require('./provider/utils');
  6. const ZhipuAI = require('./provider/base/ZhipuAPIModel');
  7. const ZHIPU_AI_KEY = require('../../config').ZHIPU_AI_KEY;
  8. class AiService {
  9. async buildGeneratePrompt({ message }) {
  10. const codePrompt = `
  11. You are a low-code component development expert.
  12. Your task is to help me generate two files for a low-code development module: index.jsx and config.js.
  13. The index.jsx file should define the structure of the component using React and Ant Design, and the config.js file should specify the component's property configurations.
  14. Here’s an example of a login form component:
  15. index.jsx file:
  16. // jsx
  17. export default ({ id, type, config, onClick, onDateChange,onTextChange }, ref) => {
  18. const { useState } = window.React;
  19. const {
  20. Button,
  21. Checkbox,
  22. ColorPicker,
  23. DatePicker,
  24. Form,
  25. Input,
  26. InputNumber,
  27. Radio,
  28. Select,
  29. Slider,
  30. Switch,
  31. } = window.antd;
  32. const { RangePicker } = DatePicker;
  33. const { TextArea } = Input;
  34. const [dateFormat, setDateFormat] = useState(
  35. config.props.dateFormat || 'YYYY-MM-DD HH:mm:ss'
  36. );
  37. const onDatePickerChange = (date, dateString) => {
  38. onDateChange && onDateChange(date, dateString, dateFormat);
  39. };
  40. const onFinish = (values) => {
  41. onClick && onClick(values);
  42. };
  43. return (
  44. <div data-id={id} data-type={type}>
  45. <Form
  46. labelCol={{ span: config.props.labelCol }}
  47. wrapperCol={{ span: config.props.wrapperCol }}
  48. layout={config.props.layout}
  49. style={{ maxWidth: config.props.maxWidth }}
  50. onFinish={onFinish}
  51. >
  52. <Form.Item label="Checkbox" name="disabled" valuePropName="checked">
  53. <Checkbox>Checkbox</Checkbox>
  54. </Form.Item>
  55. <Form.Item label="Radio">
  56. <Radio.Group>
  57. <Radio value="apple"> Apple </Radio>
  58. <Radio value="pear"> Pear </Radio>
  59. </Radio.Group>
  60. </Form.Item>
  61. <Form.Item label="Input">
  62. <Input placeholder={config.props.textInput} />
  63. </Form.Item>
  64. <Form.Item label="Select">
  65. <Select>
  66. <Select.Option value="demo">Demo</Select.Option>
  67. </Select>
  68. </Form.Item>
  69. <Form.Item label="DataFormat" name="dateFormat">
  70. <Input
  71. value={dateFormat}
  72. onChange={(e) => setDateFormat(e.target.value)}
  73. />
  74. </Form.Item>
  75. <Form.Item label="DatePicker">
  76. <DatePicker onChange={onDatePickerChange} />
  77. </Form.Item>
  78. <Form.Item label="RangePicker">
  79. <RangePicker />
  80. </Form.Item>
  81. <Form.Item label="InputNumber">
  82. <InputNumber placeholder={config.props.numberInput} />
  83. </Form.Item>
  84. <Form.Item label="TextArea">
  85. <TextArea rows={config.props.textAreaRow} showCount={config.props.showCount} onChange={onTextChange}/>
  86. </Form.Item>
  87. <Form.Item label="Switch" valuePropName="checked">
  88. <Switch />
  89. </Form.Item>
  90. <Form.Item label="Button" wrapperCol={{
  91. offset: config.props.offset,
  92. span: config.props.wrapperCol,
  93. }}>
  94. <Button block={config.props.block} type={config.props.btnType}>
  95. {config.props.loginBtn}
  96. </Button>
  97. </Form.Item>
  98. <Form.Item label="Slider">
  99. <Slider />
  100. </Form.Item>
  101. <Form.Item label="ColorPicker">
  102. <ColorPicker />
  103. </Form.Item>
  104. </Form>
  105. </div>
  106. );
  107. };
  108. config.js file:
  109. // config.js
  110. export default {
  111. // 组件属性配置JSON
  112. attrs: [
  113. {
  114. type: 'Title',
  115. label: '基础设置',
  116. key: 'basic',
  117. },
  118. {
  119. type: 'Select',
  120. label: '布局',
  121. name: ['layout'],
  122. props: {
  123. options: [
  124. { value: 'horizontal', label: 'horizontal' },
  125. { value: 'vertical', label: 'vertical' },
  126. { value: 'inline', label: 'inline' }
  127. ],
  128. },
  129. },
  130. {
  131. type: 'Select',
  132. label: '按钮类型',
  133. name: ['btnType'],
  134. props: {
  135. // options参数必须写完整
  136. options: [
  137. { value: 'primary', label: 'primary' },
  138. { value: 'default', label: 'default' },
  139. { value: 'ghost', label: 'ghost' },
  140. { value: 'dashed', label: 'dashed' },
  141. { value: 'text', label: 'text' },
  142. { value: 'link', label: 'link' },
  143. ],
  144. },
  145. },
  146. {
  147. type: 'InputNumber',
  148. label: 'labelCol',
  149. name: ['labelCol'],
  150. },
  151. {
  152. type: 'InputNumber',
  153. label: 'wrapperCol',
  154. name: ['wrapperCol'],
  155. },
  156. {
  157. type: 'InputNumber',
  158. label: 'offset',
  159. name: ['offset'],
  160. },
  161. {
  162. type: 'Input',
  163. label: '按钮名称',
  164. name: ['btnText'],
  165. },
  166. {
  167. type: 'Switch',
  168. label: '块状按钮',
  169. name: ['block'],
  170. },
  171. {
  172. type: 'Input',
  173. label: '初始文本',
  174. name: ['textInput'],
  175. },
  176. {
  177. type: 'InputNumber',
  178. label: '初始数值',
  179. name: ['numberInput'],
  180. },
  181. {
  182. type: 'InputNumber',
  183. label: '文本框占据',
  184. name: ['textAreaRow'],
  185. },
  186. {
  187. type: 'Switch',
  188. label: '显示字符数',
  189. name: ['showCount'],
  190. },
  191. ],
  192. config: {
  193. // 组件默认属性值
  194. props: {
  195. layout: 'horizontal',
  196. labelCol: 8,
  197. offset: 8,
  198. wrapperCol: 16,
  199. maxWidth: 500,
  200. btnType: 'primary',
  201. btnText: '按钮',
  202. textInput: '初始文本',
  203. numberInput: 12,
  204. textAreaRow: 4,
  205. showCount: true
  206. },
  207. style: {
  208. },
  209. events: [],
  210. },
  211. // 组件事件
  212. events: [
  213. {
  214. value: 'onClick',
  215. name: '表单提交事件',
  216. },
  217. {
  218. value: 'onDateChange',
  219. name: '日期时间变化事件',
  220. },
  221. {
  222. value: 'onTextChange',
  223. name: '文本输入事件'
  224. }
  225. ],
  226. methods: [],
  227. };
  228. Note: If you need to import hooks like useState and useEffect, you should import them using const { useState, useEffect } = window.React;.
  229. Similarly, Ant Design components should be imported in this way: const { Button, Form, DatePicker, Tag } = window.antd;.
  230. All components should use AntDesign, avoid using native or other library components.
  231. Now, based on the above structure and configuration, I need you to generate the index.jsx and config.js files for a new component.
  232. The description of this component in Chinese is #{message}. You can understand it in English and help me implement the code of the component
  233. Please return only the code for both files, without any additional descirption text or markdown syntax.
  234. Please write the components that meet the requirements according to the AntDesign library and the template above. Please don't try to omit the content, and be sure to return the code completely.
  235. `;
  236. const prompt = codePrompt.replace('#{message}', message);
  237. return prompt;
  238. }
  239. async ApiZhipuAi(message) {
  240. const prompt = await this.buildGeneratePrompt({ message });
  241. const zhipuProvider = new ZhipuAI(ZHIPU_AI_KEY);
  242. const params = {
  243. model: 'glm-4-plus',
  244. messages: [
  245. {
  246. role: 'user',
  247. content: prompt,
  248. },
  249. ],
  250. top_p: 0.7,
  251. temperature: 0.9,
  252. max_tokens: 1024,
  253. };
  254. try {
  255. const response = await zhipuProvider.createCompletion(params);
  256. } catch (error) {
  257. console.error('API call failed:', error.message);
  258. }
  259. }
  260. async codeGenerate(message) {
  261. const modelProvider = new GlmModelProvider();
  262. const aiRunnableAbortController = new AbortController();
  263. const aiRunnable = await modelProvider.createRunnable({
  264. signal: aiRunnableAbortController.signal,
  265. });
  266. const sessionId = `code_session_${Date.now()}`;
  267. const aiRunnableConfig = {
  268. configurable: {
  269. sessionId,
  270. },
  271. };
  272. const sessionIdHistoriesMap = await GlmModelProvider.sessionIdHistoriesMap;
  273. const isSessionHistoryExists = !!sessionIdHistoriesMap[sessionId];
  274. const prompt = await this.buildGeneratePrompt({ message });
  275. const buildStream = async () => {
  276. let aiStream = null;
  277. if (!isSessionHistoryExists) {
  278. delete sessionIdHistoriesMap[sessionId];
  279. aiStream = aiRunnable.stream(
  280. {
  281. input: prompt,
  282. },
  283. aiRunnableConfig,
  284. );
  285. } else {
  286. aiStream = aiRunnable.stream(
  287. {
  288. input: `
  289. continue, please do not reply with any text other than the code, and do not use markdown syntax.
  290. go continue.
  291. `,
  292. },
  293. aiRunnableConfig,
  294. );
  295. }
  296. return aiStream;
  297. };
  298. let result = [];
  299. const aiStream = await buildStream();
  300. if (aiStream) {
  301. for await (const chunk of aiStream) {
  302. const text = GlmModelProvider.answerContentToText(chunk.content);
  303. result.push(text);
  304. }
  305. }
  306. const ai_stream_string = result.join('');
  307. const code_array = extractCodeBlocks(ai_stream_string);
  308. // 解析生成的代码
  309. return [code_array[0], code_array[1]];
  310. }
  311. }
  312. module.exports = new AiService();