This commit is contained in:
kwaroran
2025-03-24 14:43:43 +09:00
15 changed files with 477 additions and 142 deletions

View File

@@ -1050,36 +1050,6 @@ export const LLMModels: LLMModel[] = [
parameters: ['temperature', 'top_k', 'top_p'],
tokenizer: LLMTokenizer.GoogleCloud
},
{
name: "Gemini Exp 1121",
id: 'gemini-exp-1121-vertex',
internalID: 'gemini-exp-1121',
provider: LLMProvider.VertexAI,
format: LLMFormat.VertexAIGemini,
flags: [LLMFlags.hasImageInput, LLMFlags.hasFirstSystemPrompt, LLMFlags.requiresAlternateRole],
parameters: ['temperature', 'top_k', 'top_p'],
tokenizer: LLMTokenizer.Gemma
},
{
name: "Gemini Pro 1.5",
id: 'gemini-1.5-pro-latest-vertex',
internalID: 'gemini-1.5-pro-latest',
provider: LLMProvider.VertexAI,
format: LLMFormat.VertexAIGemini,
flags: [LLMFlags.hasImageInput, LLMFlags.hasFirstSystemPrompt, LLMFlags.requiresAlternateRole],
parameters: ['temperature', 'top_k', 'top_p'],
tokenizer: LLMTokenizer.Gemma
},
{
name: "Gemini Flash 1.5",
id: 'gemini-1.5-flash-vertex',
internalID: 'gemini-1.5-flash',
provider: LLMProvider.VertexAI,
format: LLMFormat.VertexAIGemini,
flags: [LLMFlags.hasImageInput, LLMFlags.hasFirstSystemPrompt, LLMFlags.requiresAlternateRole],
parameters: ['temperature', 'top_k', 'top_p'],
tokenizer: LLMTokenizer.Gemma
},
{
name: "Gemini Exp 1114",
id: 'gemini-exp-1114',
@@ -1405,6 +1375,17 @@ for(let i=0; i<LLMModels.length; i++){
})
}
if(LLMModels[i].provider === LLMProvider.GoogleCloud){
LLMModels.push({
...LLMModels[i],
id: `${LLMModels[i].id}-vertex`,
name: `${LLMModels[i].name} Vertex`,
fullName: `${LLMModels[i].fullName ?? LLMModels[i].name} Vertex`,
flags: [...LLMModels[i].flags],
recommended: false,
provider: LLMProvider.VertexAI
})
}
}
export function getModelInfo(id: string): LLMModel{
@@ -1450,6 +1431,24 @@ export function getModelInfo(id: string): LLMModel{
tokenizer: LLMTokenizer.Unknown
}
}
if(id.startsWith('xcustom:::')){
const customModels = db?.customModels || []
const found = customModels.find((model) => model.id === id)
if(found){
return {
id: found.id,
name: found.name,
shortName: found.name,
fullName: found.name,
internalID: found.internalId,
provider: LLMProvider.AsIs,
format: found.format,
flags: found.flags,
parameters: ['temperature', 'top_p', 'frequency_penalty', 'presence_penalty', 'repetition_penalty', 'min_p', 'top_a', 'top_k', 'thinking_tokens'],
tokenizer: found.tokenizer
}
}
}
return {
id,

View File

@@ -1310,6 +1310,12 @@ export async function sendChat(chatProcessIndex = -1,arg:{
previewBody: arg.previewPrompt
}, 'model', abortSignal)
console.log(req)
if(req.model){
generationInfo.model = getGenerationModelString(req.model)
console.log(generationInfo.model, req.model)
}
if(arg.previewPrompt && req.type === 'success'){
previewBody = req.result
return true

View File

@@ -1,13 +1,13 @@
import { getDatabase } from "src/ts/storage/database.svelte";
export function getGenerationModelString(){
export function getGenerationModelString(name?:string){
const db = getDatabase()
switch (db.aiModel){
switch (name ?? db.aiModel){
case 'reverse_proxy':
return 'custom-' + (db.reverseProxyOobaMode ? 'ooba' : db.customProxyRequestModel)
case 'openrouter':
return 'openrouter-' + db.openrouterRequestModel
default:
return db.aiModel
return name ?? db.aiModel
}
}

View File

@@ -45,6 +45,7 @@ interface requestDataArgument{
extractJson?:string
imageResponse?:boolean
previewBody?:boolean
staticModel?: string
}
interface RequestDataArgumentExtended extends requestDataArgument{
@@ -54,6 +55,7 @@ interface RequestDataArgumentExtended extends requestDataArgument{
modelInfo?:LLMModel
customURL?:string
mode?:ModelModeExtended
key?:string
}
type requestDataResponse = {
@@ -64,18 +66,21 @@ type requestDataResponse = {
emotion?: string
},
failByServerError?: boolean
model?: string
}|{
type: "streaming",
result: ReadableStream<StreamResponseChunk>,
special?: {
emotion?: string
}
model?: string
}|{
type: "multiline",
result: ['user'|'char',string][],
special?: {
emotion?: string
}
model?: string
}
interface StreamResponseChunk{[key:string]:string}
@@ -264,87 +269,112 @@ function applyParameters(data: { [key: string]: any }, parameters: Parameter[],
export async function requestChatData(arg:requestDataArgument, model:ModelModeExtended, abortSignal:AbortSignal=null):Promise<requestDataResponse> {
const db = getDatabase()
let trys = 0
while(true){
const fallBackModels:string[] = safeStructuredClone(db?.fallbackModels?.[model] ?? [])
fallBackModels.push('')
if(pluginV2.replacerbeforeRequest.size > 0){
for(const replacer of pluginV2.replacerbeforeRequest){
arg.formated = await replacer(arg.formated, model)
}
const originalFormated = safeStructuredClone(arg.formated)
for(let fallbackIndex=0;fallbackIndex<fallBackModels.length;fallbackIndex++){
let trys = 0
arg.formated = safeStructuredClone(originalFormated)
if(fallbackIndex !== 0 && !fallBackModels[fallbackIndex]){
continue
}
try{
const currentChar = getCurrentCharacter()
if(currentChar?.type !== 'group'){
const perf = performance.now()
const d = await runTrigger(currentChar, 'request', {
chat: getCurrentChat(),
displayMode: true,
displayData: JSON.stringify(arg.formated)
})
while(true){
const got = JSON.parse(d.displayData)
if(!got || !Array.isArray(got)){
throw new Error('Invalid return')
if(pluginV2.replacerbeforeRequest.size > 0){
for(const replacer of pluginV2.replacerbeforeRequest){
arg.formated = await replacer(arg.formated, model)
}
arg.formated = got
console.log('Trigger time', performance.now() - perf)
}
}
catch(e){
console.error(e)
}
try{
const currentChar = getCurrentCharacter()
if(currentChar?.type !== 'group'){
const perf = performance.now()
const d = await runTrigger(currentChar, 'request', {
chat: getCurrentChat(),
displayMode: true,
displayData: JSON.stringify(arg.formated)
})
const da = await requestChatDataMain(arg, model, abortSignal)
if(da.type === 'success' && pluginV2.replacerafterRequest.size > 0){
for(const replacer of pluginV2.replacerafterRequest){
da.result = await replacer(da.result, model)
}
}
if(da.type === 'success' && db.banCharacterset?.length > 0){
let failed = false
for(const set of db.banCharacterset){
console.log(set)
const checkRegex = new RegExp(`\\p{Script=${set}}`, 'gu')
if(checkRegex.test(da.result)){
trys += 1
if(trys > db.requestRetrys){
return {
type: 'fail',
result: 'Banned character found, retry limit reached'
}
const got = JSON.parse(d.displayData)
if(!got || !Array.isArray(got)){
throw new Error('Invalid return')
}
failed = true
arg.formated = got
console.log('Trigger time', performance.now() - perf)
}
}
catch(e){
console.error(e)
}
const da = await requestChatDataMain({
...arg,
staticModel: fallBackModels[fallbackIndex]
}, model, abortSignal)
if(da.type === 'success' && pluginV2.replacerafterRequest.size > 0){
for(const replacer of pluginV2.replacerafterRequest){
da.result = await replacer(da.result, model)
}
}
if(da.type === 'success' && db.banCharacterset?.length > 0){
let failed = false
for(const set of db.banCharacterset){
console.log(set)
const checkRegex = new RegExp(`\\p{Script=${set}}`, 'gu')
if(checkRegex.test(da.result)){
trys += 1
failed = true
break
}
}
if(failed){
continue
}
}
if(da.type === 'success' && fallbackIndex !== fallBackModels.length-1 && db.fallbackWhenBlankResponse){
if(da.result.trim() === ''){
break
}
}
if(failed){
continue
if(da.type !== 'fail' || da.noRetry){
return {
...da,
model: fallBackModels[fallbackIndex]
}
}
}
if(da.type !== 'fail' || da.noRetry){
return da
}
if(da.failByServerError){
await sleep(1000)
if(db.antiServerOverloads){
trys -= 0.5 // reduce trys by 0.5, so that it will retry twice as much
if(da.failByServerError){
await sleep(1000)
if(db.antiServerOverloads){
trys -= 0.5 // reduce trys by 0.5, so that it will retry twice as much
}
}
}
trys += 1
if(trys > db.requestRetrys){
return da
}
trys += 1
if(trys > db.requestRetrys){
if(fallbackIndex === fallBackModels.length-1 || da.model === 'custom'){
return da
}
break
}
}
}
return {
type: 'fail',
result: "All models failed"
}
}
@@ -475,7 +505,7 @@ export async function requestChatDataMain(arg:requestDataArgument, model:ModelMo
targ.useStreaming = db.useStreaming && arg.useStreaming
targ.continue = arg.continue ?? false
targ.biasString = arg.biasString ?? []
targ.aiModel = (model === 'model' ? db.aiModel : db.subModel)
targ.aiModel = arg.staticModel ? arg.staticModel : (model === 'model' ? db.aiModel : db.subModel)
targ.multiGen = ((db.genTime > 1 && targ.aiModel.startsWith('gpt') && (!arg.continue)) && (!arg.noMultiGen))
targ.abortSignal = abortSignal
targ.modelInfo = getModelInfo(targ.aiModel)
@@ -486,8 +516,13 @@ export async function requestChatDataMain(arg:requestDataArgument, model:ModelMo
targ.modelInfo.format = db.customAPIFormat
targ.customURL = db.forceReplaceUrl
}
if(targ.aiModel.startsWith('xcustom:::')){
const found = db.customModels.find(m => m.id === targ.aiModel)
targ.customURL = found?.url
targ.key = found?.key
}
if(db.seperateModelsForAxModels){
if(db.seperateModelsForAxModels && !arg.staticModel){
if(db.seperateModels[model]){
targ.aiModel = db.seperateModels[model]
targ.modelInfo = getModelInfo(targ.aiModel)
@@ -512,6 +547,7 @@ export async function requestChatDataMain(arg:requestDataArgument, model:ModelMo
return requestPlugin(targ)
case LLMFormat.Ooba:
return requestOoba(targ)
case LLMFormat.VertexAIGemini:
case LLMFormat.GoogleCloud:
return requestGoogleCloudVertex(targ)
case LLMFormat.Kobold:
@@ -746,7 +782,7 @@ async function requestOpenAI(arg:RequestDataArgumentExtended):Promise<requestDat
max_tokens: arg.maxTokens,
}, ['temperature', 'presence_penalty', 'frequency_penalty', 'top_p'], {}, arg.mode ),
headers: {
"Authorization": "Bearer " + db.mistralKey,
"Authorization": "Bearer " + (arg.key ?? db.mistralKey),
},
abortSignal: arg.abortSignal,
chatId: arg.chatId
@@ -919,7 +955,7 @@ async function requestOpenAI(arg:RequestDataArgumentExtended):Promise<requestDat
}
let replacerURL = aiModel === 'openrouter' ? "https://openrouter.ai/api/v1/chat/completions" :
(aiModel === 'reverse_proxy') ? (arg.customURL) : ('https://api.openai.com/v1/chat/completions')
(arg.customURL) ?? ('https://api.openai.com/v1/chat/completions')
if(arg.modelInfo?.endpoint){
replacerURL = arg.modelInfo.endpoint
@@ -949,7 +985,7 @@ async function requestOpenAI(arg:RequestDataArgumentExtended):Promise<requestDat
}
let headers = {
"Authorization": "Bearer " + (aiModel === 'reverse_proxy' ? db.proxyKey : (aiModel === 'openrouter' ? db.openrouterKey : db.openAIKey)),
"Authorization": "Bearer " + (arg.key ?? (aiModel === 'reverse_proxy' ? db.proxyKey : (aiModel === 'openrouter' ? db.openrouterKey : db.openAIKey))),
"Content-Type": "application/json"
}
@@ -1135,8 +1171,23 @@ async function requestOpenAI(arg:RequestDataArgumentExtended):Promise<requestDat
}
}
if(aiModel === 'reverse_proxy'){
const additionalParams = db.additionalParams
if(aiModel === 'reverse_proxy' || aiModel.startsWith('xcustom:::')){
let additionalParams = aiModel === 'reverse_proxy' ? db.additionalParams : []
if(aiModel.startsWith('xcustom:::')){
const found = db.customModels.find(m => m.id === aiModel)
const params = found?.params
if(params){
const lines = params.split('\n')
for(const line of lines){
const split = line.split('=')
if(split.length >= 2){
additionalParams.push([split[0], split.slice(1).join('=')])
}
}
}
}
for(let i=0;i<additionalParams.length;i++){
let key = additionalParams[i][0]
let value = additionalParams[i][1]
@@ -1346,7 +1397,7 @@ async function requestOpenAILegacyInstruct(arg:RequestDataArgumentExtended):Prom
},
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer " + db.openAIKey,
"Authorization": "Bearer " + (arg.key ?? db.openAIKey)
},
chatId: arg.chatId
});
@@ -1482,7 +1533,7 @@ async function requestOpenAIResponseAPI(arg:RequestDataArgumentExtended):Promise
url: "https://api.openai.com/v1/responses",
body: body,
headers: {
"Authorization": "Bearer " + db.openAIKey,
"Authorization": "Bearer " + (arg.key ?? db.openAIKey),
"Content-Type": "application/json"
}
})
@@ -1497,7 +1548,7 @@ async function requestOpenAIResponseAPI(arg:RequestDataArgumentExtended):Promise
body: body,
headers: {
"Content-Type": "application/json",
"Authorization": "Bearer " + db.openAIKey,
"Authorization": "Bearer " + (arg.key ?? db.openAIKey),
},
chatId: arg.chatId
});
@@ -1613,7 +1664,7 @@ async function requestNovelAI(arg:RequestDataArgumentExtended):Promise<requestDa
const da = await globalFetch(aiModel === 'novelai_kayra' ? "https://text.novelai.net/ai/generate" : "https://api.novelai.net/ai/generate", {
body: body,
headers: {
"Authorization": "Bearer " + db.novelai.token
"Authorization": "Bearer " + (arg.key ?? db.novelai.token)
},
abortSignal,
chatId: arg.chatId
@@ -1873,13 +1924,15 @@ async function requestPlugin(arg:RequestDataArgumentExtended):Promise<requestDat
if(!d){
return {
type: 'fail',
result: (language.errors.unknownModel)
result: (language.errors.unknownModel),
model: 'custom'
}
}
else if(!d.success){
return {
type: 'fail',
result: d.content instanceof ReadableStream ? await (new Response(d.content)).text() : d.content
result: d.content instanceof ReadableStream ? await (new Response(d.content)).text() : d.content,
model: 'custom'
}
}
else if(d.content instanceof ReadableStream){
@@ -1896,20 +1949,23 @@ async function requestPlugin(arg:RequestDataArgumentExtended):Promise<requestDat
return {
type: 'streaming',
result: d.content.pipeThrough(piper)
result: d.content.pipeThrough(piper),
model: 'custom'
}
}
else{
return {
type: 'success',
result: d.content
result: d.content,
model: 'custom'
}
}
} catch (error) {
console.error(error)
return {
type: 'fail',
result: `Plugin Error from ${db.currentPluginProvider}: ` + JSON.stringify(error)
result: `Plugin Error from ${db.currentPluginProvider}: ` + JSON.stringify(error),
model: 'custom'
}
}
}
@@ -2439,15 +2495,10 @@ async function requestGoogleCloudVertex(arg:RequestDataArgumentExtended):Promise
const imgHTML = new Image()
const id = crypto.randomUUID()
imgHTML.src = `data:${part.inlineData.mimeType};base64,${part.inlineData.data}`
console.log('decoding', part.inlineData.mimeType, part.inlineData.data, id)
console.log('writing')
await writeInlayImage(imgHTML, {
id: id
})
console.log(JSON.stringify(rDatas))
rDatas[rDatas.length-1] += (`\n{{inlayeddata::${id}}}\n`)
console.log(JSON.stringify(rDatas))
console.log('done', id)
}
}
}
@@ -2774,7 +2825,7 @@ async function requestCohere(arg:RequestDataArgumentExtended):Promise<requestDat
url: arg.customURL ?? 'https://api.cohere.com/v1/chat',
body: body,
headers: {
"Authorization": "Bearer " + db.cohereAPIKey,
"Authorization": "Bearer " + (arg.key ?? db.cohereAPIKey),
"Content-Type": "application/json"
}
})
@@ -2784,7 +2835,7 @@ async function requestCohere(arg:RequestDataArgumentExtended):Promise<requestDat
const res = await globalFetch(arg.customURL ?? 'https://api.cohere.com/v1/chat', {
method: "POST",
headers: {
"Authorization": "Bearer " + db.cohereAPIKey,
"Authorization": "Bearer " + (arg.key ?? db.cohereAPIKey),
"Content-Type": "application/json"
},
body: body
@@ -2817,7 +2868,7 @@ async function requestClaude(arg:RequestDataArgumentExtended):Promise<requestDat
const db = getDatabase()
const aiModel = arg.aiModel
const useStreaming = arg.useStreaming
let replacerURL = (aiModel === 'reverse_proxy') ? (arg.customURL) : ('https://api.anthropic.com/v1/messages')
let replacerURL = arg.customURL ?? ('https://api.anthropic.com/v1/messages')
let apiKey = (aiModel === 'reverse_proxy') ? db.proxyKey : db.claudeAPIKey
const maxTokens = arg.maxTokens
if(aiModel === 'reverse_proxy' && db.autofillRequestUrl){

View File

@@ -12,7 +12,7 @@ import { defaultColorScheme, type ColorScheme } from '../gui/colorscheme';
import type { PromptItem, PromptSettings } from '../process/prompt';
import type { OobaChatCompletionRequestParams } from '../model/ooba';
export let appVer = "156.0.0"
export let appVer = "158.1.0"
export let webAppSubVer = ''
@@ -498,6 +498,21 @@ export function setDatabase(data:Database){
data.doNotChangeSeperateModels ??= false
data.modelTools ??= []
data.hotkeys ??= structuredClone(defaultHotkeys)
data.fallbackModels ??= {
memory: [],
emotion: [],
translate: [],
otherAx: [],
model: []
}
data.fallbackModels = {
model: data.fallbackModels.model.filter((v) => v !== ''),
memory: data.fallbackModels.memory.filter((v) => v !== ''),
emotion: data.fallbackModels.emotion.filter((v) => v !== ''),
translate: data.fallbackModels.translate.filter((v) => v !== ''),
otherAx: data.fallbackModels.otherAx.filter((v) => v !== '')
}
data.customModels ??= []
changeLanguage(data.language)
setDatabaseLite(data)
}
@@ -945,6 +960,26 @@ export interface Database{
doNotChangeSeperateModels:boolean
modelTools: string[]
hotkeys:Hotkey[]
fallbackModels: {
memory: string[],
emotion: string[],
translate: string[],
otherAx: string[]
model: string[]
}
doNotChangeFallbackModels: boolean
fallbackWhenBlankResponse: boolean
customModels: {
id: string
internalId: string
url: string
format: LLMFormat
tokenizer: LLMTokenizer
key: string
name: string
params: string
flags: LLMFlags[]
}[]
}
interface SeparateParameters{
@@ -1288,6 +1323,14 @@ export interface botPreset{
otherAx: string
}
modelTools?:string[]
fallbackModels?: {
memory: string[],
emotion: string[],
translate: string[],
otherAx: string[]
model: string[]
}
fallbackWhenBlankResponse?: boolean
}
@@ -1608,6 +1651,8 @@ export function saveCurrentPreset(){
seperateModelsForAxModels: db.doNotChangeSeperateModels ? false : db.seperateModelsForAxModels ?? false,
seperateModels: db.doNotChangeSeperateModels ? null : safeStructuredClone(db.seperateModels),
modelTools: safeStructuredClone(db.modelTools),
fallbackModels: safeStructuredClone(db.fallbackModels),
fallbackWhenBlankResponse: db.fallbackWhenBlankResponse ?? false,
}
db.botPresets = pres
setDatabase(db)
@@ -1729,6 +1774,16 @@ export function setPreset(db:Database, newPres: botPreset){
otherAx: ''
}
}
if(!db.doNotChangeFallbackModels){
db.fallbackModels = safeStructuredClone(newPres.fallbackModels) ?? {
memory: [],
emotion: [],
translate: [],
otherAx: [],
model: []
}
db.fallbackWhenBlankResponse = newPres.fallbackWhenBlankResponse ?? false
}
db.modelTools = safeStructuredClone(newPres.modelTools ?? [])
return db
@@ -1741,7 +1796,7 @@ import type { RisuModule } from '../process/modules';
import type { SerializableHypaV2Data } from '../process/memory/hypav2';
import { decodeRPack, encodeRPack } from '../rpack/rpack_bg';
import { DBState, selectedCharID } from '../stores.svelte';
import { LLMFlags, LLMFormat } from '../model/modellist';
import { LLMFlags, LLMFormat, LLMTokenizer } from '../model/modellist';
import type { Parameter } from '../process/request';
import type { HypaModel } from '../process/memory/hypamemory';
import type { SerializableHypaV3Data } from '../process/memory/hypav3';

View File

@@ -276,7 +276,7 @@ export async function translateHTML(html: string, reverse:boolean, charArg:simpl
if(db.translatorType === 'llm'){
const tr = db.translator || 'en'
const from = db.translatorInputLanguage
const r = translateLLM(html, {to: tr, from: from, regenerate})
const r = await translateLLM(html, {to: tr, from: from, regenerate})
if(db.playMessageOnTranslateEnd){
const audio = new Audio(sendSound);
audio.play();