diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index dde65089..ac2177eb 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -29,7 +29,7 @@ }, "productName": "RisuAI", "mainBinaryName": "RisuAI", - "version": "156.0.0", + "version": "158.1.0", "identifier": "co.aiclient.risu", "plugins": { "updater": { diff --git a/src/App.svelte b/src/App.svelte index 68a3873b..42cd7634 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -22,7 +22,8 @@ let didFirstSetup: boolean = $derived(DBState.db?.didFirstSetup) let gridOpen = $state(false) - + let aprilFools = $state(new Date().getMonth() === 3 && new Date().getDate() === 1) + let aprilFoolsPage = $state(0)
{ @@ -39,7 +40,60 @@ checkCharOrder() } }}> - {#if !$loadedStore} + {#if aprilFools} + +
+
+ +
+ {#if aprilFoolsPage === 0} +

What can I help you?

+ + +
{ + aprilFoolsPage = 1 + }}> + + + + +
+ {:else} +

+ Happy April Fools! +

+

+ + + Go { + aprilFoolsPage = 0 + aprilFools = false + }}> + Back to normal + +

+ + {/if} +
+
+ RisyGTP-9 +
+ {:else if !$loadedStore}
diff --git a/src/lang/en.ts b/src/lang/en.ts index ee0e3436..5b47a1b2 100644 --- a/src/lang/en.ts +++ b/src/lang/en.ts @@ -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,9 @@ 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", + customModels: "Custom Models", } diff --git a/src/lib/Setting/Pages/AdvancedSettings.svelte b/src/lib/Setting/Pages/AdvancedSettings.svelte index 50f514d9..805cd9dc 100644 --- a/src/lib/Setting/Pages/AdvancedSettings.svelte +++ b/src/lib/Setting/Pages/AdvancedSettings.svelte @@ -14,6 +14,8 @@ import { Capacitor } from "@capacitor/core"; import { capStorageInvestigation } from "src/ts/storage/mobileStorage"; import Arcodion from "src/lib/UI/Arcodion.svelte"; + import { PlusIcon, TrashIcon } from "lucide-svelte"; + import { v4 } from "uuid"; let estaStorage:{ key:string, @@ -211,11 +213,6 @@
-
- - - -
@@ -259,6 +256,117 @@ {/each} +{#snippet CustomFlagButton(index:number,name:string,flag:number)} + +{/snippet} + + + + {#each DBState.db.customModels as model, index} + + {language.name} + + {language.proxyRequestModel} + + URL + + {language.tokenizer} + { + DBState.db.customModels[index].tokenizer = parseInt(e.currentTarget.value) + }}> + tiktokenCl100kBase + tiktokenO200Base + Mistral + Llama + NovelAI + Claude + NovelList + Llama3 + Gemma + GoogleCloud + Cohere + DeepSeek + + {language.format} + { + DBState.db.customModels[index].format = parseInt(e.currentTarget.value) + }}> + OpenAICompatible + OpenAILegacyInstruct + Anthropic + AnthropicLegacy + Mistral + GoogleCloud + VertexAIGemini + NovelList + Cohere + NovelAI + OobaLegacy + Ooba + Kobold + AWSBedrockClaude + OpenAIResponseAPI + + {language.proxyAPIKey} + + {language.additionalParams} + + + {@render CustomFlagButton(index,'hasImageInput', 0)} + {@render CustomFlagButton(index,'hasImageOutput', 1)} + {@render CustomFlagButton(index,'hasAudioInput', 2)} + {@render CustomFlagButton(index,'hasAudioOutput', 3)} + {@render CustomFlagButton(index,'hasPrefill', 4)} + {@render CustomFlagButton(index,'hasCache', 5)} + {@render CustomFlagButton(index,'hasFullSystemPrompt', 6)} + {@render CustomFlagButton(index,'hasFirstSystemPrompt', 7)} + {@render CustomFlagButton(index,'hasStreaming', 8)} + {@render CustomFlagButton(index,'requiresAlternateRole', 9)} + {@render CustomFlagButton(index,'mustStartWithUserInput', 10)} + {@render CustomFlagButton(index,'hasVideoInput', 12)} + {@render CustomFlagButton(index,'OAICompletionTokens', 13)} + {@render CustomFlagButton(index,'DeveloperRole', 14)} + {@render CustomFlagButton(index,'geminiThinking', 15)} + {@render CustomFlagButton(index,'geminiBlockOff', 16)} + {@render CustomFlagButton(index,'deepSeekPrefix', 17)} + {@render CustomFlagButton(index,'deepSeekThinkingInput', 18)} + {@render CustomFlagButton(index,'deepSeekThinkingOutput', 19)} + + + {/each} +
+ + +
+
+ {#if open} -
+
{@render children?.()}
{/if} diff --git a/src/lib/UI/ModelList.svelte b/src/lib/UI/ModelList.svelte index 620549d2..99b01c32 100644 --- a/src/lib/UI/ModelList.svelte +++ b/src/lib/UI/ModelList.svelte @@ -74,6 +74,17 @@ {/await} + {#if DBState?.db.customModels?.length > 0} + + {#each DBState.db.customModels as model} + + {/each} + + + {/if} + + + {#if blankable} {/if} diff --git a/src/lib/UI/Title.svelte b/src/lib/UI/Title.svelte index faabb5b1..8654c534 100644 --- a/src/lib/UI/Title.svelte +++ b/src/lib/UI/Title.svelte @@ -44,6 +44,7 @@ +

diff --git a/src/ts/model/modellist.ts b/src/ts/model/modellist.ts index c75163c6..70f003a0 100644 --- a/src/ts/model/modellist.ts +++ b/src/ts/model/modellist.ts @@ -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 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, diff --git a/src/ts/process/index.svelte.ts b/src/ts/process/index.svelte.ts index 300289fd..8ef7d485 100644 --- a/src/ts/process/index.svelte.ts +++ b/src/ts/process/index.svelte.ts @@ -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 diff --git a/src/ts/process/models/modelString.ts b/src/ts/process/models/modelString.ts index 40e72d9f..935a3bd5 100644 --- a/src/ts/process/models/modelString.ts +++ b/src/ts/process/models/modelString.ts @@ -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 } } \ No newline at end of file diff --git a/src/ts/process/request.ts b/src/ts/process/request.ts index 5117e41b..6a6d2f22 100644 --- a/src/ts/process/request.ts +++ b/src/ts/process/request.ts @@ -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, 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 { 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 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 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 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'; diff --git a/src/ts/translator/translator.ts b/src/ts/translator/translator.ts index 3ecdaf9f..dd2b3f1d 100644 --- a/src/ts/translator/translator.ts +++ b/src/ts/translator/translator.ts @@ -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(); diff --git a/version.json b/version.json index 44db6613..9f87f6de 100644 --- a/version.json +++ b/version.json @@ -1 +1 @@ -{"version":"156.0.0"} \ No newline at end of file +{"version":"158.1.0"} \ No newline at end of file