382 lines
14 KiB
TypeScript
Raw Normal View History

2024-08-18 16:22:19 +08:00
import { isEmpty } from "lodash";
import { gptDefine } from "../../../define/gptDefine";
import axios from "axios";
2024-08-20 10:37:38 +08:00
import { RetryWithBackoff } from "../../../define/Tools/common";
2024-09-12 14:13:09 +08:00
import { Book } from "../../../model/book";
2024-08-18 16:22:19 +08:00
/**
* GPT相关的服务都在这边
*/
export class GptService {
gptUrl: string = undefined
gptModel: string = undefined
gptApiKey: string = undefined
//#region GPT 设置
/**
* GPT的所有的服务商
* @param type all
* @param callback
* @returns
*/
private async GetGPTBusinessOption(type: string, callback: Function = null): Promise<any> {
let res = await gptDefine.getGptDataByTypeAndProperty(type, "gpt_options", []);
if (res.code == 0) {
throw new Error(res.message)
} else {
if (callback) {
callback(res.data)
}
return res.data
}
}
async RefreshGptSetting() {
let all_options = await this.GetGPTBusinessOption("all", (value) => value.gpt_url);
let index = all_options.findIndex(item => item.value == global.config.gpt_business && item.gpt_url)
if (index < 0) {
throw new Error("没有找到指定的GPT服务商的配置请检查")
}
this.gptUrl = all_options[index].gpt_url;
this.gptApiKey = global.config.gpt_key;
this.gptModel = global.config.gpt_model;
}
/**
* GPT的设置
*/
async InitGptSetting(refresh = false) {
if (refresh) {
await this.RefreshGptSetting()
} else {
// 判断是不是存在必要信息
if (isEmpty(this.gptUrl) || isEmpty(this.gptModel) || isEmpty(this.gptApiKey)) {
await this.RefreshGptSetting();
}
}
}
/**
*
* @param data
* @param gpt_url
* @returns
*/
ModifyData(data: any, gpt_url: string = null) {
let res = data;
if (!gpt_url) {
gpt_url = this.gptUrl
}
if (gpt_url.includes("dashscope.aliyuncs.com")) {
res = {
"model": data.model,
"input": {
"messages": data.messages,
},
"parameters": {
"result_format": "message"
}
}
}
return res;
}
/**
*
* @param res
* @param gpt_url URL
* @returns
*/
GetResponseContent(res: any, gpt_url: string = null) {
let content = "";
if (!gpt_url) {
gpt_url = this.gptUrl
}
if (gpt_url.includes("dashscope.aliyuncs.com")) {
content = res.data.output.choices[0].message.content;
} else {
content = res.data.choices[0].message.content;
}
return content;
}
//#endregion
2024-09-12 14:13:09 +08:00
//#region GPT 通用请求
2024-08-18 16:22:19 +08:00
/**
* GPT请求
* @param {*} message
* @param {*} gpt_url gpt的urlglobal中取
* @param {*} gpt_key gpt的keyglobal中取
* @param {*} gpt_model gpt的modelglobal中取
* @returns
*/
async FetchGpt(message: any, gpt_model: string = null, gpt_key: string = null, gpt_url: string = null): Promise<string> {
try {
await this.InitGptSetting();
let data = {
"model": gpt_model ? gpt_model : this.gptModel,
"messages": message
};
data = this.ModifyData(data, gpt_url);
let config = {
method: 'post',
maxBodyLength: Infinity,
url: gpt_url ? gpt_url : this.gptUrl,
headers: {
'Authorization': `Bearer ${gpt_key ? gpt_key : this.gptApiKey}`,
'Content-Type': 'application/json'
},
data: JSON.stringify(data)
};
let res = await axios.request(config);
let content = this.GetResponseContent(res, this.gptUrl);
return content;
} catch (error) {
throw error;
}
}
2024-09-12 14:13:09 +08:00
//#endregion
//#region 原创推理
/**
*
* @param currentBookTaskDetail
* @param bookTaskDetails
* @param contextCount
*/
GetBookTaskDetailContextData(currentBookTaskDetail: Book.SelectBookTaskDetail, bookTaskDetails: Book.SelectBookTaskDetail[], contextCount: number): string {
let prefix = "";
// 拼接一个word
let i = currentBookTaskDetail.no - 1
if (i <= contextCount) {
prefix = bookTaskDetails
.filter((item, index) => index < i)
.map((item) => item.afterGpt)
.join('\r\n')
} else if (i > contextCount) {
prefix = bookTaskDetails
.filter((item, index) => i - index <= contextCount && i - index > 0)
.map((item) => item.afterGpt)
.join('\r\n')
}
let suffix = "";
let o_i = bookTaskDetails.length - i
if (o_i <= contextCount) {
suffix = bookTaskDetails
.filter((item, index) => index > i)
.map((item) => item.afterGpt)
.join('\r\n')
} else if (o_i > contextCount) {
suffix = bookTaskDetails
.filter((item, index) => index - i <= contextCount && index - i > 0)
.map((item) => item.afterGpt)
.join('\r\n')
}
return `${prefix}\r\n${currentBookTaskDetail.afterGpt}\r\n${suffix}`;
}
/**
* message
* @param currentBookTaskDetail
* @param contextData
* @param autoAnalyzeCharacter
* @returns
*/
GetGPTRequestMessage(currentBookTaskDetail: Book.SelectBookTaskDetail, contextData: string, autoAnalyzeCharacter: string): any[] {
let message = []
if (
['superSinglePrompt', 'onlyPromptMJ', 'superSinglePromptChinese'].includes(
global.config.gpt_auto_inference
)
) {
// 有返回案例的
message = gptDefine.GetExamplePromptMessage(global.config.gpt_auto_inference)
// 加当前提问的
message.push({
role: 'user',
content: currentBookTaskDetail.afterGpt
})
} else {
// 直接返回,没有案例的
message = [
{
role: 'system',
content: gptDefine.getSystemContentByType(global.config.gpt_auto_inference, {
textContent: contextData,
characterContent: autoAnalyzeCharacter
})
},
{
role: 'user',
content: gptDefine.getUserContentByType(global.config.gpt_auto_inference, {
textContent: currentBookTaskDetail.afterGpt,
wordCount:
global.config.gpt_model && global.config.gpt_model.includes('gpt-4')
? '20'
: '40'
})
}
]
}
return message
}
2024-08-18 16:22:19 +08:00
2024-09-12 14:13:09 +08:00
/**
*
* @param currentBookTaskDetail
* @param bookTaskDetails
* @param contextCount
* @param autoAnalyzeCharacter
*/
async OriginalInferencePrompt(currentBookTaskDetail: Book.SelectBookTaskDetail, bookTaskDetails: Book.SelectBookTaskDetail[], contextCount: number, autoAnalyzeCharacter: string) {
let gptPromptType = global.config.gpt_auto_inference;
let message = []
if (gptPromptType == "customize") { //自定义模式
// 自定义模式
// 获取当前自定义的推理提示词
let customize_gpt_prompt = (
await gptDefine.getGptDataByTypeAndProperty('dynamic', 'customize_gpt_prompt', [])
).data
let index = customize_gpt_prompt.findIndex(
(item: any) => item.id == global.config.customize_gpt_prompt
)
if (global.config.customize_gpt_prompt && index < 0) {
throw new Error('自定义推理时要选择对应的自定义推理词')
}
message = gptDefine.CustomizeGptPrompt(customize_gpt_prompt[index], currentBookTaskDetail.afterGpt)
message.push({
role: 'user',
content: currentBookTaskDetail.afterGpt
})
} else { // 内置模式
let context = this.GetBookTaskDetailContextData(currentBookTaskDetail, bookTaskDetails, contextCount);
message = this.GetGPTRequestMessage(currentBookTaskDetail, context, autoAnalyzeCharacter);
}
// 开始请求
let res = await RetryWithBackoff(async () => {
return this.FetchGpt(message)
}, 5, 1000)
if (res) {
if (res) {
res = res
.replace(/\)\s*\(/g, ', ')
.replace(/^\(/, '')
.replace(/\)$/, '')
.replaceAll('*', '')
.replaceAll('--', ' ')
}
}
return res
}
//#endregion
2024-08-18 16:22:19 +08:00
2024-09-12 14:13:09 +08:00
//#region 中文 繁体转简体
2024-08-18 16:22:19 +08:00
/**
*
* @param traditionalText
* @param apiKey Lai API的 Key
* @param baseUrl baseurl
* @returns
*/
async ChineseTraditionalToSimplified(traditionalText: string, apiKey: string, baseUrl: string = null): Promise<string> {
try {
let message = [
{
"role": "system",
"content": '我想让你充当中文繁体转简体专家用简体中文100%还原繁体中文,不要加其他的联想,只把原有的繁体中文转换为简体中文,请检查所有信息是否准确,并在回答时保持简活,不需要任何其他反馈。'
}, {
"role": "user",
"content": '上研究生後,發現導師竟然是曾經網戀的前男友。'
}, {
"role": "assistant",
"content": '上研究生后,发现导师竟然是曾经网恋的前男友。'
}, {
"role": "user",
"content": traditionalText
}
]
let baseSubUrl = baseUrl ? (baseUrl.endsWith('/') ? baseUrl + 'v1/chat/completions' : baseUrl + '/v1/chat/completions') : null;
let url = baseSubUrl ? baseSubUrl : "https://api.laitool.cc/v1/chat/completions"
// 开始请求这个默认是使用的是LAI API的gpt-4o-mini
2024-08-20 10:37:38 +08:00
let content = await RetryWithBackoff<string>(async () => {
return await this.FetchGpt(message, 'gpt-4o-mini', apiKey, url);
}, 5, 2000)
2024-08-18 16:22:19 +08:00
return content
} catch (error) {
throw error
}
}
//#endregion
//#region 聚合推文 文案洗稿
/**
* 稿
* @param word
* @returns
*/
async GenerateAfterGptWordByGPT(word: string,) {
try {
let prompt = `## - Role: 文案去重
<Background>:
##
1.
2.
3.
4.
##
1
输出1: 雌性每洞房一次
2
输出2: 我重生后的第一件事就是生吞羊辣子
3SSS级异能紫霄神器
输出3: 我明明觉醒了sss级暗黑系异能
3
输出3: 我爷爷觉醒了s级凶兽黄金朱厌ss级神兽泰坦巨猿
输入5: 一个先天满属性的天才却被称为废物
输出5: 一个先天满属性的天才却被称为废物
##
<要求><示例><要求>Ai部分
`;
let message = [
{
"role": "system",
"content": prompt,
}, {
"role": "user",
"content": word
}
]
// 开始请求这个默认是使用的是LAI API的gpt-4o-mini
let content = await RetryWithBackoff<string>(async () => {
return await this.FetchGpt(message, null, null, null);
}, 5, 2000)
return content;
} catch (error) {
throw error
}
}
//#endregion
2024-08-18 16:22:19 +08:00
}