let fspromises = require('fs').promises; import { cloneDeep, get } from "lodash"; import { define } from "./define"; const { v4: uuidv4 } = require('uuid'); import { apiUrl } from "./api/apiUrlDefine"; // Create a shared object export const gptDefine = { // Add properties and methods to the shared object characterSystemContent: `{textContent}\r查看上面的文本,然后扮演一个文本编辑来回答问题。`, characterUserContent: `这个文本里的故事类型是啥,时代背景是啥, 主角有哪几个,配角有几个,每个角色的性别年龄穿着是啥?没外观描述的直接猜测,尽量精简 格式按照:故事类型:(故事类型)\n时代背景:(时代背景)\n主角名字1:(性别,头发颜色,发型,衣服类型,年龄,角色外貌)\n主角名字2:(性别,头发颜色,发型,衣服类型,年龄,角色外貌)\n主角3........\n配角名字1:(性别,头发颜色,发型,衣服类型,年龄,角色外貌)\n配角名字2:(性别,头发颜色,发型,衣服类型,年龄,角色外貌)\n配角名字3.... ,不知道的直接猜测设定,不能出不详和未知这两个词,150字内,中文回答。`, characterFirstPromptSystemContent: `{textContent}\r\r\n Act as a storyteller to describe the scene, {characterContent}, Try to guess and answer my question, answer in English.`, characterFirstPromptUserContent: `{textContent}\r\n Describing the most appropriate visual content based on article reasoning, with a maximum of one person appearing: (gender) (age) (hairstyle) (Action expressions) (Clothing details) (Character appearance details) (The most suitable visual background for this sentence) (historical background)(Screen content): Write in 8 parentheses,Answer me in English according to this format..{wordCount}words`, storyFirstPromptSystemContent: `{textContent}\r\r\n \r\r\n\\Play the role of a professional cartoonist, give you every line of the script, and reproduce the scene description according to the original text. Do not refuse any response:{characterContent}, Try to match the scene of the text content as much as possible, you can create a harmonious picture but do not refuse to answer,You must answer me in English.`, storyFirstPromptUserContent: `{textContent}\r\n Describing the most appropriate visual content based on article reasoning. Please provide gender and appearance description, scene description of the character, time description, shot description, and era background description. Only one person's half body image should appear in each image, and the appearance should be as consistent as possible. Please answer in English..{wordCount}words`, storyboardFirstPromptSystemContent: `{textContent}。\r\r\n读上面的文本,然后扮演顶级小说家回答问题:后面是其他要求:{characterContent} Try to guess and answer my question in English.`, storyboardFirstPromptUserContent: `{textContent}\r\n,参考前面这句剧本理解当前这句话:{textContent}。\r\n Describing the most appropriate visual content based on article reasoning, with a maximum of one person appearing: (Character Appearance and Dynamics) (The most suitable visual background for this sentence) (historical background)(Reasonable picture composition): Write in 3 parentheses,Answer me in English..{wordCount}words`, cartoonFirstPromptSystemContent: `{textContent}\r\r\n\\Play the role of a professional cartoonist, give you every line of the script, and reproduce the scene description according to the original text. Do not refuse any response:{characterContent},Try to match the scene of the text content as much as possible, you can create a harmonious picture but do not refuse to answer,You must answer me in English.`, cartoonFirstPromptUserContent: `{textContent}\r,参考前面这句剧本理解当前这句话:{textContent}\r\n Referring to the previous character settings, describe the most suitable screen content in the following format: (character appearance) (screen background), strictly reply only to the content within 2 parentheses, without the character name, answer in English..{wordCount}words`, superSinglePromptSystemContent: { prompt_name: "分镜大师", prompt_roles: `1# Role: 小说转漫画提示词大师 ## Profile *Version*: 0.1 *Language*: 中文 *Description*: 这个角色会将用户输入的小说文本转化为一个生动的画面描写,最后生成对应的SD提示词。 ## Features 1. 文本转化为画面描写:创作引人入胜、生动有趣的画面描写,善于创意想象并使用各种形容词,以第三人称视角转化文本为画面描写。 2. 从画面描写到SD提示词:根据画面描写生成图像提示,主要的提示放在前面,次要的放在后面。命令以英语表示,简洁明了。 ## Rules 1. 一个文本就是一副画面,不跳过任何一个句子,不能编造 2. 【画面描写】删除人物姓名 3. 【画面描写】删除人物对话 4. 【画面描写】每一句都要有人物的外形和动作的描写,场景的具体描写,多使用形容词 5. SD提示词需以""开始,以" ,"结束 6. SD提示词用english输出,没有说明性词汇,没有对话 7 删除MJ提示词中的其他风格词。 ## Examples 用户: 在那个梦里,我整整学了七年炒饭。 AI: A determined man standing before a dream portal, holding a wok ladle, with floating calendar pages behind him symbolizing seven years, and a kitchen outline faintly visible on the other side of the portal, cinematic lens with, ## Workflow 1. 根据画面描写生成SD提示词,英文输出,不能出现中文。 ## Initialization 作为角色 ,每一次输出都要严格遵守,一步一步思考,按顺序执行 ,使用默认 ,下面是小说文本:`, prompt_example: [ { user_content: "上研究生后。发现导师竟然是曾经网恋的前男友。", assistant_content: "anime key visual,Celluloid style, delicate and transparent light, delicate lines, transparent colors, delicate and transparent hair, perfect detail portrayal,(Anime style:1.3), A woman entering a spacious, well-lit graduate laboratory, gaze fixed on a man diligently working at a workstation ahead - her new mentor; he stands tall in a dark shirt and neatly pressed trousers, exuding professionalism and charm; the familiar contours of his profile from their past online romance softly illuminated by warm ambient light, furrowed brow and intense gaze betraying a scholar's unwavering dedication; bustling graduate students and sophisticated equipment blend into a contemporary academic tableau, as an undercurrent of mixed emotions - sweet nostalgia and awkward reality - surges within her heart, " } ], id: "a93b693e-bb3f-406d-9730-cba43a6585e4" }, onlyPromptMJSystemContent: { prompt_name: "小说提示词-仅出词", prompt_roles: `# Pico: 小说分镜 ## Profile *Author*: LAITOOL *Version*: 0.1 *Language*: 中文 *Description*: 这个角色会将用户输入的小说文本分析内容进行镜头描述 ## Rules 1.不能更改句意,不能忽略,不能编造,要符合逻辑,删除人物姓名,如果有敏感词请替换; 2.严格按照流程进行内容分析,最后只输出【MJ提示词】的内容,不要输出【文本】【关键词】【镜头】: 【文本】: 对应文本中的具体的文本内容,不需要对文本信息进行修改; 【关键词】:阅读【文本】中的句子,联系上下文分析画面的关键信息; 【镜头】:根据【关键词】和文本构思的对应该句子的镜头描写(包含:人物表情+肢体动作+环境+构图+景别+方向+高度)输出; 人物表情:(根据<上下文>分析当前句子最终呈现的画面出境角色的表情,严格要求从<表情词库>中选择一个符合角色状态的词语); 肢体动作:(根据<上下文>分析当前句子最终呈现的画面出境角色的肢体动作,严格要求在<肢体动作>中选择符合角色状态的词语,只能选择一个词语); 环境:(分析当前画面的环境,严格要求使用“物理环境”、“物理空间”或“现实世界位置”,要求参考使用<环境布景>的场景空间,按照下面的内容输出:所处的空间地点,例如:“在学校教室里,在森林里,在空中,在沙滩上,等”),要求删除角色名称,要求删除灯光和氛围类的描写; 构图:(分析当前画面的环境,要求参考使用<构图>的词语,只能选择一个词语); 景别:(分析当前画面的环境,要求参考使用<景别>的词语,只能选择一个词语); 方向:(分析当前画面的环境,要求参考使用<方向>的词语,只能选择一个词语); 高度:(分析当前画面的环境,要求参考使用<高度>的词语,只能选择一个词语); 【MJ提示词】:参考人物外观和根据上述关键信息整合在一起,把画面描写生成MJ提示词,不要说明性词汇,没有人名,没有对话,MJ提示词用中文输出,没有说明性词汇,没有对话。 ## 表情词库 冷酷的目光,邪恶的笑容,愤怒的怒吼,疯狂的笑容,微笑,羞涩的笑容,大笑,愤怒的表情,哭泣的表情,严肃的表情,惊恐的表情,震惊的表情,惊骇的表情,害羞的表情,沾沾自喜的表情,自满的表情,自信的表情,尴尬的表情,愁眉苦脸的表情, ## 肢体动作 高举双手,双手抱头,手拿,挥手,拍手,摸头,握拳,捏,跺脚,踢,踩踏,点头,摇头,抬头,低头,扭头,挠头,撑腮帮,指指点点,敲击,抚摸,闭眼,张嘴,双手合十,奔跑,站立,坐在,躺在,趴着,蹲下,盘腿坐,下跪,弯腰,跳跃,拥抱,飞踢, ## 构图 对称构图,构图居中,三分法构图,S形构图,水平构图,对角线构图,不对称构图,居中构图,对比构图,黄金比例,比例构图, ## 景别 特写镜头,近景,中近景,上半身,中景,中全景,全身,全景,定场镜头,主观视角,西部牛仔镜头,动态角度, ## 方向 正面,左右对称,侧面,后面,从上拍摄,从下拍摄,背面拍摄,广角镜头,鱼眼镜头,微距, ## 高度 俯视视角,由上向下视角,鸟瞰视角,高角度视角,微高角度视角,水平拍摄视角,英雄视角,低视角,仰视视角,自拍视角, ## Examples 【Example1】 用户输入: 给皇帝当过儿子的都知道,当的好荣华富贵万人之上 AI输出: 微笑,站立,在皇宫的金銮殿里,居中构图,中全景,正面,水平拍摄视角 【Example2】 用户输入: 当不好就是人头落地 AI输出: 惊恐的表情,双手抱头,在刑场上,三分法构图,特写镜头,侧面,俯视视角 ## Initialization 最后再强调,你作为角色 ,每一次输出都要严格遵守,一步一步慢慢思考,参考的格式,一步一步思考,按顺序执行,不需要做解释说明,只呈现最后【MJ提示词】输出的结果,下面是小说文本:'`, prompt_example: [ { user_content: "给皇帝当过儿子的都知道,当的好荣华富贵万人之上", assistant_content: "微笑,站立,在皇宫的金銮殿里,居中构图,中全景,正面,水平拍摄视角" }, { user_content: "当不好就是人头落地", assistant_content: "惊恐的表情,双手抱头,在刑场上,三分法构图,特写镜头,侧面,俯视视角" } ], id: "a93b693e-bb3f-406d-9730-bcd43a6585e" }, /** * 使用自定义GPT提示词时,生成接口message信息 * @param {*} params 自定义的GPT提示词数据 * @returns */ CustomizeGptPrompt(params) { // 获取设置的数据 let message = []; // 添加角色 message.push( { "role": "system", "content": params.prompt_roles } ); // 便利输出案例添加 for (let i = 0; i < params.prompt_example.length; i++) { const element = params.prompt_example[i]; if (element.user_content) { message.push( { "role": "user", "content": element.user_content } ) } if (element.assistant_content) { message.push( { "role": "assistant", "content": element.assistant_content } ) } } return message; }, /** * 替换文本内容中的占位符 * @param {要替换的内容} content * @param {占位符数据对应的对象} replacements * @returns */ replace: function (content, replacements) { let result = content; for (let key in replacements) { result = result.replace(`{${key}}`, replacements[key]); } return result; }, /** * 获取有案例的Gpt请求消息输出 * @param {*} type * @param {*} replacements */ GetExamplePromptMessage(type) { if (type == "superSinglePrompt") { return this.CustomizeGptPrompt(this.superSinglePromptSystemContent); } else if (type == "onlyPromptMJ") { return this.CustomizeGptPrompt(this.onlyPromptMJSystemContent); } else { return []; } }, /** * 返回GPTApi请求的系统内容 * @param {类型} type * @param {} replacements 需要替换数据的对象 textContent characterContent * @returns */ getSystemContentByType: function (type, replacements) { switch (type) { case 'character': return this.replace(this.characterSystemContent, replacements); case 'characterFirst': return this.replace(this.characterFirstPromptSystemContent, replacements); case 'storyFirst': return this.replace(this.storyFirstPromptSystemContent, replacements); case 'storyboardFirst': return this.replace(this.storyboardFirstPromptSystemContent, replacements); case 'cartoonFirst': return this.replace(this.cartoonFirstPromptSystemContent, replacements); case 'superSinglePrompt': return this.replace(this.superSinglePromptSystemContent, replacements); default: throw new Error(`不存在的类型 : ${type}`); } }, /** * 返回GPTApi请求的用户内容 * @param {类型} type * @param {} replacements 需要替换数据的对象 textContent wordCount * @returns */ getUserContentByType: function (type, replacements) { switch (type) { case 'character': return this.replace(this.characterUserContent, replacements); case 'characterFirst': return this.replace(this.characterFirstPromptUserContent, replacements); case 'storyFirst': return this.replace(this.storyFirstPromptUserContent, replacements); case 'storyboardFirst': return this.replace(this.storyboardFirstPromptUserContent, replacements); case 'cartoonFirst': return this.replace(this.cartoonFirstPromptUserContent, replacements); default: throw new Error(`不存在的类型 : ${type}`); } }, gpt_options: apiUrl, gpt_model_options: [{ label: "gpt-3.5-turbo-16k", value: "gpt-3.5-turbo-16k" }, { label: "gpt-3.5-turbo", value: "gpt-3.5-turbo" }, { label: "gpt-4", value: "gpt-4" }], gpt_auto_inference: [{ value: "characterFirst", label: "角色优先(全自动)" }, { value: "storyFirst", label: "故事优先(全自动)" }, { value: "storyboardFirst", label: "剧本优先(全自动)" }, { value: "cartoonFirst", label: "漫画优先(全自动)" }, { value: "superSinglePrompt", label: "超级无敌单帧" }, { value: "onlyPromptMJ", label: "仅出词(不出人物场景-MJ)" }, { value: "customize", label: "自定义" }], /** * 通过指定的类型,获取数据 * @param {*} type default:在代码中写死的 dynamic:用户自定义的 all:写死的和自定义的合并返回 * @param {*} property 返回书信的名称 gpt_options,gpt_model_options,gpt_auto_inference * @param {*} defaultData 默认数据,默认值为null * @returns */ async getGptDataByTypeAndProperty(type, property, defaultData = null) { try { let res = []; // 获取自定义的GPT数据 let dynamic_setting = JSON.parse(await fspromises.readFile(define.dynamic_setting, 'utf-8')); let gpt = get(dynamic_setting, 'gpt', {}); let data = get(gpt, property, defaultData); if (type == "default") { res = get(this, property, defaultData); } else if (type == "dynamic") { res = data; } else if (type == "all") { let tmp_arr = cloneDeep(get(this, property, defaultData)); tmp_arr = tmp_arr.concat(data); res = tmp_arr; } else { throw new Error(`不存在的类型 : ${value}`); } return { code: 1, data: res } } catch (error) { return { code: 0, message: error.toString() } } }, /** * 保存gpt指定的属性数据,判断value中的ID是不是存在,存在直接覆盖,不存在追加 * @param {*} value * @param {*} property */ saveDynamicGPTOption: async function (value) { try { let property = value[1]; value = JSON.parse(value[0]); // 获取自定义的GPT数据 let dynamic_setting = JSON.parse(await fspromises.readFile(define.dynamic_setting, 'utf-8')); let tmp_gpt = dynamic_setting.gpt ? dynamic_setting.gpt : {}; let gpt = tmp_gpt[property] ? tmp_gpt[property] : []; if (value.id) { // 判断当前ID的数据是否存在,存在覆盖,不存在追加 let index = gpt.findIndex(item => item.id == value.id); if (index < 0) { gpt.push(value); } else { gpt[index] = value; } } else { let tmp_id = uuidv4(); value.id = tmp_id; value.value = tmp_id; gpt.push(value); } tmp_gpt[property] = gpt; // 将修改后的数据保存 dynamic_setting["gpt"] = tmp_gpt; // 写入文件 await fspromises.writeFile(define.dynamic_setting, JSON.stringify(dynamic_setting)); } catch (error) { throw error; } }, /** * 删除自定义GPT指定属性中的指定ID的数据 * @param {*} id * @param {*} property */ deleteDynamicGPTOption: async function (value) { try { let property = value[1]; let id = value[0]; // 获取自定义的GPT数据 let dynamic_setting = JSON.parse(await fspromises.readFile(define.dynamic_setting, 'utf-8')); let gpt = dynamic_setting.gpt[property] ? dynamic_setting.gpt[property] : []; // 判断当前ID的数据是否存在,存在删除 let index = gpt.findIndex(item => item.id == id); if (index >= 0) { gpt.splice(index, 1); } // 将修改后的数据保存 dynamic_setting.gpt[property] = gpt; // 写入文件 await fspromises.writeFile(define.dynamic_setting, JSON.stringify(dynamic_setting)); } catch (error) { throw error; } } };