x1ongzhu il y a 1 an
Parent
commit
eb5947223c

+ 1 - 0
package.json

@@ -80,6 +80,7 @@
     "nestjs-typeorm-paginate": "^4.0.3",
     "node-xlsx": "^0.23.0",
     "nodemailer": "^6.9.1",
+    "p-queue": "^8.0.1",
     "p-retry": "^6.1.0",
     "p-timeout": "^6.1.1",
     "passport": "^0.6.0",

+ 1 - 1
src/paper/paper-gen/arch.ts

@@ -53,7 +53,7 @@ rectangle 租客管理
 }
 
 export async function createArch(tools: LLMTools, title, desc) {
-    tools = createLLM('gpt-4')
+    tools = createLLM({ model: 'gpt-4' })
     const pRetry = (await eval("import('p-retry')")).default
     return await pRetry(() => _createArch(tools, title, desc), { retries: 5 })
 }

+ 4 - 6
src/paper/paper-gen/er.ts

@@ -15,17 +15,15 @@ async function genErNames(tools: LLMTools, title, desc) {
         )
     )
 
-    const res = await llm.call(
-        [
-            new HumanMessage(`${scheme.getFormatInstructions()}
+    const res = await llm.call([
+        new HumanMessage(`${scheme.getFormatInstructions()}
 ------
 你是一个计算机专业擅长写毕业论文的专家,你的任务是帮我的毕业设计撰写一篇论文
 我的毕业设计项目是${title}
 这个项目包含以下功能:
 ${desc}
 现在我们需要开始为这个系统设计数据库,首先我们分析一下系统中有哪些实体,我们需要绘制以下五个主要ER图:`)
-        ],
-    )
+    ])
     return await scheme.parse(res.content)
 }
 
@@ -71,7 +69,7 @@ price
 }
 
 export async function createER(tools: LLMTools, title, desc) {
-    tools = createLLM('gpt-4')
+    tools = createLLM({ model: 'gpt-4' })
     const pRetry = (await eval("import('p-retry')")).default
     const list = await pRetry(() => genErNames(tools, title, desc), { retries: 5 })
     return await Promise.all(list.map((item) => pRetry(() => plotER(tools, title, desc, item.title), { retries: 5 })))

+ 1 - 1
src/paper/paper-gen/flow.ts

@@ -66,7 +66,7 @@ stop
 }
 
 export async function createFlow(tools: LLMTools, title, desc) {
-    tools = createLLM('gpt-4')
+    tools = createLLM({ model: 'gpt-4' })
     const pRetry = (await eval("import('p-retry')")).default
     const list = await pRetry(() => genFlowNames(tools, title, desc), { retries: 5 })
     return await Promise.all(list.map((item) => pRetry(() => plotFlow(tools, title, desc, item.title), { retries: 5 })))

+ 50 - 22
src/paper/paper-gen/general.ts

@@ -45,7 +45,7 @@ function chapters2md(chapters, sub = []) {
 }
 
 export async function genGeneralPaper1(major: string, title: string, desc: string, chapters) {
-    const tools = createLLM('gpt-3.5-turbo')
+    const tools = createLLM()
     const { llm, usage, conversation } = tools
     const pRetry = (await eval("import('p-retry')")).default
 
@@ -64,7 +64,7 @@ title: ${title}
     for (let i = 0; i < chapters.length; i++) {
         const chapter = chapters[i]
         const sysPrompt = `你是一个擅长写${major}专业毕业论文的专家。
-你的任务是写一篇标题为"${title}"的论文。
+你的任务是写一篇标题为"${title}"的中文论文。
 全文按照以下章节组织:
 ${chapters2md(chapters)}
 
@@ -86,32 +86,60 @@ ${chapters2md(chapters)}
             //     const { content } = await llm.call([new SystemMessage(sysPrompt), new HumanMessage(prompt)])
             //     paper.write('\n\n' + content)
             // }
-
-            const content = (
-                await Promise.all(
-                    chapter.children.map(async (section, j) => {
-                        let prompt
-                        if (section.children?.length) {
-prompt = `你可以适当加入一些口语,让文章更接近真人的书写的感觉
+            const PQueue = (await eval("import('p-queue')")).default
+            const queue = new PQueue({concurrency: 4});
+            let content: any = []
+            chapter.children.forEach(async (section, j) => {
+                queue.add(async () => {
+                    let prompt
+                    if (section.children?.length) {
+                        prompt = `你可以适当加入一些口语,让文章更接近真人的书写的感觉
 请务必不能让人觉得你是一个AI,否则我可能会丢掉我的学位
 
 ${chapters2md(section.children, [i + 1, j + 1])}
 请按照以上结构编写第${i + 1}章${chapter.title}的${i + 1}.${j + 1}小节(请注意只需要编写这一小节的内容)
 ## ${i + 1}.${j + 1} ${section.title} :`
-                        } else {
-                            prompt = `请务必不能让人觉得你是一个AI,否则我可能会丢掉我的学位\n\n请编写第${i + 1}章${chapter.title}的${i + 1}.${
-                                j + 1
-                            }小节(请注意只需要编写这一小节的内容)
+                    } else {
+                        prompt = `请务必不能让人觉得你是一个AI,否则我可能会丢掉我的学位\n\n请编写第${i + 1}章${
+                            chapter.title
+                        }的${i + 1}.${j + 1}小节(请注意只需要编写这一小节的内容)
 ## ${i + 1}.${j + 1} ${section.title}:`
-                        }
-                        let { content } = await llm.call([new SystemMessage(sysPrompt), new HumanMessage(prompt)])
-                        content = content
-                            .replace(new RegExp(`#.*${chapter.title}.*\n`), '')
-                            .replace(new RegExp(`#.*${section.title}.*\n`), '')
-                        return `## ${i + 1}.${j + 1} ${section.title}\n\n` + content
-                    })
-                )
-            ).join('\n\n')
+                    }
+                    let { content: res } = await llm.call([new SystemMessage(sysPrompt), new HumanMessage(prompt)])
+                    res = res
+                        .replace(new RegExp(`#.*${chapter.title}.*\n`), '')
+                        .replace(new RegExp(`#.*${section.title}.*\n`), '')
+                    content[j] = `## ${i + 1}.${j + 1} ${section.title}\n\n` + res
+                })
+            })
+            await queue.onIdle()
+            content = content.join('\n\n')
+
+            //             const content = (
+            //                 await Promise.all(
+            //                     chapter.children.map(async (section, j) => {
+            //                         let prompt
+            //                         if (section.children?.length) {
+            //                             prompt = `你可以适当加入一些口语,让文章更接近真人的书写的感觉
+            // 请务必不能让人觉得你是一个AI,否则我可能会丢掉我的学位
+
+            // ${chapters2md(section.children, [i + 1, j + 1])}
+            // 请按照以上结构编写第${i + 1}章${chapter.title}的${i + 1}.${j + 1}小节(请注意只需要编写这一小节的内容)
+            // ## ${i + 1}.${j + 1} ${section.title} :`
+            //                         } else {
+            //                             prompt = `请务必不能让人觉得你是一个AI,否则我可能会丢掉我的学位\n\n请编写第${i + 1}章${
+            //                                 chapter.title
+            //                             }的${i + 1}.${j + 1}小节(请注意只需要编写这一小节的内容)
+            // ## ${i + 1}.${j + 1} ${section.title}:`
+            //                         }
+            //                         let { content } = await llm.call([new SystemMessage(sysPrompt), new HumanMessage(prompt)])
+            //                         content = content
+            //                             .replace(new RegExp(`#.*${chapter.title}.*\n`), '')
+            //                             .replace(new RegExp(`#.*${section.title}.*\n`), '')
+            //                         return `## ${i + 1}.${j + 1} ${section.title}\n\n` + content
+            //                     })
+            //                 )
+            //             ).join('\n\n')
             paper.write('\n\n' + content)
         } else {
             let prompt = `第${i + 1}章: # ${chapter.title} (请注意只需要编写这一章的内容)`

+ 54 - 33
src/paper/paper-gen/llm.ts

@@ -9,31 +9,24 @@ import { writeFile, mkdirSync, appendFileSync } from 'fs'
 import path = require('path')
 import { LLMTools } from './types'
 require('dotenv').config()
-export function createLLM(model = 'gpt-3.5-turbo-16k') : LLMTools {
+export function createLLM(options?: {
+    model?: 'gpt-3.5-turbo' | 'gpt-3.5-turbo-16k' | 'gpt-4' | 'gpt-4-32k'
+    provider?: 'azure' | 'openai'
+}): LLMTools {
+    options = options || {
+        model: 'gpt-3.5-turbo',
+        provider: 'azure'
+    }
+    options.model = options.model || 'gpt-3.5-turbo'
+    options.provider = options.provider || 'azure'
     const usage = { completionTokens: 0, promptTokens: 0, totalTokens: 0 }
-    const llm = new ChatOpenAI({
-        openAIApiKey: process.env.OPENAI_API_KEY,
-        modelName: model,
-
-        // azureOpenAIApiKey: process.env.AZURE_OPENAI_KEY,
-        // azureOpenAIApiVersion: process.env.AZURE_OPENAI_VERSION,
-        // azureOpenAIApiInstanceName: process.env.AZURE_OPENAI_INSTANCE,
-        // azureOpenAIApiDeploymentName: 'gpt-35-turbo-16k',
-        // azureOpenAIBasePath:`https://gateway.ai.cloudflare.com/v1/cc11bed478e65817d30878b0a796422d/openai-gateway/azure-openai/${process.env.AZURE_OPENAI_INSTANCE}`,
-
-        timeout: 1000 * 60 * 5,
-        configuration: {
-            baseURL: process.env.OPENAI_BASE_URL
-        },
+    const commonParams = {
         maxRetries: 4,
+        timeout: 1000 * 60 * 2,
         callbackManager: CallbackManager.fromHandlers({
             async handleLLMStart(llm, prompts) {
                 Logger.log(`[LLM Start]LLM: ${JSON.stringify(llm)}`)
                 Logger.log(`['LLM Start]Prompts: ${prompts.join('\n')}`)
-
-                const logFile = path.join(__dirname, 'llm.log')
-                appendFileSync(logFile, '\n--------------------------------------\n')
-                appendFileSync(logFile, prompts.join('\n'))
             },
             async handleLLMEnd(output) {
                 Logger.log(
@@ -46,16 +39,6 @@ export function createLLM(model = 'gpt-3.5-turbo-16k') : LLMTools {
                 usage.completionTokens += output.llmOutput.tokenUsage.completionTokens
                 usage.promptTokens += output.llmOutput.tokenUsage.promptTokens
                 usage.totalTokens += output.llmOutput.tokenUsage.totalTokens
-
-                const logFile = path.join(__dirname, 'llm.log')
-                appendFileSync(logFile, '\n--------------------------------------\n')
-                appendFileSync(
-                    logFile,
-                    output.generations
-                        .reduce((acc, cur) => acc.concat(cur), [])
-                        .map((i) => i.text)
-                        .join('\n')
-                )
             },
             async handleLLMError(error) {
                 Logger.error(error)
@@ -64,11 +47,49 @@ export function createLLM(model = 'gpt-3.5-turbo-16k') : LLMTools {
         onFailedAttempt(error) {
             Logger.error(error.message, error.stack, 'OpenAI API Error')
         }
+    }
+    let llm
+    if (options.provider === 'openai') {
+        llm = new ChatOpenAI({
+            ...commonParams,
+            openAIApiKey: process.env.OPENAI_API_KEY,
+            modelName: options.model,
+
+            // azureOpenAIApiKey: process.env.AZURE_OPENAI_KEY,
+            // azureOpenAIApiVersion: process.env.AZURE_OPENAI_VERSION,
+            // azureOpenAIApiInstanceName: process.env.AZURE_OPENAI_INSTANCE,
+            // azureOpenAIApiDeploymentName: 'gpt-35-turbo-16k',
+            // azureOpenAIBasePath:`https://gateway.ai.cloudflare.com/v1/cc11bed478e65817d30878b0a796422d/openai-gateway/azure-openai/${process.env.AZURE_OPENAI_INSTANCE}`,
 
-        // configuration: {
-        //     baseURL: "https://openai.c8c.top/v1",
-        // },
-    })
+            configuration: {
+                baseURL: process.env.OPENAI_BASE_URL
+            }
+        })
+    } else {
+        let azureDeployment = 'gpt-35'
+        switch (options.model) {
+            case 'gpt-3.5-turbo':
+                azureDeployment = 'gpt-35-turbo'
+                break
+            case 'gpt-3.5-turbo-16k':
+                azureDeployment = 'gpt-35-turbo-16k'
+                break
+            case 'gpt-4':
+                azureDeployment = 'gpt-4'
+                break
+            case 'gpt-4-32k':
+                azureDeployment = 'gpt-4-32k'
+                break
+        }
+        llm = new ChatOpenAI({
+            ...commonParams,
+            azureOpenAIApiKey: process.env.AZURE_OPENAI_KEY,
+            azureOpenAIApiVersion: process.env.AZURE_OPENAI_VERSION,
+            azureOpenAIApiInstanceName: process.env.AZURE_OPENAI_INSTANCE,
+            azureOpenAIApiDeploymentName: azureDeployment,
+            azureOpenAIBasePath: `https://gateway.ai.cloudflare.com/v1/cc11bed478e65817d30878b0a796422d/openai-gateway/azure-openai/${process.env.AZURE_OPENAI_INSTANCE}`
+        })
+    }
 
     function conversation(system, k = 4) {
         const chatPrompt = ChatPromptTemplate.fromMessages([

+ 1 - 1
src/paper/paper-gen/table.ts

@@ -40,7 +40,7 @@ ${list.map((i) => `- ${i}`).join('\n')}
 }
 
 export async function createTable(tools: LLMTools, title, desc) {
-    tools = createLLM('gpt-4')
+    tools = createLLM({ model: 'gpt-4' })
     const pRetry = (await eval("import('p-retry')")).default
     const list = await pRetry(() => listTables(tools, title, desc), { retries: 5 })
 

+ 1 - 1
src/paper/paper-gen/useCase.ts

@@ -46,7 +46,7 @@ ${roles.map((e, i) => `${i + 1}. ${e.roleName}: ${e.roleDesc}`).join('\n')}
 }
 
 export async function createUseCase(tools: LLMTools, title, desc) {
-    tools = createLLM('gpt-4')
+    tools = createLLM({ model: 'gpt-4' })
     const pRetry = (await eval("import('p-retry')")).default
     const roles = await pRetry(() => genRoles(tools, title, desc), {
         retries: 5,

+ 2 - 2
src/paper/paper.service.ts

@@ -91,7 +91,7 @@ export class PaperService implements OnModuleInit {
 
     async genChapters(id: number, description: string) {
         const order = await this.findOrderById(id)
-        const tools = createLLM('gpt-3.5-turbo')
+        const tools = createLLM()
         const chapters = await genChapters(tools, order.major, order.title, description)
 
         order.chapters = chapters
@@ -142,7 +142,7 @@ export class PaperService implements OnModuleInit {
             }
         }
 
-        // const { llm } = createLLM('gpt-3.5-turbo')
+        // const { llm } = createLLM()
         // const { content } = await llm.call([
         //     new HumanMessage(`你要从我给你的论文标题中提取2-3个由逗号分隔的主要关键词。\n\n标题:${title}\n关键词:`)
         // ])

+ 14 - 1
yarn.lock

@@ -4429,6 +4429,11 @@ eventemitter3@^4.0.4:
   resolved "https://registry.npmmirror.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
   integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==
 
+eventemitter3@^5.0.1:
+  version "5.0.1"
+  resolved "https://registry.npmmirror.com/eventemitter3/-/eventemitter3-5.0.1.tgz#53f5ffd0a492ac800721bb42c66b841de96423c4"
+  integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==
+
 events@3.3.0, events@^3.2.0:
   version "3.3.0"
   resolved "https://registry.npmmirror.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400"
@@ -7032,6 +7037,14 @@ p-queue@^6.6.2:
     eventemitter3 "^4.0.4"
     p-timeout "^3.2.0"
 
+p-queue@^8.0.1:
+  version "8.0.1"
+  resolved "https://registry.npmmirror.com/p-queue/-/p-queue-8.0.1.tgz#718b7f83836922ef213ddec263ff4223ce70bef8"
+  integrity sha512-NXzu9aQJTAzbBqOt2hwsR63ea7yvxJc0PwN/zobNAudYfb1B7R08SzB4TsLeSbUCuG467NhnoT0oO6w1qRO+BA==
+  dependencies:
+    eventemitter3 "^5.0.1"
+    p-timeout "^6.1.2"
+
 p-retry@4:
   version "4.6.2"
   resolved "https://registry.npmmirror.com/p-retry/-/p-retry-4.6.2.tgz#9baae7184057edd4e17231cee04264106e092a16"
@@ -7056,7 +7069,7 @@ p-timeout@^3.2.0:
   dependencies:
     p-finally "^1.0.0"
 
-p-timeout@^6.1.1:
+p-timeout@^6.1.1, p-timeout@^6.1.2:
   version "6.1.2"
   resolved "https://registry.npmmirror.com/p-timeout/-/p-timeout-6.1.2.tgz#22b8d8a78abf5e103030211c5fc6dee1166a6aa5"
   integrity sha512-UbD77BuZ9Bc9aABo74gfXhNvzC9Tx7SxtHSh1fxvx3jTLLYvmVhiQZZrJzqqU0jKbN32kb5VOKiLEQI/3bIjgQ==