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",
seperateModelsForAxModels: "Seperate Models for Auxiliary Models",
axModelsDef: "Ax Models Definition",
doNotChangeSeperateModels: "Do Not Change Seperate Models",
doNotChangeSeperateModels: "Do Not Change Seperate Models on Preset Change",
tools: "Tools",
action: "Action",
hotkey: "Hotkey",
@@ -1108,4 +1108,8 @@ export const languageEnglish = {
focusInput: "Focus Input",
},
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/>
</Check>
</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">
<Check bind:check={DBState.db.claudeRetrivalCaching} name={language.claudeCachingRetrival}>
<Help key="unrecommended" unrecommended/>

View File

@@ -1,5 +1,5 @@
<script lang="ts">
import { ArrowLeft, PlusIcon } from "lucide-svelte";
import { ArrowLeft, PlusIcon, TrashIcon } from "lucide-svelte";
import { language } from "src/lang";
import PromptDataItem from "src/lib/UI/PromptDataItem.svelte";
import { tokenizePreset, type PromptItem } from "src/ts/process/prompt";
@@ -174,6 +174,7 @@
</div>
{#if DBState.db.seperateModelsForAxModels}
<Check bind:check={DBState.db.doNotChangeSeperateModels} name={language.doNotChangeSeperateModels}></Check>
<Arcodion name={language.axModelsDef} styled>
<span class="text-textcolor mt-4">
Memory
@@ -200,4 +201,46 @@
</Arcodion>
{/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}

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{
@@ -64,18 +65,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 +268,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[] = 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 +504,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)
@@ -487,7 +516,7 @@ export async function requestChatDataMain(arg:requestDataArgument, model:ModelMo
targ.customURL = db.forceReplaceUrl
}
if(db.seperateModelsForAxModels){
if(db.seperateModelsForAxModels && !arg.staticModel){
if(db.seperateModels[model]){
targ.aiModel = db.seperateModels[model]
targ.modelInfo = getModelInfo(targ.aiModel)
@@ -1873,13 +1902,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 +1927,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'
}
}
}

View File

@@ -498,6 +498,13 @@ export function setDatabase(data:Database){
data.doNotChangeSeperateModels ??= false
data.modelTools ??= []
data.hotkeys ??= structuredClone(defaultHotkeys)
data.fallbackModels ??= {
memory: [],
emotion: [],
translate: [],
otherAx: [],
model: []
}
changeLanguage(data.language)
setDatabaseLite(data)
}
@@ -945,6 +952,15 @@ export interface Database{
doNotChangeSeperateModels:boolean
modelTools: string[]
hotkeys:Hotkey[]
fallbackModels: {
memory: string[],
emotion: string[],
translate: string[],
otherAx: string[]
model: string[]
}
doNotChangeFallbackModels: boolean
fallbackWhenBlankResponse: boolean
}
interface SeparateParameters{
@@ -1288,6 +1304,14 @@ export interface botPreset{
otherAx: 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,
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 +1755,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