LaiTool_PRO/src/main/service/aiReason/aiReasonCommon.ts

302 lines
9.6 KiB
TypeScript
Raw Normal View History

2025-08-19 14:33:59 +08:00
import { OptionRealmService } from '@/define/db/service/optionService'
import { OptionKeyName } from '@/define/enum/option'
import { optionSerialization } from '../option/optionSerialization'
import { SettingModal } from '@/define/model/setting'
import { isEmpty } from 'lodash'
import { GetOpenAISuccessResponse } from '@/define/response/openAIResponse'
import { GetApiDefineDataById } from '@/define/data/apiData'
import axios from 'axios'
import { RetryWithBackoff } from '@/define/Tools/common'
import { Book } from '@/define/model/book/book'
import { AiInferenceModelModel, GetAIPromptOptionByValue } from '@/define/data/aiData/aiData'
/**
* AI推理通用工具类
*
* AI推理相关的各种通用功能:
* -
* - API提供商和模型信息
* -
* -
* -
* -
*
* AI推理流程
*
*
* @class AiReasonCommon
* @example
* const aiReason = new AiReasonCommon();
* await aiReason.GetAISetting();
* const result = await aiReason.OriginalInferencePrompt(taskDetail, allDetails, 2, characterData);
*/
export class AiReasonCommon {
optionRealmService!: OptionRealmService
aiReasonSetting!: SettingModal.InferenceAISettings
/**
* * AiReasonCommon
* @returns {Promise<void>} - Promise
*/
async InitAiReasonCommon(): Promise<void> {
if (!this.optionRealmService) {
this.optionRealmService = await OptionRealmService.getInstance()
}
}
/**
*
* @returns {Promise<void>} - Promise
* @throws {Error} -
*/
async GetAISetting(): Promise<void> {
await this.InitAiReasonCommon()
let res = this.optionRealmService.GetOptionByKey(OptionKeyName.InferenceAI.InferenceSetting)
let aiReasonSetting = optionSerialization<SettingModal.InferenceAISettings>(
res,
'‘设置-> 推理设置’'
)
if (
isEmpty(aiReasonSetting.apiProvider) ||
isEmpty(aiReasonSetting.apiToken) ||
isEmpty(aiReasonSetting.inferenceModel) ||
isEmpty(aiReasonSetting.aiPromptValue)
) {
throw new Error(
'请检查 ‘设置-> 推理设置’ 的API提供商、API令牌、推理模型、推理模式等是不是存在'
)
}
this.aiReasonSetting = aiReasonSetting
}
/**
* API提供商信息
* @returns
*/
GetAPIProviderMessage() {
let apiProviders = GetApiDefineDataById(this.aiReasonSetting.apiProvider)
return apiProviders
}
/**
*
* @returns {any} -
* @throws {Error} -
*/
GetInferenceModelMessage(): AiInferenceModelModel {
let selectInferenceModel = GetAIPromptOptionByValue(this.aiReasonSetting.aiPromptValue)
if (isEmpty(selectInferenceModel)) {
throw new Error('请检查推理模型是否存在!')
}
return selectInferenceModel
}
/**
*
*
* {key}
* replacements
*
* @param {string} content -
* @param {Record<string, string>} replacements -
* @returns {string}
*
* @example
* // 返回 "你好,张三,今天是星期一"
* replaceObject("你好,{name},今天是{day}", { name: "张三", day: "星期一" })
*/
replaceObject(content: string, replacements: Record<string, string>): string {
let result = content
for (let key in replacements) {
result = result.replaceAll(`{${key}}`, replacements[key])
}
return result
}
/**
*
* @param currentBookTaskDetail
* @param bookTaskDetails
* @param contextCount
*/
GetBookTaskDetailContextData(
currentBookTaskDetail: Book.SelectBookTaskDetail,
bookTaskDetails: Book.SelectBookTaskDetail[],
contextCount: number
): string {
let prefix = ''
// 拼接一个word
let i = (currentBookTaskDetail.no as number) - 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,
selectInferenceModel: AiInferenceModelModel
): any[] {
let message: any = []
if (selectInferenceModel.hasExample) {
// // 有返回案例的
// message = gptDefine.GetExamplePromptMessage(global.config.gpt_auto_inference)
// // 加当前提问的
// message.push({
// role: 'user',
// content: currentBookTaskDetail.afterGpt
// })
} else {
// 直接返回,没有案例的
message = [
{
role: 'system',
content: this.replaceObject(selectInferenceModel.systemContent, {
textContent: contextData,
characterContent: autoAnalyzeCharacter
})
},
{
role: 'user',
content: this.replaceObject(selectInferenceModel.userContent, {
contextContent: contextData,
textContent: currentBookTaskDetail.afterGpt ?? '',
characterContent: autoAnalyzeCharacter,
wordCount: '40'
})
}
]
}
return message
}
/**
*
* @description
*
* @param {OpenAISuccessResponse} message -
* @returns {Promise<string>} - Promise
* @throws {Error} -
* @throws {Error} -
* @throws {Error} -
*
*/
async FetchGpt(message: any, option: any = {}): Promise<string> {
try {
let data = {
model: this.aiReasonSetting.inferenceModel,
messages: message,
...option
}
let apiProvider = this.GetAPIProviderMessage()
let config = {
method: 'post',
maxBodyLength: Infinity,
url: apiProvider.gpt_url,
headers: {
Authorization: `Bearer ${this.aiReasonSetting.apiToken}`,
'Content-Type': 'application/json'
},
data: JSON.stringify(data)
}
let res = await RetryWithBackoff(
async () => {
return await axios.request(config)
},
5,
2000
)
let content = GetOpenAISuccessResponse(res.data)
// this.GetResponseContent(res, this.gptUrl)
return content
} catch (error) {
throw error
}
}
/**
*
* @param currentBookTaskDetail
* @param bookTaskDetails
* @param contextCount
* @param autoAnalyzeCharacter
*/
async OriginalInferencePrompt(
currentBookTaskDetail: Book.SelectBookTaskDetail,
bookTaskDetails: Book.SelectBookTaskDetail[],
contextCount: number,
autoAnalyzeCharacter: string
) {
await this.GetAISetting()
// 获取当前的推理模式信息
let selectInferenceModel = this.GetInferenceModelMessage()
// 内置模式
let context = this.GetBookTaskDetailContextData(
currentBookTaskDetail,
bookTaskDetails,
contextCount
)
if (isEmpty(autoAnalyzeCharacter) && selectInferenceModel.mustCharacter) {
throw new Error('当前模式需要提前分析或者设置角色场景数据,请先分析角色/场景数据!')
}
let message = this.GetGPTRequestMessage(
currentBookTaskDetail,
context,
autoAnalyzeCharacter,
selectInferenceModel
)
// 开始请求
let res = await this.FetchGpt(message)
if (res) {
// 处理返回的数据,删除部分数据
res = res
.replace(/\)\s*\(/g, ', ')
.replace(/^\(/, '')
.replace(/\)$/, '')
.replaceAll('*', '')
.replaceAll('--', ' ')
}
return res
}
}