Add array fallback

This commit is contained in:
Kwaroran
2025-03-22 21:18:59 +09:00
parent e895900376
commit 9ed6d20994
7 changed files with 204 additions and 86 deletions

View File

@@ -1077,7 +1077,7 @@ export const languageEnglish = {
playMessageOnTranslateEnd: "Play Audio on Translate Completion", playMessageOnTranslateEnd: "Play Audio on Translate Completion",
seperateModelsForAxModels: "Seperate Models for Auxiliary Models", seperateModelsForAxModels: "Seperate Models for Auxiliary Models",
axModelsDef: "Ax Models Definition", axModelsDef: "Ax Models Definition",
doNotChangeSeperateModels: "Do Not Change Seperate Models", doNotChangeSeperateModels: "Do Not Change Seperate Models on Preset Change",
tools: "Tools", tools: "Tools",
action: "Action", action: "Action",
hotkey: "Hotkey", hotkey: "Hotkey",
@@ -1108,4 +1108,8 @@ export const languageEnglish = {
focusInput: "Focus Input", focusInput: "Focus Input",
}, },
screenTooSmall: "Screen is too small to show the interface.", screenTooSmall: "Screen is too small to show the interface.",
advancedModelSettings: "Advanced Model Settings",
fallbackModel: "Fallback Model",
fallbackWhenBlankResponse: "Fallback When Blank Response",
doNotChangeFallbackModels: "Do Not Change Fallback Models on Preset Change",
} }

View File

@@ -211,11 +211,6 @@
<Help key="unrecommended" unrecommended/> <Help key="unrecommended" unrecommended/>
</Check> </Check>
</div> </div>
<div class="flex items-center mt-4">
<Check bind:check={DBState.db.doNotChangeSeperateModels} name={language.doNotChangeSeperateModels}>
<Help key="unrecommended" unrecommended/>
</Check>
</div>
<div class="flex items-center mt-4"> <div class="flex items-center mt-4">
<Check bind:check={DBState.db.claudeRetrivalCaching} name={language.claudeCachingRetrival}> <Check bind:check={DBState.db.claudeRetrivalCaching} name={language.claudeCachingRetrival}>
<Help key="unrecommended" unrecommended/> <Help key="unrecommended" unrecommended/>

View File

@@ -1,5 +1,5 @@
<script lang="ts"> <script lang="ts">
import { ArrowLeft, PlusIcon } from "lucide-svelte"; import { ArrowLeft, PlusIcon, TrashIcon } from "lucide-svelte";
import { language } from "src/lang"; import { language } from "src/lang";
import PromptDataItem from "src/lib/UI/PromptDataItem.svelte"; import PromptDataItem from "src/lib/UI/PromptDataItem.svelte";
import { tokenizePreset, type PromptItem } from "src/ts/process/prompt"; import { tokenizePreset, type PromptItem } from "src/ts/process/prompt";
@@ -174,6 +174,7 @@
</div> </div>
{#if DBState.db.seperateModelsForAxModels} {#if DBState.db.seperateModelsForAxModels}
<Check bind:check={DBState.db.doNotChangeSeperateModels} name={language.doNotChangeSeperateModels}></Check>
<Arcodion name={language.axModelsDef} styled> <Arcodion name={language.axModelsDef} styled>
<span class="text-textcolor mt-4"> <span class="text-textcolor mt-4">
Memory Memory
@@ -200,4 +201,46 @@
</Arcodion> </Arcodion>
{/if} {/if}
{#snippet fallbackModelList(arg:'model'|'memory'|'translate'|'emotion'|'otherAx')}
{#each DBState.db.fallbackModels[arg] as model, i}
<span class="text-textcolor mt-4">
{language.model} {i + 1}
</span>
<ModelList bind:value={DBState.db.fallbackModels[arg][i]} blankable />
{/each}
<div class="flex gap-2">
<button class="bg-selected text-white p-2 rounded-md" onclick={() => {
let value = DBState.db.fallbackModels[arg] ?? []
value.push('')
DBState.db.fallbackModels[arg] = value
}}><PlusIcon /></button>
<button class="bg-red-500 text-white p-2 rounded-md" onclick={() => {
let value = DBState.db.fallbackModels[arg] ?? []
value.pop()
DBState.db.fallbackModels[arg] = value
}}><TrashIcon /></button>
</div>
{/snippet}
<Arcodion name={language.fallbackModel} styled>
<Check bind:check={DBState.db.fallbackWhenBlankResponse} name={language.fallbackWhenBlankResponse} className="mt-4"/>
<Check bind:check={DBState.db.doNotChangeFallbackModels} name={language.doNotChangeFallbackModels} className="mt-4"/>
<Arcodion name={language.model} styled>
{@render fallbackModelList('model')}
</Arcodion>
<Arcodion name={"Memory"} styled>
{@render fallbackModelList('memory')}
</Arcodion>
<Arcodion name={"Translations"} styled>
{@render fallbackModelList('translate')}
</Arcodion>
<Arcodion name={"Emotion"} styled>
{@render fallbackModelList('emotion')}
</Arcodion>
<Arcodion name={"OtherAx"} styled>
{@render fallbackModelList('otherAx')}
</Arcodion>
</Arcodion>
{/if} {/if}

View File

@@ -1310,6 +1310,12 @@ export async function sendChat(chatProcessIndex = -1,arg:{
previewBody: arg.previewPrompt previewBody: arg.previewPrompt
}, 'model', abortSignal) }, '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'){ if(arg.previewPrompt && req.type === 'success'){
previewBody = req.result previewBody = req.result
return true return true

View File

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

View File

@@ -45,6 +45,7 @@ interface requestDataArgument{
extractJson?:string extractJson?:string
imageResponse?:boolean imageResponse?:boolean
previewBody?:boolean previewBody?:boolean
staticModel?: string
} }
interface RequestDataArgumentExtended extends requestDataArgument{ interface RequestDataArgumentExtended extends requestDataArgument{
@@ -64,18 +65,21 @@ type requestDataResponse = {
emotion?: string emotion?: string
}, },
failByServerError?: boolean failByServerError?: boolean
model?: string
}|{ }|{
type: "streaming", type: "streaming",
result: ReadableStream<StreamResponseChunk>, result: ReadableStream<StreamResponseChunk>,
special?: { special?: {
emotion?: string emotion?: string
} }
model?: string
}|{ }|{
type: "multiline", type: "multiline",
result: ['user'|'char',string][], result: ['user'|'char',string][],
special?: { special?: {
emotion?: string emotion?: string
} }
model?: string
} }
interface StreamResponseChunk{[key:string]:string} interface StreamResponseChunk{[key:string]:string}
@@ -264,87 +268,112 @@ function applyParameters(data: { [key: string]: any }, parameters: Parameter[],
export async function requestChatData(arg:requestDataArgument, model:ModelModeExtended, abortSignal:AbortSignal=null):Promise<requestDataResponse> { export async function requestChatData(arg:requestDataArgument, model:ModelModeExtended, abortSignal:AbortSignal=null):Promise<requestDataResponse> {
const db = getDatabase() const db = getDatabase()
let trys = 0 const fallBackModels:string[] = db?.fallbackModels?.[model] ?? []
while(true){ fallBackModels.push('')
if(pluginV2.replacerbeforeRequest.size > 0){ const originalFormated = safeStructuredClone(arg.formated)
for(const replacer of pluginV2.replacerbeforeRequest){ for(let fallbackIndex=0;fallbackIndex<fallBackModels.length;fallbackIndex++){
arg.formated = await replacer(arg.formated, model) let trys = 0
} arg.formated = safeStructuredClone(originalFormated)
if(fallbackIndex !== 0 && !fallBackModels[fallbackIndex]){
continue
} }
try{ while(true){
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 got = JSON.parse(d.displayData) if(pluginV2.replacerbeforeRequest.size > 0){
if(!got || !Array.isArray(got)){ for(const replacer of pluginV2.replacerbeforeRequest){
throw new Error('Invalid return') 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) const got = JSON.parse(d.displayData)
if(!got || !Array.isArray(got)){
if(da.type === 'success' && pluginV2.replacerafterRequest.size > 0){ throw new Error('Invalid return')
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'
}
} }
arg.formated = got
console.log('Trigger time', performance.now() - perf)
}
}
catch(e){
console.error(e)
}
failed = true
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 break
} }
} }
if(failed){ if(da.type !== 'fail' || da.noRetry){
continue return {
...da,
model: fallBackModels[fallbackIndex]
}
}
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){
if(fallbackIndex === fallBackModels.length-1 || da.model === 'custom'){
return da
}
break
} }
} }
}
if(da.type !== 'fail' || da.noRetry){ return {
return da type: 'fail',
} result: "All models failed"
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
}
} }
} }
@@ -475,7 +504,7 @@ export async function requestChatDataMain(arg:requestDataArgument, model:ModelMo
targ.useStreaming = db.useStreaming && arg.useStreaming targ.useStreaming = db.useStreaming && arg.useStreaming
targ.continue = arg.continue ?? false targ.continue = arg.continue ?? false
targ.biasString = arg.biasString ?? [] 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.multiGen = ((db.genTime > 1 && targ.aiModel.startsWith('gpt') && (!arg.continue)) && (!arg.noMultiGen))
targ.abortSignal = abortSignal targ.abortSignal = abortSignal
targ.modelInfo = getModelInfo(targ.aiModel) targ.modelInfo = getModelInfo(targ.aiModel)
@@ -487,7 +516,7 @@ export async function requestChatDataMain(arg:requestDataArgument, model:ModelMo
targ.customURL = db.forceReplaceUrl targ.customURL = db.forceReplaceUrl
} }
if(db.seperateModelsForAxModels){ if(db.seperateModelsForAxModels && !arg.staticModel){
if(db.seperateModels[model]){ if(db.seperateModels[model]){
targ.aiModel = db.seperateModels[model] targ.aiModel = db.seperateModels[model]
targ.modelInfo = getModelInfo(targ.aiModel) targ.modelInfo = getModelInfo(targ.aiModel)
@@ -1873,13 +1902,15 @@ async function requestPlugin(arg:RequestDataArgumentExtended):Promise<requestDat
if(!d){ if(!d){
return { return {
type: 'fail', type: 'fail',
result: (language.errors.unknownModel) result: (language.errors.unknownModel),
model: 'custom'
} }
} }
else if(!d.success){ else if(!d.success){
return { return {
type: 'fail', 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){ else if(d.content instanceof ReadableStream){
@@ -1896,20 +1927,23 @@ async function requestPlugin(arg:RequestDataArgumentExtended):Promise<requestDat
return { return {
type: 'streaming', type: 'streaming',
result: d.content.pipeThrough(piper) result: d.content.pipeThrough(piper),
model: 'custom'
} }
} }
else{ else{
return { return {
type: 'success', type: 'success',
result: d.content result: d.content,
model: 'custom'
} }
} }
} catch (error) { } catch (error) {
console.error(error) console.error(error)
return { return {
type: 'fail', type: 'fail',
result: `Plugin Error from ${db.currentPluginProvider}: ` + JSON.stringify(error) result: `Plugin Error from ${db.currentPluginProvider}: ` + JSON.stringify(error),
model: 'custom'
} }
} }
} }

View File

@@ -498,6 +498,13 @@ export function setDatabase(data:Database){
data.doNotChangeSeperateModels ??= false data.doNotChangeSeperateModels ??= false
data.modelTools ??= [] data.modelTools ??= []
data.hotkeys ??= structuredClone(defaultHotkeys) data.hotkeys ??= structuredClone(defaultHotkeys)
data.fallbackModels ??= {
memory: [],
emotion: [],
translate: [],
otherAx: [],
model: []
}
changeLanguage(data.language) changeLanguage(data.language)
setDatabaseLite(data) setDatabaseLite(data)
} }
@@ -945,6 +952,15 @@ export interface Database{
doNotChangeSeperateModels:boolean doNotChangeSeperateModels:boolean
modelTools: string[] modelTools: string[]
hotkeys:Hotkey[] hotkeys:Hotkey[]
fallbackModels: {
memory: string[],
emotion: string[],
translate: string[],
otherAx: string[]
model: string[]
}
doNotChangeFallbackModels: boolean
fallbackWhenBlankResponse: boolean
} }
interface SeparateParameters{ interface SeparateParameters{
@@ -1288,6 +1304,14 @@ export interface botPreset{
otherAx: string otherAx: string
} }
modelTools?:string[] modelTools?:string[]
fallbackModels?: {
memory: string[],
emotion: string[],
translate: string[],
otherAx: string[]
model: string[]
}
fallbackWhenBlankResponse?: boolean
} }
@@ -1608,6 +1632,8 @@ export function saveCurrentPreset(){
seperateModelsForAxModels: db.doNotChangeSeperateModels ? false : db.seperateModelsForAxModels ?? false, seperateModelsForAxModels: db.doNotChangeSeperateModels ? false : db.seperateModelsForAxModels ?? false,
seperateModels: db.doNotChangeSeperateModels ? null : safeStructuredClone(db.seperateModels), seperateModels: db.doNotChangeSeperateModels ? null : safeStructuredClone(db.seperateModels),
modelTools: safeStructuredClone(db.modelTools), modelTools: safeStructuredClone(db.modelTools),
fallbackModels: safeStructuredClone(db.fallbackModels),
fallbackWhenBlankResponse: db.fallbackWhenBlankResponse ?? false,
} }
db.botPresets = pres db.botPresets = pres
setDatabase(db) setDatabase(db)
@@ -1729,6 +1755,16 @@ export function setPreset(db:Database, newPres: botPreset){
otherAx: '' otherAx: ''
} }
} }
if(!db.doNotChangeFallbackModels){
db.fallbackModels = safeStructuredClone(newPres.fallbackModels) ?? {
memory: [],
emotion: [],
translate: [],
otherAx: [],
model: []
}
db.fallbackWhenBlankResponse = newPres.fallbackWhenBlankResponse ?? false
}
db.modelTools = safeStructuredClone(newPres.modelTools ?? []) db.modelTools = safeStructuredClone(newPres.modelTools ?? [])
return db return db