From 87e442eda9001b811f0ae17978c86903fff19f84 Mon Sep 17 00:00:00 2001 From: hashcoko Date: Thu, 20 Jun 2024 12:59:46 +0900 Subject: [PATCH 001/175] Add globalApi JsDoc --- src/lang/index.ts | 2 +- src/ts/storage/globalApi.ts | 979 ++++++++++++++++++++++++++---------- 2 files changed, 708 insertions(+), 273 deletions(-) diff --git a/src/lang/index.ts b/src/lang/index.ts index 5b028eed..c79310f9 100644 --- a/src/lang/index.ts +++ b/src/lang/index.ts @@ -36,4 +36,4 @@ export function changeLanguage(lang:string){ language.help.toggleNsfw = '' language.jailbreakToggle = 'Toggle Togglable Prompt' } -} +} \ No newline at end of file diff --git a/src/ts/storage/globalApi.ts b/src/ts/storage/globalApi.ts index 89a4c23a..5db4961c 100644 --- a/src/ts/storage/globalApi.ts +++ b/src/ts/storage/globalApi.ts @@ -55,6 +55,12 @@ interface fetchLog{ let fetchLog:fetchLog[] = [] +/** + * Downloads a file with the given name and data. + * + * @param {string} name - The name of the file to be downloaded. + * @param {Uint8Array|ArrayBuffer|string} dat - The data of the file to be downloaded. + */ export async function downloadFile(name:string, dat:Uint8Array|ArrayBuffer|string) { if(typeof(dat) === 'string'){ dat = Buffer.from(dat, 'utf-8') @@ -88,6 +94,12 @@ let fileCache:{ let pathCache:{[key:string]:string} = {} let checkedPaths:string[] = [] +/** + * Checks if a file exists in the Capacitor filesystem. + * + * @param {CapFS.GetUriOptions} getUriOptions - The options for getting the URI of the file. + * @returns {Promise} - A promise that resolves to true if the file exists, false otherwise. + */ async function checkCapFileExists(getUriOptions: CapFS.GetUriOptions): Promise { try { await CapFS.Filesystem.stat(getUriOptions); @@ -100,6 +112,13 @@ async function checkCapFileExists(getUriOptions: CapFS.GetUriOptions): Promise} - A promise that resolves to the source URL of the file. + */ export async function getFileSrc(loc:string) { if(isTauri){ if(loc.startsWith('assets')){ @@ -201,6 +220,12 @@ export async function getFileSrc(loc:string) { let appDataDirPath = '' +/** + * Reads an image file and returns its data. + * + * @param {string} data - The path to the image file. + * @returns {Promise} - A promise that resolves to the data of the image file. + */ export async function readImage(data:string) { if(isTauri){ if(data.startsWith('assets')){ @@ -216,6 +241,14 @@ export async function readImage(data:string) { } } +/** + * Saves an asset file with the given data, custom ID, and file name. + * + * @param {Uint8Array} data - The data of the asset file. + * @param {string} [customId=''] - The custom ID for the asset file. + * @param {string} [fileName=''] - The name of the asset file. + * @returns {Promise} - A promise that resolves to the path of the saved asset file. + */ export async function saveAsset(data:Uint8Array, customId:string = '', fileName:string = ''){ let id = '' if(customId !== ''){ @@ -246,6 +279,12 @@ export async function saveAsset(data:Uint8Array, customId:string = '', fileName: } } +/** + * Loads an asset file with the given ID. + * + * @param {string} id - The ID of the asset file to load. + * @returns {Promise} - A promise that resolves to the data of the loaded asset file. + */ export async function loadAsset(id:string){ if(isTauri){ return await readBinaryFile(id,{dir: BaseDirectory.AppData}) @@ -257,8 +296,13 @@ export async function loadAsset(id:string){ let lastSave = '' +/** + * Saves the current state of the database. + * + * @returns {Promise} - A promise that resolves when the database has been saved. + */ export async function saveDb(){ - lastSave =JSON.stringify(get(DataBase)) + lastSave = JSON.stringify(get(DataBase)) let changed = false syncDrive() DataBase.subscribe(() => { @@ -334,7 +378,11 @@ export async function saveDb(){ } } - +/** + * Retrieves the database backups. + * + * @returns {Promise} - A promise that resolves to an array of backup timestamps. + */ async function getDbBackups() { let db = get(DataBase) if(db?.account?.useSync){ @@ -377,6 +425,11 @@ async function getDbBackups() { let usingSw = false +/** + * Loads the application data. + * + * @returns {Promise} - A promise that resolves when the data has been loaded. + */ export async function loadData() { const loaded = get(loadedStore) if(!loaded){ @@ -523,31 +576,53 @@ export async function loadData() { } } -export async function getFetchData(id:string) { - for(const log of fetchLog){ - if(log.chatId === id){ - return log - } +/** + * Retrieves fetch data for a given chat ID. + * + * @param {string} id - The chat ID to search for in the fetch log. + * @returns {fetchLog | null} - The fetch log entry if found, otherwise null. + */ +export async function getFetchData(id: string) { + for (const log of fetchLog) { + if (log.chatId === id) { + return log; + } } - return null + return null; } -function updateErrorHandling(){ - removeDefaultHandler() - const errorHandler = (event: ErrorEvent) => { - console.error(event.error) - alertError(event.error) - } - const rejectHandler = (event: PromiseRejectionEvent) => { - console.error(event.reason) - alertError(event.reason) - } - window.addEventListener('error', errorHandler) - window.addEventListener('unhandledrejection', rejectHandler) +/** + * Updates the error handling by removing the default handler and adding custom handlers for errors and unhandled promise rejections. + */ +function updateErrorHandling() { + removeDefaultHandler(); + const errorHandler = (event: ErrorEvent) => { + console.error(event.error); + alertError(event.error); + }; + const rejectHandler = (event: PromiseRejectionEvent) => { + console.error(event.reason); + alertError(event.reason); + }; + window.addEventListener('error', errorHandler); + window.addEventListener('unhandledrejection', rejectHandler); } -const knownHostes = ["localhost","127.0.0.1","0.0.0.0"] +const knownHostes = ["localhost", "127.0.0.1", "0.0.0.0"]; +/** + * Interface representing the arguments for the global fetch function. + * + * @interface GlobalFetchArgs + * @property {boolean} [plainFetchForce] - Whether to force plain fetch. + * @property {any} [body] - The body of the request. + * @property {{ [key: string]: string }} [headers] - The headers of the request. + * @property {boolean} [rawResponse] - Whether to return the raw response. + * @property {'POST' | 'GET'} [method] - The HTTP method to use. + * @property {AbortSignal} [abortSignal] - The abort signal to cancel the request. + * @property {boolean} [useRisuToken] - Whether to use the Risu token. + * @property {string} [chatId] - The chat ID associated with the request. + */ interface GlobalFetchArgs { plainFetchForce?: boolean; body?: any; @@ -559,70 +634,103 @@ interface GlobalFetchArgs { chatId?: string; } +/** + * Interface representing the result of the global fetch function. + * + * @interface GlobalFetchResult + * @property {boolean} ok - Whether the request was successful. + * @property {any} data - The data returned from the request. + * @property {{ [key: string]: string }} headers - The headers returned from the request. + */ interface GlobalFetchResult { ok: boolean; data: any; headers: { [key: string]: string }; } -export function addFetchLog(arg:{ - body:any, - headers?:{[key:string]:string}, - response:any, - success:boolean, - url:string, - resType?:string, - chatId?:string -}){ +/** + * Adds a fetch log entry. + * + * @param {Object} arg - The arguments for the fetch log entry. + * @param {any} arg.body - The body of the request. + * @param {{ [key: string]: string }} [arg.headers] - The headers of the request. + * @param {any} arg.response - The response from the request. + * @param {boolean} arg.success - Whether the request was successful. + * @param {string} arg.url - The URL of the request. + * @param {string} [arg.resType] - The response type. + * @param {string} [arg.chatId] - The chat ID associated with the request. + * @returns {number} - The index of the added fetch log entry. + */ +export function addFetchLog(arg: { + body: any, + headers?: { [key: string]: string }, + response: any, + success: boolean, + url: string, + resType?: string, + chatId?: string +}): number { fetchLog.unshift({ - body: typeof(arg.body) === 'string' ? arg.body : JSON.stringify(arg.body, null, 2), - header: JSON.stringify(arg.headers ?? {}, null, 2), - response: typeof(arg.response) === 'string' ? arg.response : JSON.stringify(arg.response, null, 2), - responseType: arg.resType ?? 'json', - success: arg.success, - date: (new Date()).toLocaleTimeString(), - url: arg.url, - chatId: arg.chatId - }) - return fetchLog.length - 1 + body: typeof (arg.body) === 'string' ? arg.body : JSON.stringify(arg.body, null, 2), + header: JSON.stringify(arg.headers ?? {}, null, 2), + response: typeof (arg.response) === 'string' ? arg.response : JSON.stringify(arg.response, null, 2), + responseType: arg.resType ?? 'json', + success: arg.success, + date: (new Date()).toLocaleTimeString(), + url: arg.url, + chatId: arg.chatId + }); + return fetchLog.length - 1; } - - +/** + * Performs a global fetch request. + * + * @param {string} url - The URL to fetch. + * @param {GlobalFetchArgs} [arg={}] - The arguments for the fetch request. + * @returns {Promise} - The result of the fetch request. + */ export async function globalFetch(url: string, arg: GlobalFetchArgs = {}): Promise { try { - const db = get(DataBase) - const method = arg.method ?? "POST" - db.requestmet = "normal" + const db = get(DataBase); + const method = arg.method ?? "POST"; + db.requestmet = "normal"; - if (arg.abortSignal?.aborted) { return { ok: false, data: 'aborted', headers: {} }} + if (arg.abortSignal?.aborted) { return { ok: false, data: 'aborted', headers: {} }; } - const urlHost = new URL(url).hostname - const forcePlainFetch = (knownHostes.includes(urlHost) && !isTauri) || db.usePlainFetch || arg.plainFetchForce + const urlHost = new URL(url).hostname; + const forcePlainFetch = (knownHostes.includes(urlHost) && !isTauri) || db.usePlainFetch || arg.plainFetchForce; - if (knownHostes.includes(urlHost) && !isTauri && !isNodeServer){ - return { ok: false, headers: {}, data: 'You are trying local request on web version. This is not allowed due to browser security policy. Use the desktop version instead, or use a tunneling service like ngrok and set the CORS to allow all.' } + if (knownHostes.includes(urlHost) && !isTauri && !isNodeServer) { + return { ok: false, headers: {}, data: 'You are trying local request on web version. This is not allowed due to browser security policy. Use the desktop version instead, or use a tunneling service like ngrok and set the CORS to allow all.' }; } // Simplify the globalFetch function: Detach built-in functions if (forcePlainFetch) { - return await fetchWithPlainFetch(url, arg); + return await fetchWithPlainFetch(url, arg); } if (isTauri) { - return await fetchWithTauri(url, arg); + return await fetchWithTauri(url, arg); } if (Capacitor.isNativePlatform()) { - return await fetchWithCapacitor(url, arg); + return await fetchWithCapacitor(url, arg); } return await fetchWithProxy(url, arg); - + } catch (error) { console.error(error); return { ok: false, data: `${error}`, headers: {} }; } } -// Decoupled globalFetch built-in function +/** + * Adds a fetch log entry in the global fetch log. + * + * @param {any} response - The response data. + * @param {boolean} success - Indicates if the fetch was successful. + * @param {string} url - The URL of the fetch request. + * @param {GlobalFetchArgs} arg - The arguments for the fetch request. + */ function addFetchLogInGlobalFetch(response:any, success:boolean, url:string, arg:GlobalFetchArgs){ try{ fetchLog.unshift({ @@ -652,7 +760,13 @@ function addFetchLogInGlobalFetch(response:any, success:boolean, url:string, arg } } -// Decoupled globalFetch built-in function +/** + * Performs a fetch request using plain fetch. + * + * @param {string} url - The URL to fetch. + * @param {GlobalFetchArgs} arg - The arguments for the fetch request. + * @returns {Promise} - The result of the fetch request. + */ async function fetchWithPlainFetch(url: string, arg: GlobalFetchArgs): Promise { try { const headers = { 'Content-Type': 'application/json', ...arg.headers }; @@ -666,7 +780,13 @@ async function fetchWithPlainFetch(url: string, arg: GlobalFetchArgs): Promise} - The result of the fetch request. + */ async function fetchWithTauri(url: string, arg: GlobalFetchArgs): Promise { const body = !arg.body ? null : arg.body instanceof URLSearchParams ? Body.text(arg.body.toString()) : Body.json(arg.body); const headers = arg.headers ?? {}; @@ -696,7 +816,13 @@ async function fetchWithTauri(url: string, arg: GlobalFetchArgs): Promise} - The result of the fetch request. + */ async function fetchWithCapacitor(url: string, arg: GlobalFetchArgs): Promise { const { body, headers = {}, rawResponse } = arg; headers["Content-Type"] = body instanceof URLSearchParams ? "application/x-www-form-urlencoded" : "application/json"; @@ -712,7 +838,13 @@ async function fetchWithCapacitor(url: string, arg: GlobalFetchArgs): Promise} - The result of the fetch request. + */ async function fetchWithProxy(url: string, arg: GlobalFetchArgs): Promise { try { const furl = !isTauri && !isNodeServer ? `${hubURL}/proxy2` : `/proxy2`; @@ -750,263 +882,314 @@ async function fetchWithProxy(url: string, arg: GlobalFetchArgs): Promise} - A promise that resolves when the service worker is registered and initialized. + */ async function registerSw() { await navigator.serviceWorker.register("/sw.js", { scope: "/" }); - await sleep(100) - const da = await fetch('/sw/init') - if(!(da.status >= 200 && da.status < 300)){ - location.reload() - } - else{ - + await sleep(100); + const da = await fetch('/sw/init'); + if (!(da.status >= 200 && da.status < 300)) { + location.reload(); } } -const re = /\\/g -function getBasename(data:string){ - const splited = data.replace(re, '/').split('/') - const lasts = splited[splited.length-1] - return lasts +/** + * Regular expression to match backslashes. + * + * @constant {RegExp} + */ +const re = /\\/g; + +/** + * Gets the basename of a given path. + * + * @param {string} data - The path to get the basename from. + * @returns {string} - The basename of the path. + */ +function getBasename(data: string) { + const splited = data.replace(re, '/').split('/'); + const lasts = splited[splited.length - 1]; + return lasts; } -export function getUnpargeables(db:Database, uptype:'basename'|'pure' = 'basename') { - let unpargeable:string[] = [] +/** + * Retrieves unpargeable resources from the database. + * + * @param {Database} db - The database to retrieve unpargeable resources from. + * @param {'basename'|'pure'} [uptype='basename'] - The type of unpargeable resources to retrieve. + * @returns {string[]} - An array of unpargeable resources. + */ +export function getUnpargeables(db: Database, uptype: 'basename' | 'pure' = 'basename') { + let unpargeable: string[] = []; - function addUnparge(data:string){ - if(!data){ - return + /** + * Adds a resource to the unpargeable list if it is not already included. + * + * @param {string} data - The resource to add. + */ + function addUnparge(data: string) { + if (!data) { + return; } - if(data === ''){ - return + if (data === '') { + return; } - const bn = uptype === 'basename' ? getBasename(data) : data - if(!unpargeable.includes(bn)){ - unpargeable.push(bn) + const bn = uptype === 'basename' ? getBasename(data) : data; + if (!unpargeable.includes(bn)) { + unpargeable.push(bn); } } - addUnparge(db.customBackground) - addUnparge(db.userIcon) + addUnparge(db.customBackground); + addUnparge(db.userIcon); - for(const cha of db.characters){ - if(cha.image){ - addUnparge(cha.image) + for (const cha of db.characters) { + if (cha.image) { + addUnparge(cha.image); } - if(cha.emotionImages){ - for(const em of cha.emotionImages){ - addUnparge(em[1]) + if (cha.emotionImages) { + for (const em of cha.emotionImages) { + addUnparge(em[1]); } } - if(cha.type !== 'group'){ - if(cha.additionalAssets){ - for(const em of cha.additionalAssets){ - addUnparge(em[1]) + if (cha.type !== 'group') { + if (cha.additionalAssets) { + for (const em of cha.additionalAssets) { + addUnparge(em[1]); } } - if(cha.vits){ - const keys = Object.keys(cha.vits.files) - for(const key of keys){ - const vit = cha.vits.files[key] - addUnparge(vit) + if (cha.vits) { + const keys = Object.keys(cha.vits.files); + for (const key of keys) { + const vit = cha.vits.files[key]; + addUnparge(vit); } } - if(cha.ccAssets){ - for(const asset of cha.ccAssets){ - addUnparge(asset.uri) + if (cha.ccAssets) { + for (const asset of cha.ccAssets) { + addUnparge(asset.uri); } } } } - if(db.personas){ + if (db.personas) { db.personas.map((v) => { - addUnparge(v.icon) - }) + addUnparge(v.icon); + }); } - return unpargeable + return unpargeable; } -export function replaceDbResources(db:Database,replacer:{[key:string]:string}) { - let unpargeable:string[] = [] +/** + * Replaces database resources with the provided replacer object. + * + * @param {Database} db - The database object containing resources to be replaced. + * @param {{[key: string]: string}} replacer - An object mapping original resource keys to their replacements. + * @returns {Database} - The updated database object with replaced resources. + */ +export function replaceDbResources(db: Database, replacer: { [key: string]: string }): Database { + let unpargeable: string[] = []; - function replaceData(data:string){ - if(!data){ - return data + /** + * Replaces a given data string with its corresponding value from the replacer object. + * + * @param {string} data - The data string to be replaced. + * @returns {string} - The replaced data string or the original data if no replacement is found. + */ + function replaceData(data: string): string { + if (!data) { + return data; } - return replacer[data] ?? data + return replacer[data] ?? data; } - db.customBackground = replaceData(db.customBackground) - db.userIcon = replaceData(db.userIcon) + db.customBackground = replaceData(db.customBackground); + db.userIcon = replaceData(db.userIcon); - for(const cha of db.characters){ - if(cha.image){ - cha.image = replaceData(cha.image) + for (const cha of db.characters) { + if (cha.image) { + cha.image = replaceData(cha.image); } - if(cha.emotionImages){ - for(let i=0;i} - A promise that resolves when the database format check and update is complete. + */ +async function checkNewFormat(): Promise { + let db = get(DataBase); - //check data integrity + // Check data integrity db.characters = db.characters.map((v) => { - if(!v){ - return null + if (!v) { + return null; } - v.chaId ??= uuidv4() - v.type ??= 'character' - v.chatPage ??= 0 - v.chats ??= [] - v.customscript ??= [] - v.firstMessage ??= '' - v.globalLore ??= [] - v.name ??= '' - v.viewScreen ??= 'none' - v.emotionImages = v.emotionImages ?? [] + v.chaId ??= uuidv4(); + v.type ??= 'character'; + v.chatPage ??= 0; + v.chats ??= []; + v.customscript ??= []; + v.firstMessage ??= ''; + v.globalLore ??= []; + v.name ??= ''; + v.viewScreen ??= 'none'; + v.emotionImages = v.emotionImages ?? []; - if(v.type === 'character'){ - v.bias ??= [] - v.characterVersion ??= '' - v.creator ??= '' - v.desc ??= '' - v.utilityBot ??= false - v.tags ??= [] - v.systemPrompt ??= '' - v.scenario ??= '' + if (v.type === 'character') { + v.bias ??= []; + v.characterVersion ??= ''; + v.creator ??= ''; + v.desc ??= ''; + v.utilityBot ??= false; + v.tags ??= []; + v.systemPrompt ??= ''; + v.scenario ??= ''; } - return v + return v; }).filter((v) => { - return v !== null - }) + return v !== null; + }); db.modules = (db.modules ?? []).map((v) => { - if(v.lorebook){ - v.lorebook = updateLorebooks(v.lorebook) + if (v.lorebook) { + v.lorebook = updateLorebooks(v.lorebook); } - return v - }) + return v; + }); - if(!db.formatversion){ - function checkParge(data:string){ - - if(data.startsWith('assets') || (data.length < 3)){ - return data - } - else{ - const d = 'assets/' + (data.replace(/\\/g, '/').split('assets/')[1]) - if(!d){ - return data + if (!db.formatversion) { + /** + * Checks and updates the path of a given data string. + * + * @param {string} data - The data string to be checked and updated. + * @returns {string} - The updated data string with the correct path. + */ + function checkParge(data: string): string { + if (data.startsWith('assets') || (data.length < 3)) { + return data; + } else { + const d = 'assets/' + (data.replace(/\\/g, '/').split('assets/')[1]); + if (!d) { + return data; } - return d + return d; } } - - db.customBackground = checkParge(db.customBackground) - db.userIcon = checkParge(db.userIcon) - - for(let i=0;i= 2){ - db.characters[i].emotionImages[i2][1] = checkParge(db.characters[i].emotionImages[i2][1]) + if (db.characters[i].emotionImages) { + for (let i2 = 0; i2 < db.characters[i].emotionImages.length; i2++) { + if (db.characters[i].emotionImages[i2] && db.characters[i].emotionImages[i2].length >= 2) { + db.characters[i].emotionImages[i2][1] = checkParge(db.characters[i].emotionImages[i2][1]); } } } } - - db.formatversion = 2 - } - if(db.formatversion < 3){ - for(let i=0;i 0){ - const id = v4() - let regexModule:RisuModule = { + if (db.formatversion < 4) { + db.modules ??= []; + db.enabledModules ??= []; + // Convert global lore and global regex to modules + if (db.globalscript && db.globalscript.length > 0) { + const id = v4(); + let regexModule: RisuModule = { name: "Global Regex", description: "Converted from legacy global regex", id: id, regex: structuredClone(db.globalscript) - } - db.modules.push(regexModule) - db.enabledModules.push(id) - db.globalscript = [] + }; + db.modules.push(regexModule); + db.enabledModules.push(id); + db.globalscript = []; } - if(db.loreBook && db.loreBook.length > 0){ - const selIndex = db.loreBookPage - for(let i=0;i 0) { + const selIndex = db.loreBookPage; + for (let i = 0; i < db.loreBook.length; i++) { + const id = v4(); + let lbModule: RisuModule = { name: db.loreBook[i].name || "Unnamed Global Lorebook", description: "Converted from legacy global lorebook", id: id, lorebook: structuredClone(db.loreBook[i].data) + }; + db.modules.push(lbModule); + if (i === selIndex) { + db.enabledModules.push(id); } - db.modules.push(lbModule) - if(i === selIndex){ - db.enabledModules.push(id) - } - db.globalscript = [] + db.globalscript = []; } - db.loreBook = [] + db.loreBook = []; } - db.formatversion = 4 + db.formatversion = 4; } - if(!db.characterOrder){ - db.characterOrder = [] + if (!db.characterOrder) { + db.characterOrder = []; } - if(db.mainPrompt === oldMainPrompt){ - db.mainPrompt = defaultMainPrompt + if (db.mainPrompt === oldMainPrompt) { + db.mainPrompt = defaultMainPrompt; } - if(db.mainPrompt === oldJailbreak){ - db.mainPrompt = defaultJailbreak + if (db.mainPrompt === oldJailbreak) { + db.mainPrompt = defaultJailbreak; } - for(let i=0;i} - A promise that resolves to a boolean indicating success. + */ + async init(name = 'Binary', ext = ['bin']): Promise { + if (isTauri) { const filePath = await save({ filters: [{ - name: name, - extensions: ext + name: name, + extensions: ext }] }); - if(!filePath){ + if (!filePath) { return false } this.writer = new TauriWriter(filePath) return true } - if(Capacitor.isNativePlatform()){ + if (Capacitor.isNativePlatform()) { this.writer = new MobileWriter(name + '.' + ext[0]) return true } @@ -1235,7 +1489,14 @@ export class LocalWriter{ this.writer = writableStream.getWriter() return true } - async writeBackup(name:string,data: Uint8Array){ + + /** + * Writes backup data to the file. + * + * @param {string} name - The name of the backup. + * @param {Uint8Array} data - The data to write. + */ + async writeBackup(name: string, data: Uint8Array): Promise { const encodedName = new TextEncoder().encode(getBasename(name)) const nameLength = new Uint32Array([encodedName.byteLength]) await this.writer.write(new Uint8Array(nameLength.buffer)) @@ -1244,55 +1505,132 @@ export class LocalWriter{ await this.writer.write(new Uint8Array(dataLength.buffer)) await this.writer.write(data) } - async write(data:Uint8Array) { + + /** + * Writes data to the file. + * + * @param {Uint8Array} data - The data to write. + */ + async write(data: Uint8Array): Promise { await this.writer.write(data) } - async close(){ + + /** + * Closes the writer. + */ + async close(): Promise { await this.writer.close() } } -export class VirtualWriter{ +/** + * Class representing a virtual writer. + */ +export class VirtualWriter { buf = new AppendableBuffer() - async write(data:Uint8Array) { + + /** + * Writes data to the buffer. + * + * @param {Uint8Array} data - The data to write. + */ + async write(data: Uint8Array): Promise { this.buf.append(data) } - async close(){ + + /** + * Closes the writer. (No operation for VirtualWriter) + */ + async close(): Promise { // do nothing } } +/** + * Index for fetch operations. + * @type {number} + */ let fetchIndex = 0 -let nativeFetchData:{[key:string]:StreamedFetchChunk[]} = {} -interface StreamedFetchChunkData{ - type:'chunk', - body:string, - id:string +/** + * Stores native fetch data. + * @type {{ [key: string]: StreamedFetchChunk[] }} + */ +let nativeFetchData: { [key: string]: StreamedFetchChunk[] } = {} + +/** + * Interface representing a streamed fetch chunk data. + * @interface + */ +interface StreamedFetchChunkData { + type: 'chunk', + body: string, + id: string } -interface StreamedFetchHeaderData{ - type:'headers', - body:{[key:string]:string}, - id:string, - status:number +/** + * Interface representing a streamed fetch header data. + * @interface + */ +interface StreamedFetchHeaderData { + type: 'headers', + body: { [key: string]: string }, + id: string, + status: number } -interface StreamedFetchEndData{ - type:'end', - id:string +/** + * Interface representing a streamed fetch end data. + * @interface + */ +interface StreamedFetchEndData { + type: 'end', + id: string } -type StreamedFetchChunk = StreamedFetchChunkData|StreamedFetchHeaderData|StreamedFetchEndData +/** + * Type representing a streamed fetch chunk. + * @typedef {StreamedFetchChunkData | StreamedFetchHeaderData | StreamedFetchEndData} StreamedFetchChunk + */ +type StreamedFetchChunk = StreamedFetchChunkData | StreamedFetchHeaderData | StreamedFetchEndData + +/** + * Interface representing a streamed fetch plugin. + * @interface + */ interface StreamedFetchPlugin { - streamedFetch(options: { id: string, url:string, body:string, headers:{[key:string]:string} }): Promise<{"error":string,"success":boolean}>; - addListener(eventName: 'streamed_fetch', listenerFunc: (data:StreamedFetchChunk) => void): void; + /** + * Performs a streamed fetch operation. + * @param {Object} options - The options for the fetch operation. + * @param {string} options.id - The ID of the fetch operation. + * @param {string} options.url - The URL to fetch. + * @param {string} options.body - The body of the fetch request. + * @param {{ [key: string]: string }} options.headers - The headers of the fetch request. + * @returns {Promise<{ error: string, success: boolean }>} - The result of the fetch operation. + */ + streamedFetch(options: { id: string, url: string, body: string, headers: { [key: string]: string } }): Promise<{ "error": string, "success": boolean }>; + + /** + * Adds a listener for the specified event. + * @param {string} eventName - The name of the event. + * @param {(data: StreamedFetchChunk) => void} listenerFunc - The function to call when the event is triggered. + */ + addListener(eventName: 'streamed_fetch', listenerFunc: (data: StreamedFetchChunk) => void): void; } +/** + * Indicates whether streamed fetch listening is active. + * @type {boolean} + */ let streamedFetchListening = false -let capStreamedFetch:StreamedFetchPlugin|undefined -if(isTauri){ +/** + * The streamed fetch plugin instance. + * @type {StreamedFetchPlugin | undefined} + */ +let capStreamedFetch: StreamedFetchPlugin | undefined + +if (isTauri) { listen('streamed_fetch', (event) => { try { const parsed = JSON.parse(event.payload as string) @@ -1305,7 +1643,8 @@ if(isTauri){ streamedFetchListening = true }) } -if(Capacitor.isNativePlatform()){ + +if (Capacitor.isNativePlatform()) { capStreamedFetch = registerPlugin('CapacitorHttp', CapacitorHttp) capStreamedFetch.addListener('streamed_fetch', (data) => { @@ -1318,37 +1657,71 @@ if(Capacitor.isNativePlatform()){ streamedFetchListening = true } -export class AppendableBuffer{ - buffer:Uint8Array - deapended:number = 0 - constructor(){ +/** + * A class to manage a buffer that can be appended to and deappended from. + */ +export class AppendableBuffer { + buffer: Uint8Array + deapended: number = 0 + + /** + * Creates an instance of AppendableBuffer. + */ + constructor() { this.buffer = new Uint8Array(0) } - append(data:Uint8Array){ + + /** + * Appends data to the buffer. + * @param {Uint8Array} data - The data to append. + */ + append(data: Uint8Array) { const newBuffer = new Uint8Array(this.buffer.length + data.length) newBuffer.set(this.buffer, 0) newBuffer.set(data, this.buffer.length) this.buffer = newBuffer } - deappend(length:number){ + + /** + * Deappends a specified length from the buffer. + * @param {number} length - The length to deappend. + */ + deappend(length: number) { this.buffer = this.buffer.slice(length) this.deapended += length } - slice(start:number, end:number){ + + /** + * Slices the buffer from start to end. + * @param {number} start - The start index. + * @param {number} end - The end index. + * @returns {Uint8Array} - The sliced buffer. + */ + slice(start: number, end: number) { return this.buffer.slice(start - this.deapended, end - this.deapended) } - length(){ + + /** + * Gets the total length of the buffer including deappended length. + * @returns {number} - The total length. + */ + length() { return this.buffer.length + this.deapended } - } -const pipeFetchLog = (fetchLogIndex:number, readableStream:ReadableStream) => { +/** + * Pipes the fetch log to a readable stream. + * @param {number} fetchLogIndex - The index of the fetch log. + * @param {ReadableStream} readableStream - The readable stream to pipe. + * @returns {ReadableStream} - The new readable stream. + */ +const pipeFetchLog = (fetchLogIndex: number, readableStream: ReadableStream) => { let textDecoderBuffer = new AppendableBuffer() let textDecoderPointer = 0 const textDecoder = TextDecoderStream ? (new TextDecoderStream()) : new TransformStream({ transform(chunk, controller) { - try{ + try { textDecoderBuffer.append(chunk) const decoded = new TextDecoder('utf-8', { fatal: true @@ -1356,8 +1729,7 @@ const pipeFetchLog = (fetchLogIndex:number, readableStream:ReadableStream} - A promise that resolves to an object containing the response body, headers, and status. + * @returns {ReadableStream} body - The response body as a readable stream. + * @returns {Headers} headers - The response headers. + * @returns {number} status - The response status code. + * @throws {Error} - Throws an error if the request is aborted or if there is an error in the response. + */ export async function fetchNative(url:string, arg:{ body:string, headers?:{[key:string]:string}, @@ -1530,24 +1918,46 @@ export async function fetchNative(url:string, arg:{ } } +/** + * Converts a ReadableStream of Uint8Array to a text string. + * + * @param {ReadableStream} stream - The readable stream to convert. + * @returns {Promise} A promise that resolves to the text content of the stream. + */ export function textifyReadableStream(stream:ReadableStream){ return new Response(stream).text() } +/** + * Toggles the fullscreen mode of the document. + * If the document is currently in fullscreen mode, it exits fullscreen. + * If the document is not in fullscreen mode, it requests fullscreen with navigation UI hidden. + */ export function toggleFullscreen(){ - const fullscreenElement = document.fullscreenElement fullscreenElement ? document.exitFullscreen() : document.documentElement.requestFullscreen({ navigationUI: "hide" }) } +/** + * Removes non-Latin characters from a string, replaces multiple spaces with a single space, and trims the string. + * + * @param {string} data - The input string to be processed. + * @returns {string} The processed string with non-Latin characters removed, multiple spaces replaced by a single space, and trimmed. + */ export function trimNonLatin(data:string){ return data .replace(/[^\x00-\x7F]/g, "") .replace(/ +/g, ' ') .trim() } +/** + * Updates the height mode of the document based on the value stored in the database. + * + * The height mode can be one of the following values: 'auto', 'vh', 'dvh', 'lvh', 'svh', or 'percent'. + * The corresponding CSS variable '--risu-height-size' is set accordingly. + */ export function updateHeightMode(){ const db = get(DataBase) const root = document.querySelector(':root') as HTMLElement; @@ -1573,16 +1983,41 @@ export function updateHeightMode(){ } } +/** + * A class that provides a blank writer implementation. + * + * This class is used to provide a no-op implementation of a writer, making it compatible with other writer interfaces. + */ export class BlankWriter{ constructor(){ } + + /** + * Initializes the writer. + * + * This method does nothing and is provided for compatibility with other writer interfaces. + */ async init(){ //do nothing, just to make compatible with other writer - } + + /** + * Writes data to the writer. + * + * This method does nothing and is provided for compatibility with other writer interfaces. + * + * @param {string} key - The key associated with the data. + * @param {Uint8Array|string} data - The data to be written. + */ async write(key:string,data:Uint8Array|string){ //do nothing, just to make compatible with other writer } + + /** + * Ends the writing process. + * + * This method does nothing and is provided for compatibility with other writer interfaces. + */ async end(){ //do nothing, just to make compatible with other writer } From 6f5dd7f179de7616b79c88ea56816995aa92f2e1 Mon Sep 17 00:00:00 2001 From: poroyo <132068975+poroyo@users.noreply.github.com> Date: Sat, 28 Sep 2024 16:08:32 +0900 Subject: [PATCH 002/175] Add support for character-specific translation prompts with Ax. model --- src/lang/en.ts | 2 ++ src/lang/ko.ts | 6 ++++-- src/lib/SideBars/CharConfig.svelte | 3 +++ src/ts/storage/database.ts | 1 + src/ts/translator/translator.ts | 13 +++++++++++-- 5 files changed, 21 insertions(+), 4 deletions(-) diff --git a/src/lang/en.ts b/src/lang/en.ts index 0e1c7a42..af62cebc 100644 --- a/src/lang/en.ts +++ b/src/lang/en.ts @@ -161,6 +161,7 @@ export const languageEnglish = { , strictJsonSchema: "If enabled, it will strictly follow the Provided Schema for JSON on some models. if it is disabled, it may ignore the JSON Schema.", extractJson: "If it is not blank, it will extract specific JSON data from the response. for example, if you want to extract `response.text[0]` in response `{\"response\": {\"text\": [\"hello\"]}}`, you can put `response.text.0`.", + translatorNote: "Here, you can add a unique translation prompt for each character. This option only applies when using the Ax. model for translation. To apply it, include `{{slot::tnote}}` in the language settings. It doesn't work in group chats.", }, setup: { chooseProvider: "Choose AI Provider", @@ -741,4 +742,5 @@ export const languageEnglish = { leadingDoubleQuote: "Leading Double Quote", trailingSingleQuote: "Trailing Single Quote", trailingDoubleQuote: "Trailing Double Quote", + translatorNote: "Translator's Note", } \ No newline at end of file diff --git a/src/lang/ko.ts b/src/lang/ko.ts index 7ce33eee..6b279eae 100644 --- a/src/lang/ko.ts +++ b/src/lang/ko.ts @@ -95,7 +95,8 @@ export const languageKorean = { "emotionPrompt": "이 옵션은 감정을 감지하는 데 사용되는 프롬프트를 설정하는 데 사용됩니다. 비어 있으면 기본 프롬프트를 사용합니다.", "removePunctuationHypa": "활성화되면 HypaMemory를 실행하기 전에 구두점을 제거합니다.", "defaultVariables": "여기에서는 기본 변수를 정의할 수 있습니다. `<변수 이름>=<변수 값>` 형식으로 작성하고 개행으로 구분합니다. 예를 들어, `name=RisuAI`는 트리거 스크립트 및 변수 CBS와 함께 `{{getvar::A}}`, `{{setvar::A::B}}` 또는 `{{? $A + 1}}`과 같이 사용할 수 있습니다. 프롬프트 템플릿의 기본 변수와 캐릭터의 기본 변수가 동일한 이름을 가진 경우 캐릭터의 기본 변수가 사용됩니다.", - "combineTranslation": "활성화된 경우, 한 문장이지만 HTML 태그로 분리된 텍스트를 모두 합쳐서 번역한 후, 번역된 결과에 다시 디스플레이 수정 스크립트를 적용합니다.\n이를 통해 번역기가 올바른 번역을 하도록 도와줍니다.\n이 옵션을 활성화하고 UI가 이상해지면 옵션을 끄고 제보해 주세요." + "combineTranslation": "활성화된 경우, 한 문장이지만 HTML 태그로 분리된 텍스트를 모두 합쳐서 번역한 후, 번역된 결과에 다시 디스플레이 수정 스크립트를 적용합니다.\n이를 통해 번역기가 올바른 번역을 하도록 도와줍니다.\n이 옵션을 활성화하고 UI가 이상해지면 옵션을 끄고 제보해 주세요.", + "translatorNote": "여기에서 캐릭터마다 별도의 번역 프롬프트를 넣을 수 있습니다. 해당 옵션은 Ax. model 번역을 사용할 때만 적용됩니다. 언어 설정에서 `{{slot::tnote}}`를 넣으세요. 그룹챗에서는 작동하지 않습니다.", }, "setup": { "chooseProvider": "AI 제공자를 선택해 주세요", @@ -587,5 +588,6 @@ export const languageKorean = { "supaDesc": "수파메모리는 요약을 사용하는 장기기억 시스템입니다.", "hanuraiDesc": "하느라이메모리는 벡터 검색을 사용하는 장기기억 시스템입니다.", "v2Warning": "주의: V2 카드는 더 이상 지원되지 않습니다. 일부 데이터가 누락될 수 있습니다.", - + "translatorNote": "번역가의 노트", + } \ No newline at end of file diff --git a/src/lib/SideBars/CharConfig.svelte b/src/lib/SideBars/CharConfig.svelte index b9c744a4..e5b5fae9 100644 --- a/src/lib/SideBars/CharConfig.svelte +++ b/src/lib/SideBars/CharConfig.svelte @@ -921,6 +921,9 @@ {language.defaultVariables} + {language.translatorNote} + + {language.creator} diff --git a/src/ts/storage/database.ts b/src/ts/storage/database.ts index 550f69ef..24f1c364 100644 --- a/src/ts/storage/database.ts +++ b/src/ts/storage/database.ts @@ -901,6 +901,7 @@ export interface character{ lowLevelAccess?:boolean hideChatIcon?:boolean lastInteraction?:number + translatorNote?:string } diff --git a/src/ts/translator/translator.ts b/src/ts/translator/translator.ts index e7c99a2e..b0b13b20 100644 --- a/src/ts/translator/translator.ts +++ b/src/ts/translator/translator.ts @@ -456,14 +456,23 @@ async function translateLLM(text:string, arg:{to:string}){ }) const db = get(DataBase) + const charIndex = get(selectedCharID) + const currentChar = db.characters[charIndex] + let translatorNote + if (currentChar.type === "character") { + translatorNote = currentChar.translatorNote ?? "" + } else { + translatorNote = "" + } + let formated:OpenAIChat[] = [] let prompt = db.translatorPrompt || `You are a translator. translate the following html or text into {{slot}}. do not output anything other than the translation.` - let parsedPrompt = parseChatML(prompt.replaceAll('{{slot}}', arg.to).replaceAll('{{solt::content}}', text)) + let parsedPrompt = parseChatML(prompt.replaceAll('{{slot}}', arg.to).replaceAll('{{solt::content}}', text).replaceAll('{{slot::tnote}}', translatorNote)) if(parsedPrompt){ formated = parsedPrompt } else{ - prompt = prompt.replaceAll('{{slot}}', arg.to) + prompt = prompt.replaceAll('{{slot}}', arg.to).replaceAll('{{slot::tnote}}', translatorNote) formated = [ { 'role': 'system', From e85ecef89454d9b793f8c347205d501f044b22cd Mon Sep 17 00:00:00 2001 From: kwaroran Date: Sun, 29 Sep 2024 20:23:57 +0900 Subject: [PATCH 003/175] Fix lorebook import broken --- src/ts/characterCards.ts | 44 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/src/ts/characterCards.ts b/src/ts/characterCards.ts index 3b8a45b7..47b3a91a 100644 --- a/src/ts/characterCards.ts +++ b/src/ts/characterCards.ts @@ -415,6 +415,43 @@ export async function characterURLImport() { function convertOffSpecCards(charaData:OldTavernChar|CharacterCardV2Risu, imgp:string|undefined = undefined):character{ const data = charaData.spec_version === '2.0' ? charaData.data : charaData console.log("Off spec detected, converting") + const charbook = charaData.spec_version === '2.0' ? charaData.data.character_book : null + let lorebook:loreBook[] = [] + let loresettings:undefined|loreSettings = undefined + let loreExt:undefined|any = undefined + if(charbook){ + if((!checkNullish(charbook.recursive_scanning)) && + (!checkNullish(charbook.scan_depth)) && + (!checkNullish(charbook.token_budget))){ + loresettings = { + tokenBudget:charbook.token_budget, + scanDepth:charbook.scan_depth, + recursiveScanning: charbook.recursive_scanning, + fullWordMatching: charbook?.extensions?.risu_fullWordMatching ?? false, + } + } + + loreExt = charbook.extensions + + for(const book of charbook.entries){ + lorebook.push({ + key: book.keys.join(', '), + secondkey: book.secondary_keys?.join(', ') ?? '', + insertorder: book.insertion_order, + comment: book.name ?? book.comment ?? "", + content: book.content, + mode: "normal", + alwaysActive: book.constant ?? false, + selective: book.selective ?? false, + extentions: {...book.extensions, risu_case_sensitive: book.case_sensitive}, + activationPercent: book.extensions?.risu_activationPercent, + loreCache: book.extensions?.risu_loreCache ?? null, + //@ts-ignore + useRegex: book.use_regex ?? false + }) + } + } + return { name: data.name ?? 'unknown name', firstMessage: data.first_mes ?? 'unknown first message', @@ -430,7 +467,7 @@ function convertOffSpecCards(charaData:OldTavernChar|CharacterCardV2Risu, imgp:s image: imgp, emotionImages: [], bias: [], - globalLore: [], + globalLore: lorebook, viewScreen: 'none', chaId: uuidv4(), sdData: defaultSdDataFunc(), @@ -449,7 +486,10 @@ function convertOffSpecCards(charaData:OldTavernChar|CharacterCardV2Risu, imgp:s firstMsgIndex: -1, replaceGlobalNote: "", triggerscript: [], - additionalText: '' + additionalText: '', + loreExt: loreExt, + loreSettings: loresettings, + } } From d06dd287bd851f16c720bd0e7fd6369cb164e760 Mon Sep 17 00:00:00 2001 From: kwaroran Date: Sun, 29 Sep 2024 21:40:09 +0900 Subject: [PATCH 004/175] Add groupOtherBotRole and groupTemplate --- src/lang/en.ts | 5 +++++ src/lib/Setting/Pages/PromptSettings.svelte | 12 ++++++++++- src/ts/parser.ts | 11 +++++++--- src/ts/process/index.ts | 24 +++++++++++++++++---- src/ts/storage/database.ts | 9 ++++++++ 5 files changed, 53 insertions(+), 8 deletions(-) diff --git a/src/lang/en.ts b/src/lang/en.ts index af62cebc..62ccab7e 100644 --- a/src/lang/en.ts +++ b/src/lang/en.ts @@ -162,6 +162,8 @@ export const languageEnglish = { strictJsonSchema: "If enabled, it will strictly follow the Provided Schema for JSON on some models. if it is disabled, it may ignore the JSON Schema.", extractJson: "If it is not blank, it will extract specific JSON data from the response. for example, if you want to extract `response.text[0]` in response `{\"response\": {\"text\": [\"hello\"]}}`, you can put `response.text.0`.", translatorNote: "Here, you can add a unique translation prompt for each character. This option only applies when using the Ax. model for translation. To apply it, include `{{slot::tnote}}` in the language settings. It doesn't work in group chats.", + groupInnerFormat: "This defines a format that is used in group chat for characters that isn't speaker. if it is not blank, it will use this format instead of the default format. if `Group Other Bot Role` is `assistant`, it will also be applied to the speaker.", + groupOtherBotRole: "This defines a role that is used in group chat for characters that isn't speaker.", }, setup: { chooseProvider: "Choose AI Provider", @@ -743,4 +745,7 @@ export const languageEnglish = { trailingSingleQuote: "Trailing Single Quote", trailingDoubleQuote: "Trailing Double Quote", translatorNote: "Translator's Note", + formatGroupInSingle: "Format Group in Single", + groupInnerFormat: "Non-Speaker Inner Format", + groupOtherBotRole: "Non-Speaker Role in Group", } \ No newline at end of file diff --git a/src/lib/Setting/Pages/PromptSettings.svelte b/src/lib/Setting/Pages/PromptSettings.svelte index 8e594476..1a025efa 100644 --- a/src/lib/Setting/Pages/PromptSettings.svelte +++ b/src/lib/Setting/Pages/PromptSettings.svelte @@ -10,6 +10,8 @@ import NumberInput from "src/lib/UI/GUI/NumberInput.svelte"; import Help from "src/lib/Others/Help.svelte"; import TextAreaInput from "src/lib/UI/GUI/TextAreaInput.svelte"; + import SelectInput from "src/lib/UI/GUI/SelectInput.svelte"; + import OptionInput from "src/lib/UI/GUI/OptionInput.svelte"; let sorted = 0 let opened = 0 @@ -112,7 +114,7 @@ - + @@ -124,10 +126,18 @@ {/if} {language.maxThoughtTagDepth} + {language.groupOtherBotRole} + + User + System + assistant + {language.customPromptTemplateToggle} {language.defaultVariables} + {language.groupInnerFormat} + \n{{slot}}\n`} bind:value={$DataBase.groupTemplate}/> {#if $DataBase.jsonSchemaEnabled} {language.jsonSchema} diff --git a/src/ts/parser.ts b/src/ts/parser.ts index 979fbae5..d2af1c79 100644 --- a/src/ts/parser.ts +++ b/src/ts/parser.ts @@ -1782,9 +1782,14 @@ export function risuChatParser(da:string, arg:{ if(aChara){ if(typeof(aChara) !== 'string' && aChara.type === 'group'){ - const gc = findCharacterbyId(aChara.chats[aChara.chatPage].message.at(-1).saying ?? '') - if(gc.name !== 'Unknown Character'){ - chara = gc + if(aChara.chats[aChara.chatPage].message.length > 0){ + const gc = findCharacterbyId(aChara.chats[aChara.chatPage].message.at(-1).saying ?? '') + if(gc.name !== 'Unknown Character'){ + chara = gc + } + } + else{ + chara = 'bot' } } else{ diff --git a/src/ts/process/index.ts b/src/ts/process/index.ts index 4d5f6fdb..18ad7ac1 100644 --- a/src/ts/process/index.ts +++ b/src/ts/process/index.ts @@ -699,10 +699,25 @@ export async function sendChat(chatProcessIndex = -1,arg:{ } let attr:string[] = [] + let role:'user'|'assistant'|'system' = msg.role === 'user' ? 'user' : 'assistant' - if(nowChatroom.type === 'group' || (usingPromptTemplate && db.promptSettings.sendName)){ - formatedChat = name + ': ' + formatedChat - attr.push('nameAdded') + if( + (nowChatroom.type === 'group' && findCharacterbyIdwithCache(msg.saying).chaId !== currentChar.chaId) || + (nowChatroom.type === 'group' && db.groupOtherBotRole === 'assistant') || + (usingPromptTemplate && db.promptSettings.sendName) + ){ + const form = db.groupTemplate || `<{{char}}\'s Message>\n{{slot}}\n` + formatedChat = risuChatParser(form, {chara: findCharacterbyIdwithCache(msg.saying).name}).replace('{{slot}}', formatedChat) + switch(db.groupOtherBotRole){ + case 'user': + case 'assistant': + case 'system': + role = db.groupOtherBotRole + break + default: + role = 'assistant' + break + } } if(usingPromptTemplate && db.promptSettings.maxThoughtTagDepth !== -1){ const depth = ms.length - index @@ -712,7 +727,7 @@ export async function sendChat(chatProcessIndex = -1,arg:{ } const chat:OpenAIChat = { - role: msg.role === 'user' ? 'user' : 'assistant', + role: role, content: formatedChat, memo: msg.chatId, attr: attr, @@ -725,6 +740,7 @@ export async function sendChat(chatProcessIndex = -1,arg:{ currentTokens += await tokenizer.tokenizeChat(chat) index++ } + console.log(JSON.stringify(chats, null, 2)) const depthPrompts = lorepmt.actives.filter(v => { return (v.pos === 'depth' && v.depth > 0) || v.pos === 'reverse_depth' diff --git a/src/ts/storage/database.ts b/src/ts/storage/database.ts index 24f1c364..e2361b36 100644 --- a/src/ts/storage/database.ts +++ b/src/ts/storage/database.ts @@ -442,6 +442,7 @@ export function setDatabase(data:Database){ } data.customQuotes ??= false data.customQuotesData ??= ['“','”','‘','’'] + data.groupOtherBotRole ??= 'user' changeLanguage(data.language) DataBase.set(data) } @@ -747,6 +748,8 @@ export interface Database{ } customQuotes:boolean customQuotesData?:[string, string, string, string] + groupTemplate?:string + groupOtherBotRole?:string } export interface customscript{ @@ -1008,6 +1011,8 @@ export interface botPreset{ jsonSchema?:string strictJsonSchema?:boolean extractJson?:string + groupTemplate?:string + groupOtherBotRole?:string } @@ -1298,6 +1303,8 @@ export function saveCurrentPreset(){ jsonSchema:db.jsonSchema ?? '', strictJsonSchema:db.strictJsonSchema ?? true, extractJson:db.extractJson ?? '', + groupOtherBotRole: db.groupOtherBotRole ?? 'user', + groupTemplate: db.groupTemplate ?? '', } db.botPresets = pres setDatabase(db) @@ -1390,6 +1397,8 @@ export function setPreset(db:Database, newPres: botPreset){ db.jsonSchema = newPres.jsonSchema ?? '' db.strictJsonSchema = newPres.strictJsonSchema ?? true db.extractJson = newPres.extractJson ?? '' + db.groupOtherBotRole = newPres.groupOtherBotRole ?? 'user' + db.groupTemplate = newPres.groupTemplate ?? '' return db } From 92e4aa312f065e4ccad536cda7aeaea2daae192b Mon Sep 17 00:00:00 2001 From: kwaroran Date: Sun, 29 Sep 2024 21:58:31 +0900 Subject: [PATCH 005/175] Update version to 135.0.0 --- src-tauri/tauri.conf.json | 2 +- src/ts/storage/database.ts | 2 +- version.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 3ff9e7d1..6b12292d 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -8,7 +8,7 @@ }, "package": { "productName": "RisuAI", - "version": "134.1.0" + "version": "135.0.0" }, "tauri": { "allowlist": { diff --git a/src/ts/storage/database.ts b/src/ts/storage/database.ts index e2361b36..d81bfbde 100644 --- a/src/ts/storage/database.ts +++ b/src/ts/storage/database.ts @@ -1,6 +1,6 @@ export const DataBase = writable({} as any as Database) export const loadedStore = writable(false) -export let appVer = "134.1.0" +export let appVer = "135.0.0" export let webAppSubVer = '' import { get, writable } from 'svelte/store'; diff --git a/version.json b/version.json index 0184675f..271cc82d 100644 --- a/version.json +++ b/version.json @@ -1 +1 @@ -{"version":"134.1.0"} \ No newline at end of file +{"version":"135.0.0"} \ No newline at end of file From 8f1850680bfb2b676b421588c73fd9a8fea5d846 Mon Sep 17 00:00:00 2001 From: bangonicdd <157843588+bangonicdd2@users.noreply.github.com> Date: Tue, 8 Oct 2024 18:09:28 +0900 Subject: [PATCH 006/175] Fix Sortable initialization error --- src/lib/SideBars/Scripts/TriggerList.svelte | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/lib/SideBars/Scripts/TriggerList.svelte b/src/lib/SideBars/Scripts/TriggerList.svelte index b546fddd..53fd4b9f 100644 --- a/src/lib/SideBars/Scripts/TriggerList.svelte +++ b/src/lib/SideBars/Scripts/TriggerList.svelte @@ -18,6 +18,9 @@ let sorted = 0 let opened = 0 const createStb = () => { + if (!ele) { + return; + } stb = Sortable.create(ele, { onEnd: async () => { let idx:number[] = [] From 140710dba8dfe96f1b907ecd348f942e02e98e38 Mon Sep 17 00:00:00 2001 From: kwaroran Date: Wed, 9 Oct 2024 18:40:21 +0900 Subject: [PATCH 007/175] Change Lite theme --- src/App.svelte | 12 +++++------- src/lib/Mobile/MobileBody.svelte | 3 ++- src/ts/gui/colorscheme.ts | 16 ++++++++++++++-- vite.config.ts | 5 +---- 4 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/App.svelte b/src/App.svelte index 7bbcae25..0b497740 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -13,13 +13,11 @@ import RealmFrame from './lib/UI/Realm/RealmFrame.svelte'; import { AccountWarning } from './ts/storage/accountStorage'; import AccountWarningComp from './lib/Others/AccountWarningComp.svelte'; - import { isLite } from './ts/lite'; - import LiteMain from './LiteMain.svelte'; - import Botpreset from './lib/Setting/botpreset.svelte'; - import ListedPersona from './lib/Setting/listedPersona.svelte'; - import MobileHeader from './lib/Mobile/MobileHeader.svelte'; - import MobileBody from './lib/Mobile/MobileBody.svelte'; - import MobileFooter from './lib/Mobile/MobileFooter.svelte'; + import Botpreset from './lib/Setting/botpreset.svelte'; + import ListedPersona from './lib/Setting/listedPersona.svelte'; + import MobileHeader from './lib/Mobile/MobileHeader.svelte'; + import MobileBody from './lib/Mobile/MobileBody.svelte'; + import MobileFooter from './lib/Mobile/MobileFooter.svelte'; let didFirstSetup: boolean = false diff --git a/src/lib/Mobile/MobileBody.svelte b/src/lib/Mobile/MobileBody.svelte index 8beca6e3..47224884 100644 --- a/src/lib/Mobile/MobileBody.svelte +++ b/src/lib/Mobile/MobileBody.svelte @@ -9,9 +9,10 @@ import { language } from "src/lang"; import SideChatList from "../SideBars/SideChatList.svelte"; import DevTool from "../SideBars/DevTool.svelte"; + import { isLite } from "src/ts/lite"; -{#if $MobileSideBar > 0} +{#if $MobileSideBar > 0 && !$isLite}
+ + +
+
+ +
+ {#if subMenu === 0} + {#each builtComponentTrees as component, i} + + {/each} + {:else if subMenu === 1} + {#each builtContainerTrees as container, i} + + {/each} + {:else if subMenu === 2} +

Left click to select, Right click to delete

+

Press a component/container in the menu to add it to the selected container

+ {/if} + +{:else} + +{/if} \ No newline at end of file diff --git a/src/lib/Setting/Pages/DisplaySettings.svelte b/src/lib/Setting/Pages/DisplaySettings.svelte index d5bcede5..ae9120c5 100644 --- a/src/lib/Setting/Pages/DisplaySettings.svelte +++ b/src/lib/Setting/Pages/DisplaySettings.svelte @@ -14,6 +14,9 @@ import TextInput from "src/lib/UI/GUI/TextInput.svelte"; import ColorInput from "src/lib/UI/GUI/ColorInput.svelte"; import TextAreaInput from "src/lib/UI/GUI/TextAreaInput.svelte"; + import Arcodion from "src/lib/UI/Arcodion.svelte"; + import Button from "src/lib/UI/GUI/Button.svelte"; + import { CustomGUISettingMenuStore } from "src/ts/stores"; const onSchemeInputChange = (e:Event) => { changeColorScheme((e.target as HTMLInputElement).value) @@ -50,8 +53,15 @@ Standard Risu Waifulike WaifuCut + + {#if $DataBase.theme === "custom"} + + {/if} + {#if $DataBase.theme === "waifu"} {language.waifuWidth} diff --git a/src/ts/storage/database.ts b/src/ts/storage/database.ts index aa7cf35b..c31a27f4 100644 --- a/src/ts/storage/database.ts +++ b/src/ts/storage/database.ts @@ -444,6 +444,7 @@ export function setDatabase(data:Database){ data.customQuotes ??= false data.customQuotesData ??= ['“','”','‘','’'] data.groupOtherBotRole ??= 'user' + data.customGUI ??= '' changeLanguage(data.language) DataBase.set(data) } @@ -752,6 +753,7 @@ export interface Database{ customQuotesData?:[string, string, string, string] groupTemplate?:string groupOtherBotRole?:string + customGUI:string } export interface customscript{ diff --git a/src/ts/stores.ts b/src/ts/stores.ts index 5d75fc73..7a633889 100644 --- a/src/ts/stores.ts +++ b/src/ts/stores.ts @@ -50,6 +50,7 @@ export const CustomCSSStore = writable('') export const SafeModeStore = writable(false) export const MobileSearch = writable('') export const CharConfigSubMenu = writable(0) +export const CustomGUISettingMenuStore = writable(false) let lastGlobalEnabledModules: string[] = [] let lastChatEnabledModules: string[] = [] From ac370a8718c59af88578a22206f03349c2fbb956 Mon Sep 17 00:00:00 2001 From: Naki Date: Sun, 13 Oct 2024 01:29:12 +0900 Subject: [PATCH 027/175] Support Docker --- .dockerignore | 1 + Dockerfile | 10 ++++++++++ docker-compose.yml | 12 ++++++++++++ 3 files changed, 23 insertions(+) create mode 100644 .dockerignore create mode 100644 Dockerfile create mode 100644 docker-compose.yml diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..3c3629e6 --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +node_modules diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..1f696b9d --- /dev/null +++ b/Dockerfile @@ -0,0 +1,10 @@ +FROM node:20 +WORKDIR /app +COPY . . +RUN corepack enable && \ + corepack install --global pnpm@latest +RUN pnpm install --frozen-lockfile +RUN pnpm build +ENV NODE_ENV=production +EXPOSE 6001 +CMD ["pnpm", "runserver"] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..ba961c18 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,12 @@ +services: + risuai: + container_name: risuai + build: . + restart: always + expose: + - 6001 + volumes: + - risuai-save:/app/save + +volumes: + risuai-save: From 4a61b19a0574adb10dd857339381b463c6ad81c7 Mon Sep 17 00:00:00 2001 From: Naki Date: Sun, 13 Oct 2024 01:39:46 +0900 Subject: [PATCH 028/175] Update README.md --- README.md | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index fae4b3ff..32616bde 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,9 @@ RisuAI, or Risu for short, is a cross platform AI chatting software / web application with powerful features such as multiple API support, assets in the chat, regex functions and much more. # Screenshots -|![Screenshot_6](https://github.com/kwaroran/RisuAI/assets/116663078/cccb9b33-5dbd-47d7-9c85-61464790aafe) | ![image](https://github.com/kwaroran/RisuAI/assets/116663078/30d29f85-1380-4c73-9b82-1a40f2c5d2ea) | -| --- | --- | -|![a04a68f26852d53a1aedd661f3ebbc5fd78400007e1cf85ff28f3a09243fb3ca](https://github.com/kwaroran/RisuAI/assets/116663078/faad0de5-56f3-4176-b38e-61c2d3a8698e) | ![Screenshot_11](https://github.com/kwaroran/RisuAI/assets/116663078/ef946882-2311-43e7-81e7-5ca2d484fa90) | +| ![Screenshot_6](https://github.com/kwaroran/RisuAI/assets/116663078/cccb9b33-5dbd-47d7-9c85-61464790aafe) | ![image](https://github.com/kwaroran/RisuAI/assets/116663078/30d29f85-1380-4c73-9b82-1a40f2c5d2ea) | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | +| ![a04a68f26852d53a1aedd661f3ebbc5fd78400007e1cf85ff28f3a09243fb3ca](https://github.com/kwaroran/RisuAI/assets/116663078/faad0de5-56f3-4176-b38e-61c2d3a8698e) | ![Screenshot_11](https://github.com/kwaroran/RisuAI/assets/116663078/ef946882-2311-43e7-81e7-5ca2d484fa90) | ## Features @@ -39,3 +39,20 @@ You can get detailed information on https://github.com/kwaroran/RisuAI/wiki (Wor - [RisuAI Website](https://risuai.net) (Recommended) - [Github Releases](https://github.com/kwaroran/RisuAI/releases) + +### Docker Installation + +You can also run RisuAI using Docker. This method is particularly useful for web hosting. + +1. Clone the repository: + ``` + git clone https://github.com/kwaroran/RisuAI.git + cd RisuAI + ``` + +2. Build and run the Docker container: + ``` + docker-compose up -d + ``` + +3. Access RisuAI at `http://localhost:6001` in your web browser. From 3f9d02c5d36c9a03ff1f71529b28592e0f46ac3b Mon Sep 17 00:00:00 2001 From: Naki Date: Sun, 13 Oct 2024 01:46:57 +0900 Subject: [PATCH 029/175] Improve README.md --- README.md | 44 ++++++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 32616bde..3461e916 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # RisuAI + text @@ -9,31 +10,38 @@ RisuAI, or Risu for short, is a cross platform AI chatting software / web application with powerful features such as multiple API support, assets in the chat, regex functions and much more. # Screenshots -| ![Screenshot_6](https://github.com/kwaroran/RisuAI/assets/116663078/cccb9b33-5dbd-47d7-9c85-61464790aafe) | ![image](https://github.com/kwaroran/RisuAI/assets/116663078/30d29f85-1380-4c73-9b82-1a40f2c5d2ea) | -| ------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------- | -| ![a04a68f26852d53a1aedd661f3ebbc5fd78400007e1cf85ff28f3a09243fb3ca](https://github.com/kwaroran/RisuAI/assets/116663078/faad0de5-56f3-4176-b38e-61c2d3a8698e) | ![Screenshot_11](https://github.com/kwaroran/RisuAI/assets/116663078/ef946882-2311-43e7-81e7-5ca2d484fa90) | +| Screenshot 1 | Screenshot 2 | +| :--------------------------: | :--------------------------: | +| ![Screenshot 1][screenshot1] | ![Screenshot 2][screenshot2] | +| ![Screenshot 3][screenshot3] | ![Screenshot 4][screenshot4] | + +[screenshot1]: https://github.com/kwaroran/RisuAI/assets/116663078/cccb9b33-5dbd-47d7-9c85-61464790aafe +[screenshot2]: https://github.com/kwaroran/RisuAI/assets/116663078/30d29f85-1380-4c73-9b82-1a40f2c5d2ea +[screenshot3]: https://github.com/kwaroran/RisuAI/assets/116663078/faad0de5-56f3-4176-b38e-61c2d3a8698e +[screenshot4]: https://github.com/kwaroran/RisuAI/assets/116663078/ef946882-2311-43e7-81e7-5ca2d484fa90 ## Features - - **Multiple API Supports**: Supports OAI, Claude, Ooba, OpenRouter... and More! - - **Emotion Images**: Display the image of the current character, according to his/her expressions! - - **Group Chats**: Multiple characters in one chat. - - **Plugins**: Add your features and providers, and simple share. - - **Regex Script**: Modify model's output by regex, to make a custom gui and others - - **Powerful Translators**: Automaticly translate the input/output, so you can roleplay without knowing model's language. - - **Lorebook**: Also known as world infos or memory book, which can made character memorize more. - - **Themes**: Choose it from 3 themes, Classic, WaifuLike, WaifuCut. - - **Powerful Prompting**: Change the prompting order easily, Impersonaite inside prompts, Use conditions, varables... and more! - - **Customizable, Friendly UI**: Great Accessibility and mobile friendly - - **TTS**: Use TTS to make the output text into voice. - - **Additonal Assets**: Embed your images, audios and videos to bot, and make it display at chat or background! - - And More! + +- **Multiple API Supports**: Supports OAI, Claude, Ooba, OpenRouter... and More! +- **Emotion Images**: Display the image of the current character, according to his/her expressions! +- **Group Chats**: Multiple characters in one chat. +- **Plugins**: Add your features and providers, and simply share. +- **Regex Script**: Modify model's output by regex, to make a custom GUI and others +- **Powerful Translators**: Automatically translate the input/output, so you can roleplay without knowing model's language. +- **Lorebook**: Also known as world infos or memory book, which can make character memorize more. +- **Themes**: Choose it from 3 themes, Classic, WaifuLike, WaifuCut. +- **Powerful Prompting**: Change the prompting order easily, Impersonate inside prompts, Use conditions, variables... and more! +- **Customizable, Friendly UI**: Great Accessibility and mobile friendly +- **TTS**: Use TTS to make the output text into voice. +- **Additonal Assets**: Embed your images, audios and videos to bot, and make it display at chat or background! +- And More! You can get detailed information on https://github.com/kwaroran/RisuAI/wiki (Work in Progress) - ## Discord - - https://discord.gg/JzP8tB9ZK8 + +- https://discord.gg/JzP8tB9ZK8 ## Installation From 5fe104661fff24c5ad62ceb611d4a5e686ae52ac Mon Sep 17 00:00:00 2001 From: Naki Date: Sun, 13 Oct 2024 01:49:53 +0900 Subject: [PATCH 030/175] Change `expose` to `ports` in docker-compose.yml --- docker-compose.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index ba961c18..90be6345 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -3,8 +3,8 @@ services: container_name: risuai build: . restart: always - expose: - - 6001 + ports: + - 6001:6001 volumes: - risuai-save:/app/save From 6af4c4d0adea16761e021cc363d370a85e4ec1d4 Mon Sep 17 00:00:00 2001 From: kwaroran Date: Tue, 22 Oct 2024 19:21:06 +0900 Subject: [PATCH 031/175] Fix drive backup & rust warning --- src-tauri/src/main.rs | 2 +- src/ts/drive/backuplocal.ts | 2 +- src/ts/drive/drive.ts | 3 +-- src/ts/storage/database.ts | 1 + 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 1ff516aa..291a28c6 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -424,7 +424,7 @@ fn main() { #[cfg(desktop)] { - builder = builder.plugin(tauri_plugin_single_instance::init(|app, args, cwd| { + builder = builder.plugin(tauri_plugin_single_instance::init(|app, _args, _cwd| { let _ = app .get_webview_window("main") .expect("no main window") diff --git a/src/ts/drive/backuplocal.ts b/src/ts/drive/backuplocal.ts index 5921a6ac..dfbc4f84 100644 --- a/src/ts/drive/backuplocal.ts +++ b/src/ts/drive/backuplocal.ts @@ -50,7 +50,7 @@ export async function SaveLocalBackup(){ if(!key || !key.endsWith('.png')){ continue } - await writer.writeBackup(key, await readFile(asset.name, {baseDir: BaseDirectory.AppData})) + await writer.writeBackup(key, await readFile('assets/' + asset.name, {baseDir: BaseDirectory.AppData})) } } else{ diff --git a/src/ts/drive/drive.ts b/src/ts/drive/drive.ts index 4abfa56a..3827f042 100644 --- a/src/ts/drive/drive.ts +++ b/src/ts/drive/drive.ts @@ -5,7 +5,6 @@ import { forageStorage, getUnpargeables, isTauri, openURL } from "../storage/glo import { BaseDirectory, exists, readFile, readDir, writeFile } from "@tauri-apps/plugin-fs"; import { language } from "../../lang"; import { relaunch } from '@tauri-apps/plugin-process'; -import { isEqual } from "lodash"; import { sleep } from "../util"; import { hubURL } from "../characterCards"; import { decodeRisuSave, encodeRisuSave } from "../storage/risuSave"; @@ -155,7 +154,7 @@ async function backupDrive(ACCESS_TOKEN:string) { } const formatedKey = newFormatKeys(key) if(!fileNames.includes(formatedKey)){ - await createFileInFolder(ACCESS_TOKEN, formatedKey, await readFile(asset.name, {baseDir: BaseDirectory.AppData})) + await createFileInFolder(ACCESS_TOKEN, formatedKey, await readFile('assets/' + asset.name, {baseDir: BaseDirectory.AppData})) } } } diff --git a/src/ts/storage/database.ts b/src/ts/storage/database.ts index c31a27f4..ee3973f8 100644 --- a/src/ts/storage/database.ts +++ b/src/ts/storage/database.ts @@ -445,6 +445,7 @@ export function setDatabase(data:Database){ data.customQuotesData ??= ['“','”','‘','’'] data.groupOtherBotRole ??= 'user' data.customGUI ??= '' + data.theme = '' changeLanguage(data.language) DataBase.set(data) } From 1109bb15e44a7147eb551fd9baa011f2a46ff403 Mon Sep 17 00:00:00 2001 From: kwaroran Date: Tue, 22 Oct 2024 19:25:34 +0900 Subject: [PATCH 032/175] Update version to 136.0.2 --- src-tauri/tauri.conf.json | 2 +- src/ts/storage/database.ts | 2 +- version.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index a251bc72..906b50c1 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -29,7 +29,7 @@ }, "productName": "RisuAI", "mainBinaryName": "RisuAI", - "version": "136.0.1", + "version": "136.0.2", "identifier": "co.aiclient.risu", "plugins": { "updater": { diff --git a/src/ts/storage/database.ts b/src/ts/storage/database.ts index ee3973f8..aa9e6fba 100644 --- a/src/ts/storage/database.ts +++ b/src/ts/storage/database.ts @@ -1,6 +1,6 @@ export const DataBase = writable({} as any as Database) export const loadedStore = writable(false) -export let appVer = "136.0.1" +export let appVer = "136.0.2" export let webAppSubVer = '' import { get, writable } from 'svelte/store'; diff --git a/version.json b/version.json index a3afc201..544f5d2a 100644 --- a/version.json +++ b/version.json @@ -1 +1 @@ -{"version":"136.0.1"} \ No newline at end of file +{"version":"136.0.2"} \ No newline at end of file From c12ec62b346a46f69aa6bcea92176ee187d21629 Mon Sep 17 00:00:00 2001 From: kwaroran Date: Tue, 22 Oct 2024 19:33:09 +0900 Subject: [PATCH 033/175] Update version to 137.0.0 --- src-tauri/tauri.conf.json | 2 +- src/ts/storage/database.ts | 2 +- version.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 906b50c1..4310b93c 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -29,7 +29,7 @@ }, "productName": "RisuAI", "mainBinaryName": "RisuAI", - "version": "136.0.2", + "version": "137.0.0", "identifier": "co.aiclient.risu", "plugins": { "updater": { diff --git a/src/ts/storage/database.ts b/src/ts/storage/database.ts index aa9e6fba..600d164c 100644 --- a/src/ts/storage/database.ts +++ b/src/ts/storage/database.ts @@ -1,6 +1,6 @@ export const DataBase = writable({} as any as Database) export const loadedStore = writable(false) -export let appVer = "136.0.2" +export let appVer = "137.0.0" export let webAppSubVer = '' import { get, writable } from 'svelte/store'; diff --git a/version.json b/version.json index 544f5d2a..092238bd 100644 --- a/version.json +++ b/version.json @@ -1 +1 @@ -{"version":"136.0.2"} \ No newline at end of file +{"version":"137.0.0"} \ No newline at end of file From e0edf74b465a080c4723bfc949bb125e89e10d74 Mon Sep 17 00:00:00 2001 From: ModMapper Date: Tue, 22 Oct 2024 20:46:00 +0900 Subject: [PATCH 034/175] Fixed the bug where the character list was not updating in certain situations after importing a URL. --- src/ts/characterCards.ts | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/src/ts/characterCards.ts b/src/ts/characterCards.ts index 80d64e0c..fb2672af 100644 --- a/src/ts/characterCards.ts +++ b/src/ts/characterCards.ts @@ -301,11 +301,17 @@ export async function characterURLImport() { const hash = location.hash if(hash.startsWith('#import=')){ const url = hash.replace('#import=', '') - const res = await fetch(url, { - method: 'GET', - }) - const data = new Uint8Array(await res.arrayBuffer()) - importFile(getFileName(res), data) + try { + const res = await fetch(url, { + method: 'GET', + }) + const data = new Uint8Array(await res.arrayBuffer()) + await importFile(getFileName(res), data) + checkCharOrder() + } catch (error) { + alertError(language.errors.noData) + return null + } } if(hash.startsWith('#import_module=')){ const data = hash.replace('#import_module=', '') @@ -381,7 +387,7 @@ export async function characterURLImport() { for(const f of files){ const file = await f.getFile() const data = new Uint8Array(await file.arrayBuffer()) - importFile(f.name, data); + await importFile(f.name, data); } } //@ts-ignore @@ -399,7 +405,7 @@ export async function characterURLImport() { if(files){ for(const file of files){ const data = await readFile(file) - importFile(file, data) + await importFile(file, data) } } } @@ -451,9 +457,9 @@ export async function characterURLImport() { } function getFileName(res : Response) : string { - return getFormContent(res.headers.get('content-disposition')) || getFromURL(res.url); + return getFromContent(res.headers.get('content-disposition')) || getFromURL(res.url); - function getFormContent(contentDisposition : string) { + function getFromContent(contentDisposition : string) { if (!contentDisposition) return null; const pattern = /filename\*=UTF-8''([^"';\n]+)|filename[^;\n=]*=["']?([^"';\n]+)["']?/; const matches = contentDisposition.match(pattern); From 95f3bfdd4b812a64b7ea560269cc29ed7e9cce86 Mon Sep 17 00:00:00 2001 From: ModMapper Date: Tue, 22 Oct 2024 20:47:27 +0900 Subject: [PATCH 035/175] Removed the hash after import to prevent the same character from being imported again upon refresh. --- src/ts/characterCards.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ts/characterCards.ts b/src/ts/characterCards.ts index fb2672af..5af76a59 100644 --- a/src/ts/characterCards.ts +++ b/src/ts/characterCards.ts @@ -300,6 +300,7 @@ export async function characterURLImport() { const hash = location.hash if(hash.startsWith('#import=')){ + location.hash = '' const url = hash.replace('#import=', '') try { const res = await fetch(url, { From 8e1d014f6ea1d3b8e017c150f88219d1ad6dfef4 Mon Sep 17 00:00:00 2001 From: kwaroran Date: Tue, 22 Oct 2024 22:06:20 +0900 Subject: [PATCH 036/175] Fix svelte 4 components to fit into requirements --- src/lib/ChatScreens/Chat.svelte | 8 ++++---- src/lib/ChatScreens/DefaultChatScreen.svelte | 6 +++--- src/lib/Others/ChatList.svelte | 8 ++++---- src/lib/Setting/Pages/BotSettings.svelte | 4 ++++ src/lib/Setting/Pages/Module/ModuleMenu.svelte | 2 ++ src/lib/Setting/botpreset.svelte | 12 ++++++------ src/lib/Setting/lorepreset.svelte | 4 ++-- src/lib/SideBars/CharConfig.svelte | 8 ++++++++ src/lib/SideBars/SideChatList.svelte | 16 ++++++++-------- 9 files changed, 41 insertions(+), 27 deletions(-) diff --git a/src/lib/ChatScreens/Chat.svelte b/src/lib/ChatScreens/Chat.svelte index e417710f..e49f617e 100644 --- a/src/lib/ChatScreens/Chat.svelte +++ b/src/lib/ChatScreens/Chat.svelte @@ -24,7 +24,7 @@ export let img:string|Promise = '' export let idx = -1 export let rerollIcon = false - export let MessageGenerationInfo:MessageGenerationInfo|null = null + export let messageGenerationInfo:MessageGenerationInfo|null = null export let onReroll = () => {} export let unReroll = () => {} export let character:simpleCharacterArgument|string|null = null @@ -264,20 +264,20 @@ {/if} - {#if MessageGenerationInfo && $DataBase.requestInfoInsideChat} + {#if messageGenerationInfo && $DataBase.requestInfoInsideChat}
diff --git a/src/lib/ChatScreens/DefaultChatScreen.svelte b/src/lib/ChatScreens/DefaultChatScreen.svelte index 618948ad..5adac938 100644 --- a/src/lib/ChatScreens/DefaultChatScreen.svelte +++ b/src/lib/ChatScreens/DefaultChatScreen.svelte @@ -566,7 +566,7 @@ isLastMemory={$CurrentChat.lastMemory === (chat.chatId ?? 'none') && $CurrentShowMemoryLimit} character={$CurrentSimpleCharacter} largePortrait={$CurrentCharacter.largePortrait} - MessageGenerationInfo={chat.generationInfo} + messageGenerationInfo={chat.generationInfo} /> {:else} {/if} {:else} @@ -592,7 +592,7 @@ img={$ConnectionOpenStore ? '' : getCharImage($CurrentUserIcon, 'css')} isLastMemory={$CurrentChat.lastMemory === (chat.chatId ?? 'none') && $CurrentShowMemoryLimit} largePortrait={$UserIconProtrait} - MessageGenerationInfo={chat.generationInfo} + messageGenerationInfo={chat.generationInfo} /> {/if} {/each} diff --git a/src/lib/Others/ChatList.svelte b/src/lib/Others/ChatList.svelte index 0a6632be..d0e234ce 100644 --- a/src/lib/Others/ChatList.svelte +++ b/src/lib/Others/ChatList.svelte @@ -35,13 +35,13 @@ {chat.name} {/if}
- -
+
{ e.stopPropagation() if($CurrentCharacter.chats.length === 1){ alertError(language.errors.onlyOneChat) @@ -56,7 +56,7 @@ } }}> - +
{/each} diff --git a/src/lib/Setting/Pages/BotSettings.svelte b/src/lib/Setting/Pages/BotSettings.svelte index ac569e72..f2f9002f 100644 --- a/src/lib/Setting/Pages/BotSettings.svelte +++ b/src/lib/Setting/Pages/BotSettings.svelte @@ -475,6 +475,7 @@ {#if submenu === 3 || submenu === -1} + @@ -508,6 +509,7 @@ {/each} +
Bias {language.value}
-
+
{ e.stopPropagation() const data = await alertCardExport('preset') console.log(data.type) @@ -57,8 +57,8 @@ }}> - -
+
{ e.stopPropagation() if($DataBase.botPresets.length === 1){ alertError(language.errors.onlyOneChat) @@ -74,7 +74,7 @@ } }}> - +
{/each} diff --git a/src/lib/Setting/lorepreset.svelte b/src/lib/Setting/lorepreset.svelte index f6fe31d1..0f9c42a0 100644 --- a/src/lib/Setting/lorepreset.svelte +++ b/src/lib/Setting/lorepreset.svelte @@ -30,7 +30,7 @@ {lore.name} {/if}
- +
{/each} diff --git a/src/lib/SideBars/CharConfig.svelte b/src/lib/SideBars/CharConfig.svelte index dd754bce..a7bb4ef5 100644 --- a/src/lib/SideBars/CharConfig.svelte +++ b/src/lib/SideBars/CharConfig.svelte @@ -530,6 +530,7 @@
+ @@ -556,6 +557,7 @@ {/each} {/if} +
{language.image} {language.emotion}
@@ -618,6 +620,7 @@
+ @@ -655,6 +658,7 @@ {/each} +
Bias {language.value}
@@ -1015,6 +1019,7 @@ {language.altGreet}
+ {/each} +
{language.value} @@ -1053,12 +1058,14 @@
+ {/each} {/if} +
{language.value} @@ -1119,6 +1126,7 @@
diff --git a/src/lib/SideBars/SideChatList.svelte b/src/lib/SideBars/SideChatList.svelte index 6287974e..289dc99e 100644 --- a/src/lib/SideBars/SideChatList.svelte +++ b/src/lib/SideBars/SideChatList.svelte @@ -100,7 +100,7 @@ {chat.name} {/if}
- -
+
{ editMode = !editMode }}> - -
+
{ e.stopPropagation() exportChat(i) }}> - -
+
{ e.stopPropagation() if(chara.chats.length === 1){ alertError(language.errors.onlyOneChat) @@ -168,7 +168,7 @@ } }}> - +
{/each} From e8a38c65704c11202f179475701ec4e752d02775 Mon Sep 17 00:00:00 2001 From: kwaroran Date: Tue, 22 Oct 2024 22:11:36 +0900 Subject: [PATCH 037/175] Refactor table rendering in BotSettings, ModuleMenu, and CharConfig components --- src/lib/Setting/Pages/BotSettings.svelte | 4 ++-- src/lib/Setting/Pages/Module/ModuleMenu.svelte | 2 +- src/lib/SideBars/CharConfig.svelte | 9 +++++---- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/lib/Setting/Pages/BotSettings.svelte b/src/lib/Setting/Pages/BotSettings.svelte index f2f9002f..907f30c8 100644 --- a/src/lib/Setting/Pages/BotSettings.svelte +++ b/src/lib/Setting/Pages/BotSettings.svelte @@ -542,8 +542,8 @@ {#if $DataBase.bias.length === 0} - -
{language.noData}
+ + {language.noData} {/if} {#each $DataBase.additionalParams as additionalParams, i} diff --git a/src/lib/Setting/Pages/Module/ModuleMenu.svelte b/src/lib/Setting/Pages/Module/ModuleMenu.svelte index 2ced0bf6..432932bb 100644 --- a/src/lib/Setting/Pages/Module/ModuleMenu.svelte +++ b/src/lib/Setting/Pages/Module/ModuleMenu.svelte @@ -192,7 +192,7 @@ {#if (!currentModule.assets) || currentModule.assets.length === 0} -
No Assets
+ {language.noData} {:else} {#each currentModule.assets as assets, i} diff --git a/src/lib/SideBars/CharConfig.svelte b/src/lib/SideBars/CharConfig.svelte index a7bb4ef5..a5956e04 100644 --- a/src/lib/SideBars/CharConfig.svelte +++ b/src/lib/SideBars/CharConfig.svelte @@ -538,7 +538,7 @@ {#if currentChar.data.emotionImages.length === 0} -
{language.noImages}
+ {language.noImages} {:else} {#each emos as emo, i} @@ -636,7 +636,8 @@ {#if currentChar.data.bias.length === 0} -
{language.noBias}
+ {language.noBias} + {/if} {#each currentChar.data.bias as bias, i} @@ -1036,7 +1037,7 @@ {#if currentChar.data.alternateGreetings.length === 0} -
No Messages
+ {language.noData} {/if} {#each currentChar.data.alternateGreetings as bias, i} @@ -1092,7 +1093,7 @@ {#if (!currentChar.data.additionalAssets) || currentChar.data.additionalAssets.length === 0} -
No Assets
+ No Assets {:else} {#each currentChar.data.additionalAssets as assets, i} From e434c7ab969f77ca4a71ac8e527b0c6cb5b1eef4 Mon Sep 17 00:00:00 2001 From: kwaroran Date: Tue, 22 Oct 2024 22:13:52 +0900 Subject: [PATCH 038/175] Refactor table rendering in BotSettings, ModuleMenu, and CharConfig components --- src/lib/Setting/Pages/BotSettings.svelte | 2 +- src/lib/SideBars/CharConfig.svelte | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/lib/Setting/Pages/BotSettings.svelte b/src/lib/Setting/Pages/BotSettings.svelte index 907f30c8..ff025581 100644 --- a/src/lib/Setting/Pages/BotSettings.svelte +++ b/src/lib/Setting/Pages/BotSettings.svelte @@ -489,7 +489,7 @@ {#if $DataBase.bias.length === 0} -
{language.noBias}
+ {language.noBias} {/if} {#each $DataBase.bias as bias, i} diff --git a/src/lib/SideBars/CharConfig.svelte b/src/lib/SideBars/CharConfig.svelte index a5956e04..8c6e18d7 100644 --- a/src/lib/SideBars/CharConfig.svelte +++ b/src/lib/SideBars/CharConfig.svelte @@ -551,9 +551,12 @@ - + + + + {/each} {/if} From c7330719ad0ee270715672ca097bafc5e208cb81 Mon Sep 17 00:00:00 2001 From: kwaroran Date: Wed, 23 Oct 2024 02:31:37 +0900 Subject: [PATCH 039/175] Migrate to svelte 5 --- package.json | 12 +- pnpm-lock.yaml | 855 +++++++++--------- src/App.svelte | 6 +- src/LiteMain.svelte | 2 +- src/lib/ChatScreens/AdvancedChatEditor.svelte | 33 +- src/lib/ChatScreens/AssetInput.svelte | 24 +- src/lib/ChatScreens/BackgroundDom.svelte | 4 +- src/lib/ChatScreens/Chat.svelte | 144 +-- src/lib/ChatScreens/ChatScreen.svelte | 38 +- src/lib/ChatScreens/CreatorQuote.svelte | 10 +- src/lib/ChatScreens/DefaultChatScreen.svelte | 221 ++--- src/lib/ChatScreens/EmotionBox.svelte | 2 +- src/lib/ChatScreens/ResizeBox.svelte | 10 +- src/lib/ChatScreens/Suggestion.svelte | 50 +- src/lib/ChatScreens/TransitionImage.svelte | 28 +- src/lib/LiteUI/LiteCardIcon.svelte | 16 +- src/lib/Mobile/MobileBody.svelte | 11 +- src/lib/Mobile/MobileCharacters.svelte | 4 +- src/lib/Mobile/MobileFooter.svelte | 18 +- src/lib/Mobile/MobileHeader.svelte | 13 +- src/lib/Others/AccountWarningComp.svelte | 2 +- src/lib/Others/AlertComp.svelte | 142 +-- src/lib/Others/ChatList.svelte | 47 +- src/lib/Others/GithubStars.svelte | 10 +- src/lib/Others/GridCatalog.svelte | 26 +- src/lib/Others/Help.svelte | 12 +- src/lib/Others/WelcomeRisu.svelte | 40 +- src/lib/Playground/PlaygroundEmbedding.svelte | 22 +- src/lib/Playground/PlaygroundImageGen.svelte | 12 +- src/lib/Playground/PlaygroundJinja.svelte | 8 +- src/lib/Playground/PlaygroundMenu.svelte | 24 +- src/lib/Playground/PlaygroundParser.svelte | 4 +- src/lib/Playground/PlaygroundSyntax.svelte | 4 +- src/lib/Playground/PlaygroundTokenizer.svelte | 6 +- src/lib/Playground/ToolConvertion.svelte | 6 +- src/lib/Setting/Pages/AdvancedSettings.svelte | 8 +- src/lib/Setting/Pages/BotSettings.svelte | 52 +- src/lib/Setting/Pages/Communities.svelte | 6 +- .../Setting/Pages/CustomGUISettingMenu.svelte | 44 +- src/lib/Setting/Pages/DisplaySettings.svelte | 62 +- src/lib/Setting/Pages/FilesSettings.svelte | 6 +- .../Pages/GlobalLoreBookSettings.svelte | 8 +- src/lib/Setting/Pages/GlobalRegex.svelte | 6 +- src/lib/Setting/Pages/LanguageSettings.svelte | 4 +- .../Pages/Module/ModuleChatMenu.svelte | 30 +- .../Setting/Pages/Module/ModuleMenu.svelte | 44 +- .../Pages/Module/ModuleSettings.svelte | 26 +- src/lib/Setting/Pages/OobaSettings.svelte | 10 +- src/lib/Setting/Pages/OtherBotSettings.svelte | 34 +- src/lib/Setting/Pages/PersonaSettings.svelte | 22 +- src/lib/Setting/Pages/PluginSettings.svelte | 4 +- src/lib/Setting/Pages/PromptSettings.svelte | 36 +- src/lib/Setting/Pages/ThanksPage.svelte | 4 +- src/lib/Setting/Pages/UserSettings.svelte | 24 +- src/lib/Setting/Settings.svelte | 28 +- src/lib/Setting/botpreset.svelte | 24 +- src/lib/Setting/listedPersona.svelte | 10 +- src/lib/Setting/lorepreset.svelte | 15 +- src/lib/SideBars/BarIcon.svelte | 13 +- src/lib/SideBars/CharConfig.svelte | 210 +++-- src/lib/SideBars/DevTool.svelte | 48 +- src/lib/SideBars/DropList.svelte | 7 +- src/lib/SideBars/LoreBook/LoreBookData.svelte | 31 +- src/lib/SideBars/LoreBook/LoreBookList.svelte | 44 +- .../SideBars/LoreBook/LoreBookSetting.svelte | 44 +- src/lib/SideBars/Scripts/RegexData.svelte | 28 +- src/lib/SideBars/Scripts/RegexList.svelte | 10 +- src/lib/SideBars/Scripts/TriggerData.svelte | 77 +- src/lib/SideBars/Scripts/TriggerList.svelte | 26 +- src/lib/SideBars/SideChatList.svelte | 38 +- src/lib/SideBars/Sidebar.svelte | 114 +-- src/lib/SideBars/SidebarAvatar.svelte | 49 +- src/lib/SideBars/SidebarIndicator.svelte | 8 +- src/lib/UI/3DLoader.svelte | 6 +- src/lib/UI/Arcodion.svelte | 31 +- src/lib/UI/BaseRoundedButton.svelte | 13 +- src/lib/UI/GUI/Button.svelte | 28 +- src/lib/UI/GUI/CheckInput.svelte | 37 +- src/lib/UI/GUI/ColorInput.svelte | 18 +- src/lib/UI/GUI/MultiLangDisplay.svelte | 22 +- src/lib/UI/GUI/MultiLangInput.svelte | 28 +- src/lib/UI/GUI/NumberInput.svelte | 46 +- src/lib/UI/GUI/OptionInput.svelte | 11 +- src/lib/UI/GUI/OptionalInput.svelte | 21 +- src/lib/UI/GUI/SelectInput.svelte | 24 +- src/lib/UI/GUI/SideBarArrow.svelte | 7 +- src/lib/UI/GUI/SliderInput.svelte | 49 +- src/lib/UI/GUI/TextAreaInput.svelte | 88 +- src/lib/UI/GUI/TextAreaResizable.svelte | 14 +- src/lib/UI/GUI/TextInput.svelte | 64 +- src/lib/UI/MainMenu.svelte | 12 +- src/lib/UI/ModelList.svelte | 185 ++-- src/lib/UI/PromptDataItem.svelte | 25 +- src/lib/UI/Realm/RealmFrame.svelte | 20 +- src/lib/UI/Realm/RealmHubIcon.svelte | 18 +- src/lib/UI/Realm/RealmLicense.svelte | 14 +- src/lib/UI/Realm/RealmMain.svelte | 48 +- src/lib/UI/Realm/RealmPopUp.svelte | 34 +- src/lib/UI/Realm/RealmUpload.svelte | 50 +- src/lib/UI/Title.svelte | 32 +- src/lib/VisualNovel/VisualNovelChat.svelte | 21 +- src/lib/VisualNovel/VisualNovelMain.svelte | 26 +- src/main.ts | 7 +- src/ts/characterCards.ts | 8 +- src/ts/observer.ts | 9 +- src/ts/parser.ts | 6 +- src/ts/process/command.ts | 10 +- src/ts/process/lua.ts | 44 +- src/ts/process/modules.ts | 10 +- src/ts/process/templates/chatTemplate.ts | 5 +- src/ts/process/triggers.ts | 12 +- src/ts/process/tts.ts | 4 +- src/ts/realm.ts | 6 +- src/ts/storage/database.ts | 25 + src/ts/stores.ts | 140 +-- src/ts/sync/multiuser.ts | 8 +- src/ts/tokenizer.ts | 5 +- src/ts/util.ts | 6 - tsconfig.json | 12 +- vite.config.ts | 2 +- 120 files changed, 2398 insertions(+), 2033 deletions(-) diff --git a/package.json b/package.json index 616e5444..be880f23 100644 --- a/package.json +++ b/package.json @@ -87,7 +87,7 @@ "devDependencies": { "@capacitor/assets": "^3.0.4", "@capacitor/cli": "^5.6.0", - "@sveltejs/vite-plugin-svelte": "3.0.1", + "@sveltejs/vite-plugin-svelte": "^4.0.0", "@swc/core": "1.5.7", "@tailwindcss/typography": "^0.5.10", "@tauri-apps/cli": "2.0.2", @@ -108,13 +108,13 @@ "autoprefixer": "^10.4.16", "internal-ip": "^7.0.0", "postcss": "^8.4.33", - "svelte": "4.2.8", - "svelte-check": "3.6.3", - "svelte-preprocess": "5.1.3", + "svelte": "^5.0.0", + "svelte-check": "^4.0.0", + "svelte-preprocess": "^6.0.0", "tailwindcss": "^3.4.1", "tslib": "^2.6.2", - "typescript": "^5.3.3", - "vite": "5.2.12", + "typescript": "^5.5.0", + "vite": "^5.4.4", "vite-plugin-top-level-await": "1.4.1", "vite-plugin-wasm": "3.3.0" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 064ca1a7..1f6db48e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -139,7 +139,7 @@ importers: version: 4.17.21 lucide-svelte: specifier: ^0.292.0 - version: 0.292.0(svelte@4.2.8) + version: 0.292.0(svelte@5.0.5) markdown-it: specifier: ^14.1.0 version: 14.1.0 @@ -187,7 +187,7 @@ importers: version: 2.0.6 svelte-awesome-color-picker: specifier: ^3.1.0 - version: 3.1.0(svelte@4.2.8) + version: 3.1.0(svelte@5.0.5) three: specifier: ^0.154.0 version: 0.154.0 @@ -209,19 +209,19 @@ importers: devDependencies: '@capacitor/assets': specifier: ^3.0.4 - version: 3.0.4(@swc/core@1.5.7)(@types/node@18.19.7)(typescript@5.3.3) + version: 3.0.4(@swc/core@1.5.7)(@types/node@18.19.7)(typescript@5.6.3) '@capacitor/cli': specifier: ^5.6.0 version: 5.6.0 '@sveltejs/vite-plugin-svelte': - specifier: 3.0.1 - version: 3.0.1(svelte@4.2.8)(vite@5.2.12(@types/node@18.19.7)) + specifier: ^4.0.0 + version: 4.0.0(svelte@5.0.5)(vite@5.4.9(@types/node@18.19.7)) '@swc/core': specifier: 1.5.7 version: 1.5.7 '@tailwindcss/typography': specifier: ^0.5.10 - version: 0.5.10(tailwindcss@3.4.1(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.7)(typescript@5.3.3))) + version: 0.5.10(tailwindcss@3.4.1(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.7)(typescript@5.6.3))) '@tauri-apps/cli': specifier: 2.0.2 version: 2.0.2 @@ -277,32 +277,32 @@ importers: specifier: ^8.4.33 version: 8.4.33 svelte: - specifier: 4.2.8 - version: 4.2.8 + specifier: ^5.0.0 + version: 5.0.5 svelte-check: - specifier: 3.6.3 - version: 3.6.3(postcss-load-config@4.0.2(postcss@8.4.33)(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.7)(typescript@5.3.3)))(postcss@8.4.33)(svelte@4.2.8) + specifier: ^4.0.0 + version: 4.0.5(svelte@5.0.5)(typescript@5.6.3) svelte-preprocess: - specifier: 5.1.3 - version: 5.1.3(postcss-load-config@4.0.2(postcss@8.4.33)(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.7)(typescript@5.3.3)))(postcss@8.4.33)(svelte@4.2.8)(typescript@5.3.3) + specifier: ^6.0.0 + version: 6.0.3(postcss-load-config@4.0.2(postcss@8.4.33)(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.7)(typescript@5.6.3)))(postcss@8.4.33)(svelte@5.0.5)(typescript@5.6.3) tailwindcss: specifier: ^3.4.1 - version: 3.4.1(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.7)(typescript@5.3.3)) + version: 3.4.1(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.7)(typescript@5.6.3)) tslib: specifier: ^2.6.2 version: 2.6.2 typescript: - specifier: ^5.3.3 - version: 5.3.3 + specifier: ^5.5.0 + version: 5.6.3 vite: - specifier: 5.2.12 - version: 5.2.12(@types/node@18.19.7) + specifier: ^5.4.4 + version: 5.4.9(@types/node@18.19.7) vite-plugin-top-level-await: specifier: 1.4.1 - version: 1.4.1(rollup@3.29.4)(vite@5.2.12(@types/node@18.19.7)) + version: 1.4.1(rollup@3.29.4)(vite@5.4.9(@types/node@18.19.7)) vite-plugin-wasm: specifier: 3.3.0 - version: 3.3.0(vite@5.2.12(@types/node@18.19.7)) + version: 3.3.0(vite@5.4.9(@types/node@18.19.7)) packages: @@ -313,8 +313,8 @@ packages: resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} engines: {node: '>=10'} - '@ampproject/remapping@2.2.1': - resolution: {integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==} + '@ampproject/remapping@2.3.0': + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} engines: {node: '>=6.0.0'} '@asamuzakjp/dom-selector@2.0.2': @@ -412,140 +412,140 @@ packages: '@dqbd/tiktoken@1.0.7': resolution: {integrity: sha512-bhR5k5W+8GLzysjk8zTMVygQZsgvf7W1F0IlL4ZQ5ugjo5rCyiwGM5d8DYriXspytfu98tv59niang3/T+FoDw==} - '@esbuild/aix-ppc64@0.20.2': - resolution: {integrity: sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==} + '@esbuild/aix-ppc64@0.21.5': + resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} engines: {node: '>=12'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.20.2': - resolution: {integrity: sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==} + '@esbuild/android-arm64@0.21.5': + resolution: {integrity: sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==} engines: {node: '>=12'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.20.2': - resolution: {integrity: sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==} + '@esbuild/android-arm@0.21.5': + resolution: {integrity: sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==} engines: {node: '>=12'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.20.2': - resolution: {integrity: sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==} + '@esbuild/android-x64@0.21.5': + resolution: {integrity: sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==} engines: {node: '>=12'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.20.2': - resolution: {integrity: sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==} + '@esbuild/darwin-arm64@0.21.5': + resolution: {integrity: sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==} engines: {node: '>=12'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.20.2': - resolution: {integrity: sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==} + '@esbuild/darwin-x64@0.21.5': + resolution: {integrity: sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==} engines: {node: '>=12'} cpu: [x64] os: [darwin] - '@esbuild/freebsd-arm64@0.20.2': - resolution: {integrity: sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==} + '@esbuild/freebsd-arm64@0.21.5': + resolution: {integrity: sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==} engines: {node: '>=12'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.20.2': - resolution: {integrity: sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==} + '@esbuild/freebsd-x64@0.21.5': + resolution: {integrity: sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==} engines: {node: '>=12'} cpu: [x64] os: [freebsd] - '@esbuild/linux-arm64@0.20.2': - resolution: {integrity: sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==} + '@esbuild/linux-arm64@0.21.5': + resolution: {integrity: sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==} engines: {node: '>=12'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.20.2': - resolution: {integrity: sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==} + '@esbuild/linux-arm@0.21.5': + resolution: {integrity: sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==} engines: {node: '>=12'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.20.2': - resolution: {integrity: sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==} + '@esbuild/linux-ia32@0.21.5': + resolution: {integrity: sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==} engines: {node: '>=12'} cpu: [ia32] os: [linux] - '@esbuild/linux-loong64@0.20.2': - resolution: {integrity: sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==} + '@esbuild/linux-loong64@0.21.5': + resolution: {integrity: sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==} engines: {node: '>=12'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.20.2': - resolution: {integrity: sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==} + '@esbuild/linux-mips64el@0.21.5': + resolution: {integrity: sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==} engines: {node: '>=12'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.20.2': - resolution: {integrity: sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==} + '@esbuild/linux-ppc64@0.21.5': + resolution: {integrity: sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==} engines: {node: '>=12'} cpu: [ppc64] os: [linux] - '@esbuild/linux-riscv64@0.20.2': - resolution: {integrity: sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==} + '@esbuild/linux-riscv64@0.21.5': + resolution: {integrity: sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==} engines: {node: '>=12'} cpu: [riscv64] os: [linux] - '@esbuild/linux-s390x@0.20.2': - resolution: {integrity: sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==} + '@esbuild/linux-s390x@0.21.5': + resolution: {integrity: sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==} engines: {node: '>=12'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.20.2': - resolution: {integrity: sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==} + '@esbuild/linux-x64@0.21.5': + resolution: {integrity: sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==} engines: {node: '>=12'} cpu: [x64] os: [linux] - '@esbuild/netbsd-x64@0.20.2': - resolution: {integrity: sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==} + '@esbuild/netbsd-x64@0.21.5': + resolution: {integrity: sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==} engines: {node: '>=12'} cpu: [x64] os: [netbsd] - '@esbuild/openbsd-x64@0.20.2': - resolution: {integrity: sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==} + '@esbuild/openbsd-x64@0.21.5': + resolution: {integrity: sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==} engines: {node: '>=12'} cpu: [x64] os: [openbsd] - '@esbuild/sunos-x64@0.20.2': - resolution: {integrity: sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==} + '@esbuild/sunos-x64@0.21.5': + resolution: {integrity: sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==} engines: {node: '>=12'} cpu: [x64] os: [sunos] - '@esbuild/win32-arm64@0.20.2': - resolution: {integrity: sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==} + '@esbuild/win32-arm64@0.21.5': + resolution: {integrity: sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==} engines: {node: '>=12'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.20.2': - resolution: {integrity: sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==} + '@esbuild/win32-ia32@0.21.5': + resolution: {integrity: sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==} engines: {node: '>=12'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.20.2': - resolution: {integrity: sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==} + '@esbuild/win32-x64@0.21.5': + resolution: {integrity: sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==} engines: {node: '>=12'} cpu: [x64] os: [win32] @@ -602,6 +602,10 @@ packages: resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} engines: {node: '>=6.0.0'} + '@jridgewell/gen-mapping@0.3.5': + resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} + engines: {node: '>=6.0.0'} + '@jridgewell/resolve-uri@3.1.1': resolution: {integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==} engines: {node: '>=6.0.0'} @@ -610,12 +614,22 @@ packages: resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} engines: {node: '>=6.0.0'} + '@jridgewell/set-array@1.2.1': + resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} + engines: {node: '>=6.0.0'} + '@jridgewell/sourcemap-codec@1.4.15': resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + '@jridgewell/sourcemap-codec@1.5.0': + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + '@jridgewell/trace-mapping@0.3.21': resolution: {integrity: sha512-SRfKmRe1KvYnxjEMtxEr+J4HIeMX5YBg/qhRHpxEIGjhX1rshcHlnFUE9K0GazhVKWM7B+nARSkV8LuvJdJ5/g==} + '@jridgewell/trace-mapping@0.3.25': + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + '@jridgewell/trace-mapping@0.3.9': resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} @@ -724,83 +738,83 @@ packages: rollup: optional: true - '@rollup/rollup-android-arm-eabi@4.18.0': - resolution: {integrity: sha512-Tya6xypR10giZV1XzxmH5wr25VcZSncG0pZIjfePT0OVBvqNEurzValetGNarVrGiq66EBVAFn15iYX4w6FKgQ==} + '@rollup/rollup-android-arm-eabi@4.24.0': + resolution: {integrity: sha512-Q6HJd7Y6xdB48x8ZNVDOqsbh2uByBhgK8PiQgPhwkIw/HC/YX5Ghq2mQY5sRMZWHb3VsFkWooUVOZHKr7DmDIA==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.18.0': - resolution: {integrity: sha512-avCea0RAP03lTsDhEyfy+hpfr85KfyTctMADqHVhLAF3MlIkq83CP8UfAHUssgXTYd+6er6PaAhx/QGv4L1EiA==} + '@rollup/rollup-android-arm64@4.24.0': + resolution: {integrity: sha512-ijLnS1qFId8xhKjT81uBHuuJp2lU4x2yxa4ctFPtG+MqEE6+C5f/+X/bStmxapgmwLwiL3ih122xv8kVARNAZA==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.18.0': - resolution: {integrity: sha512-IWfdwU7KDSm07Ty0PuA/W2JYoZ4iTj3TUQjkVsO/6U+4I1jN5lcR71ZEvRh52sDOERdnNhhHU57UITXz5jC1/w==} + '@rollup/rollup-darwin-arm64@4.24.0': + resolution: {integrity: sha512-bIv+X9xeSs1XCk6DVvkO+S/z8/2AMt/2lMqdQbMrmVpgFvXlmde9mLcbQpztXm1tajC3raFDqegsH18HQPMYtA==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.18.0': - resolution: {integrity: sha512-n2LMsUz7Ynu7DoQrSQkBf8iNrjOGyPLrdSg802vk6XT3FtsgX6JbE8IHRvposskFm9SNxzkLYGSq9QdpLYpRNA==} + '@rollup/rollup-darwin-x64@4.24.0': + resolution: {integrity: sha512-X6/nOwoFN7RT2svEQWUsW/5C/fYMBe4fnLK9DQk4SX4mgVBiTA9h64kjUYPvGQ0F/9xwJ5U5UfTbl6BEjaQdBQ==} cpu: [x64] os: [darwin] - '@rollup/rollup-linux-arm-gnueabihf@4.18.0': - resolution: {integrity: sha512-C/zbRYRXFjWvz9Z4haRxcTdnkPt1BtCkz+7RtBSuNmKzMzp3ZxdM28Mpccn6pt28/UWUCTXa+b0Mx1k3g6NOMA==} + '@rollup/rollup-linux-arm-gnueabihf@4.24.0': + resolution: {integrity: sha512-0KXvIJQMOImLCVCz9uvvdPgfyWo93aHHp8ui3FrtOP57svqrF/roSSR5pjqL2hcMp0ljeGlU4q9o/rQaAQ3AYA==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.18.0': - resolution: {integrity: sha512-l3m9ewPgjQSXrUMHg93vt0hYCGnrMOcUpTz6FLtbwljo2HluS4zTXFy2571YQbisTnfTKPZ01u/ukJdQTLGh9A==} + '@rollup/rollup-linux-arm-musleabihf@4.24.0': + resolution: {integrity: sha512-it2BW6kKFVh8xk/BnHfakEeoLPv8STIISekpoF+nBgWM4d55CZKc7T4Dx1pEbTnYm/xEKMgy1MNtYuoA8RFIWw==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.18.0': - resolution: {integrity: sha512-rJ5D47d8WD7J+7STKdCUAgmQk49xuFrRi9pZkWoRD1UeSMakbcepWXPF8ycChBoAqs1pb2wzvbY6Q33WmN2ftw==} + '@rollup/rollup-linux-arm64-gnu@4.24.0': + resolution: {integrity: sha512-i0xTLXjqap2eRfulFVlSnM5dEbTVque/3Pi4g2y7cxrs7+a9De42z4XxKLYJ7+OhE3IgxvfQM7vQc43bwTgPwA==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.18.0': - resolution: {integrity: sha512-be6Yx37b24ZwxQ+wOQXXLZqpq4jTckJhtGlWGZs68TgdKXJgw54lUUoFYrg6Zs/kjzAQwEwYbp8JxZVzZLRepQ==} + '@rollup/rollup-linux-arm64-musl@4.24.0': + resolution: {integrity: sha512-9E6MKUJhDuDh604Qco5yP/3qn3y7SLXYuiC0Rpr89aMScS2UAmK1wHP2b7KAa1nSjWJc/f/Lc0Wl1L47qjiyQw==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.18.0': - resolution: {integrity: sha512-hNVMQK+qrA9Todu9+wqrXOHxFiD5YmdEi3paj6vP02Kx1hjd2LLYR2eaN7DsEshg09+9uzWi2W18MJDlG0cxJA==} + '@rollup/rollup-linux-powerpc64le-gnu@4.24.0': + resolution: {integrity: sha512-2XFFPJ2XMEiF5Zi2EBf4h73oR1V/lycirxZxHZNc93SqDN/IWhYYSYj8I9381ikUFXZrz2v7r2tOVk2NBwxrWw==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.18.0': - resolution: {integrity: sha512-ROCM7i+m1NfdrsmvwSzoxp9HFtmKGHEqu5NNDiZWQtXLA8S5HBCkVvKAxJ8U+CVctHwV2Gb5VUaK7UAkzhDjlg==} + '@rollup/rollup-linux-riscv64-gnu@4.24.0': + resolution: {integrity: sha512-M3Dg4hlwuntUCdzU7KjYqbbd+BLq3JMAOhCKdBE3TcMGMZbKkDdJ5ivNdehOssMCIokNHFOsv7DO4rlEOfyKpg==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.18.0': - resolution: {integrity: sha512-0UyyRHyDN42QL+NbqevXIIUnKA47A+45WyasO+y2bGJ1mhQrfrtXUpTxCOrfxCR4esV3/RLYyucGVPiUsO8xjg==} + '@rollup/rollup-linux-s390x-gnu@4.24.0': + resolution: {integrity: sha512-mjBaoo4ocxJppTorZVKWFpy1bfFj9FeCMJqzlMQGjpNPY9JwQi7OuS1axzNIk0nMX6jSgy6ZURDZ2w0QW6D56g==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.18.0': - resolution: {integrity: sha512-xuglR2rBVHA5UsI8h8UbX4VJ470PtGCf5Vpswh7p2ukaqBGFTnsfzxUBetoWBWymHMxbIG0Cmx7Y9qDZzr648w==} + '@rollup/rollup-linux-x64-gnu@4.24.0': + resolution: {integrity: sha512-ZXFk7M72R0YYFN5q13niV0B7G8/5dcQ9JDp8keJSfr3GoZeXEoMHP/HlvqROA3OMbMdfr19IjCeNAnPUG93b6A==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.18.0': - resolution: {integrity: sha512-LKaqQL9osY/ir2geuLVvRRs+utWUNilzdE90TpyoX0eNqPzWjRm14oMEE+YLve4k/NAqCdPkGYDaDF5Sw+xBfg==} + '@rollup/rollup-linux-x64-musl@4.24.0': + resolution: {integrity: sha512-w1i+L7kAXZNdYl+vFvzSZy8Y1arS7vMgIy8wusXJzRrPyof5LAb02KGr1PD2EkRcl73kHulIID0M501lN+vobQ==} cpu: [x64] os: [linux] - '@rollup/rollup-win32-arm64-msvc@4.18.0': - resolution: {integrity: sha512-7J6TkZQFGo9qBKH0pk2cEVSRhJbL6MtfWxth7Y5YmZs57Pi+4x6c2dStAUvaQkHQLnEQv1jzBUW43GvZW8OFqA==} + '@rollup/rollup-win32-arm64-msvc@4.24.0': + resolution: {integrity: sha512-VXBrnPWgBpVDCVY6XF3LEW0pOU51KbaHhccHw6AS6vBWIC60eqsH19DAeeObl+g8nKAz04QFdl/Cefta0xQtUQ==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.18.0': - resolution: {integrity: sha512-Txjh+IxBPbkUB9+SXZMpv+b/vnTEtFyfWZgJ6iyCmt2tdx0OF5WhFowLmnh8ENGNpfUlUZkdI//4IEmhwPieNg==} + '@rollup/rollup-win32-ia32-msvc@4.24.0': + resolution: {integrity: sha512-xrNcGDU0OxVcPTH/8n/ShH4UevZxKIO6HJFK0e15XItZP2UcaiLFd5kiX7hJnqCbSztUF8Qot+JWBC/QXRPYWQ==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.18.0': - resolution: {integrity: sha512-UOo5FdvOL0+eIVTgS4tIdbW+TtnBLWg1YBCcU2KWM7nuNwRz9bksDX1bekJJCpu25N1DVWaCwnT39dVQxzqS8g==} + '@rollup/rollup-win32-x64-msvc@4.24.0': + resolution: {integrity: sha512-fbMkAF7fufku0N2dE5TBXcNlg0pt0cJue4xBRE2Qc5Vqikxr4VCgKj/ht6SMdFcOacVA9rqF70APJ8RN/4vMJw==} cpu: [x64] os: [win32] @@ -843,19 +857,19 @@ packages: resolution: {integrity: sha512-qOiVORSPm6Ce4/Yu6hbSgNHABLP2VMv8QOC3tTDNHHlWY19pPyc++fBTbZPtx6egPXi4HQxKDnMxVxpbtX2GoA==} engines: {node: '>=14.0.0'} - '@sveltejs/vite-plugin-svelte-inspector@2.0.0': - resolution: {integrity: sha512-gjr9ZFg1BSlIpfZ4PRewigrvYmHWbDrq2uvvPB1AmTWKuM+dI1JXQSUu2pIrYLb/QncyiIGkFDFKTwJ0XqQZZg==} - engines: {node: ^18.0.0 || >=20} + '@sveltejs/vite-plugin-svelte-inspector@3.0.0': + resolution: {integrity: sha512-hBxSYW/66989cq9dN248omD/ziskSdIV1NqfuueuAI1z6jGcg14k9Zd98pDIEnoA6wC9kWUGuQ6adzBbWwQyRg==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22} peerDependencies: - '@sveltejs/vite-plugin-svelte': ^3.0.0 - svelte: ^4.0.0 || ^5.0.0-next.0 + '@sveltejs/vite-plugin-svelte': ^4.0.0-next.0||^4.0.0 + svelte: ^5.0.0-next.96 || ^5.0.0 vite: ^5.0.0 - '@sveltejs/vite-plugin-svelte@3.0.1': - resolution: {integrity: sha512-CGURX6Ps+TkOovK6xV+Y2rn8JKa8ZPUHPZ/NKgCxAmgBrXReavzFl8aOSCj3kQ1xqT7yGJj53hjcV/gqwDAaWA==} - engines: {node: ^18.0.0 || >=20} + '@sveltejs/vite-plugin-svelte@4.0.0': + resolution: {integrity: sha512-kpVJwF+gNiMEsoHaw+FJL76IYiwBikkxYU83+BpqQLdVMff19KeRKLd2wisS8niNBMJ2omv5gG+iGDDwd8jzag==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22} peerDependencies: - svelte: ^4.0.0 || ^5.0.0-next.0 + svelte: ^5.0.0-next.96 || ^5.0.0 vite: ^5.0.0 '@swc/core-darwin-arm64@1.5.7': @@ -1069,6 +1083,9 @@ packages: '@types/estree@1.0.5': resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} + '@types/estree@1.0.6': + resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} + '@types/fs-extra@8.1.5': resolution: {integrity: sha512-0dzKcwO+S8s2kuF5Z9oUWatQJj5Uq/iqphEtE3GQJVRRYm/tD1LglU2UnXi2A8jLq5umkGouOXOR9y0n613ZwQ==} @@ -1105,9 +1122,6 @@ packages: '@types/normalize-package-data@2.4.4': resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} - '@types/pug@2.0.10': - resolution: {integrity: sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA==} - '@types/showdown@2.0.6': resolution: {integrity: sha512-pTvD/0CIeqe4x23+YJWlX2gArHa8G0J0Oh6GKaVXV7TAeickpkkZiNOgFcFcmLQ5lB/K0qBJL1FtRYltBfbGCQ==} @@ -1166,6 +1180,11 @@ packages: resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} engines: {node: '>= 0.6'} + acorn-typescript@1.4.13: + resolution: {integrity: sha512-xsc9Xv0xlVfwp2o7sQ+GCQ1PgbkdcpWdTzrwXxO3xDMTAywVS3oXVOcOHuRjAPkS4P9b+yc/qNF15460v+jp4Q==} + peerDependencies: + acorn: '>=8.9.0' + acorn-walk@8.3.2: resolution: {integrity: sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==} engines: {node: '>=0.4.0'} @@ -1175,6 +1194,11 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + acorn@8.13.0: + resolution: {integrity: sha512-8zSiw54Oxrdym50NlZ9sUusyO1Z1ZchgRLWRaK6c86XJFClyCgFKetdowBg5bKxyp/u+CDBJG4Mpp0m3HLZl9w==} + engines: {node: '>=0.4.0'} + hasBin: true + add-stream@1.0.0: resolution: {integrity: sha512-qQLMr+8o0WC4FZGQTcJiKBVC59JylcPSrTtk6usvmIDFUOCKegapy1VHQwRbFMOFyb/inzUVqHs+eMYKDM1YeQ==} @@ -1233,8 +1257,9 @@ packages: argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - aria-query@5.3.0: - resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} + aria-query@5.3.2: + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + engines: {node: '>= 0.4'} array-flatten@1.1.1: resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} @@ -1271,8 +1296,9 @@ packages: peerDependencies: postcss: ^8.1.0 - axobject-query@3.2.1: - resolution: {integrity: sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==} + axobject-query@4.1.0: + resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} + engines: {node: '>= 0.4'} b4a@1.6.4: resolution: {integrity: sha512-fpWrvyVHEKyeEvbKZTVOeZF3VSKKWtJxFIxX/jaVPf+cLbGUSitjb49pHLqPV2BUNNZ0LcoeEGfE/YCpyDYHIw==} @@ -1356,10 +1382,6 @@ packages: call-bind@1.0.5: resolution: {integrity: sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==} - callsites@3.1.0: - resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} - engines: {node: '>=6'} - camelcase-css@2.0.1: resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} engines: {node: '>= 6'} @@ -1397,6 +1419,10 @@ packages: resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} engines: {node: '>= 8.10.0'} + chokidar@4.0.1: + resolution: {integrity: sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==} + engines: {node: '>= 14.16.0'} + chownr@1.1.4: resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} @@ -1418,9 +1444,6 @@ packages: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} - code-red@1.0.4: - resolution: {integrity: sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==} - codemirror@5.65.16: resolution: {integrity: sha512-br21LjYmSlVL0vFCPWPfhzUCT34FM/pAdK7rRIZwa0rrtrIdotvP4Oh4GUHsu2E3IrQMCfRkL/fN3ytMNxVQvg==} @@ -1649,6 +1672,15 @@ packages: supports-color: optional: true + debug@4.3.7: + resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + decamelize-keys@1.1.1: resolution: {integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==} engines: {node: '>=0.10.0'} @@ -1703,18 +1735,10 @@ packages: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} - dequal@2.0.3: - resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} - engines: {node: '>=6'} - destroy@1.2.0: resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} - detect-indent@6.1.0: - resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} - engines: {node: '>=8'} - detect-libc@2.0.2: resolution: {integrity: sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==} engines: {node: '>=8'} @@ -1811,11 +1835,8 @@ packages: error-ex@1.3.2: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} - es6-promise@3.3.1: - resolution: {integrity: sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==} - - esbuild@0.20.2: - resolution: {integrity: sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==} + esbuild@0.21.5: + resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} engines: {node: '>=12'} hasBin: true @@ -1830,8 +1851,11 @@ packages: resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} engines: {node: '>=0.8.0'} - estree-walker@3.0.3: - resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + esm-env@1.0.0: + resolution: {integrity: sha512-Cf6VksWPsTuW01vU9Mk/3vRue91Zevka5SjyNf3nEpokFRuqt/KjUQoGAwq9qMmhpLTHmXzSIrFRw8zxWzmFBA==} + + esrap@1.2.2: + resolution: {integrity: sha512-F2pSJklxx1BlQIQgooczXCPHmcWpn6EsP5oo73LQfonG9fIlIENQ8vMmfGXeojP9MrkzUNAfyU5vdFlR9shHAw==} etag@1.8.1: resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} @@ -1872,6 +1896,14 @@ packages: fd-slicer@1.1.0: resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} + fdir@6.4.2: + resolution: {integrity: sha512-KnhMXsKSPZlAhp7+IjUkRZKPb4fUyccpDrdFXbi4QL1qkmFh9kVY09Yox+n4MaOb3lHZ1Tv829C3oaaXoMYPDQ==} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + fflate@0.6.10: resolution: {integrity: sha512-IQrh3lEPM93wVCEczc9SaAOvkmcoQn/G8Bo1e8ZPlY3X3bnAxWaBdvTdvM1hP62iZp0BXWDy4vTAy4fF0+Dlpg==} @@ -2124,10 +2156,6 @@ packages: immediate@3.0.6: resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} - import-fresh@3.3.0: - resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} - engines: {node: '>=6'} - indent-string@4.0.0: resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} engines: {node: '>=8'} @@ -2379,9 +2407,8 @@ packages: peerDependencies: svelte: '>=3 <5' - magic-string@0.30.5: - resolution: {integrity: sha512-7xlpfBaQaP/T6Vh8MO/EqXSW5En6INHEvEXQiuff7Gku0PWjU3uf6w/j9o7O+SpB5fOAkrI5HeoNgwjEO0pFsA==} - engines: {node: '>=12'} + magic-string@0.30.12: + resolution: {integrity: sha512-Ea8I3sQMVXr8JhN4z+H/d8zwo+tYDgHE9+5G4Wnrwhs0gaK9fXTKx0Tw5Xwsd/bCPTTZNRAdpyzvoeORe9LYpw==} make-dir@3.1.0: resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} @@ -2513,10 +2540,6 @@ packages: mkdirp-classic@0.5.3: resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} - mkdirp@0.5.6: - resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} - hasBin: true - mkdirp@1.0.4: resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} engines: {node: '>=10'} @@ -2755,10 +2778,6 @@ packages: pako@1.0.11: resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} - parent-module@1.0.1: - resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} - engines: {node: '>=6'} - parse-json@4.0.0: resolution: {integrity: sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==} engines: {node: '>=4'} @@ -2827,15 +2846,15 @@ packages: pend@1.2.0: resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} - periscopic@3.1.0: - resolution: {integrity: sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==} - picocolors@1.0.0: resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} picocolors@1.0.1: resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + picomatch@2.3.1: resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} engines: {node: '>=8.6'} @@ -2913,8 +2932,8 @@ packages: resolution: {integrity: sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==} engines: {node: ^10 || ^12 || >=14} - postcss@8.4.38: - resolution: {integrity: sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==} + postcss@8.4.47: + resolution: {integrity: sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==} engines: {node: ^10 || ^12 || >=14} prebuild-install@7.1.1: @@ -3026,6 +3045,10 @@ packages: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} + readdirp@4.0.2: + resolution: {integrity: sha512-yDMz9g+VaZkqBYS/ozoBJwaBhTbZo3UNYQHNRw1D3UFQB8oHB4uS/tAODO+ZLjGWmUbKnIlOWO+aaIiAxrUWHA==} + engines: {node: '>= 14.16.0'} + redent@3.0.0: resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==} engines: {node: '>=8'} @@ -3052,10 +3075,6 @@ packages: requires-port@1.0.0: resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} - resolve-from@4.0.0: - resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} - engines: {node: '>=4'} - resolve@1.22.8: resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} hasBin: true @@ -3064,10 +3083,6 @@ packages: resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - rimraf@2.7.1: - resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} - hasBin: true - rimraf@3.0.2: resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} hasBin: true @@ -3082,8 +3097,8 @@ packages: engines: {node: '>=14.18.0', npm: '>=8.0.0'} hasBin: true - rollup@4.18.0: - resolution: {integrity: sha512-QmJz14PX3rzbJCN1SG4Xe/bAAX2a6NpCP8ab2vfu2GiUr8AQcr2nCV/oEO3yneFarB67zk8ShlIyWb2LGTb3Sg==} + rollup@4.24.0: + resolution: {integrity: sha512-DOmrlGSXNk1DM0ljiQA+i+o0rSLhtii1je5wgk60j49d1jHT5YYttBv1iWOnYSTG+fZZESUOSNiAl89SIet+Cg==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -3106,9 +3121,6 @@ packages: safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - sander@0.5.1: - resolution: {integrity: sha512-3lVqBir7WuKDHGrKRDn/1Ye3kwpXaDOMsiRP1wd6wpZW56gJhsbp5RqQpA6JG/P+pkXizygnr1dKR8vzWaVsfA==} - sax@1.1.4: resolution: {integrity: sha512-5f3k2PbGGp+YtKJjOItpg3P99IMD84E4HOvcfleTb5joCHNXYLsR9yWFPOYGgaeMPDubQILTCMdsFb2OMeOjtg==} @@ -3219,10 +3231,6 @@ packages: sliced@1.0.1: resolution: {integrity: sha512-VZBmZP8WU3sMOZm1bdgTadsQbcscK0UM8oKxKVBs4XAhUo2Xxzm/OFMGBkPusxw9xL3Uy8LrzEqGqJhclsr0yA==} - sorcery@0.11.0: - resolution: {integrity: sha512-J69LQ22xrQB1cIFJhPfgtLuI6BpWRiWu1Y3vSsIwK/eAScqJxd/+CJlUuHQRdX2C9NGFamq+KqNywGgaThwfHw==} - hasBin: true - sortablejs@1.15.2: resolution: {integrity: sha512-FJF5jgdfvoKn1MAKSdGs33bIqLi3LmsgVTliuX6iITj834F+JRQZN90Z93yql8h0K2t0RwDPBmxwlbZfDcxNZA==} @@ -3230,8 +3238,8 @@ packages: resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} engines: {node: '>=0.10.0'} - source-map-js@1.2.0: - resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} source-map@0.6.1: @@ -3335,33 +3343,29 @@ packages: peerDependencies: svelte: ^3.54.0 || ^4.0.0 - svelte-check@3.6.3: - resolution: {integrity: sha512-Q2nGnoysxUnB9KjnjpQLZwdjK62DHyW6nuH/gm2qteFnDk0lCehe/6z8TsIvYeKjC6luKaWxiNGyOcWiLLPSwA==} + svelte-check@4.0.5: + resolution: {integrity: sha512-icBTBZ3ibBaywbXUat3cK6hB5Du+Kq9Z8CRuyLmm64XIe2/r+lQcbuBx/IQgsbrC+kT2jQ0weVpZSSRIPwB6jQ==} + engines: {node: '>= 18.0.0'} hasBin: true peerDependencies: - svelte: ^3.55.0 || ^4.0.0-next.0 || ^4.0.0 || ^5.0.0-next.0 + svelte: ^4.0.0 || ^5.0.0-next.0 + typescript: '>=5.0.0' - svelte-hmr@0.15.3: - resolution: {integrity: sha512-41snaPswvSf8TJUhlkoJBekRrABDXDMdpNpT2tfHIv4JuhgvHqLMhEPGtaQn0BmbNSTkuz2Ed20DF2eHw0SmBQ==} - engines: {node: ^12.20 || ^14.13.1 || >= 16} - peerDependencies: - svelte: ^3.19.0 || ^4.0.0 - - svelte-preprocess@5.1.3: - resolution: {integrity: sha512-xxAkmxGHT+J/GourS5mVJeOXZzne1FR5ljeOUAMXUkfEhkLEllRreXpbl3dIYJlcJRfL1LO1uIAPpBpBfiqGPw==} - engines: {node: '>= 16.0.0', pnpm: ^8.0.0} + svelte-preprocess@6.0.3: + resolution: {integrity: sha512-PLG2k05qHdhmRG7zR/dyo5qKvakhm8IJ+hD2eFRQmMLHp7X3eJnjeupUtvuRpbNiF31RjVw45W+abDwHEmP5OA==} + engines: {node: '>= 18.0.0'} peerDependencies: '@babel/core': ^7.10.2 coffeescript: ^2.5.1 less: ^3.11.3 || ^4.0.0 postcss: ^7 || ^8 - postcss-load-config: ^2.1.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 + postcss-load-config: '>=3' pug: ^3.0.0 sass: ^1.26.8 - stylus: ^0.55.0 + stylus: '>=0.55' sugarss: ^2.0.0 || ^3.0.0 || ^4.0.0 - svelte: ^3.23.0 || ^4.0.0-next.0 || ^4.0.0 || ^5.0.0-next.0 - typescript: '>=3.9.5 || ^4.0.0 || ^5.0.0' + svelte: ^4.0.0 || ^5.0.0-next.100 || ^5.0.0 + typescript: ^5.0.0 peerDependenciesMeta: '@babel/core': optional: true @@ -3384,9 +3388,9 @@ packages: typescript: optional: true - svelte@4.2.8: - resolution: {integrity: sha512-hU6dh1MPl8gh6klQZwK/n73GiAHiR95IkFsesLPbMeEZi36ydaXL/ZAb4g9sayT0MXzpxyZjR28yderJHxcmYA==} - engines: {node: '>=16'} + svelte@5.0.5: + resolution: {integrity: sha512-f4WBlP5g8W6pEoDfx741lewMlemy+LIGpEqjGPWqnHVP92wqlQXl87U5O5Bi2tkSUrO95OxOoqwU8qlqiHmFKA==} + engines: {node: '>=18'} symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} @@ -3528,8 +3532,8 @@ packages: resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} engines: {node: '>= 0.6'} - typescript@5.3.3: - resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==} + typescript@5.6.3: + resolution: {integrity: sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==} engines: {node: '>=14.17'} hasBin: true @@ -3611,8 +3615,8 @@ packages: peerDependencies: vite: ^2 || ^3 || ^4 || ^5 - vite@5.2.12: - resolution: {integrity: sha512-/gC8GxzxMK5ntBwb48pR32GGhENnjtY30G4A0jemunsBkiEZFw60s8InGpN8gkhHEkjnRK1aSAxeQgwvFhUHAA==} + vite@5.4.9: + resolution: {integrity: sha512-20OVpJHh0PAM0oSOELa5GaZNWeDjcAvQjGXy2Uyr+Tp+/D2/Hdz6NLgpJLsarPTA2QJ6v8mX2P1ZfbsSKvdMkg==} engines: {node: ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: @@ -3620,6 +3624,7 @@ packages: less: '*' lightningcss: ^1.21.0 sass: '*' + sass-embedded: '*' stylus: '*' sugarss: '*' terser: ^5.4.0 @@ -3632,6 +3637,8 @@ packages: optional: true sass: optional: true + sass-embedded: + optional: true stylus: optional: true sugarss: @@ -3639,10 +3646,10 @@ packages: terser: optional: true - vitefu@0.2.5: - resolution: {integrity: sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==} + vitefu@1.0.3: + resolution: {integrity: sha512-iKKfOMBHob2WxEJbqbJjHAkmYgvFDPhuqrO82om83S8RLk+17FtyMBfcyeH8GqD0ihShtkMW/zzJgiA51hCNCQ==} peerDependencies: - vite: ^3.0.0 || ^4.0.0 || ^5.0.0 + vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0-beta.0 peerDependenciesMeta: vite: optional: true @@ -3819,16 +3826,19 @@ packages: resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} engines: {node: '>=6'} + zimmerframe@1.1.2: + resolution: {integrity: sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==} + snapshots: '@adobe/css-tools@4.3.2': {} '@alloc/quick-lru@5.2.0': {} - '@ampproject/remapping@2.2.1': + '@ampproject/remapping@2.3.0': dependencies: - '@jridgewell/gen-mapping': 0.3.3 - '@jridgewell/trace-mapping': 0.3.21 + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 '@asamuzakjp/dom-selector@2.0.2': dependencies: @@ -3886,12 +3896,12 @@ snapshots: dependencies: '@capacitor/core': 5.6.0 - '@capacitor/assets@3.0.4(@swc/core@1.5.7)(@types/node@18.19.7)(typescript@5.3.3)': + '@capacitor/assets@3.0.4(@swc/core@1.5.7)(@types/node@18.19.7)(typescript@5.6.3)': dependencies: '@capacitor/cli': 5.6.0 '@ionic/utils-array': 2.1.6 '@ionic/utils-fs': 3.1.7 - '@trapezedev/project': 7.0.10(@swc/core@1.5.7)(@types/node@18.19.7)(typescript@5.3.3) + '@trapezedev/project': 7.0.10(@swc/core@1.5.7)(@types/node@18.19.7)(typescript@5.6.3) commander: 8.3.0 debug: 4.3.4 fs-extra: 10.1.0 @@ -3962,73 +3972,73 @@ snapshots: '@dqbd/tiktoken@1.0.7': {} - '@esbuild/aix-ppc64@0.20.2': + '@esbuild/aix-ppc64@0.21.5': optional: true - '@esbuild/android-arm64@0.20.2': + '@esbuild/android-arm64@0.21.5': optional: true - '@esbuild/android-arm@0.20.2': + '@esbuild/android-arm@0.21.5': optional: true - '@esbuild/android-x64@0.20.2': + '@esbuild/android-x64@0.21.5': optional: true - '@esbuild/darwin-arm64@0.20.2': + '@esbuild/darwin-arm64@0.21.5': optional: true - '@esbuild/darwin-x64@0.20.2': + '@esbuild/darwin-x64@0.21.5': optional: true - '@esbuild/freebsd-arm64@0.20.2': + '@esbuild/freebsd-arm64@0.21.5': optional: true - '@esbuild/freebsd-x64@0.20.2': + '@esbuild/freebsd-x64@0.21.5': optional: true - '@esbuild/linux-arm64@0.20.2': + '@esbuild/linux-arm64@0.21.5': optional: true - '@esbuild/linux-arm@0.20.2': + '@esbuild/linux-arm@0.21.5': optional: true - '@esbuild/linux-ia32@0.20.2': + '@esbuild/linux-ia32@0.21.5': optional: true - '@esbuild/linux-loong64@0.20.2': + '@esbuild/linux-loong64@0.21.5': optional: true - '@esbuild/linux-mips64el@0.20.2': + '@esbuild/linux-mips64el@0.21.5': optional: true - '@esbuild/linux-ppc64@0.20.2': + '@esbuild/linux-ppc64@0.21.5': optional: true - '@esbuild/linux-riscv64@0.20.2': + '@esbuild/linux-riscv64@0.21.5': optional: true - '@esbuild/linux-s390x@0.20.2': + '@esbuild/linux-s390x@0.21.5': optional: true - '@esbuild/linux-x64@0.20.2': + '@esbuild/linux-x64@0.21.5': optional: true - '@esbuild/netbsd-x64@0.20.2': + '@esbuild/netbsd-x64@0.21.5': optional: true - '@esbuild/openbsd-x64@0.20.2': + '@esbuild/openbsd-x64@0.21.5': optional: true - '@esbuild/sunos-x64@0.20.2': + '@esbuild/sunos-x64@0.21.5': optional: true - '@esbuild/win32-arm64@0.20.2': + '@esbuild/win32-arm64@0.21.5': optional: true - '@esbuild/win32-ia32@0.20.2': + '@esbuild/win32-ia32@0.21.5': optional: true - '@esbuild/win32-x64@0.20.2': + '@esbuild/win32-x64@0.21.5': optional: true '@huggingface/jinja@0.2.2': {} @@ -4140,17 +4150,32 @@ snapshots: '@jridgewell/sourcemap-codec': 1.4.15 '@jridgewell/trace-mapping': 0.3.21 + '@jridgewell/gen-mapping@0.3.5': + dependencies: + '@jridgewell/set-array': 1.2.1 + '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/trace-mapping': 0.3.25 + '@jridgewell/resolve-uri@3.1.1': {} '@jridgewell/set-array@1.1.2': {} + '@jridgewell/set-array@1.2.1': {} + '@jridgewell/sourcemap-codec@1.4.15': {} + '@jridgewell/sourcemap-codec@1.5.0': {} + '@jridgewell/trace-mapping@0.3.21': dependencies: '@jridgewell/resolve-uri': 3.1.1 '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/trace-mapping@0.3.25': + dependencies: + '@jridgewell/resolve-uri': 3.1.1 + '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/trace-mapping@0.3.9': dependencies: '@jridgewell/resolve-uri': 3.1.1 @@ -4245,52 +4270,52 @@ snapshots: optionalDependencies: rollup: 3.29.4 - '@rollup/rollup-android-arm-eabi@4.18.0': + '@rollup/rollup-android-arm-eabi@4.24.0': optional: true - '@rollup/rollup-android-arm64@4.18.0': + '@rollup/rollup-android-arm64@4.24.0': optional: true - '@rollup/rollup-darwin-arm64@4.18.0': + '@rollup/rollup-darwin-arm64@4.24.0': optional: true - '@rollup/rollup-darwin-x64@4.18.0': + '@rollup/rollup-darwin-x64@4.24.0': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.18.0': + '@rollup/rollup-linux-arm-gnueabihf@4.24.0': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.18.0': + '@rollup/rollup-linux-arm-musleabihf@4.24.0': optional: true - '@rollup/rollup-linux-arm64-gnu@4.18.0': + '@rollup/rollup-linux-arm64-gnu@4.24.0': optional: true - '@rollup/rollup-linux-arm64-musl@4.18.0': + '@rollup/rollup-linux-arm64-musl@4.24.0': optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.18.0': + '@rollup/rollup-linux-powerpc64le-gnu@4.24.0': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.18.0': + '@rollup/rollup-linux-riscv64-gnu@4.24.0': optional: true - '@rollup/rollup-linux-s390x-gnu@4.18.0': + '@rollup/rollup-linux-s390x-gnu@4.24.0': optional: true - '@rollup/rollup-linux-x64-gnu@4.18.0': + '@rollup/rollup-linux-x64-gnu@4.24.0': optional: true - '@rollup/rollup-linux-x64-musl@4.18.0': + '@rollup/rollup-linux-x64-musl@4.24.0': optional: true - '@rollup/rollup-win32-arm64-msvc@4.18.0': + '@rollup/rollup-win32-arm64-msvc@4.24.0': optional: true - '@rollup/rollup-win32-ia32-msvc@4.18.0': + '@rollup/rollup-win32-ia32-msvc@4.24.0': optional: true - '@rollup/rollup-win32-x64-msvc@4.18.0': + '@rollup/rollup-win32-x64-msvc@4.24.0': optional: true '@smithy/eventstream-codec@2.0.16': @@ -4347,26 +4372,25 @@ snapshots: '@smithy/util-buffer-from': 2.0.0 tslib: 2.6.2 - '@sveltejs/vite-plugin-svelte-inspector@2.0.0(@sveltejs/vite-plugin-svelte@3.0.1(svelte@4.2.8)(vite@5.2.12(@types/node@18.19.7)))(svelte@4.2.8)(vite@5.2.12(@types/node@18.19.7))': + '@sveltejs/vite-plugin-svelte-inspector@3.0.0(@sveltejs/vite-plugin-svelte@4.0.0(svelte@5.0.5)(vite@5.4.9(@types/node@18.19.7)))(svelte@5.0.5)(vite@5.4.9(@types/node@18.19.7))': dependencies: - '@sveltejs/vite-plugin-svelte': 3.0.1(svelte@4.2.8)(vite@5.2.12(@types/node@18.19.7)) - debug: 4.3.4 - svelte: 4.2.8 - vite: 5.2.12(@types/node@18.19.7) + '@sveltejs/vite-plugin-svelte': 4.0.0(svelte@5.0.5)(vite@5.4.9(@types/node@18.19.7)) + debug: 4.3.7 + svelte: 5.0.5 + vite: 5.4.9(@types/node@18.19.7) transitivePeerDependencies: - supports-color - '@sveltejs/vite-plugin-svelte@3.0.1(svelte@4.2.8)(vite@5.2.12(@types/node@18.19.7))': + '@sveltejs/vite-plugin-svelte@4.0.0(svelte@5.0.5)(vite@5.4.9(@types/node@18.19.7))': dependencies: - '@sveltejs/vite-plugin-svelte-inspector': 2.0.0(@sveltejs/vite-plugin-svelte@3.0.1(svelte@4.2.8)(vite@5.2.12(@types/node@18.19.7)))(svelte@4.2.8)(vite@5.2.12(@types/node@18.19.7)) - debug: 4.3.4 + '@sveltejs/vite-plugin-svelte-inspector': 3.0.0(@sveltejs/vite-plugin-svelte@4.0.0(svelte@5.0.5)(vite@5.4.9(@types/node@18.19.7)))(svelte@5.0.5)(vite@5.4.9(@types/node@18.19.7)) + debug: 4.3.7 deepmerge: 4.3.1 kleur: 4.1.5 - magic-string: 0.30.5 - svelte: 4.2.8 - svelte-hmr: 0.15.3(svelte@4.2.8) - vite: 5.2.12(@types/node@18.19.7) - vitefu: 0.2.5(vite@5.2.12(@types/node@18.19.7)) + magic-string: 0.30.12 + svelte: 5.0.5 + vite: 5.4.9(@types/node@18.19.7) + vitefu: 1.0.3(vite@5.4.9(@types/node@18.19.7)) transitivePeerDependencies: - supports-color @@ -4422,13 +4446,13 @@ snapshots: dependencies: '@swc/counter': 0.1.3 - '@tailwindcss/typography@0.5.10(tailwindcss@3.4.1(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.7)(typescript@5.3.3)))': + '@tailwindcss/typography@0.5.10(tailwindcss@3.4.1(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.7)(typescript@5.6.3)))': dependencies: lodash.castarray: 4.4.0 lodash.isplainobject: 4.0.6 lodash.merge: 4.6.2 postcss-selector-parser: 6.0.10 - tailwindcss: 3.4.1(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.7)(typescript@5.3.3)) + tailwindcss: 3.4.1(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.7)(typescript@5.6.3)) '@tauri-apps/api@2.0.0': {} @@ -4509,7 +4533,7 @@ snapshots: '@trapezedev/gradle-parse@7.0.10': {} - '@trapezedev/project@7.0.10(@swc/core@1.5.7)(@types/node@18.19.7)(typescript@5.3.3)': + '@trapezedev/project@7.0.10(@swc/core@1.5.7)(@types/node@18.19.7)(typescript@5.6.3)': dependencies: '@ionic/utils-fs': 3.1.7 '@ionic/utils-subprocess': 2.1.14 @@ -4533,7 +4557,7 @@ snapshots: replace: 1.2.2 tempy: 1.0.1 tmp: 0.2.1 - ts-node: 10.9.2(@swc/core@1.5.7)(@types/node@18.19.7)(typescript@5.3.3) + ts-node: 10.9.2(@swc/core@1.5.7)(@types/node@18.19.7)(typescript@5.6.3) xcode: 3.0.1 xml-js: 1.6.11 xpath: 0.0.32 @@ -4572,6 +4596,8 @@ snapshots: '@types/estree@1.0.5': {} + '@types/estree@1.0.6': {} + '@types/fs-extra@8.1.5': dependencies: '@types/node': 18.19.7 @@ -4607,8 +4633,6 @@ snapshots: '@types/normalize-package-data@2.4.4': {} - '@types/pug@2.0.10': {} - '@types/showdown@2.0.6': {} '@types/slice-ansi@4.0.0': {} @@ -4668,10 +4692,16 @@ snapshots: mime-types: 2.1.35 negotiator: 0.6.3 + acorn-typescript@1.4.13(acorn@8.13.0): + dependencies: + acorn: 8.13.0 + acorn-walk@8.3.2: {} acorn@8.11.3: {} + acorn@8.13.0: {} + add-stream@1.0.0: {} agent-base@6.0.2: @@ -4728,9 +4758,7 @@ snapshots: argparse@2.0.1: {} - aria-query@5.3.0: - dependencies: - dequal: 2.0.3 + aria-query@5.3.2: {} array-flatten@1.1.1: {} @@ -4758,9 +4786,7 @@ snapshots: postcss: 8.4.33 postcss-value-parser: 4.2.0 - axobject-query@3.2.1: - dependencies: - dequal: 2.0.3 + axobject-query@4.1.0: {} b4a@1.6.4: {} @@ -4874,8 +4900,6 @@ snapshots: get-intrinsic: 1.2.2 set-function-length: 1.2.0 - callsites@3.1.0: {} - camelcase-css@2.0.1: {} camelcase-keys@6.2.2: @@ -4936,6 +4960,10 @@ snapshots: optionalDependencies: fsevents: 2.3.3 + chokidar@4.0.1: + dependencies: + readdirp: 4.0.2 + chownr@1.1.4: {} chownr@2.0.0: {} @@ -4960,14 +4988,6 @@ snapshots: strip-ansi: 6.0.1 wrap-ansi: 7.0.0 - code-red@1.0.4: - dependencies: - '@jridgewell/sourcemap-codec': 1.4.15 - '@types/estree': 1.0.5 - acorn: 8.11.3 - estree-walker: 3.0.3 - periscopic: 3.1.0 - codemirror@5.65.16: {} color-convert@1.9.3: @@ -5209,6 +5229,10 @@ snapshots: dependencies: ms: 2.1.2 + debug@4.3.7: + dependencies: + ms: 2.1.3 + decamelize-keys@1.1.1: dependencies: decamelize: 1.2.0 @@ -5261,12 +5285,8 @@ snapshots: depd@2.0.0: {} - dequal@2.0.3: {} - destroy@1.2.0: {} - detect-indent@6.1.0: {} - detect-libc@2.0.2: {} didyoumean@1.2.2: {} @@ -5353,33 +5373,31 @@ snapshots: dependencies: is-arrayish: 0.2.1 - es6-promise@3.3.1: {} - - esbuild@0.20.2: + esbuild@0.21.5: optionalDependencies: - '@esbuild/aix-ppc64': 0.20.2 - '@esbuild/android-arm': 0.20.2 - '@esbuild/android-arm64': 0.20.2 - '@esbuild/android-x64': 0.20.2 - '@esbuild/darwin-arm64': 0.20.2 - '@esbuild/darwin-x64': 0.20.2 - '@esbuild/freebsd-arm64': 0.20.2 - '@esbuild/freebsd-x64': 0.20.2 - '@esbuild/linux-arm': 0.20.2 - '@esbuild/linux-arm64': 0.20.2 - '@esbuild/linux-ia32': 0.20.2 - '@esbuild/linux-loong64': 0.20.2 - '@esbuild/linux-mips64el': 0.20.2 - '@esbuild/linux-ppc64': 0.20.2 - '@esbuild/linux-riscv64': 0.20.2 - '@esbuild/linux-s390x': 0.20.2 - '@esbuild/linux-x64': 0.20.2 - '@esbuild/netbsd-x64': 0.20.2 - '@esbuild/openbsd-x64': 0.20.2 - '@esbuild/sunos-x64': 0.20.2 - '@esbuild/win32-arm64': 0.20.2 - '@esbuild/win32-ia32': 0.20.2 - '@esbuild/win32-x64': 0.20.2 + '@esbuild/aix-ppc64': 0.21.5 + '@esbuild/android-arm': 0.21.5 + '@esbuild/android-arm64': 0.21.5 + '@esbuild/android-x64': 0.21.5 + '@esbuild/darwin-arm64': 0.21.5 + '@esbuild/darwin-x64': 0.21.5 + '@esbuild/freebsd-arm64': 0.21.5 + '@esbuild/freebsd-x64': 0.21.5 + '@esbuild/linux-arm': 0.21.5 + '@esbuild/linux-arm64': 0.21.5 + '@esbuild/linux-ia32': 0.21.5 + '@esbuild/linux-loong64': 0.21.5 + '@esbuild/linux-mips64el': 0.21.5 + '@esbuild/linux-ppc64': 0.21.5 + '@esbuild/linux-riscv64': 0.21.5 + '@esbuild/linux-s390x': 0.21.5 + '@esbuild/linux-x64': 0.21.5 + '@esbuild/netbsd-x64': 0.21.5 + '@esbuild/openbsd-x64': 0.21.5 + '@esbuild/sunos-x64': 0.21.5 + '@esbuild/win32-arm64': 0.21.5 + '@esbuild/win32-ia32': 0.21.5 + '@esbuild/win32-x64': 0.21.5 escalade@3.1.1: {} @@ -5387,8 +5405,11 @@ snapshots: escape-string-regexp@1.0.5: {} - estree-walker@3.0.3: + esm-env@1.0.0: {} + + esrap@1.2.2: dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 '@types/estree': 1.0.5 etag@1.8.1: {} @@ -5467,6 +5488,8 @@ snapshots: dependencies: pend: 1.2.0 + fdir@6.4.2: {} + fflate@0.6.10: {} fflate@0.8.1: {} @@ -5749,11 +5772,6 @@ snapshots: immediate@3.0.6: {} - import-fresh@3.3.0: - dependencies: - parent-module: 1.0.1 - resolve-from: 4.0.0 - indent-string@4.0.0: {} inflight@1.0.6: @@ -5984,13 +6002,13 @@ snapshots: dependencies: yallist: 4.0.0 - lucide-svelte@0.292.0(svelte@4.2.8): + lucide-svelte@0.292.0(svelte@5.0.5): dependencies: - svelte: 4.2.8 + svelte: 5.0.5 - magic-string@0.30.5: + magic-string@0.30.12: dependencies: - '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/sourcemap-codec': 1.5.0 make-dir@3.1.0: dependencies: @@ -6111,10 +6129,6 @@ snapshots: mkdirp-classic@0.5.3: {} - mkdirp@0.5.6: - dependencies: - minimist: 1.2.8 - mkdirp@1.0.4: {} ml-array-mean@1.1.6: @@ -6376,10 +6390,6 @@ snapshots: pako@1.0.11: {} - parent-module@1.0.1: - dependencies: - callsites: 3.1.0 - parse-json@4.0.0: dependencies: error-ex: 1.3.2 @@ -6444,16 +6454,12 @@ snapshots: pend@1.2.0: {} - periscopic@3.1.0: - dependencies: - '@types/estree': 1.0.5 - estree-walker: 3.0.3 - is-reference: 3.0.2 - picocolors@1.0.0: {} picocolors@1.0.1: {} + picocolors@1.1.1: {} + picomatch@2.3.1: {} pify@2.3.0: {} @@ -6493,13 +6499,13 @@ snapshots: camelcase-css: 2.0.1 postcss: 8.4.33 - postcss-load-config@4.0.2(postcss@8.4.33)(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.7)(typescript@5.3.3)): + postcss-load-config@4.0.2(postcss@8.4.33)(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.7)(typescript@5.6.3)): dependencies: lilconfig: 3.0.0 yaml: 2.4.2 optionalDependencies: postcss: 8.4.33 - ts-node: 10.9.2(@swc/core@1.5.7)(@types/node@18.19.7)(typescript@5.3.3) + ts-node: 10.9.2(@swc/core@1.5.7)(@types/node@18.19.7)(typescript@5.6.3) postcss-nested@6.0.1(postcss@8.4.33): dependencies: @@ -6524,11 +6530,11 @@ snapshots: picocolors: 1.0.0 source-map-js: 1.0.2 - postcss@8.4.38: + postcss@8.4.47: dependencies: nanoid: 3.3.7 - picocolors: 1.0.1 - source-map-js: 1.2.0 + picocolors: 1.1.1 + source-map-js: 1.2.1 prebuild-install@7.1.1: dependencies: @@ -6673,6 +6679,8 @@ snapshots: dependencies: picomatch: 2.3.1 + readdirp@4.0.2: {} + redent@3.0.0: dependencies: indent-string: 4.0.0 @@ -6694,8 +6702,6 @@ snapshots: requires-port@1.0.0: {} - resolve-from@4.0.0: {} - resolve@1.22.8: dependencies: is-core-module: 2.13.1 @@ -6704,10 +6710,6 @@ snapshots: reusify@1.0.4: {} - rimraf@2.7.1: - dependencies: - glob: 7.2.3 - rimraf@3.0.2: dependencies: glob: 7.2.3 @@ -6720,26 +6722,26 @@ snapshots: optionalDependencies: fsevents: 2.3.3 - rollup@4.18.0: + rollup@4.24.0: dependencies: - '@types/estree': 1.0.5 + '@types/estree': 1.0.6 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.18.0 - '@rollup/rollup-android-arm64': 4.18.0 - '@rollup/rollup-darwin-arm64': 4.18.0 - '@rollup/rollup-darwin-x64': 4.18.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.18.0 - '@rollup/rollup-linux-arm-musleabihf': 4.18.0 - '@rollup/rollup-linux-arm64-gnu': 4.18.0 - '@rollup/rollup-linux-arm64-musl': 4.18.0 - '@rollup/rollup-linux-powerpc64le-gnu': 4.18.0 - '@rollup/rollup-linux-riscv64-gnu': 4.18.0 - '@rollup/rollup-linux-s390x-gnu': 4.18.0 - '@rollup/rollup-linux-x64-gnu': 4.18.0 - '@rollup/rollup-linux-x64-musl': 4.18.0 - '@rollup/rollup-win32-arm64-msvc': 4.18.0 - '@rollup/rollup-win32-ia32-msvc': 4.18.0 - '@rollup/rollup-win32-x64-msvc': 4.18.0 + '@rollup/rollup-android-arm-eabi': 4.24.0 + '@rollup/rollup-android-arm64': 4.24.0 + '@rollup/rollup-darwin-arm64': 4.24.0 + '@rollup/rollup-darwin-x64': 4.24.0 + '@rollup/rollup-linux-arm-gnueabihf': 4.24.0 + '@rollup/rollup-linux-arm-musleabihf': 4.24.0 + '@rollup/rollup-linux-arm64-gnu': 4.24.0 + '@rollup/rollup-linux-arm64-musl': 4.24.0 + '@rollup/rollup-linux-powerpc64le-gnu': 4.24.0 + '@rollup/rollup-linux-riscv64-gnu': 4.24.0 + '@rollup/rollup-linux-s390x-gnu': 4.24.0 + '@rollup/rollup-linux-x64-gnu': 4.24.0 + '@rollup/rollup-linux-x64-musl': 4.24.0 + '@rollup/rollup-win32-arm64-msvc': 4.24.0 + '@rollup/rollup-win32-ia32-msvc': 4.24.0 + '@rollup/rollup-win32-x64-msvc': 4.24.0 fsevents: 2.3.3 rrweb-cssom@0.6.0: {} @@ -6758,13 +6760,6 @@ snapshots: safer-buffer@2.1.2: {} - sander@0.5.1: - dependencies: - es6-promise: 3.3.1 - graceful-fs: 4.2.11 - mkdirp: 0.5.6 - rimraf: 2.7.1 - sax@1.1.4: {} sax@1.3.0: {} @@ -6898,18 +6893,11 @@ snapshots: sliced@1.0.1: {} - sorcery@0.11.0: - dependencies: - '@jridgewell/sourcemap-codec': 1.4.15 - buffer-crc32: 0.2.13 - minimist: 1.2.8 - sander: 0.5.1 - sortablejs@1.15.2: {} source-map-js@1.0.2: {} - source-map-js@1.2.0: {} + source-map-js@1.2.1: {} source-map@0.6.1: {} @@ -7002,74 +6990,55 @@ snapshots: supports-preserve-symlinks-flag@1.0.0: {} - svelte-awesome-color-picker@3.1.0(svelte@4.2.8): + svelte-awesome-color-picker@3.1.0(svelte@5.0.5): dependencies: colord: 2.9.3 - svelte: 4.2.8 - svelte-awesome-slider: 1.1.0(svelte@4.2.8) + svelte: 5.0.5 + svelte-awesome-slider: 1.1.0(svelte@5.0.5) - svelte-awesome-slider@1.1.0(svelte@4.2.8): + svelte-awesome-slider@1.1.0(svelte@5.0.5): dependencies: - svelte: 4.2.8 + svelte: 5.0.5 - svelte-check@3.6.3(postcss-load-config@4.0.2(postcss@8.4.33)(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.7)(typescript@5.3.3)))(postcss@8.4.33)(svelte@4.2.8): + svelte-check@4.0.5(svelte@5.0.5)(typescript@5.6.3): dependencies: - '@jridgewell/trace-mapping': 0.3.21 - chokidar: 3.5.3 - fast-glob: 3.3.2 - import-fresh: 3.3.0 - picocolors: 1.0.0 + '@jridgewell/trace-mapping': 0.3.25 + chokidar: 4.0.1 + fdir: 6.4.2 + picocolors: 1.0.1 sade: 1.8.1 - svelte: 4.2.8 - svelte-preprocess: 5.1.3(postcss-load-config@4.0.2(postcss@8.4.33)(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.7)(typescript@5.3.3)))(postcss@8.4.33)(svelte@4.2.8)(typescript@5.3.3) - typescript: 5.3.3 + svelte: 5.0.5 + typescript: 5.6.3 transitivePeerDependencies: - - '@babel/core' - - coffeescript - - less - - postcss - - postcss-load-config - - pug - - sass - - stylus - - sugarss + - picomatch - svelte-hmr@0.15.3(svelte@4.2.8): + svelte-preprocess@6.0.3(postcss-load-config@4.0.2(postcss@8.4.33)(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.7)(typescript@5.6.3)))(postcss@8.4.33)(svelte@5.0.5)(typescript@5.6.3): dependencies: - svelte: 4.2.8 - - svelte-preprocess@5.1.3(postcss-load-config@4.0.2(postcss@8.4.33)(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.7)(typescript@5.3.3)))(postcss@8.4.33)(svelte@4.2.8)(typescript@5.3.3): - dependencies: - '@types/pug': 2.0.10 - detect-indent: 6.1.0 - magic-string: 0.30.5 - sorcery: 0.11.0 - strip-indent: 3.0.0 - svelte: 4.2.8 + svelte: 5.0.5 optionalDependencies: postcss: 8.4.33 - postcss-load-config: 4.0.2(postcss@8.4.33)(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.7)(typescript@5.3.3)) - typescript: 5.3.3 + postcss-load-config: 4.0.2(postcss@8.4.33)(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.7)(typescript@5.6.3)) + typescript: 5.6.3 - svelte@4.2.8: + svelte@5.0.5: dependencies: - '@ampproject/remapping': 2.2.1 - '@jridgewell/sourcemap-codec': 1.4.15 - '@jridgewell/trace-mapping': 0.3.21 - acorn: 8.11.3 - aria-query: 5.3.0 - axobject-query: 3.2.1 - code-red: 1.0.4 - css-tree: 2.3.1 - estree-walker: 3.0.3 + '@ampproject/remapping': 2.3.0 + '@jridgewell/sourcemap-codec': 1.5.0 + '@types/estree': 1.0.5 + acorn: 8.13.0 + acorn-typescript: 1.4.13(acorn@8.13.0) + aria-query: 5.3.2 + axobject-query: 4.1.0 + esm-env: 1.0.0 + esrap: 1.2.2 is-reference: 3.0.2 locate-character: 3.0.0 - magic-string: 0.30.5 - periscopic: 3.1.0 + magic-string: 0.30.12 + zimmerframe: 1.1.2 symbol-tree@3.2.4: {} - tailwindcss@3.4.1(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.7)(typescript@5.3.3)): + tailwindcss@3.4.1(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.7)(typescript@5.6.3)): dependencies: '@alloc/quick-lru': 5.2.0 arg: 5.0.2 @@ -7088,7 +7057,7 @@ snapshots: postcss: 8.4.33 postcss-import: 15.1.0(postcss@8.4.33) postcss-js: 4.0.1(postcss@8.4.33) - postcss-load-config: 4.0.2(postcss@8.4.33)(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.7)(typescript@5.3.3)) + postcss-load-config: 4.0.2(postcss@8.4.33)(ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.7)(typescript@5.6.3)) postcss-nested: 6.0.1(postcss@8.4.33) postcss-selector-parser: 6.0.15 resolve: 1.22.8 @@ -7202,7 +7171,7 @@ snapshots: ts-interface-checker@0.1.13: {} - ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.7)(typescript@5.3.3): + ts-node@10.9.2(@swc/core@1.5.7)(@types/node@18.19.7)(typescript@5.6.3): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.9 @@ -7216,7 +7185,7 @@ snapshots: create-require: 1.1.1 diff: 4.0.2 make-error: 1.3.6 - typescript: 5.3.3 + typescript: 5.6.3 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 optionalDependencies: @@ -7243,7 +7212,7 @@ snapshots: media-typer: 0.3.0 mime-types: 2.1.35 - typescript@5.3.3: {} + typescript@5.6.3: {} uc.micro@2.1.0: {} @@ -7294,32 +7263,32 @@ snapshots: vary@1.1.2: {} - vite-plugin-top-level-await@1.4.1(rollup@3.29.4)(vite@5.2.12(@types/node@18.19.7)): + vite-plugin-top-level-await@1.4.1(rollup@3.29.4)(vite@5.4.9(@types/node@18.19.7)): dependencies: '@rollup/plugin-virtual': 3.0.2(rollup@3.29.4) '@swc/core': 1.5.7 uuid: 9.0.1 - vite: 5.2.12(@types/node@18.19.7) + vite: 5.4.9(@types/node@18.19.7) transitivePeerDependencies: - '@swc/helpers' - rollup - vite-plugin-wasm@3.3.0(vite@5.2.12(@types/node@18.19.7)): + vite-plugin-wasm@3.3.0(vite@5.4.9(@types/node@18.19.7)): dependencies: - vite: 5.2.12(@types/node@18.19.7) + vite: 5.4.9(@types/node@18.19.7) - vite@5.2.12(@types/node@18.19.7): + vite@5.4.9(@types/node@18.19.7): dependencies: - esbuild: 0.20.2 - postcss: 8.4.38 - rollup: 4.18.0 + esbuild: 0.21.5 + postcss: 8.4.47 + rollup: 4.24.0 optionalDependencies: '@types/node': 18.19.7 fsevents: 2.3.3 - vitefu@0.2.5(vite@5.2.12(@types/node@18.19.7)): + vitefu@1.0.3(vite@5.4.9(@types/node@18.19.7)): optionalDependencies: - vite: 5.2.12(@types/node@18.19.7) + vite: 5.4.9(@types/node@18.19.7) w3c-xmlserializer@5.0.0: dependencies: @@ -7479,3 +7448,5 @@ snapshots: fd-slicer: 1.1.0 yn@3.1.1: {} + + zimmerframe@1.1.2: {} diff --git a/src/App.svelte b/src/App.svelte index 1820e12d..c4adcb0b 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -21,8 +21,8 @@ import CustomGUISettingMenu from './lib/Setting/Pages/CustomGUISettingMenu.svelte'; - let didFirstSetup: boolean = false - let gridOpen = false + let didFirstSetup: boolean = $state(false) + let gridOpen = $state(false) DataBase.subscribe(db => { if(db.didFirstSetup !== didFirstSetup){ @@ -61,7 +61,7 @@ {gridOpen = true}} hidden={!$sideBarStore} /> {:else}
- + {gridOpen = true}} hidden={false} /> diff --git a/src/LiteMain.svelte b/src/LiteMain.svelte index af8c0c9b..bb651596 100644 --- a/src/LiteMain.svelte +++ b/src/LiteMain.svelte @@ -32,7 +32,7 @@ 로딩중... {:then cards} {#each cards as card} - { + { await downloadRisuHub(card.id, { forceRedirect: true }) diff --git a/src/lib/ChatScreens/AdvancedChatEditor.svelte b/src/lib/ChatScreens/AdvancedChatEditor.svelte index 59342673..6f5ef3e6 100644 --- a/src/lib/ChatScreens/AdvancedChatEditor.svelte +++ b/src/lib/ChatScreens/AdvancedChatEditor.svelte @@ -1,18 +1,21 @@ {#if currentCharacter.type ==='character'} - {#if currentCharacter.additionalAssets} {#each currentCharacter.additionalAssets as additionalAsset, i} - @@ -13,6 +13,10 @@ import { language } from "src/lang"; import MultiLangDisplay from "../UI/GUI/MultiLangDisplay.svelte"; - export let onRemove: () => void - export let quote:string + interface Props { + onRemove: () => void; + quote: string; + } + + let { onRemove, quote }: Props = $props(); \ No newline at end of file diff --git a/src/lib/ChatScreens/DefaultChatScreen.svelte b/src/lib/ChatScreens/DefaultChatScreen.svelte index 5adac938..45c957c5 100644 --- a/src/lib/ChatScreens/DefaultChatScreen.svelte +++ b/src/lib/ChatScreens/DefaultChatScreen.svelte @@ -1,8 +1,10 @@ - - -
{ + + +
{ openMenu = false }}> {#if $selectedCharID < 0} @@ -404,16 +412,16 @@ {/if} {:else} -
{ +
{ //@ts-ignore const scrolled = (e.target.scrollHeight - e.target.clientHeight + e.target.scrollTop) - if(scrolled < 100 && $CurrentChat.message.length > loadPages){ + if(scrolled < 100 && $DataBase.characters[$selectedCharID].chats[$DataBase.characters[$selectedCharID].chatPage].message.length > loadPages){ loadPages += 15 } }}>
{#if $DataBase.useChatSticker && currentCharacter.type !== 'group'} -
{toggleStickers = !toggleStickers}} +
{toggleStickers = !toggleStickers}} class={"ml-4 bg-textcolor2 flex justify-center items-center w-12 h-12 rounded-md hover:bg-green-500 transition-colors "+(toggleStickers ? 'text-green-500':'text-textcolor')}>
@@ -423,7 +431,7 @@ {:else} { updateInputTransateMessage(e.detail.translate);}} /> {/if} {#if $doingChat || doingChatInputTranslate} {:else} {/if} - {#if $CurrentCharacter?.chaId !== '§playground'} + {#if $DataBase.characters[$selectedCharID]?.chaId !== '§playground'} {:else} -
{ - $CurrentChat.message.push({ +
{ + $DataBase.characters[$selectedCharID].chats[$DataBase.characters[$selectedCharID].chatPage].message.push({ role: 'char', data: '' }) - $CurrentChat = $CurrentChat + $DataBase.characters[$selectedCharID].chats[$DataBase.characters[$selectedCharID].chatPage] = $DataBase.characters[$selectedCharID].chats[$DataBase.characters[$selectedCharID].chatPage] }} class="peer-focus:border-textcolor mr-2 flex border-y border-r border-darkborderc justify-center items-center text-gray-100 p-3 rounded-r-md hover:bg-blue-500 transition-colors" style:height={inputHeight} @@ -489,7 +496,7 @@
{/if}
- {#if $DataBase.useAutoTranslateInput && !$DataBase.useAdvancedEditor && $CurrentCharacter?.chaId !== '§playground'} + {#if $DataBase.useAutoTranslateInput && !$DataBase.useAdvancedEditor && $DataBase.characters[$selectedCharID]?.chaId !== '§playground'}
{/if} @@ -552,20 +559,20 @@ )} {send}/> {/if} - {#each messageForm($CurrentChat.message, loadPages) as chat, i} + {#each messageForm($DataBase.characters[$selectedCharID].chats[$DataBase.characters[$selectedCharID].chatPage].message, loadPages) as chat, i} {#if chat.role === 'char'} - {#if $CurrentCharacter.type !== 'group'} + {#if $DataBase.characters[$selectedCharID].type !== 'group'} {:else} @@ -577,7 +584,7 @@ onReroll={reroll} unReroll={unReroll} img={getCharImage(findCharacterbyId(chat.saying).image, 'css')} - isLastMemory={$CurrentChat.lastMemory === (chat.chatId ?? 'none') && $CurrentShowMemoryLimit} + isLastMemory={$DataBase.characters[$selectedCharID].chats[$DataBase.characters[$selectedCharID].chatPage].lastMemory === (chat.chatId ?? 'none') && $CurrentShowMemoryLimit} character={chat.saying} largePortrait={findCharacterbyId(chat.saying).largePortrait} messageGenerationInfo={chat.generationInfo} @@ -585,32 +592,32 @@ {/if} {:else} {/if} {/each} - {#if $CurrentChat.message.length <= loadPages} - {#if $CurrentCharacter.type !== 'group' } + {#if $DataBase.characters[$selectedCharID].chats[$DataBase.characters[$selectedCharID].chatPage].message.length <= loadPages} + {#if $DataBase.characters[$selectedCharID].type !== 'group' } 0} - largePortrait={$CurrentCharacter.largePortrait} + altGreeting={$DataBase.characters[$selectedCharID].alternateGreetings.length > 0} + largePortrait={$DataBase.characters[$selectedCharID].largePortrait} firstMessage={true} onReroll={() => { - const cha = $CurrentCharacter - const chat = $CurrentChat + const cha = $DataBase.characters[$selectedCharID] + const chat = $DataBase.characters[$selectedCharID].chats[$DataBase.characters[$selectedCharID].chatPage] if(cha.type !== 'group'){ if (chat.fmIndex >= (cha.alternateGreetings.length - 1)){ chat.fmIndex = -1 @@ -619,11 +626,11 @@ chat.fmIndex += 1 } } - $CurrentChat = chat + $DataBase.characters[$selectedCharID].chats[$DataBase.characters[$selectedCharID].chatPage] = chat }} unReroll={() => { - const cha = $CurrentCharacter - const chat = $CurrentChat + const cha = $DataBase.characters[$selectedCharID] + const chat = $DataBase.characters[$selectedCharID].chats[$DataBase.characters[$selectedCharID].chatPage] if(cha.type !== 'group'){ if (chat.fmIndex === -1){ chat.fmIndex = (cha.alternateGreetings.length - 1) @@ -632,38 +639,38 @@ chat.fmIndex -= 1 } } - $CurrentChat = chat + $DataBase.characters[$selectedCharID].chats[$DataBase.characters[$selectedCharID].chatPage] = chat }} isLastMemory={false} /> - {#if !$CurrentCharacter.removedQuotes && $CurrentCharacter.creatorNotes.length >= 2} - { - const cha = $CurrentCharacter + {#if !$DataBase.characters[$selectedCharID].removedQuotes && $DataBase.characters[$selectedCharID].creatorNotes.length >= 2} + { + const cha = $DataBase.characters[$selectedCharID] if(cha.type !== 'group'){ cha.removedQuotes = true } - $CurrentCharacter = cha + $DataBase.characters[$selectedCharID] = cha }} /> {/if} {/if} {/if} {#if openMenu} -
{ +
{ e.stopPropagation() }}> - {#if $CurrentCharacter.type === 'group'} -
+ {#if $DataBase.characters[$selectedCharID].type === 'group'} +
{language.autoMode}
{/if} - - {#if $CurrentCharacter.ttsMode === 'webspeech' || $CurrentCharacter.ttsMode === 'elevenlab'} -
{ + + {#if $DataBase.characters[$selectedCharID].ttsMode === 'webspeech' || $DataBase.characters[$selectedCharID].ttsMode === 'elevenlab'} +
{ stopTTS() }}> @@ -672,9 +679,9 @@ {/if}
{ - if(($CurrentChat.message.length < 2) || ($CurrentChat.message[$CurrentChat.message.length - 1].role !== 'char')){ + class:text-textcolor2={($DataBase.characters[$selectedCharID].chats[$DataBase.characters[$selectedCharID].chatPage].message.length < 2) || ($DataBase.characters[$selectedCharID].chats[$DataBase.characters[$selectedCharID].chatPage].message[$DataBase.characters[$selectedCharID].chats[$DataBase.characters[$selectedCharID].chatPage].message.length - 1].role !== 'char')} + onclick={() => { + if(($DataBase.characters[$selectedCharID].chats[$DataBase.characters[$selectedCharID].chatPage].message.length < 2) || ($DataBase.characters[$selectedCharID].chats[$DataBase.characters[$selectedCharID].chatPage].message[$DataBase.characters[$selectedCharID].chats[$DataBase.characters[$selectedCharID].chatPage].message.length - 1].role !== 'char')){ return } sendContinue(); @@ -686,7 +693,7 @@ {#if $DataBase.showMenuChatList} -
{ +
{ openChatList = true openMenu = false }}> @@ -696,7 +703,7 @@ {/if} {#if $DataBase.translator !== ''} -
{ +
{ $DataBase.useAutoTranslateInput = !$DataBase.useAutoTranslateInput }}> @@ -705,14 +712,14 @@ {/if} -
{ +
{ screenShot() }}> {language.screenshot}
-
{ +
{ const res = await postChatFile(messageInput) if(res?.type === 'image'){ fileInput.push(res.data) @@ -728,7 +735,7 @@
-
{ +
{ $DataBase.useAutoSuggestions = !$DataBase.useAutoSuggestions }}> @@ -736,8 +743,8 @@
-
{ - $CurrentChat.modules ??= [] +
{ + $DataBase.characters[$selectedCharID].chats[$DataBase.characters[$selectedCharID].chatPage].modules ??= [] openModuleList = true openMenu = false }}> @@ -746,7 +753,7 @@
{#if $DataBase.sideMenuRerollButton} -
+
{language.reroll}
diff --git a/src/lib/ChatScreens/EmotionBox.svelte b/src/lib/ChatScreens/EmotionBox.svelte index f12df537..5297b83d 100644 --- a/src/lib/ChatScreens/EmotionBox.svelte +++ b/src/lib/ChatScreens/EmotionBox.svelte @@ -6,7 +6,7 @@ {#await getEmotion($DataBase,$CharEmotion, 'contain') then images} {#each images as image, i} -
+
{/each} {/await} diff --git a/src/lib/ChatScreens/ResizeBox.svelte b/src/lib/ChatScreens/ResizeBox.svelte index 495e7830..ea11aa3a 100644 --- a/src/lib/ChatScreens/ResizeBox.svelte +++ b/src/lib/ChatScreens/ResizeBox.svelte @@ -6,7 +6,7 @@ import { getEmotion } from '../../ts/util'; import { DataBase } from '../../ts/storage/database'; - let box; + let box = $state(); let isResizing = false; let initialWidth; let initialHeight; @@ -88,9 +88,9 @@
\ No newline at end of file diff --git a/src/lib/ChatScreens/Suggestion.svelte b/src/lib/ChatScreens/Suggestion.svelte index 5ff24a6c..0c362a4d 100644 --- a/src/lib/ChatScreens/Suggestion.svelte +++ b/src/lib/ChatScreens/Suggestion.svelte @@ -1,8 +1,10 @@
{#if progress}
-
+
{language.creatingSuggestions}
{:else if !$doingChat} {#if $DataBase.translator !== ''}
{#each suggestMessages??[] as suggest, i}
- - +
{tag}
{/if} {/each}
diff --git a/src/lib/Mobile/MobileBody.svelte b/src/lib/Mobile/MobileBody.svelte index 47224884..73124580 100644 --- a/src/lib/Mobile/MobileBody.svelte +++ b/src/lib/Mobile/MobileBody.svelte @@ -1,5 +1,5 @@ {#if $MobileSideBar > 0 && !$isLite}
- - -
- - - - - - - - {language.menu} {:else if $selectedCharID !== -1} - - {$CurrentCharacter.name} + {$DataBase.characters[$selectedCharID].name}
-
{:else if $MobileGUIStack === 2 && $SettingsMenuIndex > -1} - -
{:else if $alertStore.type === 'tos'}
- -
{:else if $alertStore.type === 'select'} {#each $alertStore.msg.split('||') as n, i} - {/each} {:else if $alertStore.type === 'error' || $alertStore.type === 'normal' || $alertStore.type === 'markdown'} - {:else if $alertStore.type === 'input'} -
{:else if $alertStore.type === 'requestdata'}
- - - - - -
{#if generationInfoMenuIndex === 0}
- {#each $CurrentChat.hypaV2Data.chunks as chunk} + {#each $DataBase.characters[$selectedCharID].chats[$DataBase.characters[$selectedCharID].chatPage].hypaV2Data.chunks as chunk} {/each} -
{:else} - {#each $CurrentChat.hypaV2Data.chunks as chunk, i} + {#each $DataBase.characters[$selectedCharID].chats[$DataBase.characters[$selectedCharID].chatPage].hypaV2Data.chunks as chunk, i}
{#if i === 0} Active @@ -305,12 +315,12 @@ {:else if $alertStore.type === 'addchar'}
- -
-
-
-
-
{#if $DataBase.useExperimental} -
{/if} -
{:else if $alertStore.type === 'cardexport'} -
-
+
+

{language.shareExport} - - + + {:else if $alertStore.submsg === 'module'} - - + + {:else} - - + - + {/if}

{#if $alertStore.submsg === '' && cardExportType === ''} @@ -493,7 +503,7 @@ CHARX {/if} -
- {#each $CurrentCharacter.chats as chat, i} - {/each}
- - - {/if} - - - {#if !isTauri} - Catalog @@ -61,13 +65,13 @@
- - -
@@ -100,12 +104,12 @@

{char.name || "Unnamed"}

{parseMultilangString(char.desc)['en'] || parseMultilangString(char.desc)['xx'] || 'No description'}
- - - - - - - - - - - @@ -61,11 +61,11 @@
{/each} - - - - - - - - - -
diff --git a/src/lib/Playground/PlaygroundParser.svelte b/src/lib/Playground/PlaygroundParser.svelte index 903b43a3..e022041b 100644 --- a/src/lib/Playground/PlaygroundParser.svelte +++ b/src/lib/Playground/PlaygroundParser.svelte @@ -1,8 +1,8 @@

{language.advancedSettings}

@@ -146,7 +146,7 @@
{/if} {#if Capacitor.isNativePlatform()} - - -
- - + {/if} {/if} @@ -623,9 +631,9 @@ {#if $DataBase.promptTemplate && submenu === -1}
- +
{/if} {#if submenu === -1} - + {/if} \ No newline at end of file diff --git a/src/lib/Setting/Pages/Communities.svelte b/src/lib/Setting/Pages/Communities.svelte index d65eb291..e97a5e42 100644 --- a/src/lib/Setting/Pages/Communities.svelte +++ b/src/lib/Setting/Pages/Communities.svelte @@ -5,17 +5,17 @@

{language.community}

\ No newline at end of file diff --git a/src/lib/Setting/Pages/CustomGUISettingMenu.svelte b/src/lib/Setting/Pages/CustomGUISettingMenu.svelte index 8d7dd5fd..c7548110 100644 --- a/src/lib/Setting/Pages/CustomGUISettingMenu.svelte +++ b/src/lib/Setting/Pages/CustomGUISettingMenu.svelte @@ -1,5 +1,4 @@ + +
{ + role="option" + tabindex="0" + onclick={() => { selectedContatiner = 'root' renderMainTree(tree) }} - on:contextmenu|preventDefault + oncontextmenu={(e) => { + e.preventDefault() + oncontextmenu?.(e) + }} bind:this={mainTree} > @@ -221,13 +237,13 @@ {#if menuOpen}
- - -
@@ -236,14 +252,14 @@
{#if subMenu === 0} {#each builtComponentTrees as component, i} - {/each} {:else if subMenu === 1} {#each builtContainerTrees as container, i} - @@ -254,7 +270,7 @@ {/if}
{:else} - {/if} \ No newline at end of file diff --git a/src/lib/Setting/Pages/DisplaySettings.svelte b/src/lib/Setting/Pages/DisplaySettings.svelte index ae9120c5..c0df2851 100644 --- a/src/lib/Setting/Pages/DisplaySettings.svelte +++ b/src/lib/Setting/Pages/DisplaySettings.svelte @@ -22,24 +22,24 @@ changeColorScheme((e.target as HTMLInputElement).value) } - let submenu = $DataBase.useLegacyGUI ? -1 : 0 + let submenu = $state($DataBase.useLegacyGUI ? -1 : 0)

{language.display}

{#if submenu !== -1}
- - - {/if} @@ -74,7 +74,7 @@ {/if} {language.colorScheme} - + {#each colorSchemeList as scheme} {scheme} {/each} @@ -83,53 +83,53 @@ {#if $DataBase.colorSchemeName === "custom"}
- + Light Dark
- + Background
- + Dark Background
- + Color 1
- + Color 2
- + Color 3
- + Color 4
- + Color 5
- + Text Color
- + Text Color 2
- - - - -
- - + {:else if mode === 1}

{language.createModule}

- @@ -117,7 +117,7 @@

{language.editModule}

{#if tempModule.name !== ''} - diff --git a/src/lib/Setting/Pages/OobaSettings.svelte b/src/lib/Setting/Pages/OobaSettings.svelte index 71d0bc1e..f13bebdc 100644 --- a/src/lib/Setting/Pages/OobaSettings.svelte +++ b/src/lib/Setting/Pages/OobaSettings.svelte @@ -9,7 +9,11 @@ import TextInput from "src/lib/UI/GUI/TextInput.svelte"; import Arcodion from "src/lib/UI/Arcodion.svelte"; import ChatFormatSettings from "./ChatFormatSettings.svelte"; - export let instructionMode = false + interface Props { + instructionMode?: boolean; + } + + let { instructionMode = false }: Props = $props(); @@ -131,7 +135,7 @@ {#if $DataBase.localStopStrings}
-
- - - - @@ -199,7 +201,7 @@ Reference image - @@ -372,7 +374,7 @@ $DataBase.hypav2 ? 'hypaV2' : $DataBase.supaModelType !== 'none' ? 'supaMemory' : $DataBase.hanuraiEnable ? 'hanuraiMemory' : 'none' - } on:change={(v) => { + } onchange={(v) => { //@ts-ignore const value = v.target.value if (value === 'supaMemory'){ diff --git a/src/lib/Setting/Pages/PersonaSettings.svelte b/src/lib/Setting/Pages/PersonaSettings.svelte index 38fdb523..a2c7da19 100644 --- a/src/lib/Setting/Pages/PersonaSettings.svelte +++ b/src/lib/Setting/Pages/PersonaSettings.svelte @@ -16,16 +16,16 @@
{#each $DataBase.personas as persona, i} - @@ -62,14 +62,14 @@
- @@ -80,10 +80,10 @@ {language.description} is a 20 year old girl.]`} />
- - + + -
- {language.promptTemplate}
- -
- - -

Risu Account{#if $DataBase.account} - {/if} - +

{#if openIframe} diff --git a/src/lib/Setting/Settings.svelte b/src/lib/Setting/Settings.svelte index 86a1847d..e05fb540 100644 --- a/src/lib/Setting/Settings.svelte +++ b/src/lib/Setting/Settings.svelte @@ -22,7 +22,7 @@ import ModuleSettings from "./Pages/Module/ModuleSettings.svelte"; import { isLite } from "src/ts/lite"; - let openLoreList = false + let openLoreList = $state(false) if(window.innerWidth >= 900 && $SettingsMenuIndex === -1 && !$MobileGUI){ $SettingsMenuIndex = 1 } @@ -40,7 +40,7 @@ {/if} {#if window.innerWidth < 700 && !$MobileGUI} - {/if} @@ -192,7 +192,7 @@
{/key} {#if !$MobileGUI} -
{#each $DataBase.botPresets as presets, i} - {/each}
- - -
{#each $DataBase.personas as persona, i} -
{#each $DataBase.loreBook as lore, ind} - {/each}
- - + {:then as} - + {/await} +
--> + {:else if dom.tagName === 'RISUTEXTBOX'} + {@render textBox()} + {:else if dom.tagName === 'RISUICON'} + {@render icon()} + {:else if dom.tagName === 'RISUBUTTONS'} + {@render icons()} + {:else if dom.tagName === 'RISUGENINFO'} + {@render genInfo()} + {:else if dom.tagName === 'STYLE'} + + {dom.innerHTML} + + {:else} +
+ {@render renderChilds(dom)} +
+ {/if} + + +{/snippet} + +{#snippet renderChilds(dom:HTMLElement)} + {#each dom.children as node} + {@render renderGuiHtmlPart((node as HTMLElement))} + {/each} +{/snippet} +
{#if DBState.db.theme === 'mobilechat' && !blankMessage} @@ -390,12 +545,14 @@ {@render icons({applyTextColors: false})}
+ {:else if DBState.db.theme === 'customHTML' && !blankMessage} + {@render renderGuiHtmlPart(RenderGUIHtml(DBState.db.guiHTML))} {:else} {@render icon({rounded: DBState.db.roundIcons})} -
+
{#if DBState.db.characters[$selectedCharID]?.chaId === "§playground" && !blankMessage} - + {name === 'assistant' ? 'Assistant' : 'User'} {:else if !blankMessage && !$HideIconStore} - {name} + {name} {/if} {@render icons()}
@@ -412,21 +569,4 @@ {/if}
-
- - - \ No newline at end of file +
\ No newline at end of file diff --git a/src/lib/Setting/Pages/DisplaySettings.svelte b/src/lib/Setting/Pages/DisplaySettings.svelte index c41cc439..d0923bdb 100644 --- a/src/lib/Setting/Pages/DisplaySettings.svelte +++ b/src/lib/Setting/Pages/DisplaySettings.svelte @@ -57,7 +57,7 @@ Mobile Chat CardBoard - + Custom HTML
{#if DBState.db.theme === "custom"} @@ -67,6 +67,12 @@ {/if} + {#if DBState.db.theme === 'customHTML'} + {language.chatHTML} + + {/if} + + {#if DBState.db.theme === "waifu"} {language.waifuWidth} diff --git a/src/lib/UI/GUI/TextAreaResizable.svelte b/src/lib/UI/GUI/TextAreaResizable.svelte index fa152149..6b4b2d9a 100644 --- a/src/lib/UI/GUI/TextAreaResizable.svelte +++ b/src/lib/UI/GUI/TextAreaResizable.svelte @@ -35,7 +35,7 @@ oninput={handleInput} use:longpress={handleLongPress} bind:value={value} - class="rounded-md p-2 text-textcolor bg-transparent resize-none overflow-y-hidden border border-darkborderc" + class="rounded-md p-2 text-textcolor bg-transparent resize-none overflow-y-hidden border border-darkborderc w-full" style:font-size="{0.875 * (DBState.db.zoomsize / 100)}rem" style:line-height="{(DBState.db.lineHeight ?? 1.25) * (DBState.db.zoomsize / 100)}rem" > \ No newline at end of file diff --git a/src/styles.css b/src/styles.css index 4a8eaab9..a1711efe 100644 --- a/src/styles.css +++ b/src/styles.css @@ -254,4 +254,15 @@ html, body{ .z-100{ z-index: 100; +} + +.flexium{ + display: flex; + flex-direction: row; + justify-content: flex-start; +} +.chat-width{ + max-width: calc(100% - 0.5rem); + word-break: normal; + overflow-wrap: anywhere; } \ No newline at end of file diff --git a/src/ts/storage/database.svelte.ts b/src/ts/storage/database.svelte.ts index b3e00135..e6a1fb15 100644 --- a/src/ts/storage/database.svelte.ts +++ b/src/ts/storage/database.svelte.ts @@ -813,6 +813,7 @@ export interface Database{ groupTemplate?:string groupOtherBotRole?:string customGUI:string + guiHTML:string } export interface customscript{ From b8f412f6a0780afc17b2f65b35e8f34cda53e094 Mon Sep 17 00:00:00 2001 From: kwaroran Date: Sat, 2 Nov 2024 14:50:40 +0900 Subject: [PATCH 087/175] Bump version to 138.0.0 --- version.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.json b/version.json index b4b00a8b..d26a138a 100644 --- a/version.json +++ b/version.json @@ -1 +1 @@ -{"version":"137.1.0"} \ No newline at end of file +{"version":"138.0.0"} \ No newline at end of file From cb51301a610176322462a386dbcd09e9cb5e8d0f Mon Sep 17 00:00:00 2001 From: kwaroran Date: Sat, 2 Nov 2024 15:12:49 +0900 Subject: [PATCH 088/175] Bump application version to 138.0.0 --- src-tauri/tauri.conf.json | 2 +- src/ts/storage/database.svelte.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index ef8aee54..be74fd84 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -29,7 +29,7 @@ }, "productName": "RisuAI", "mainBinaryName": "RisuAI", - "version": "137.1.0", + "version": "138.0.0", "identifier": "co.aiclient.risu", "plugins": { "updater": { diff --git a/src/ts/storage/database.svelte.ts b/src/ts/storage/database.svelte.ts index e6a1fb15..ab21b445 100644 --- a/src/ts/storage/database.svelte.ts +++ b/src/ts/storage/database.svelte.ts @@ -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 = "137.1.0" +export let appVer = "138.0.0" export let webAppSubVer = '-svelte5-exp' From 8937a9c6e2c3b1761a85ba2934ee872a9c70bcb6 Mon Sep 17 00:00:00 2001 From: kwaroran Date: Sat, 2 Nov 2024 15:13:11 +0900 Subject: [PATCH 089/175] Fix version naming --- src/ts/storage/database.svelte.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ts/storage/database.svelte.ts b/src/ts/storage/database.svelte.ts index ab21b445..2680f1b8 100644 --- a/src/ts/storage/database.svelte.ts +++ b/src/ts/storage/database.svelte.ts @@ -13,7 +13,7 @@ import type { PromptItem, PromptSettings } from '../process/prompt'; import type { OobaChatCompletionRequestParams } from '../model/ooba'; export let appVer = "138.0.0" -export let webAppSubVer = '-svelte5-exp' +export let webAppSubVer = '' export function setDatabase(data:Database){ From 5dcb9340695a908b0d5dfdda8aa88732972db667 Mon Sep 17 00:00:00 2001 From: kwaroran Date: Sat, 2 Nov 2024 16:48:17 +0900 Subject: [PATCH 090/175] Fix welcome page freezing --- src/lib/ChatScreens/Chat.svelte | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/lib/ChatScreens/Chat.svelte b/src/lib/ChatScreens/Chat.svelte index ce4d14ec..74fc1b33 100644 --- a/src/lib/ChatScreens/Chat.svelte +++ b/src/lib/ChatScreens/Chat.svelte @@ -93,11 +93,19 @@ } function getCbsCondition(){ - const cbsConditions:CbsConditions = { - firstmsg: firstMessage ?? false, - chatRole: DBState.db.characters[$selectedCharID].chats[DBState.db.characters[$selectedCharID].chatPage]?.message?.[idx]?.role ?? null, + try{ + const cbsConditions:CbsConditions = { + firstmsg: firstMessage ?? false, + chatRole: DBState.db.characters[$selectedCharID].chats[DBState.db.characters[$selectedCharID].chatPage]?.message?.[idx]?.role ?? null, + } + return cbsConditions + } + catch(e){ + return { + firstmsg: firstMessage ?? false, + chatRole: null, + } } - return cbsConditions } function displaya(message:string){ From 28f41b499df17fbb93f3d9d9f550f3a970593f90 Mon Sep 17 00:00:00 2001 From: kwaroran Date: Sat, 2 Nov 2024 17:12:48 +0900 Subject: [PATCH 091/175] Fix binded persona --- src/lib/ChatScreens/DefaultChatScreen.svelte | 33 +++++++++++++++++--- src/ts/stores.svelte.ts | 1 - 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/src/lib/ChatScreens/DefaultChatScreen.svelte b/src/lib/ChatScreens/DefaultChatScreen.svelte index d7f8aac8..036bd8af 100644 --- a/src/lib/ChatScreens/DefaultChatScreen.svelte +++ b/src/lib/ChatScreens/DefaultChatScreen.svelte @@ -3,13 +3,13 @@ import Suggestion from './Suggestion.svelte'; import AdvancedChatEditor from './AdvancedChatEditor.svelte'; import { CameraIcon, DatabaseIcon, DicesIcon, GlobeIcon, ImagePlusIcon, LanguagesIcon, Laugh, MenuIcon, MicOffIcon, PackageIcon, Plus, RefreshCcwIcon, ReplyIcon, Send, StepForwardIcon } from "lucide-svelte"; - import { selectedCharID, PlaygroundStore, UserIconProtrait, createSimpleCharacter } from "../../ts/stores.svelte"; + import { selectedCharID, PlaygroundStore, createSimpleCharacter } from "../../ts/stores.svelte"; import Chat from "./Chat.svelte"; import { type Message, type character, type groupChat } from "../../ts/storage/database.svelte"; import { DBState } from 'src/ts/stores.svelte'; import { getCharImage } from "../../ts/characters"; import { chatProcessStage, doingChat, sendChat } from "../../ts/process/index.svelte"; - import { findCharacterbyId, messageForm, sleep } from "../../ts/util"; + import { findCharacterbyId, getUserIconProtrait, messageForm, sleep } from "../../ts/util"; import { language } from "../../lang"; import { isExpTranslator, translate } from "../../ts/translator/translator"; import { alertError, alertNormal, alertWait } from "../../ts/alert"; @@ -262,6 +262,29 @@ openChatList?: boolean; customStyle?: string; } + + let userIconProtrait = $state(false) + let currentUsername = $state(DBState.db.username) + let userIcon = $state(DBState.db.userIcon) + + + $effect.pre(() =>{ + const bindedPersona = DBState?.db?.characters?.[$selectedCharID]?.chats?.[DBState?.db?.characters?.[$selectedCharID]?.chatPage]?.bindedPersona + + if(bindedPersona){ + const persona = DBState.db.personas.find((p) => p.id === bindedPersona) + if(persona){ + currentUsername = persona.name + userIconProtrait = persona.largePortrait + userIcon = persona.icon + return + } + } + + currentUsername = DBState.db.username + userIconProtrait = DBState.db.personas[DBState.db.selectedPersona].largePortrait + userIcon = DBState.db.personas[DBState.db.selectedPersona].icon + }) let { openModuleList = $bindable(false), openChatList = $bindable(false), customStyle = '' }: Props = $props(); let inputHeight = $state("44px") @@ -590,11 +613,11 @@ {/if} diff --git a/src/ts/stores.svelte.ts b/src/ts/stores.svelte.ts index 9ca59842..746e74da 100644 --- a/src/ts/stores.svelte.ts +++ b/src/ts/stores.svelte.ts @@ -39,7 +39,6 @@ export const OpenRealmStore = writable(false) export const ShowRealmFrameStore = writable('') export const PlaygroundStore = writable(0) export const HideIconStore = writable(false) -export const UserIconProtrait = writable(false) export const CustomCSSStore = writable('') export const SafeModeStore = writable(false) export const MobileSearch = writable('') From db54124e7ab5cf269261731a2418b3933e28ba2d Mon Sep 17 00:00:00 2001 From: kwaroran Date: Sat, 2 Nov 2024 17:41:22 +0900 Subject: [PATCH 092/175] Fix autotranslate --- src/lib/ChatScreens/Chat.svelte | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/lib/ChatScreens/Chat.svelte b/src/lib/ChatScreens/Chat.svelte index 74fc1b33..0e7b0b98 100644 --- a/src/lib/ChatScreens/Chat.svelte +++ b/src/lib/ChatScreens/Chat.svelte @@ -19,9 +19,6 @@ import { ConnectionOpenStore } from "src/ts/sync/multiuser"; import { onDestroy, onMount } from "svelte"; let translating = $state(false) - try { - translating = DBState.db.autoTranslate - } catch (error) {} let editMode = $state(false) let statusMessage:string = $state('') interface Props { @@ -138,12 +135,19 @@ lastChatId = chatID translateText = false try { - translated = DBState.db.autoTranslate - if(translated){ + console.log('Checking autoTranslate') + if(DBState.db.autoTranslate){ translateText = true } - } catch (error) {} + + setTimeout(() => { + translated = translateText + }, 10) + } catch (error) { + console.error(error) + } } + console.log(`Translattext: ${translateText}, autoTranslate: ${DBState.db.autoTranslate}`) if(translateText){ if(!DBState.db.legacyTranslation){ const marked = await ParseMarkdown(data, charArg, 'pretranslate', chatID, getCbsCondition()) From b4d08b1f7dce05e05ef7da7b103b826119ceb0f3 Mon Sep 17 00:00:00 2001 From: kwaroran Date: Sat, 2 Nov 2024 22:52:54 +0900 Subject: [PATCH 093/175] fix save lag --- src/ts/globalApi.svelte.ts | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/src/ts/globalApi.svelte.ts b/src/ts/globalApi.svelte.ts index 641693ee..ba472cf0 100644 --- a/src/ts/globalApi.svelte.ts +++ b/src/ts/globalApi.svelte.ts @@ -13,7 +13,7 @@ import { v4 as uuidv4, v4 } from 'uuid'; import { appDataDir, join } from "@tauri-apps/api/path"; import { get } from "svelte/store"; import {open} from '@tauri-apps/plugin-shell' -import { setDatabase, type Database, defaultSdDataFunc, getDatabase } from "./storage/database.svelte"; +import { setDatabase, type Database, defaultSdDataFunc, getDatabase, type character } from "./storage/database.svelte"; import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow"; import { checkRisuUpdate } from "./update"; import { MobileGUI, botMakerMode, selectedCharID, loadedStore, DBState } from "./stores.svelte"; @@ -309,7 +309,7 @@ let lastSave = '' * @returns {Promise} - A promise that resolves when the database has been saved. */ export async function saveDb(){ - let changed = true + let changed = false syncDrive() let gotChannel = false const sessionID = v4() @@ -330,10 +330,37 @@ export async function saveDb(){ } } + + $effect.root(() => { + + let selIdState = $state(0) + + selectedCharID.subscribe((v) => { + selIdState = v + }) + + $effect(() => { + $state.snapshot(DBState?.db?.characters?.[selIdState]) + for(const key in DBState.db){ + if(key !== 'characters'){ + $state.snapshot(DBState.db[key]) + } + } + changed = true + }) + }) + let savetrys = 0 let lastDbData = new Uint8Array(0) await sleep(1000) while(true){ + if(!changed){ + await sleep(1000) + continue + } + + changed = false + try { if(gotChannel){ //Data is saved in other tab @@ -382,6 +409,8 @@ export async function saveDb(){ if(z.formatversion){ await forageStorage.setItem('database/database.bin', dbData) } + + await sleep(3000) } } if(!forageStorage.isAccount){ From e631b8fb514fce4685ff98349f765161ab922614 Mon Sep 17 00:00:00 2001 From: kwaroran Date: Sat, 2 Nov 2024 22:56:13 +0900 Subject: [PATCH 094/175] Revert textareainput --- src/lib/UI/GUI/TextAreaInput.svelte | 29 ++++++----------------------- 1 file changed, 6 insertions(+), 23 deletions(-) diff --git a/src/lib/UI/GUI/TextAreaInput.svelte b/src/lib/UI/GUI/TextAreaInput.svelte index 6dae7150..e26f6a99 100644 --- a/src/lib/UI/GUI/TextAreaInput.svelte +++ b/src/lib/UI/GUI/TextAreaInput.svelte @@ -88,8 +88,6 @@ }} bind:this={inputDom} translate="no" - role="textbox" - tabindex="0" >{value ?? ''}
{:else}
{value ?? ''}
{/if} {#if backgroundHTML || $moduleBackgroundEmbedding} - {#if $selectedCharID > -1} + {#if selIdState.selId > -1} {#key $ReloadGUIPointer}
{#await ParseMarkdown(risuChatParser((backgroundHTML || '') + ($moduleBackgroundEmbedding || ''), {chara:currentChar}), currentChar, 'back') then md} diff --git a/src/ts/process/modules.ts b/src/ts/process/modules.ts index 60f0ab59..f430ec5b 100644 --- a/src/ts/process/modules.ts +++ b/src/ts/process/modules.ts @@ -8,7 +8,7 @@ import { convertExternalLorebook } from "./lorebook.svelte" import { decodeRPack, encodeRPack } from "../rpack/rpack_bg" import { convertImage } from "../parser.svelte" import { Capacitor } from "@capacitor/core" -import { HideIconStore, moduleBackgroundEmbedding } from "../stores.svelte" +import { DBState, HideIconStore, moduleBackgroundEmbedding } from "../stores.svelte" export interface RisuModule{ name: string @@ -397,7 +397,6 @@ export async function applyModule() { let lastGlobalEnabledModules: string[] = [] let lastChatEnabledModules: string[] = [] -let characterHideIcon = false export function moduleUpdate(){ if(!Array.isArray(lastGlobalEnabledModules)){ @@ -425,7 +424,10 @@ export function moduleUpdate(){ }) if(backgroundEmbedding){ + console.log('Background Embedding:', backgroundEmbedding) moduleBackgroundEmbedding.set(backgroundEmbedding) } - HideIconStore.set(characterHideIcon || moduleHideIcon) + console.log('Module Hide Icon:', moduleHideIcon) + console.log(Date.now()) + HideIconStore.set(getCurrentCharacter()?.hideChatIcon || moduleHideIcon) } \ No newline at end of file diff --git a/src/ts/stores.svelte.ts b/src/ts/stores.svelte.ts index 42080c55..9edec75c 100644 --- a/src/ts/stores.svelte.ts +++ b/src/ts/stores.svelte.ts @@ -49,6 +49,10 @@ export const alertStore = writable({ type: 'none', msg: 'n', } as alertData) +export const selIdState = $state({ + selId: -1 +}) + CustomCSSStore.subscribe((css) => { console.log(css) @@ -91,18 +95,16 @@ export const DBState = $state({ export const disableHighlight = writable(true) -let selIdState = $state(0) - -selectedCharID.subscribe((v) => { - selIdState = v -}) - $effect.root(() => { + selectedCharID.subscribe((v) => { + selIdState.selId = v + }) $effect(() => { $state.snapshot(DBState.db.modules) DBState?.db?.enabledModules DBState?.db?.enabledModules?.length - DBState?.db?.characters?.[selIdState]?.chats?.[DBState?.db?.characters?.[selIdState]?.chatPage]?.modules?.length + DBState?.db?.characters?.[selIdState.selId]?.chats?.[DBState?.db?.characters?.[selIdState.selId]?.chatPage]?.modules?.length + DBState?.db?.characters?.[selIdState.selId]?.hideChatIcon DBState?.db?.moduleIntergration ReloadGUIPointer.set(get(ReloadGUIPointer) + 1) moduleUpdate() From c22f6b789aca466cb2cea63973ff57fbbdc273a7 Mon Sep 17 00:00:00 2001 From: kwaroran Date: Sun, 3 Nov 2024 23:16:51 +0900 Subject: [PATCH 102/175] Improve performance --- src/lib/ChatScreens/Chat.svelte | 38 ++++++++++++++++----------------- src/ts/plugins/embedscript.ts | 10 ++++----- src/ts/process/modules.ts | 19 +++++++++-------- src/ts/process/scripts.ts | 32 +++++++++++++++++++++++++++ src/ts/stores.svelte.ts | 7 ++++-- 5 files changed, 71 insertions(+), 35 deletions(-) diff --git a/src/lib/ChatScreens/Chat.svelte b/src/lib/ChatScreens/Chat.svelte index 0e7b0b98..ba1d9bec 100644 --- a/src/lib/ChatScreens/Chat.svelte +++ b/src/lib/ChatScreens/Chat.svelte @@ -6,7 +6,7 @@ import { language } from "../../lang"; import { type MessageGenerationInfo } from "../../ts/storage/database.svelte"; import { DBState } from 'src/ts/stores.svelte'; - import { HideIconStore, ReloadGUIPointer, selectedCharID } from "../../ts/stores.svelte"; + import { HideIconStore, ReloadGUIPointer, selIdState } from "../../ts/stores.svelte"; import { translateHTML } from "../../ts/translator/translator"; import { risuChatParser } from "src/ts/process/scripts"; import { get, type Unsubscriber } from "svelte/store"; @@ -18,6 +18,7 @@ import { ColorSchemeTypeStore } from "src/ts/gui/colorscheme"; import { ConnectionOpenStore } from "src/ts/sync/multiuser"; import { onDestroy, onMount } from "svelte"; + import { PerformanceDebugger } from "src/ts/globalApi.svelte"; let translating = $state(false) let editMode = $state(false) let statusMessage:string = $state('') @@ -55,12 +56,12 @@ let msgDisplay = $state('') let translated = $state(DBState.db.autoTranslate) - let role = $derived(DBState.db.characters[$selectedCharID].chats[DBState.db.characters[$selectedCharID].chatPage].message[idx]?.role) + let role = $derived(DBState.db.characters[selIdState.selId].chats[DBState.db.characters[selIdState.selId].chatPage].message[idx]?.role) async function rm(e:MouseEvent, rec?:boolean){ if(e.shiftKey){ - let msg = DBState.db.characters[$selectedCharID].chats[DBState.db.characters[$selectedCharID].chatPage].message + let msg = DBState.db.characters[selIdState.selId].chats[DBState.db.characters[selIdState.selId].chatPage].message msg = msg.slice(0, idx) - DBState.db.characters[$selectedCharID].chats[DBState.db.characters[$selectedCharID].chatPage].message = msg + DBState.db.characters[selIdState.selId].chats[DBState.db.characters[selIdState.selId].chatPage].message = msg return } @@ -68,32 +69,32 @@ if(rm){ if(DBState.db.instantRemove || rec){ const r = await alertConfirm(language.instantRemoveConfirm) - let msg = DBState.db.characters[$selectedCharID].chats[DBState.db.characters[$selectedCharID].chatPage].message + let msg = DBState.db.characters[selIdState.selId].chats[DBState.db.characters[selIdState.selId].chatPage].message if(!r){ msg = msg.slice(0, idx) } else{ msg.splice(idx, 1) } - DBState.db.characters[$selectedCharID].chats[DBState.db.characters[$selectedCharID].chatPage].message = msg + DBState.db.characters[selIdState.selId].chats[DBState.db.characters[selIdState.selId].chatPage].message = msg } else{ - let msg = DBState.db.characters[$selectedCharID].chats[DBState.db.characters[$selectedCharID].chatPage].message + let msg = DBState.db.characters[selIdState.selId].chats[DBState.db.characters[selIdState.selId].chatPage].message msg.splice(idx, 1) - DBState.db.characters[$selectedCharID].chats[DBState.db.characters[$selectedCharID].chatPage].message = msg + DBState.db.characters[selIdState.selId].chats[DBState.db.characters[selIdState.selId].chatPage].message = msg } } } async function edit(){ - DBState.db.characters[$selectedCharID].chats[DBState.db.characters[$selectedCharID].chatPage].message[idx].data = message + DBState.db.characters[selIdState.selId].chats[DBState.db.characters[selIdState.selId].chatPage].message[idx].data = message } function getCbsCondition(){ try{ const cbsConditions:CbsConditions = { firstmsg: firstMessage ?? false, - chatRole: DBState.db.characters[$selectedCharID].chats[DBState.db.characters[$selectedCharID].chatPage]?.message?.[idx]?.role ?? null, + chatRole: DBState.db.characters[selIdState.selId].chats[DBState.db.characters[selIdState.selId].chatPage]?.message?.[idx]?.role ?? null, } return cbsConditions } @@ -106,6 +107,7 @@ } function displaya(message:string){ + const perf = performance.now() msgDisplay = risuChatParser(message, {chara: name, chatID: idx, rmVar: true, visualize: true, cbsConditions: getCbsCondition()}) } @@ -135,7 +137,6 @@ lastChatId = chatID translateText = false try { - console.log('Checking autoTranslate') if(DBState.db.autoTranslate){ translateText = true } @@ -147,7 +148,6 @@ console.error(error) } } - console.log(`Translattext: ${translateText}, autoTranslate: ${DBState.db.autoTranslate}`) if(translateText){ if(!DBState.db.legacyTranslation){ const marked = await ParseMarkdown(data, charArg, 'pretranslate', chatID, getCbsCondition()) @@ -284,7 +284,7 @@ {/if} {#if idx > -1} - {#if DBState.db.characters[$selectedCharID].type !== 'group' && DBState.db.characters[$selectedCharID].ttsMode !== 'none' && (DBState.db.characters[$selectedCharID].ttsMode)} + {#if DBState.db.characters[selIdState.selId].type !== 'group' && DBState.db.characters[selIdState.selId].ttsMode !== 'none' && (DBState.db.characters[selIdState.selId].ttsMode)} {:else if !blankMessage && !$HideIconStore} diff --git a/src/ts/plugins/embedscript.ts b/src/ts/plugins/embedscript.ts index 8b260b8c..a2db413d 100644 --- a/src/ts/plugins/embedscript.ts +++ b/src/ts/plugins/embedscript.ts @@ -245,12 +245,9 @@ export async function runCharacterJS(arg:{ mode: ScriptMode|'onButtonClick'|'modifyRequestChat' data: any }):Promise{ + const perf = performance.now() try { - if(arg.code === null){ - const db = getDatabase() - const selectedChar = get(selectedCharID) - arg.code = db.characters[selectedChar].virtualscript - } + arg.code = arg.code ?? '' const codes = { "editinput": 'editInput', "editoutput": 'editOutput', @@ -320,5 +317,8 @@ export async function runCharacterJS(arg:{ } return arg.data } + finally{ + console.log('runCharacterJS',performance.now() - perf) + } } \ No newline at end of file diff --git a/src/ts/process/modules.ts b/src/ts/process/modules.ts index f430ec5b..6f21d032 100644 --- a/src/ts/process/modules.ts +++ b/src/ts/process/modules.ts @@ -8,7 +8,8 @@ import { convertExternalLorebook } from "./lorebook.svelte" import { decodeRPack, encodeRPack } from "../rpack/rpack_bg" import { convertImage } from "../parser.svelte" import { Capacitor } from "@capacitor/core" -import { DBState, HideIconStore, moduleBackgroundEmbedding } from "../stores.svelte" +import { HideIconStore, moduleBackgroundEmbedding, ReloadGUIPointer } from "../stores.svelte" +import {get} from "svelte/store" export interface RisuModule{ name: string @@ -395,18 +396,18 @@ export async function applyModule() { alertNormal(language.successApplyModule) } -let lastGlobalEnabledModules: string[] = [] -let lastChatEnabledModules: string[] = [] +let lastModuleIds:string = '' export function moduleUpdate(){ - if(!Array.isArray(lastGlobalEnabledModules)){ - lastGlobalEnabledModules = [] - } - if(!Array.isArray(lastChatEnabledModules)){ - lastChatEnabledModules = [] - } + const m = getModules() + + const ids = m.map((m) => m.id).join('-') + if(lastModuleIds !== ids){ + ReloadGUIPointer.set(get(ReloadGUIPointer) + 1) + lastModuleIds = ids + } let moduleHideIcon = false let backgroundEmbedding = '' diff --git a/src/ts/process/scripts.ts b/src/ts/process/scripts.ts index f69b8489..f7e7e2e5 100644 --- a/src/ts/process/scripts.ts +++ b/src/ts/process/scripts.ts @@ -65,9 +65,38 @@ export async function importRegex(o?:customscript[]):Promise{ } let bestMatchCache = new Map() +let processScriptCache = new Map() + +function cacheScript(scripts:customscript[], data:string, result:string){ + let hash = data + '|||' + for(const script of scripts){ + hash += `${script.in}|||${script.out}|||${script.flag}|||${script.ableFlag}|||${script.type}` + } + + processScriptCache.set(hash, result) + +} + +function getScriptCache(scripts:customscript[], data:string){ + let hash = data + '|||' + for(const script of scripts){ + hash += `${script.in}|||${script.out}|||${script.flag}|||${script.ableFlag}|||${script.type}` + } + + return processScriptCache.get(hash) +} + +export function resetScriptCache(){ + processScriptCache = new Map() +} export async function processScriptFull(char:character|groupChat|simpleCharacterArgument, data:string, mode:ScriptMode, chatID = -1, cbsConditions:CbsConditions = {}){ let db = getDatabase() + const originalData = data + const cached = getScriptCache((db.globalscript ?? []).concat(char.customscript), originalData) + if(cached){ + return {data: cached, emoChanged: false} + } let emoChanged = false const scripts = (db.globalscript ?? []).concat(char.customscript).concat(getModuleRegexScripts()) data = await runCharacterJS({ @@ -77,6 +106,7 @@ export async function processScriptFull(char:character|groupChat|simpleCharacter }) data = await runLuaEditTrigger(char, mode, data) if(scripts.length === 0){ + cacheScript(scripts, originalData, data) return {data, emoChanged} } function executeScript(pscript:pScript){ @@ -311,6 +341,8 @@ export async function processScriptFull(char:character|groupChat|simpleCharacter } } + cacheScript(scripts, originalData, data) + return {data, emoChanged} } diff --git a/src/ts/stores.svelte.ts b/src/ts/stores.svelte.ts index 9edec75c..fd4fa156 100644 --- a/src/ts/stores.svelte.ts +++ b/src/ts/stores.svelte.ts @@ -3,6 +3,7 @@ import type { character, Database, groupChat } from "./storage/database.svelte"; import type { simpleCharacterArgument } from "./parser.svelte"; import type { alertData } from "./alert"; import { getModules, moduleUpdate } from "./process/modules"; +import { resetScriptCache } from "./process/scripts"; function updateSize(){ SizeStore.set({ @@ -95,6 +96,10 @@ export const DBState = $state({ export const disableHighlight = writable(true) +ReloadGUIPointer.subscribe(() => { + resetScriptCache() +}) + $effect.root(() => { selectedCharID.subscribe((v) => { selIdState.selId = v @@ -106,8 +111,6 @@ $effect.root(() => { DBState?.db?.characters?.[selIdState.selId]?.chats?.[DBState?.db?.characters?.[selIdState.selId]?.chatPage]?.modules?.length DBState?.db?.characters?.[selIdState.selId]?.hideChatIcon DBState?.db?.moduleIntergration - ReloadGUIPointer.set(get(ReloadGUIPointer) + 1) moduleUpdate() - }) }) \ No newline at end of file From bb37ee4af012e1241484bc6e076c0751951e7cd3 Mon Sep 17 00:00:00 2001 From: kwaroran Date: Sun, 3 Nov 2024 23:26:49 +0900 Subject: [PATCH 103/175] Remove debug logging from moduleUpdate function --- src/ts/process/modules.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/ts/process/modules.ts b/src/ts/process/modules.ts index 6f21d032..2e7a42af 100644 --- a/src/ts/process/modules.ts +++ b/src/ts/process/modules.ts @@ -425,10 +425,7 @@ export function moduleUpdate(){ }) if(backgroundEmbedding){ - console.log('Background Embedding:', backgroundEmbedding) moduleBackgroundEmbedding.set(backgroundEmbedding) } - console.log('Module Hide Icon:', moduleHideIcon) - console.log(Date.now()) HideIconStore.set(getCurrentCharacter()?.hideChatIcon || moduleHideIcon) } \ No newline at end of file From 76dfa5dcdfc22fbb3a6eebf243706a3fc10e493a Mon Sep 17 00:00:00 2001 From: kwaroran Date: Sun, 3 Nov 2024 23:27:19 +0900 Subject: [PATCH 104/175] Refactor moduleUpdate function to move lastModuleIds check to the end --- src/ts/process/modules.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/ts/process/modules.ts b/src/ts/process/modules.ts index 2e7a42af..0aec3e2d 100644 --- a/src/ts/process/modules.ts +++ b/src/ts/process/modules.ts @@ -404,10 +404,6 @@ export function moduleUpdate(){ const m = getModules() const ids = m.map((m) => m.id).join('-') - if(lastModuleIds !== ids){ - ReloadGUIPointer.set(get(ReloadGUIPointer) + 1) - lastModuleIds = ids - } let moduleHideIcon = false let backgroundEmbedding = '' @@ -428,4 +424,9 @@ export function moduleUpdate(){ moduleBackgroundEmbedding.set(backgroundEmbedding) } HideIconStore.set(getCurrentCharacter()?.hideChatIcon || moduleHideIcon) + + if(lastModuleIds !== ids){ + ReloadGUIPointer.set(get(ReloadGUIPointer) + 1) + lastModuleIds = ids + } } \ No newline at end of file From 40b890cd4206dc2f0b11f899f43bb026ae3219fc Mon Sep 17 00:00:00 2001 From: kwaroran Date: Sun, 3 Nov 2024 23:28:04 +0900 Subject: [PATCH 105/175] Bump version to 138.0.3 --- src-tauri/tauri.conf.json | 2 +- src/ts/storage/database.svelte.ts | 2 +- version.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 88d8550d..e9b4a344 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -29,7 +29,7 @@ }, "productName": "RisuAI", "mainBinaryName": "RisuAI", - "version": "138.0.2", + "version": "138.0.3", "identifier": "co.aiclient.risu", "plugins": { "updater": { diff --git a/src/ts/storage/database.svelte.ts b/src/ts/storage/database.svelte.ts index b2c6edc2..67bb736b 100644 --- a/src/ts/storage/database.svelte.ts +++ b/src/ts/storage/database.svelte.ts @@ -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 = "138.0.2" +export let appVer = "138.0.3" export let webAppSubVer = '' diff --git a/version.json b/version.json index 9d501355..1a640b37 100644 --- a/version.json +++ b/version.json @@ -1 +1 @@ -{"version":"138.0.2"} \ No newline at end of file +{"version":"138.0.3"} \ No newline at end of file From 911ff7fd8b9544f2c2a3a279e5174bcf28377fa6 Mon Sep 17 00:00:00 2001 From: kwaroran Date: Mon, 4 Nov 2024 15:26:17 +0900 Subject: [PATCH 106/175] Add compression-streams-polyfill and integrate CompressionStream support --- package.json | 1 + pnpm-lock.yaml | 15 +++++++++++++++ src/ts/storage/risuSave.ts | 14 ++++++++++++++ 3 files changed, 30 insertions(+) diff --git a/package.json b/package.json index c74c0ccc..4d96dce0 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "body-parser": "^1.20.2", "buffer": "^6.0.3", "codemirror": "^5.65.16", + "compression-streams-polyfill": "^0.1.7", "core-js": "^3.35.0", "cors": "^2.8.5", "crc": "^4.3.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1670758c..6b072698 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -86,6 +86,9 @@ importers: codemirror: specifier: ^5.65.16 version: 5.65.16 + compression-streams-polyfill: + specifier: ^0.1.7 + version: 0.1.7 core-js: specifier: ^3.35.0 version: 3.35.0 @@ -1491,6 +1494,9 @@ packages: compare-func@2.0.0: resolution: {integrity: sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==} + compression-streams-polyfill@0.1.7: + resolution: {integrity: sha512-Y6VkJzi9JkvmcgDuJ2jJZ5DGljNhUrFBGxx5+gQ2RNg+uPesuRDDL5zN1NTk5NTuynyVkQ4MNip5z37MmtbdpQ==} + concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} @@ -1908,6 +1914,9 @@ packages: fflate@0.8.1: resolution: {integrity: sha512-/exOvEuc+/iaUm105QIiOt4LpBdMTWsXxqR0HDF35vx3fmaKzw7354gTilCh5rkzEt8WYyG//ku3h3nRmd7CHQ==} + fflate@0.8.2: + resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==} + fill-range@7.0.1: resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} engines: {node: '>=8'} @@ -5031,6 +5040,10 @@ snapshots: array-ify: 1.0.0 dot-prop: 5.3.0 + compression-streams-polyfill@0.1.7: + dependencies: + fflate: 0.8.2 + concat-map@0.0.1: {} console-control-strings@1.1.0: @@ -5493,6 +5506,8 @@ snapshots: fflate@0.8.1: {} + fflate@0.8.2: {} + fill-range@7.0.1: dependencies: to-regex-range: 5.0.1 diff --git a/src/ts/storage/risuSave.ts b/src/ts/storage/risuSave.ts index a4e6fd6c..41e41aa9 100644 --- a/src/ts/storage/risuSave.ts +++ b/src/ts/storage/risuSave.ts @@ -15,6 +15,18 @@ const magicHeader = new Uint8Array([0, 82, 73, 83, 85, 83, 65, 86, 69, 0, 7]); const magicCompressedHeader = new Uint8Array([0, 82, 73, 83, 85, 83, 65, 86, 69, 0, 8]); const magicStreamCompressedHeader = new Uint8Array([0, 82, 73, 83, 85, 83, 65, 86, 69, 0, 9]); + +async function checkCompressionStreams(){ + if(!CompressionStream){ + const {makeCompressionStream} = await import('compression-streams-polyfill/ponyfill'); + globalThis.CompressionStream = makeCompressionStream(TransformStream); + } + if(!DecompressionStream){ + const {makeDecompressionStream} = await import('compression-streams-polyfill/ponyfill'); + globalThis.DecompressionStream = makeDecompressionStream(TransformStream); + } +} + export function encodeRisuSaveLegacy(data:any, compression:'noCompression'|'compression' = 'noCompression'){ let encoded:Uint8Array = packr.encode(data) if(compression === 'compression'){ @@ -33,6 +45,7 @@ export function encodeRisuSaveLegacy(data:any, compression:'noCompression'|'comp } export async function encodeRisuSave(data:any) { + await checkCompressionStreams() let encoded:Uint8Array = packr.encode(data) const cs = new CompressionStream('gzip'); const writer = cs.writable.getWriter(); @@ -55,6 +68,7 @@ export async function decodeRisuSave(data:Uint8Array){ data = data.slice(magicHeader.length) return unpackr.decode(data) case "stream":{ + await checkCompressionStreams() data = data.slice(magicStreamCompressedHeader.length) const cs = new DecompressionStream('gzip'); const writer = cs.writable.getWriter(); From 106ddd61d743a62f17323ecb037f29d8b7d2efe0 Mon Sep 17 00:00:00 2001 From: kwaroran Date: Mon, 4 Nov 2024 15:30:52 +0900 Subject: [PATCH 107/175] fix typo disableHighlight --- src/lib/UI/GUI/TextAreaInput.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/UI/GUI/TextAreaInput.svelte b/src/lib/UI/GUI/TextAreaInput.svelte index a71bfac6..73df1ba2 100644 --- a/src/lib/UI/GUI/TextAreaInput.svelte +++ b/src/lib/UI/GUI/TextAreaInput.svelte @@ -38,7 +38,7 @@ hideAutoComplete() }} > - {#if !highlight || disableHighlight} + {#if !highlight || $disableHighlight} {:else} @@ -86,6 +87,9 @@ insertTextAtSelection(text) } }} + onchange={(e) => { + onchange() + }} bind:this={inputDom} translate="no" >{value ?? ''}
@@ -119,6 +123,7 @@ className?: string; optimaizedInput?: boolean; highlight?: boolean; + onchange?: () => void; } let { @@ -134,7 +139,8 @@ height = 'default', className = '', optimaizedInput = true, - highlight = false + highlight = false, + onchange = () => {} }: Props = $props(); let selectingAutoComplete = $state(0) let highlightId = highlight ? getNewHighlightId() : 0 From 4f69bb8880f0f75fc60030b39ee455a64a441650 Mon Sep 17 00:00:00 2001 From: kwaroran Date: Tue, 5 Nov 2024 20:15:17 +0900 Subject: [PATCH 113/175] Add loggen related stuff and fix bugs --- src-tauri/capabilities/migrated.json | 1 + src-tauri/gen/schemas/capabilities.json | 2 +- src/lang/en.ts | 2 + src/lib/ChatScreens/Chat.svelte | 15 +- src/lib/Others/AlertComp.svelte | 13 +- src/lib/Setting/Pages/DisplaySettings.svelte | 4 + src/ts/alert.ts | 2 +- src/ts/characterCards.ts | 8 +- src/ts/loggen.ts | 754 +++++++++++++++++++ src/ts/storage/database.svelte.ts | 1 + src/ts/update.ts | 2 +- 11 files changed, 795 insertions(+), 9 deletions(-) create mode 100644 src/ts/loggen.ts diff --git a/src-tauri/capabilities/migrated.json b/src-tauri/capabilities/migrated.json index c08f013c..e3d6abea 100644 --- a/src-tauri/capabilities/migrated.json +++ b/src-tauri/capabilities/migrated.json @@ -35,6 +35,7 @@ "dialog:allow-message", "dialog:allow-ask", "dialog:allow-confirm", + "updater:default", { "identifier": "http:default", "allow": [ diff --git a/src-tauri/gen/schemas/capabilities.json b/src-tauri/gen/schemas/capabilities.json index bac4c1ed..bcf62660 100644 --- a/src-tauri/gen/schemas/capabilities.json +++ b/src-tauri/gen/schemas/capabilities.json @@ -1 +1 @@ -{"desktop-capability":{"identifier":"desktop-capability","description":"","local":true,"permissions":["updater:default","process:default","shell:default","http:default","deep-link:default"],"platforms":["macOS","windows","linux"]},"migrated":{"identifier":"migrated","description":"permissions that were migrated from v1","local":true,"windows":["main"],"permissions":["core:default","fs:allow-read-file","fs:allow-write-file","fs:allow-read-dir","fs:allow-copy-file","fs:allow-mkdir","fs:allow-remove","fs:allow-remove","fs:allow-rename","fs:allow-exists",{"identifier":"fs:scope","allow":["$APPDATA","$APPDATA/*","$APPDATA/**/*","$DOWNLOAD/*","/data/**/*","$RESOURCE/*"]},"core:window:allow-maximize","core:window:allow-set-fullscreen","shell:allow-open","dialog:allow-open","dialog:allow-save","dialog:allow-message","dialog:allow-ask","dialog:allow-confirm",{"identifier":"http:default","allow":[{"url":"https://*/*"},{"url":"https://*/**/*"},{"url":"http://*/*"},{"url":"http://*/**/*"}]},"os:allow-platform","os:allow-version","os:allow-os-type","os:allow-family","os:allow-arch","os:allow-exe-extension","os:allow-locale","os:allow-hostname","process:allow-restart","core:app:allow-app-show","core:app:allow-app-hide","fs:default","os:default","dialog:default","process:default","shell:default","http:default","deep-link:default"]}} \ No newline at end of file +{"desktop-capability":{"identifier":"desktop-capability","description":"","local":true,"permissions":["updater:default","process:default","shell:default","http:default","deep-link:default"],"platforms":["macOS","windows","linux"]},"migrated":{"identifier":"migrated","description":"permissions that were migrated from v1","local":true,"windows":["main"],"permissions":["core:default","fs:allow-read-file","fs:allow-write-file","fs:allow-read-dir","fs:allow-copy-file","fs:allow-mkdir","fs:allow-remove","fs:allow-remove","fs:allow-rename","fs:allow-exists",{"identifier":"fs:scope","allow":["$APPDATA","$APPDATA/*","$APPDATA/**/*","$DOWNLOAD/*","/data/**/*","$RESOURCE/*"]},"core:window:allow-maximize","core:window:allow-set-fullscreen","shell:allow-open","dialog:allow-open","dialog:allow-save","dialog:allow-message","dialog:allow-ask","dialog:allow-confirm","updater:default",{"identifier":"http:default","allow":[{"url":"https://*/*"},{"url":"https://*/**/*"},{"url":"http://*/*"},{"url":"http://*/**/*"}]},"os:allow-platform","os:allow-version","os:allow-os-type","os:allow-family","os:allow-arch","os:allow-exe-extension","os:allow-locale","os:allow-hostname","process:allow-restart","core:app:allow-app-show","core:app:allow-app-hide","fs:default","os:default","dialog:default","process:default","shell:default","http:default","deep-link:default"]}} \ No newline at end of file diff --git a/src/lang/en.ts b/src/lang/en.ts index 1ef9c394..f60817ff 100644 --- a/src/lang/en.ts +++ b/src/lang/en.ts @@ -751,4 +751,6 @@ export const languageEnglish = { groupOtherBotRole: "Non-Speaker Role in Group", defineCustomGUI: "Define Custom GUI", chatHTML: "Chat HTML", + logShare: "Show Share Log Button", + preview: "Preview", } \ No newline at end of file diff --git a/src/lib/ChatScreens/Chat.svelte b/src/lib/ChatScreens/Chat.svelte index ba1d9bec..3eac694b 100644 --- a/src/lib/ChatScreens/Chat.svelte +++ b/src/lib/ChatScreens/Chat.svelte @@ -1,11 +1,11 @@ {#if openOptions} @@ -42,123 +42,20 @@

{language.model}

- - - - - - - {#if showUnrec} - - - - - - - - - - - - - - - - - - - - - + + {#each providers as provider} + {#if provider.providerName === '@as-is'} + {#each provider.models as model} + + {/each} + {:else} + + {#each provider.models as model} + + {/each} + {/if} - - - - - {#if showUnrec} - - - - - - - - - - - - - {/if} - - - {#if DBState.db.tpo && isTauri} - - {/if} - - {#if showUnrec} - - {/if} - - - - {#if showUnrec} - - - - {/if} - - - - - - - - - - - - - - - - - {#if showUnrec} - - - - - {/if} - - - - - - - - - - - - - - - - - - - - - + {/each} {#await getHordeModels()} @@ -175,15 +72,6 @@ {/each} {/await} - - {#if showUnrec} - - - - - - - {/if} {#if DBState.db.plugins.length > 0} {/if} @@ -197,6 +85,6 @@ diff --git a/src/ts/model/modellist.ts b/src/ts/model/modellist.ts index e70ecef5..462b3e79 100644 --- a/src/ts/model/modellist.ts +++ b/src/ts/model/modellist.ts @@ -4,31 +4,52 @@ export enum LLMFlags{ hasAudioInput, hasAudioOutput, hasPrefill, - hasCache + hasCache, + hasFullSystemPrompt, + hasFirstSystemPrompt, } export enum LLMProvider{ OpenAI, - Antropic, + Anthropic, GoogleCloud, VertexAI, AsIs, - Mistral + Mistral, + NovelList, + Cohere, + NovelAI, + WebLLM, + Horde, + AWS, } export enum LLMFormat{ OpenAICompatible, OpenAILegacyInstruct, - Antropic, + Anthropic, AnthropicLegacy, - AsIs, - Mistral + Mistral, + GoogleCloud, + VertexAIGemini, + NovelList, + Cohere, + NovelAI, + WebLLM, + OobaLegacy, + Plugin, + Ooba, + Kobold, + Ollama, + Horde, + AWSBedrockClaude } export interface LLMModel{ id: string name: string shortName?: string + fullName?: string internalID?: string provider: LLMProvider flags: LLMFlags[] @@ -36,6 +57,21 @@ export interface LLMModel{ recommended?: boolean } +const ProviderNames = new Map([ + [LLMProvider.OpenAI, 'OpenAI'], + [LLMProvider.Anthropic, 'Anthropic'], + [LLMProvider.GoogleCloud, 'Google Cloud'], + [LLMProvider.VertexAI, 'Vertex AI'], + [LLMProvider.AsIs, 'As Is'], + [LLMProvider.Mistral, 'MistralAI'], + [LLMProvider.NovelList, 'NovelList'], + [LLMProvider.Cohere, 'Cohere'], + [LLMProvider.NovelAI, 'NovelAI'], + [LLMProvider.WebLLM, 'WebLLM'], + [LLMProvider.Horde, 'Horde'], + [LLMProvider.AWS, 'AWS'], +]) + export const LLMModels: LLMModel[] = [ { id: 'gpt35', @@ -69,7 +105,8 @@ export const LLMModels: LLMModel[] = [ format: LLMFormat.OpenAICompatible, flags: [ LLMFlags.hasImageInput - ] + ], + recommended: true }, { id: 'gpt4om', @@ -79,7 +116,8 @@ export const LLMModels: LLMModel[] = [ format: LLMFormat.OpenAICompatible, flags: [ LLMFlags.hasImageInput - ] + ], + recommended: true }, { id: 'gpt4', @@ -257,116 +295,169 @@ export const LLMModels: LLMModel[] = [ format: LLMFormat.OpenAICompatible, flags: [], }, + { + name: "Claude 3.5 Sonnet", + id: 'claude-3-5-sonnet-latest', + shortName: "3.5 Sonnet", + provider: LLMProvider.Anthropic, + format: LLMFormat.Anthropic, + flags: [LLMFlags.hasPrefill, LLMFlags.hasImageInput], + recommended: true + }, + { + name: "Claude 3.5 Haiku", + id: 'claude-3-5-haiku-latest', + shortName: "3.5 Haiku", + provider: LLMProvider.Anthropic, + format: LLMFormat.Anthropic, + flags: [LLMFlags.hasPrefill, LLMFlags.hasImageInput], + recommended: true + }, { name: 'Claude 3.5 Sonnet (20241022)', id: 'claude-3-5-sonnet-20241022', - provider: LLMProvider.Antropic, - format: LLMFormat.Antropic, + shortName: "3.5 Sonnet 1022", + provider: LLMProvider.Anthropic, + format: LLMFormat.Anthropic, + flags: [LLMFlags.hasPrefill, LLMFlags.hasImageInput], + }, + { + name: "Claude 3.5 Haiku (20241022)", + id: 'claude-3-5-haiku-20241022', + shortName: "3.5 Haiku 1022", + provider: LLMProvider.Anthropic, + format: LLMFormat.Anthropic, flags: [LLMFlags.hasPrefill, LLMFlags.hasImageInput], }, { name: 'Claude 3 Haiku (20240307)', id: 'claude-3-haiku-20240307', - provider: LLMProvider.Antropic, - format: LLMFormat.Antropic, + shortName: "3 Haiku 0307", + provider: LLMProvider.Anthropic, + format: LLMFormat.Anthropic, flags: [LLMFlags.hasPrefill, LLMFlags.hasImageInput], }, { name: 'Claude 3.5 Sonnet (20240620)', id: 'claude-3-5-sonnet-20240620', - provider: LLMProvider.Antropic, - format: LLMFormat.Antropic, + shortName: "3.5 Sonnet 0620", + provider: LLMProvider.Anthropic, + format: LLMFormat.Anthropic, flags: [LLMFlags.hasPrefill, LLMFlags.hasImageInput], }, { name: 'Claude 3 Opus (20240229)', id: 'claude-3-opus-20240229', - provider: LLMProvider.Antropic, - format: LLMFormat.Antropic, + shortName: "3 Opus 0229", + provider: LLMProvider.Anthropic, + format: LLMFormat.Anthropic, flags: [LLMFlags.hasPrefill, LLMFlags.hasImageInput], }, { name: 'Claude 3 Sonnet (20240229)', id: 'claude-3-sonnet-20240229', - provider: LLMProvider.Antropic, - format: LLMFormat.Antropic, + shortName: "3 Sonnet 0229", + provider: LLMProvider.Anthropic, + format: LLMFormat.Anthropic, flags: [LLMFlags.hasPrefill, LLMFlags.hasImageInput], }, { name: 'Claude 2.1', id: 'claude-2.1', - provider: LLMProvider.Antropic, - format: LLMFormat.Antropic, + provider: LLMProvider.Anthropic, + format: LLMFormat.AnthropicLegacy, flags: [LLMFlags.hasPrefill], }, { name: 'Claude 2', id: 'claude-2', - provider: LLMProvider.Antropic, - format: LLMFormat.Antropic, + provider: LLMProvider.Anthropic, + format: LLMFormat.AnthropicLegacy, flags: [LLMFlags.hasPrefill], }, { name: 'Claude 2 100k', id: 'claude-2-100k', - provider: LLMProvider.Antropic, - format: LLMFormat.Antropic, + provider: LLMProvider.Anthropic, + format: LLMFormat.AnthropicLegacy, flags: [LLMFlags.hasPrefill], }, { name: 'Claude v1', id: 'claude-v1', - provider: LLMProvider.Antropic, - format: LLMFormat.Antropic, + provider: LLMProvider.Anthropic, + format: LLMFormat.AnthropicLegacy, flags: [LLMFlags.hasPrefill], }, { name: 'Claude v1 100k', id: 'claude-v1-100k', - provider: LLMProvider.Antropic, - format: LLMFormat.Antropic, + provider: LLMProvider.Anthropic, + format: LLMFormat.AnthropicLegacy, flags: [LLMFlags.hasPrefill], }, { name: 'Claude Instant v1', id: 'claude-instant-v1', - provider: LLMProvider.Antropic, - format: LLMFormat.Antropic, + provider: LLMProvider.Anthropic, + format: LLMFormat.AnthropicLegacy, flags: [LLMFlags.hasPrefill], }, { name: 'Claude Instant v1 100k', id: 'claude-instant-v1-100k', - provider: LLMProvider.Antropic, - format: LLMFormat.Antropic, + provider: LLMProvider.Anthropic, + format: LLMFormat.AnthropicLegacy, flags: [LLMFlags.hasPrefill], }, { name: 'Claude v1.2', id: 'claude-1.2', - provider: LLMProvider.Antropic, - format: LLMFormat.Antropic, + provider: LLMProvider.Anthropic, + format: LLMFormat.AnthropicLegacy, flags: [LLMFlags.hasPrefill], }, { name: 'Claude v1.0', id: 'claude-1.0', - provider: LLMProvider.Antropic, - format: LLMFormat.Antropic, + provider: LLMProvider.Anthropic, + format: LLMFormat.AnthropicLegacy, flags: [LLMFlags.hasPrefill], }, + { + name: 'Claude 3.5 Sonnet (20241022) v2', + id: 'anthropic.claude-3-5-sonnet-20241022-v2:0', + provider: LLMProvider.AWS, + format: LLMFormat.AWSBedrockClaude, + flags: [LLMFlags.hasPrefill, LLMFlags.hasImageInput], + }, + { + name: 'Claude 3.5 Sonnet (20240620) v1', + id: 'anthropic.claude-3-5-sonnet-20240620-v1:0', + provider: LLMProvider.AWS, + format: LLMFormat.AWSBedrockClaude, + flags: [LLMFlags.hasPrefill, LLMFlags.hasImageInput], + }, + { + name: 'Claude 3 Opus (20240229) v1', + id: 'anthropic.claude-3-opus-20240229-v1:0', + provider: LLMProvider.AWS, + format: LLMFormat.AWSBedrockClaude, + flags: [LLMFlags.hasPrefill, LLMFlags.hasImageInput], + }, { name: 'Ooba', id: 'ooba', provider: LLMProvider.AsIs, - format: LLMFormat.AsIs, + format: LLMFormat.Ooba, flags: [], + recommended: true }, { name: 'Mancer', id: 'mancer', provider: LLMProvider.AsIs, - format: LLMFormat.AsIs, + format: LLMFormat.OobaLegacy, flags: [], }, { @@ -375,12 +466,382 @@ export const LLMModels: LLMModel[] = [ provider: LLMProvider.AsIs, format: LLMFormat.OpenAICompatible, flags: [], + recommended: true }, { - name: 'mistral-small-latest', + name: 'Mistral Small Latest', id: 'mistral-small-latest', + shortName: 'Mistral S', provider: LLMProvider.Mistral, format: LLMFormat.Mistral, flags: [], + recommended: true + }, + { + name: 'Mistral Medium Latest', + id: 'mistral-medium-latest', + shortName: 'Mistral M', + provider: LLMProvider.Mistral, + format: LLMFormat.Mistral, + flags: [], + recommended: true + }, + { + name: 'Mistral Large 2411', + id: 'mistral-large-2411', + shortName: 'Mistral L 2411', + provider: LLMProvider.Mistral, + format: LLMFormat.Mistral, + flags: [], + }, + { + name: 'Mistral Nemo', + id: 'open-mistral-nemo', + shortName: 'Mistral Nemo', + provider: LLMProvider.Mistral, + format: LLMFormat.Mistral, + flags: [], + }, + { + name: 'Mistral Large Latest', + id: 'mistral-large-latest', + shortName: 'Mistral L', + provider: LLMProvider.Mistral, + format: LLMFormat.Mistral, + flags: [], + recommended: true + }, + { + name: "Gemini Pro 1.5 0827", + id: 'gemini-1.5-pro-exp-0827', + provider: LLMProvider.GoogleCloud, + format: LLMFormat.GoogleCloud, + flags: [LLMFlags.hasImageInput], + }, + { + name: "Gemini Exp 1121", + id: 'gemini-exp-1121', + provider: LLMProvider.GoogleCloud, + format: LLMFormat.GoogleCloud, + flags: [LLMFlags.hasImageInput], + recommended: true + }, + { + name: "Gemini Pro 1.5", + id: 'gemini-1.5-pro-latest', + provider: LLMProvider.GoogleCloud, + format: LLMFormat.GoogleCloud, + flags: [LLMFlags.hasImageInput], + recommended: true + }, + { + name: "Gemini Flash 1.5", + id: 'gemini-1.5-flash', + provider: LLMProvider.GoogleCloud, + format: LLMFormat.GoogleCloud, + flags: [LLMFlags.hasImageInput], + recommended: true + }, + { + name: "Gemini Exp 1121", + id: 'gemini-exp-1121-vertex', + internalID: 'gemini-exp-1121', + provider: LLMProvider.GoogleCloud, + format: LLMFormat.VertexAIGemini, + flags: [LLMFlags.hasImageInput], + }, + { + name: "Gemini Pro 1.5", + id: 'gemini-1.5-pro-latest-vertex', + internalID: 'gemini-1.5-pro-latest', + provider: LLMProvider.GoogleCloud, + format: LLMFormat.VertexAIGemini, + flags: [LLMFlags.hasImageInput], + }, + { + name: "Gemini Flash 1.5", + id: 'gemini-1.5-flash-vertex', + internalID: 'gemini-1.5-flash', + provider: LLMProvider.GoogleCloud, + format: LLMFormat.VertexAIGemini, + flags: [LLMFlags.hasImageInput], + }, + { + name: "Gemini Exp 1114", + id: 'gemini-exp-1114', + provider: LLMProvider.GoogleCloud, + format: LLMFormat.GoogleCloud, + flags: [LLMFlags.hasImageInput], + }, + { + name: "Gemini Pro 1.5 002", + id: 'gemini-1.5-pro-002', + provider: LLMProvider.GoogleCloud, + format: LLMFormat.GoogleCloud, + flags: [LLMFlags.hasImageInput], + }, + { + name: "Gemini Flash 1.5 002", + id: 'gemini-1.5-flash-002', + provider: LLMProvider.GoogleCloud, + format: LLMFormat.GoogleCloud, + flags: [LLMFlags.hasImageInput], + }, + { + name: "Gemini Pro", + id: 'gemini-pro', + provider: LLMProvider.GoogleCloud, + format: LLMFormat.GoogleCloud, + flags: [LLMFlags.hasImageInput], + }, + { + name: "Gemini Pro Vision", + id: 'gemini-pro-vision', + provider: LLMProvider.GoogleCloud, + format: LLMFormat.GoogleCloud, + flags: [LLMFlags.hasImageInput], + }, + { + name: "Gemini Ultra", + id: 'gemini-ultra', + provider: LLMProvider.GoogleCloud, + format: LLMFormat.GoogleCloud, + flags: [LLMFlags.hasImageInput], + }, + { + name: "Gemini Ultra Vision", + id: 'gemini-ultra-vision', + provider: LLMProvider.GoogleCloud, + format: LLMFormat.GoogleCloud, + flags: [LLMFlags.hasImageInput], + }, + { + name: 'Kobold', + id: 'kobold', + provider: LLMProvider.AsIs, + format: LLMFormat.Kobold, + flags: [], + recommended: true + }, + { + name: "SuperTrin", + id: 'novellist', + provider: LLMProvider.NovelList, + format: LLMFormat.NovelList, + flags: [], + }, + { + name: "Damsel", + id: 'novellist_damsel', + provider: LLMProvider.NovelList, + format: LLMFormat.NovelList, + flags: [], + }, + { + name: "Command R", + id: 'cohere-command-r', + internalID: 'command-r', + provider: LLMProvider.Cohere, + format: LLMFormat.Cohere, + flags: [], + recommended: true + }, + { + name: "Command R Plus", + id: 'cohere-command-r-plus', + internalID: 'command-r-plus', + provider: LLMProvider.Cohere, + format: LLMFormat.Cohere, + flags: [], + recommended: true + }, + { + name: "Command R 08-2024", + id: 'cohere-command-r-08-2024', + internalID: 'command-r-08-2024', + provider: LLMProvider.Cohere, + format: LLMFormat.Cohere, + flags: [], + }, + { + name: "Command R 03-2024", + id: 'cohere-command-r-03-2024', + internalID: 'command-r-03-2024', + provider: LLMProvider.Cohere, + format: LLMFormat.Cohere, + flags: [], + }, + { + name: "Command R Plus 08-2024", + id: 'cohere-command-r-plus-08-2024', + internalID: 'command-r-plus-08-2024', + provider: LLMProvider.Cohere, + format: LLMFormat.Cohere, + flags: [], + }, + { + name: "Command R Plus 04-2024", + id: 'cohere-command-r-plus-04-2024', + internalID: 'command-r-plus-04-2024', + provider: LLMProvider.Cohere, + format: LLMFormat.Cohere, + flags: [], + }, + { + name: "Clio", + id: 'novelai', + provider: LLMProvider.NovelAI, + format: LLMFormat.NovelAI, + flags: [], + recommended: true + }, + { + name: "Kayra", + id: 'novelai_kayra', + provider: LLMProvider.NovelAI, + format: LLMFormat.NovelAI, + flags: [], + recommended: true + }, + { + id: 'ollama-hosted', + name: 'Ollama', + provider: LLMProvider.AsIs, + format: LLMFormat.Ollama, + flags: [], + }, + { + id: 'hf:::Xenova/opt-350m', + name: 'opt-350m', + provider: LLMProvider.WebLLM, + format: LLMFormat.WebLLM, + flags: [], + }, + { + id: 'hf:::Xenova/tiny-random-mistral', + name: 'tiny-random-mistral', + provider: LLMProvider.WebLLM, + format: LLMFormat.WebLLM, + flags: [], + }, + { + id: 'hf:::Xenova/gpt2-large-conversational', + name: 'gpt2-large-conversational', + provider: LLMProvider.WebLLM, + format: LLMFormat.WebLLM, + flags: [], + }, + { + id: 'custom', + name: "Plugin", + provider: LLMProvider.AsIs, + format: LLMFormat.Plugin, + flags: [], + recommended: true + }, + { + id: 'reverse_proxy', + name: "Custom API", + provider: LLMProvider.AsIs, + format: LLMFormat.OpenAICompatible, + flags: [], + recommended: true } -] \ No newline at end of file +] + +for(let model of LLMModels){ + model.shortName ??= model.name + model.internalID ??= model.id + model.fullName ??= model.provider !== LLMProvider.AsIs ? `${ProviderNames.get(model.provider) ?? ''} ${model.name}`.trim() : model.name +} + +export function getModelInfo(id: string): LLMModel{ + + const found = LLMModels.find(model => model.id === id) ?? { + id, + name: id, + provider: LLMProvider.AsIs, + format: LLMFormat.OpenAICompatible, + flags: [], + } + + if(found) return found + + if(id.startsWith('hf:::')){ + const withoutPrefix = id.replace('hf:::', '') + return { + id, + name: withoutPrefix, + shortName: withoutPrefix, + fullName: withoutPrefix, + internalID: withoutPrefix, + provider: LLMProvider.WebLLM, + format: LLMFormat.WebLLM, + flags: [], + } + } + if(id.startsWith('horde:::')){ + const withoutPrefix = id.replace('horde:::', '') + return { + id, + name: withoutPrefix, + shortName: withoutPrefix, + fullName: withoutPrefix, + internalID: withoutPrefix, + provider: LLMProvider.Horde, + format: LLMFormat.Horde, + flags: [], + } + } + + return { + id, + name: id, + shortName: id, + fullName: id, + internalID: id, + provider: LLMProvider.AsIs, + format: LLMFormat.OpenAICompatible, + flags: [], + } +} + +interface GetModelListGroup { + providerName: string + models: LLMModel[] +} + +export function getModelList(arg:{ + recommendedOnly?:boolean, + groupedByProvider?:T +} = {}): T extends true ? GetModelListGroup[] : LLMModel[]{ + let models = LLMModels + if(arg.recommendedOnly){ + models = models.filter(model => model.recommended) + } + if(arg.groupedByProvider){ + let group: GetModelListGroup[] = [] + for(let model of models){ + if(model.provider === LLMProvider.AsIs){ + group.push({ + providerName: '@as-is', + models: [model] + }) + continue + } + + let providerName = ProviderNames.get(model.provider) || 'Unknown' + let groupIndex = group.findIndex(g => g.providerName === providerName) + if(groupIndex === -1){ + group.push({ + providerName, + models: [model] + }) + }else{ + group[groupIndex].models.push(model) + } + } + return group as any + } + return models as any +} \ No newline at end of file diff --git a/src/ts/model/names.ts b/src/ts/model/names.ts deleted file mode 100644 index ea34842e..00000000 --- a/src/ts/model/names.ts +++ /dev/null @@ -1,204 +0,0 @@ - -export function getModelName(name:string){ - switch(name){ - case "gpt35": - return "GPT-3.5 Turbo" - case "gpt35_0613": - return "GPT-3.5 Turbo 0613" - case "gpt35_0301": - return "GPT-3.5 Turbo 0301" - case "gpt35_16k": - return "GPT-3.5 Turbo 16k" - case "gpt35_16k_0613": - return "GPT-3.5 Turbo 16k 0613" - case 'instructgpt35': - return 'GPT-3.5 Turbo Instruct' - case "gpt4": - return "GPT-4" - case "gpt4_0301": - return "GPT-4 0301" - case "gpt4_32k": - return "GPT-4 32k" - case "gpt4_0613": - return "GPT-4 0613" - case "gpt4_32k_0613": - return "GPT-4 32k 0613" - case "gpt4_1106": - return "GPT-4 Turbo 1106" - case 'gpt45': - return 'GPT-4.5' - case "gpt35_1106": - return "GPT-3.5 Turbo 1106" - case 'local_gptq': - return 'Local Model GPTQ' - case "palm2": - return "PaLM2 Bison" - case "textgen_webui": - return "Oobabooga Legacy" - case 'ooba': - return 'Oobabooga' - case "mancer": - return "Mancer" - case "kobold": - return "Kobold" - case "custom": - return "Plugin" - case "novelai": - return "NovelAI Clio" - case "novelai_kayra": - return "NovelAI Kayra" - case "novellist": - return "NovelList SuperTrin" - case "novellist damsel": - return "NovelList Damsel" - case 'reverse_proxy': - return "Custom (OpenAI-compatible)" - case 'openrouter': - return "OpenRouter" - case 'gptvi4_1106': - return "GPT-4 Turbo 1106 Vision" - case 'palm2_unicorn': - return "PaLM2 Unicorn" - case 'mistral-tiny': - return "Mistral Tiny" - case 'mistral-small': - return "Mistral Small" - case 'mistral-medium': - return "Mistral Medium" - case 'gemini-pro': - return "Gemini Pro" - case 'horde:::auto': - return 'Horde Auto Model' - case 'gpt4_0125': - return 'GPT-4 Turbo 0125' - case 'gpt35_0125': - return 'GPT-3.5 Turbo 0125' - case 'gemini-ultra': - return 'Gemini Ultra' - case 'gemini-ultra-vision': - return 'Gemini Ultra Vision' - case 'claude-3-opus-20240229': - return 'Claude 3 Opus (20240229)' - case 'claude-3-5-sonnet-20240620': - return 'Claude 3.5 Sonnet (20240620)' - case 'claude-3-5-sonnet-20241022': - return 'Claude 3.5 Sonnet (20241022)' - case 'claude-3-sonnet-20240229': - return 'Claude 3 Sonnet (20240229)' - case 'mistral-large-latest': - return 'Mistral Large' - case 'mistral-small-latest': - return 'Mistral Small' - case 'mistral-medium-latest': - return 'Mistral Medium' - case 'claude-3-haiku-20240307': - return 'Claude 3 Haiku (20240307)' - case 'gpt4_turbo': - return 'GPT-4 Turbo' - case 'gpt4_turbo_20240409': - return 'GPT-4 Turbo (20240409)' - case 'gpt4o': - return 'GPT-4o' - case 'gpt4o-2024-05-13': - return 'GPT-4o (2024-05-13)' - case 'gpt4o-2024-08-06': - return 'GPT-4o (2024-08-06)' - case 'gpt4o-2024-11-20': - return 'GPT-4o (2024-11-20)' - case 'gpt4o-chatgpt': - return 'GPT-4o ChatGPT' - case 'gpt4om': - return 'GPT-4o Mini' - case 'gpt4o1-preview': - return 'o1 Preview' - case 'gpt4o1-mini': - return 'o1 Mini' - case 'gpt4om-2024-07-18': - return 'GPT-4o Mini (2024-07-18)' - case 'gemini-1.5-pro-latest': - return 'Gemini 1.5 Pro' - case 'gemini-1.5-pro-exp-0801': - return 'Gemini 1.5 Pro Exp (0801)' - case 'gemini-1.5-pro-exp-0827': - return 'Gemini 1.5 Pro Exp (0827)' - case 'gemini-1.5-flash': - return 'Gemini 1.5 Flash' - case 'ollama-hosted': - return 'Ollama' - case 'cohere-command-r': - return 'Cohere Command-R' - case 'cohere-command-r-plus': - return 'Cohere Command-R Plus' - default: - if(name.startsWith("horde:::")){ - const split = name.split(":::") - return `Horde ${split[1]}` - } - if(name.startsWith('tf:::')){ - const split = name.split(":::") - return `${split[1]}` - } - if(name.startsWith('local_')){ - const realName = name.replace('local_', '').split(/(\\|\/)/g).at(-1) - return `GGUF ${realName}` - } - return name - } -} - -export function getModelShortName(model:string){ - if(model.startsWith("gpt35")){ - return "GPT-3.5" - } - if(model.startsWith("cohere-")){ - return model.replace("cohere-", "") - } - if(model.startsWith('gpt4om')){ - return "GPT-4o Mini" - } - if(model.startsWith("gpt4o")){ - return "GPT-4o" - } - if(model.startsWith("gpt4")){ - return "GPT-4" - } - if(model.startsWith("gptvi4")){ - return "GPT-4V" - } - if(model.startsWith("mistral")){ - return getModelName(model).split(" ").at(-1) - } - if(model.startsWith("mancer")){ - return "Mancer" - } - if(model.startsWith('tf:::')){ - const split = model.split(":::") - return split[1] - } - if(model.startsWith('local_')){ - const realName = model.replace('local_', '').split(/(\\|\/)/g).at(-1) - return realName - } - if(model.startsWith('horde:::')){ - const split = model.split(":::") - return split[1] - } - - if(model.startsWith('claude-3')){ - const split = model.split("-") - if(!isNaN(parseInt(split[split.length-1]))){ - return split[split.length-2] - } - else{ - return split[split.length-1] - } - } - if(model.startsWith('reverse_proxy')){ - return 'Custom' - } - if(model.startsWith('oaicomp')){ - return 'Custom' - } - return getModelName(model) - -} \ No newline at end of file diff --git a/src/ts/process/request.ts b/src/ts/process/request.ts index e22c24d2..0932b9c5 100644 --- a/src/ts/process/request.ts +++ b/src/ts/process/request.ts @@ -1,6 +1,6 @@ import { get } from "svelte/store"; import type { MultiModal, OpenAIChat, OpenAIChatFull } from "./index.svelte"; -import { getDatabase, type character } from "../storage/database.svelte"; +import { getCurrentCharacter, getDatabase, type character } from "../storage/database.svelte"; import { pluginProcess } from "../plugins/plugins"; import { language } from "../../lang"; import { stringlizeAINChat, stringlizeChat, getStopStrings, unstringlizeAIN, unstringlizeChat } from "./stringlize"; @@ -23,6 +23,7 @@ import {Ollama} from 'ollama/dist/browser.mjs' import { applyChatTemplate } from "./templates/chatTemplate"; import { OobaParams } from "./prompt"; import { extractJSON, getOpenAIJSONSchema } from "./templates/jsonSchema"; +import { getModelInfo, LLMFormat, type LLMModel } from "../model/modellist"; @@ -43,6 +44,14 @@ interface requestDataArgument{ noMultiGen?:boolean } +interface RequestDataArgumentExtended extends requestDataArgument{ + aiModel?:string + multiGen?:boolean + realAIModel?:string + abortSignal?:AbortSignal + modelInfo?:LLMModel +} + type requestDataResponse = { type: 'success'|'fail' result: string @@ -179,2531 +188,2260 @@ export interface OpenAIChatExtra { export async function requestChatDataMain(arg:requestDataArgument, model:'model'|'submodel', abortSignal:AbortSignal=null):Promise { const db = getDatabase() - let formated = safeStructuredClone(arg.formated) - let maxTokens = arg.maxTokens ??db.maxResponse - let temperature = arg.temperature ?? (db.temperature / 100) - let bias = arg.bias - let currentChar = arg.currentChar - let useStreaming = db.useStreaming && arg.useStreaming - arg.continue = arg.continue ?? false - let biasString = arg.biasString ?? [] - const aiModel = model === 'model' ? db.aiModel : db.subModel - const multiGen = (db.genTime > 1 && aiModel.startsWith('gpt') && (!arg.continue)) && (!arg.noMultiGen) - - let raiModel = aiModel - if(aiModel === 'reverse_proxy'){ + const targ:RequestDataArgumentExtended = arg + targ.formated = safeStructuredClone(arg.formated) + targ.maxTokens = arg.maxTokens ??db.maxResponse + targ.temperature = arg.temperature ?? (db.temperature / 100) + targ.bias = arg.bias + targ.currentChar = arg.currentChar + targ.useStreaming = db.useStreaming && arg.useStreaming + targ.continue = arg.continue ?? false + targ.biasString = arg.biasString ?? [] + targ.aiModel = (model === 'model' ? db.aiModel : db.subModel) + targ.multiGen = ((db.genTime > 1 && targ.aiModel.startsWith('gpt') && (!arg.continue)) && (!arg.noMultiGen)) + targ.realAIModel = targ.aiModel + targ.abortSignal = abortSignal + targ.modelInfo = getModelInfo(targ.aiModel) + if(targ.aiModel === 'reverse_proxy'){ if(db.proxyRequestModel === 'custom' && db.customProxyRequestModel.startsWith('claude')){ - raiModel = db.customProxyRequestModel + targ.realAIModel = db.customProxyRequestModel } if(db.proxyRequestModel.startsWith('claude')){ - raiModel = db.proxyRequestModel + targ.realAIModel = db.proxyRequestModel } if(db.forceProxyAsOpenAI){ - raiModel = 'reverse_proxy' + targ.realAIModel = 'reverse_proxy' } } - console.log(formated) - switch(raiModel){ - case 'gpt35': - case 'gpt35_0613': - case 'gpt35_16k': - case 'gpt35_16k_0613': - case 'gpt4': - case 'gpt45': - case 'gpt4_32k': - case 'gpt4_0613': - case 'gpt4_32k_0613': - case 'gpt4_1106': - case 'gpt4_0125': - case 'gpt35_0125': - case 'gpt35_1106': - case 'gpt35_0301': - case 'gpt4_0314': - case 'gptvi4_1106': - case 'openrouter': - case 'mistral-tiny': - case 'mistral-small': - case 'mistral-medium': - case 'mistral-small-latest': - case 'mistral-medium-latest': - case 'mistral-large-latest': - case 'mistral-large-2411': - case 'open-mistral-nemo': - case 'gpt4_turbo_20240409': - case 'gpt4_turbo': - case 'gpt4o': - case 'gpt4o-2024-05-13': - case 'gpt4om': - case 'gpt4om-2024-07-18': - case 'gpt4o-2024-08-06': - case 'gpt4o-2024-11-20': - case 'gpt4o-chatgpt': - case 'gpt4o1-preview': - case 'gpt4o1-mini': - case 'jamba-1.5-large': - case 'jamba-1.5-medium': - case 'reverse_proxy':{ - let formatedChat:OpenAIChatExtra[] = [] - for(let i=0;i 0 && m.role === 'user'){ - let v:OpenAIChatExtra = safeStructuredClone(m) - let contents:OpenAIContents[] = [] - for(let j=0;j{ + let formatedChat:OpenAIChatExtra[] = [] + const formated = arg.formated + const db = getDatabase() + const aiModel = arg.aiModel + for(let i=0;i 0 && m.role === 'user'){ + let v:OpenAIChatExtra = safeStructuredClone(m) + let contents:OpenAIContents[] = [] + for(let j=0;j 0){ + formatedChat.push({ + role: 'system', + content: oobaSystemPrompts.join('\n') + }) + } + + + if(db.newOAIHandle){ + formatedChat = formatedChat.filter(m => { + return m.content !== '' + }) + } + + if(aiModel.startsWith('gpt4o1')){ + for(let i=0;i${formatedChat[i].content}` + formatedChat[i].role = 'user' + } + } + } + + for(let i=0;i 0){ - formatedChat.push({ - role: 'system', - content: oobaSystemPrompts.join('\n') - }) - } - - - if(db.newOAIHandle){ - formatedChat = formatedChat.filter(m => { - return m.content !== '' - }) - } - - if(aiModel.startsWith('gpt4o1')){ - for(let i=0;i${formatedChat[i].content}` - formatedChat[i].role = 'user' - } - } - } - - for(let i=0;i 0){ - body.seed = db.generationSeed - } - - if(db.putUserOpen){ - body.user = getOpenUserString() - } - - if(db.jsonSchemaEnabled){ - body.response_format = { - "type": "json_schema", - "json_schema": getOpenAIJSONSchema() - } - } - - if(db.OAIPrediction){ - body.prediction = { - type: "content", - content: db.OAIPrediction - } - } - - if(aiModel === 'openrouter'){ - if(db.openrouterFallback){ - body.route = "fallback" - } - body.transforms = db.openrouterMiddleOut ? ['middle-out'] : [] - - if(db.openrouterProvider){ - body.provider = { - order: [db.openrouterProvider] - } - } - - if(db.useInstructPrompt){ - delete body.messages - const prompt = applyChatTemplate(formated) - body.prompt = prompt - } - } - - body = applyParameters(body, - aiModel === 'openrouter' ? ['temperature', 'top_p', 'frequency_penalty', 'presence_penalty', 'repetition_penalty', 'min_p', 'top_a', 'top_k'] : ['temperature', 'top_p', 'frequency_penalty', 'presence_penalty'] - ) - - if(aiModel === 'reverse_proxy' && db.reverseProxyOobaMode){ - const OobaBodyTemplate = db.reverseProxyOobaArgs - - const keys = Object.keys(OobaBodyTemplate) - for(const key of keys){ - if(OobaBodyTemplate[key] !== undefined && OobaBodyTemplate[key] !== null){ - // @ts-ignore - body[key] = OobaBodyTemplate[key] - } - } - - } - - if(supportsInlayImage()){ - // inlay models doesn't support logit_bias - // OpenAI's gpt based llm model supports both logit_bias and inlay image - if(!( - aiModel.startsWith('gpt') || - (aiModel == 'reverse_proxy' && ( - db.proxyRequestModel?.startsWith('gpt') || - (db.proxyRequestModel === 'custom' && db.customProxyRequestModel.startsWith('gpt')) - )))){ - // @ts-ignore - delete body.logit_bias - } - } - - let replacerURL = aiModel === 'openrouter' ? "https://openrouter.ai/api/v1/chat/completions" : - (aiModel === 'reverse_proxy') ? (db.forceReplaceUrl) : ('https://api.openai.com/v1/chat/completions') - - let risuIdentify = false - if(replacerURL.startsWith("risu::")){ - risuIdentify = true - replacerURL = replacerURL.replace("risu::", '') - } - - if(aiModel === 'reverse_proxy' && db.autofillRequestUrl){ - if(replacerURL.endsWith('v1')){ - replacerURL += '/chat/completions' - } - else if(replacerURL.endsWith('v1/')){ - replacerURL += 'chat/completions' - } - else if(!(replacerURL.endsWith('completions') || replacerURL.endsWith('completions/'))){ - if(replacerURL.endsWith('/')){ - replacerURL += 'v1/chat/completions' - } - else{ - replacerURL += '/v1/chat/completions' - } - } - } - - let headers = { - "Authorization": "Bearer " + (aiModel === 'reverse_proxy' ? db.proxyKey : (aiModel === 'openrouter' ? db.openrouterKey : db.openAIKey)), - "Content-Type": "application/json" - } - - if(aiModel === 'openrouter'){ - headers["X-Title"] = 'RisuAI' - headers["HTTP-Referer"] = 'https://risuai.xyz' - } - if(risuIdentify){ - headers["X-Proxy-Risu"] = 'RisuAI' - } - if(aiModel.startsWith('jamba')){ - headers['Authorization'] = 'Bearer ' + db.ai21Key - replacerURL = 'https://api.ai21.com/studio/v1/chat/completions' - } - if(multiGen){ - // @ts-ignore - body.n = db.genTime - } - let throughProxi = (!isTauri) && (!isNodeServer) && (!db.usePlainFetch) && (!Capacitor.isNativePlatform()) - if(useStreaming){ - body.stream = true - let urlHost = new URL(replacerURL).host - if(urlHost.includes("localhost") || urlHost.includes("172.0.0.1") || urlHost.includes("0.0.0.0")){ - if(!isTauri){ - return { - type: 'fail', - result: 'You are trying local request on streaming. this is not allowed dude to browser/os security policy. turn off streaming.', - } - } - } - const da = await fetchNative(replacerURL, { - body: JSON.stringify(body), - method: "POST", - headers: headers, - signal: abortSignal, - chatId: arg.chatId - }) - - if(da.status !== 200){ - return { - type: "fail", - result: await textifyReadableStream(da.body) - } - } - - if (!da.headers.get('Content-Type').includes('text/event-stream')){ - return { - type: "fail", - result: await textifyReadableStream(da.body) - } - } - - addFetchLog({ - body: body, - response: "Streaming", - success: true, - url: replacerURL, - }) - - let dataUint:Uint8Array|Buffer = new Uint8Array([]) - - const transtream = new TransformStream( { - async transform(chunk, control) { - dataUint = Buffer.from(new Uint8Array([...dataUint, ...chunk])) - let JSONreaded:{[key:string]:string} = {} - try { - const datas = dataUint.toString().split('\n') - let readed:{[key:string]:string} = {} - for(const data of datas){ - if(data.startsWith("data: ")){ - try { - const rawChunk = data.replace("data: ", "") - if(rawChunk === "[DONE]"){ - if(db.extractJson && db.jsonSchemaEnabled){ - for(const key in readed){ - const extracted = extractJSON(readed[key], db.extractJson) - JSONreaded[key] = extracted - } - console.log(JSONreaded) - control.enqueue(JSONreaded) - } - else{ - control.enqueue(readed) - } - return - } - const choices = JSON.parse(rawChunk).choices - for(const choice of choices){ - const chunk = choice.delta.content ?? choices.text - if(chunk){ - if(multiGen){ - const ind = choice.index.toString() - if(!readed[ind]){ - readed[ind] = "" - } - readed[ind] += chunk - } - else{ - if(!readed["0"]){ - readed["0"] = "" - } - readed["0"] += chunk - } - } - } - } catch (error) {} - } - } - if(db.extractJson && db.jsonSchemaEnabled){ - for(const key in readed){ - const extracted = extractJSON(readed[key], db.extractJson) - JSONreaded[key] = extracted - } - console.log(JSONreaded) - control.enqueue(JSONreaded) - } - else{ - control.enqueue(readed) - } - } catch (error) { - - } - } - },) - - da.body.pipeTo(transtream.writable) - - return { - type: 'streaming', - result: transtream.readable - } - } - - if(raiModel === 'reverse_proxy'){ - const additionalParams = db.additionalParams - for(let i=0;i { - const extracted = extractJSON(v.message.content, db.extractJson) - return ["char",extracted] - }) - - return { - type: 'multiline', - result: c - } - - } - return { - type: 'multiline', - result: dat.choices.map((v) => { - return ["char",v.message.content] - }) - } - - } - - if(dat?.choices[0]?.text){ - if(db.extractJson && db.jsonSchemaEnabled){ - try { - const parsed = JSON.parse(dat.choices[0].text) - const extracted = extractJSON(parsed, db.extractJson) - return { - type: 'success', - result: extracted - } - } catch (error) { - console.log(error) - return { - type: 'success', - result: dat.choices[0].text - } - } - } - return { - type: 'success', - result: dat.choices[0].text - } - } - if(db.extractJson && db.jsonSchemaEnabled){ - return { - type: 'success', - result: extractJSON(dat.choices[0].message.content, db.extractJson) - } - } - const msg:OpenAIChatFull = (dat.choices[0].message) - return { - type: 'success', - result: msg.content - } - } catch (error) { - return { - type: 'fail', - result: (language.errors.httpError + `${JSON.stringify(dat)}`) - } - } - } - else{ - if(dat.error && dat.error.message){ - return { - type: 'fail', - result: (language.errors.httpError + `${dat.error.message}`) - } - } - else{ - return { - type: 'fail', - result: (language.errors.httpError + `${JSON.stringify(res.data)}`) - } - } - } - - break - } - case 'novelai': - case 'novelai_kayra':{ - console.log(arg.continue) - const prompt = stringlizeNAIChat(formated, currentChar?.name ?? '', arg.continue) - let logit_bias_exp:{ - sequence: number[], bias: number, ensure_sequence_finish: false, generate_once: true - }[] = [] - - for(let i=0;i m.content?.trim()).map(m => { - let author = ''; - - if(m.role == 'system'){ - m.content = m.content.trim(); - } - - console.log(m.role +":"+m.content); - switch (m.role) { - case 'user': author = 'User'; break; - case 'assistant': author = 'Assistant'; break; - case 'system': author = 'Instruction'; break; - default: author = m.role; break; - } - - return `\n## ${author}\n${m.content.trim()}`; - //return `\n\n${author}: ${m.content.trim()}`; - }).join("") + `\n## Response\n`; - - const response = await globalFetch( "https://api.openai.com/v1/completions", { - body: { - model: "gpt-3.5-turbo-instruct", - prompt: prompt, - max_tokens: maxTokens, - temperature: temperature, - top_p: 1, - stop:["User:"," User:", "user:", " user:"], - presence_penalty: arg.PresensePenalty || (db.PresensePenalty / 100), - frequency_penalty: arg.frequencyPenalty || (db.frequencyPenalty / 100), - }, - headers: { - "Content-Type": "application/json", - "Authorization": "Bearer " + db.openAIKey, - }, - chatId: arg.chatId - }); - - if(!response.ok){ - return { - type: 'fail', - result: (language.errors.httpError + `${JSON.stringify(response.data)}`) - } - } - const text:string = response.data.choices[0].text - return { - type: 'success', - result: text.replace(/##\n/g, '') - } - } - case "textgen_webui": - case 'mancer':{ - let streamUrl = db.textgenWebUIStreamURL.replace(/\/api.*/, "/api/v1/stream") - let blockingUrl = db.textgenWebUIBlockingURL.replace(/\/api.*/, "/api/v1/generate") - let bodyTemplate:{[key:string]:any} = {} - const suggesting = model === "submodel" - const prompt = applyChatTemplate(formated) - let stopStrings = getStopStrings(suggesting) - if(db.localStopStrings){ - stopStrings = db.localStopStrings.map((v) => { - return risuChatParser(v.replace(/\\n/g, "\n")) - }) - } - bodyTemplate = { - 'max_new_tokens': db.maxResponse, - 'do_sample': db.ooba.do_sample, - 'temperature': (db.temperature / 100), - 'top_p': db.ooba.top_p, - 'typical_p': db.ooba.typical_p, - 'repetition_penalty': db.ooba.repetition_penalty, - 'encoder_repetition_penalty': db.ooba.encoder_repetition_penalty, - 'top_k': db.ooba.top_k, - 'min_length': db.ooba.min_length, - 'no_repeat_ngram_size': db.ooba.no_repeat_ngram_size, - 'num_beams': db.ooba.num_beams, - 'penalty_alpha': db.ooba.penalty_alpha, - 'length_penalty': db.ooba.length_penalty, - 'early_stopping': false, - 'truncation_length': maxTokens, - 'ban_eos_token': db.ooba.ban_eos_token, - 'stopping_strings': stopStrings, - 'seed': -1, - add_bos_token: db.ooba.add_bos_token, - topP: db.top_p, - prompt: prompt - } - - const headers = (aiModel === 'textgen_webui') ? {} : { - 'X-API-KEY': db.mancerHeader - } - - if(useStreaming){ - const oobaboogaSocket = new WebSocket(streamUrl); - const statusCode = await new Promise((resolve) => { - oobaboogaSocket.onopen = () => resolve(0) - oobaboogaSocket.onerror = () => resolve(1001) - oobaboogaSocket.onclose = ({ code }) => resolve(code) - }) - if(abortSignal.aborted || statusCode !== 0) { - oobaboogaSocket.close() - return ({ - type: "fail", - result: abortSignal.reason || `WebSocket connection failed to '${streamUrl}' failed!`, + else if(chat.role === 'function'){ + reformatedChat.push({ + role: 'user', + content: chat.content }) } - - const close = () => { - oobaboogaSocket.close() + else{ + reformatedChat.push({ + role: chat.role, + content: chat.content + }) } - const stream = new ReadableStream({ - start(controller){ - let readed = ""; - oobaboogaSocket.onmessage = async (event) => { - const json = JSON.parse(event.data); - if (json.event === "stream_end") { - close() - controller.close() - return - } - if (json.event !== "text_stream") return - readed += json.text - controller.enqueue(readed) - }; - oobaboogaSocket.send(JSON.stringify(bodyTemplate)); - }, - cancel(){ - close() - } - }) - oobaboogaSocket.onerror = close - oobaboogaSocket.onclose = close - abortSignal.addEventListener("abort", close) + } + } + + const res = await globalFetch("https://api.mistral.ai/v1/chat/completions", { + body: { + model: requestModel, + messages: reformatedChat, + temperature: arg.temperature, + max_tokens: arg.maxTokens, + top_p: db.top_p, + safe_prompt: false + }, + headers: { + "Authorization": "Bearer " + db.mistralKey, + }, + abortSignal: arg.abortSignal, + chatId: arg.chatId + }) + const dat = res.data as any + if(res.ok){ + try { + const msg:OpenAIChatFull = (dat.choices[0].message) return { - type: 'streaming', - result: stream + type: 'success', + result: msg.content + } + } catch (error) { + return { + type: 'fail', + result: (language.errors.httpError + `${JSON.stringify(dat)}`) } } - - const res = await globalFetch(blockingUrl, { - body: bodyTemplate, - headers: headers, - abortSignal, - chatId: arg.chatId - }) - - const dat = res.data as any - if(res.ok){ - try { - let result:string = dat.results[0].text - if(suggesting){ - result = "\n" + db.autoSuggestPrefix + result - } - - return { - type: 'success', - result: unstringlizeChat(result, formated, currentChar?.name ?? '') - } - } catch (error) { - return { - type: 'fail', - result: (language.errors.httpError + `${error}`) - } + } + else{ + if(dat.error && dat.error.message){ + return { + type: 'fail', + result: (language.errors.httpError + `${dat.error.message}`) } } - else{ + else{ return { type: 'fail', result: (language.errors.httpError + `${JSON.stringify(res.data)}`) } } } - - case 'ooba': { - const suggesting = model === "submodel" + } + + db.cipherChat = false + let body:{ + [key:string]:any + } = ({ + model: aiModel === 'openrouter' ? openrouterRequestModel : + requestModel === 'gpt35' ? 'gpt-3.5-turbo' + : requestModel === 'gpt35_0613' ? 'gpt-3.5-turbo-0613' + : requestModel === 'gpt35_16k' ? 'gpt-3.5-turbo-16k' + : requestModel === 'gpt35_16k_0613' ? 'gpt-3.5-turbo-16k-0613' + : requestModel === 'gpt4' ? 'gpt-4' + : requestModel === 'gpt45' ? 'gpt-4.5-preview' + : requestModel === 'gpt4_32k' ? 'gpt-4-32k' + : requestModel === "gpt4_0613" ? 'gpt-4-0613' + : requestModel === "gpt4_32k_0613" ? 'gpt-4-32k-0613' + : requestModel === "gpt4_1106" ? 'gpt-4-1106-preview' + : requestModel === 'gpt4_0125' ? 'gpt-4-0125-preview' + : requestModel === "gptvi4_1106" ? 'gpt-4-vision-preview' + : requestModel === "gpt35_0125" ? 'gpt-3.5-turbo-0125' + : requestModel === "gpt35_1106" ? 'gpt-3.5-turbo-1106' + : requestModel === 'gpt35_0301' ? 'gpt-3.5-turbo-0301' + : requestModel === 'gpt4_0314' ? 'gpt-4-0314' + : requestModel === 'gpt4_turbo_20240409' ? 'gpt-4-turbo-2024-04-09' + : requestModel === 'gpt4_turbo' ? 'gpt-4-turbo' + : requestModel === 'gpt4o' ? 'gpt-4o' + : requestModel === 'gpt4o-2024-05-13' ? 'gpt-4o-2024-05-13' + : requestModel === 'gpt4om' ? 'gpt-4o-mini' + : requestModel === 'gpt4om-2024-07-18' ? 'gpt-4o-mini-2024-07-18' + : requestModel === 'gpt4o-2024-08-06' ? 'gpt-4o-2024-08-06' + : requestModel === 'gpt4o-2024-11-20' ? 'gpt-4o-2024-11-20' + : requestModel === 'gpt4o-chatgpt' ? 'chatgpt-4o-latest' + : requestModel === 'gpt4o1-preview' ? 'o1-preview' + : requestModel === 'gpt4o1-mini' ? 'o1-mini' + : (!requestModel) ? 'gpt-3.5-turbo' + : requestModel, + messages: formatedChat, + max_tokens: arg.maxTokens, + logit_bias: arg.bias, + stream: false, + + }) + + if(aiModel.startsWith('gpt4o1')){ + body.max_completion_tokens = body.max_tokens + delete body.max_tokens + } + + if(db.generationSeed > 0){ + body.seed = db.generationSeed + } + + if(db.jsonSchemaEnabled){ + body.response_format = { + "type": "json_schema", + "json_schema": getOpenAIJSONSchema() + } + } + + if(db.OAIPrediction){ + body.prediction = { + type: "content", + content: db.OAIPrediction + } + } + + if(aiModel === 'openrouter'){ + if(db.openrouterFallback){ + body.route = "fallback" + } + body.transforms = db.openrouterMiddleOut ? ['middle-out'] : [] + + if(db.openrouterProvider){ + body.provider = { + order: [db.openrouterProvider] + } + } + + if(db.useInstructPrompt){ + delete body.messages const prompt = applyChatTemplate(formated) - let stopStrings = getStopStrings(suggesting) - if(db.localStopStrings){ - stopStrings = db.localStopStrings.map((v) => { - return risuChatParser(v.replace(/\\n/g, "\n")) - }) + body.prompt = prompt + } + } + + body = applyParameters(body, + aiModel === 'openrouter' ? ['temperature', 'top_p', 'frequency_penalty', 'presence_penalty', 'repetition_penalty', 'min_p', 'top_a', 'top_k'] : ['temperature', 'top_p', 'frequency_penalty', 'presence_penalty'] + ) + + if(aiModel === 'reverse_proxy' && db.reverseProxyOobaMode){ + const OobaBodyTemplate = db.reverseProxyOobaArgs + + const keys = Object.keys(OobaBodyTemplate) + for(const key of keys){ + if(OobaBodyTemplate[key] !== undefined && OobaBodyTemplate[key] !== null){ + // @ts-ignore + body[key] = OobaBodyTemplate[key] } - let bodyTemplate:Record = { - 'prompt': prompt, - presence_penalty: arg.PresensePenalty || (db.PresensePenalty / 100), - frequency_penalty: arg.frequencyPenalty || (db.frequencyPenalty / 100), - logit_bias: {}, - max_tokens: maxTokens, - stop: stopStrings, - temperature: temperature, - top_p: db.top_p, + } + + } + + if(supportsInlayImage()){ + // inlay models doesn't support logit_bias + // OpenAI's gpt based llm model supports both logit_bias and inlay image + if(!( + aiModel.startsWith('gpt') || + (aiModel == 'reverse_proxy' && ( + db.proxyRequestModel?.startsWith('gpt') || + (db.proxyRequestModel === 'custom' && db.customProxyRequestModel.startsWith('gpt')) + )))){ + // @ts-ignore + delete body.logit_bias + } + } + + let replacerURL = aiModel === 'openrouter' ? "https://openrouter.ai/api/v1/chat/completions" : + (aiModel === 'reverse_proxy') ? (db.forceReplaceUrl) : ('https://api.openai.com/v1/chat/completions') + + let risuIdentify = false + if(replacerURL.startsWith("risu::")){ + risuIdentify = true + replacerURL = replacerURL.replace("risu::", '') + } + + if(aiModel === 'reverse_proxy' && db.autofillRequestUrl){ + if(replacerURL.endsWith('v1')){ + replacerURL += '/chat/completions' + } + else if(replacerURL.endsWith('v1/')){ + replacerURL += 'chat/completions' + } + else if(!(replacerURL.endsWith('completions') || replacerURL.endsWith('completions/'))){ + if(replacerURL.endsWith('/')){ + replacerURL += 'v1/chat/completions' } - - const url = new URL(db.textgenWebUIBlockingURL) - url.pathname = "/v1/completions" - const urlStr = url.toString() - - const OobaBodyTemplate = db.reverseProxyOobaArgs - const keys = Object.keys(OobaBodyTemplate) - for(const key of keys){ - if(OobaBodyTemplate[key] !== undefined && OobaBodyTemplate[key] !== null && OobaParams.includes(key)){ - bodyTemplate[key] = OobaBodyTemplate[key] - } - else if(bodyTemplate[key]){ - delete bodyTemplate[key] - } + else{ + replacerURL += '/v1/chat/completions' } + } + } - const response = await globalFetch(urlStr, { - body: bodyTemplate, - chatId: arg.chatId - }) + let headers = { + "Authorization": "Bearer " + (aiModel === 'reverse_proxy' ? db.proxyKey : (aiModel === 'openrouter' ? db.openrouterKey : db.openAIKey)), + "Content-Type": "application/json" + } - if(!response.ok){ + if(aiModel === 'openrouter'){ + headers["X-Title"] = 'RisuAI' + headers["HTTP-Referer"] = 'https://risuai.xyz' + } + if(risuIdentify){ + headers["X-Proxy-Risu"] = 'RisuAI' + } + if(aiModel.startsWith('jamba')){ + headers['Authorization'] = 'Bearer ' + db.ai21Key + replacerURL = 'https://api.ai21.com/studio/v1/chat/completions' + } + if(arg.multiGen){ + // @ts-ignore + body.n = db.genTime + } + let throughProxi = (!isTauri) && (!isNodeServer) && (!db.usePlainFetch) && (!Capacitor.isNativePlatform()) + if(arg.useStreaming){ + body.stream = true + let urlHost = new URL(replacerURL).host + if(urlHost.includes("localhost") || urlHost.includes("172.0.0.1") || urlHost.includes("0.0.0.0")){ + if(!isTauri){ return { type: 'fail', - result: (language.errors.httpError + `${JSON.stringify(response.data)}`) + result: 'You are trying local request on streaming. this is not allowed dude to browser/os security policy. turn off streaming.', } } - const text:string = response.data.choices[0].text + } + const da = await fetchNative(replacerURL, { + body: JSON.stringify(body), + method: "POST", + headers: headers, + signal: arg.abortSignal, + chatId: arg.chatId + }) + + if(da.status !== 200){ return { - type: 'success', - result: text.replace(/##\n/g, '') - } - - } - - case 'custom':{ - const d = await pluginProcess({ - bias: bias, - prompt_chat: formated, - temperature: (db.temperature / 100), - max_tokens: maxTokens, - presence_penalty: (db.PresensePenalty / 100), - frequency_penalty: (db.frequencyPenalty / 100) - }) - if(!d){ - return { - type: 'fail', - result: (language.errors.unknownModel) - } - } - else if(!d.success){ - return { - type: 'fail', - result: d.content - } - } - else{ - return { - type: 'success', - result: d.content - } - } - break - } - case 'palm2': - case 'palm2_unicorn':{ - const bodyData = { - "instances": [ - { - "content": stringlizeChat(formated, currentChar?.name ?? '', arg.continue) - } - ], - "parameters": { - "candidateCount": 1, - "maxOutputTokens": maxTokens, - "stopSequences": [ - "system:", "user:", "assistant:" - ], - "temperature": temperature, - } - }; - - const API_ENDPOINT="us-central1-aiplatform.googleapis.com" - const PROJECT_ID=db.google.projectId - const MODEL_ID= aiModel === 'palm2' ? 'text-bison' : - aiModel ==='palm2_unicorn' ? 'text-unicorn' : - '' - const LOCATION_ID="us-central1" - - const url = `https://${API_ENDPOINT}/v1/projects/${PROJECT_ID}/locations/${LOCATION_ID}/publishers/google/models/${MODEL_ID}:predict`; - const res = await globalFetch(url, { - body: bodyData, - headers: { - "Content-Type": "application/json", - "Authorization": "Bearer " + db.google.accessToken - }, - abortSignal, - chatId: arg.chatId - }) - if(res.ok){ - console.log(res.data) - if(res.data.predictions){ - let output:string = res.data.predictions[0].content - const ind = output.search(/(system note)|(user)|(assistant):/gi) - if(ind >= 0){ - output = output.substring(0, ind) - } - return { - type: 'success', - result: output - } - } - else{ - return { - type: 'fail', - result: `${JSON.stringify(res.data)}` - } - } - } - else{ - return { - type: 'fail', - result: `${JSON.stringify(res.data)}` - } + type: "fail", + result: await textifyReadableStream(da.body) } } - case 'gemini-pro': - case 'gemini-pro-vision': - case 'gemini-1.5-pro-latest': - case 'gemini-1.5-pro-exp-0801': - case 'gemini-1.5-pro-exp-0827': - case 'gemini-exp-1114': - case 'gemini-exp-1121': - case 'gemini-1.5-flash': - case 'gemini-1.5-pro-002': - case 'gemini-1.5-flash-002': - case 'gemini-ultra': - case 'gemini-ultra-vision':{ - interface GeminiPart{ - text?:string - "inlineData"?: { - "mimeType": string, - "data": string - }, - } - - interface GeminiChat { - role: "USER"|"MODEL" - parts:|GeminiPart[] + + if (!da.headers.get('Content-Type').includes('text/event-stream')){ + return { + type: "fail", + result: await textifyReadableStream(da.body) } + } + addFetchLog({ + body: body, + response: "Streaming", + success: true, + url: replacerURL, + }) - let reformatedChat:GeminiChat[] = [] - let pendingImage = '' + let dataUint:Uint8Array|Buffer = new Uint8Array([]) - for(let i=0;i( { + async transform(chunk, control) { + dataUint = Buffer.from(new Uint8Array([...dataUint, ...chunk])) + let JSONreaded:{[key:string]:string} = {} + try { + const datas = dataUint.toString().split('\n') + let readed:{[key:string]:string} = {} + for(const data of datas){ + if(data.startsWith("data: ")){ + try { + const rawChunk = data.replace("data: ", "") + if(rawChunk === "[DONE]"){ + if(db.extractJson && db.jsonSchemaEnabled){ + for(const key in readed){ + const extracted = extractJSON(readed[key], db.extractJson) + JSONreaded[key] = extracted + } + console.log(JSONreaded) + control.enqueue(JSONreaded) + } + else{ + control.enqueue(readed) + } + return + } + const choices = JSON.parse(rawChunk).choices + for(const choice of choices){ + const chunk = choice.delta.content ?? choices.text + if(chunk){ + if(arg.multiGen){ + const ind = choice.index.toString() + if(!readed[ind]){ + readed[ind] = "" + } + readed[ind] += chunk + } + else{ + if(!readed["0"]){ + readed["0"] = "" + } + readed["0"] += chunk + } + } + } + } catch (error) {} + } + } + if(db.extractJson && db.jsonSchemaEnabled){ + for(const key in readed){ + const extracted = extractJSON(readed[key], db.extractJson) + JSONreaded[key] = extracted + } + console.log(JSONreaded) + control.enqueue(JSONreaded) } else{ - reformatedChat.push({ - role: "USER", - parts: [{ - text: chat.role + ':' + chat.content - }] - }) + control.enqueue(readed) } + } catch (error) { + + } + } + },) + + da.body.pipeTo(transtream.writable) + + return { + type: 'streaming', + result: transtream.readable + } + } + + if(arg.realAIModel === 'reverse_proxy'){ + const additionalParams = db.additionalParams + for(let i=0;i { + const extracted = extractJSON(v.message.content, db.extractJson) + return ["char",extracted] + }) + + return { + type: 'multiline', + result: c } - else if(chat.role === 'system'){ - if(prevChat.role === 'USER'){ - reformatedChat[reformatedChat.length-1].parts[0].text += '\nsystem:' + chat.content + + } + return { + type: 'multiline', + result: dat.choices.map((v) => { + return ["char",v.message.content] + }) + } + + } + + if(dat?.choices[0]?.text){ + if(db.extractJson && db.jsonSchemaEnabled){ + try { + const parsed = JSON.parse(dat.choices[0].text) + const extracted = extractJSON(parsed, db.extractJson) + return { + type: 'success', + result: extracted } - else{ - reformatedChat.push({ - role: "USER", - parts: [{ - text: chat.role + ':' + chat.content - }] - }) + } catch (error) { + console.log(error) + return { + type: 'success', + result: dat.choices[0].text } } - else if(chat.role === 'user' && pendingImage !== ''){ - //conver image to jpeg so it can be inlined - const canv = document.createElement('canvas') - const img = new Image() - img.src = pendingImage - await img.decode() - canv.width = img.width - canv.height = img.height - const ctx = canv.getContext('2d') - ctx.drawImage(img, 0, 0) - const base64 = canv.toDataURL('image/jpeg').replace(/^data:image\/jpeg;base64,/, "") - const mimeType = 'image/jpeg' - pendingImage = '' - canv.remove() - img.remove() + } + return { + type: 'success', + result: dat.choices[0].text + } + } + if(db.extractJson && db.jsonSchemaEnabled){ + return { + type: 'success', + result: extractJSON(dat.choices[0].message.content, db.extractJson) + } + } + const msg:OpenAIChatFull = (dat.choices[0].message) + return { + type: 'success', + result: msg.content + } + } catch (error) { + return { + type: 'fail', + result: (language.errors.httpError + `${JSON.stringify(dat)}`) + } + } + } + else{ + if(dat.error && dat.error.message){ + return { + type: 'fail', + result: (language.errors.httpError + `${dat.error.message}`) + } + } + else{ + return { + type: 'fail', + result: (language.errors.httpError + `${JSON.stringify(res.data)}`) + } + } + } +} - reformatedChat.push({ - role: "USER", - parts: [ - { - text: chat.content, - }, - { - inlineData: { - mimeType: mimeType, +async function requestOpenAILegacyInstruct(arg:RequestDataArgumentExtended):Promise{ + const formated = arg.formated + const db = getDatabase() + const maxTokens = arg.maxTokens + const temperature = arg.temperature + const prompt = formated.filter(m => m.content?.trim()).map(m => { + let author = ''; + + if(m.role == 'system'){ + m.content = m.content.trim(); + } + + console.log(m.role +":"+m.content); + switch (m.role) { + case 'user': author = 'User'; break; + case 'assistant': author = 'Assistant'; break; + case 'system': author = 'Instruction'; break; + default: author = m.role; break; + } + + return `\n## ${author}\n${m.content.trim()}`; + //return `\n\n${author}: ${m.content.trim()}`; + }).join("") + `\n## Response\n`; + + const response = await globalFetch( "https://api.openai.com/v1/completions", { + body: { + model: "gpt-3.5-turbo-instruct", + prompt: prompt, + max_tokens: maxTokens, + temperature: temperature, + top_p: 1, + stop:["User:"," User:", "user:", " user:"], + presence_penalty: arg.PresensePenalty || (db.PresensePenalty / 100), + frequency_penalty: arg.frequencyPenalty || (db.frequencyPenalty / 100), + }, + headers: { + "Content-Type": "application/json", + "Authorization": "Bearer " + db.openAIKey, + }, + chatId: arg.chatId + }); + + if(!response.ok){ + return { + type: 'fail', + result: (language.errors.httpError + `${JSON.stringify(response.data)}`) + } + } + const text:string = response.data.choices[0].text + return { + type: 'success', + result: text.replace(/##\n/g, '') + } + +} + +async function requestNovelAI(arg:RequestDataArgumentExtended):Promise{ + const formated = arg.formated + const db = getDatabase() + const aiModel = arg.aiModel + const temperature = arg.temperature + const maxTokens = arg.maxTokens + const biasString = arg.biasString + const currentChar = getCurrentCharacter() + const prompt = stringlizeNAIChat(formated, currentChar?.name ?? '', arg.continue) + const abortSignal = arg.abortSignal + let logit_bias_exp:{ + sequence: number[], bias: number, ensure_sequence_finish: false, generate_once: true + }[] = [] + + for(let i=0;i { + const formated = arg.formated + const db = getDatabase() + const aiModel = arg.aiModel + const maxTokens = arg.maxTokens + const currentChar = getCurrentCharacter() + const useStreaming = arg.useStreaming + const abortSignal = arg.abortSignal + let streamUrl = db.textgenWebUIStreamURL.replace(/\/api.*/, "/api/v1/stream") + let blockingUrl = db.textgenWebUIBlockingURL.replace(/\/api.*/, "/api/v1/generate") + let bodyTemplate:{[key:string]:any} = {} + const prompt = applyChatTemplate(formated) + let stopStrings = getStopStrings(false) + if(db.localStopStrings){ + stopStrings = db.localStopStrings.map((v) => { + return risuChatParser(v.replace(/\\n/g, "\n")) + }) + } + bodyTemplate = { + 'max_new_tokens': db.maxResponse, + 'do_sample': db.ooba.do_sample, + 'temperature': (db.temperature / 100), + 'top_p': db.ooba.top_p, + 'typical_p': db.ooba.typical_p, + 'repetition_penalty': db.ooba.repetition_penalty, + 'encoder_repetition_penalty': db.ooba.encoder_repetition_penalty, + 'top_k': db.ooba.top_k, + 'min_length': db.ooba.min_length, + 'no_repeat_ngram_size': db.ooba.no_repeat_ngram_size, + 'num_beams': db.ooba.num_beams, + 'penalty_alpha': db.ooba.penalty_alpha, + 'length_penalty': db.ooba.length_penalty, + 'early_stopping': false, + 'truncation_length': maxTokens, + 'ban_eos_token': db.ooba.ban_eos_token, + 'stopping_strings': stopStrings, + 'seed': -1, + add_bos_token: db.ooba.add_bos_token, + topP: db.top_p, + prompt: prompt + } + + const headers = (aiModel === 'textgen_webui') ? {} : { + 'X-API-KEY': db.mancerHeader + } + + if(useStreaming){ + const oobaboogaSocket = new WebSocket(streamUrl); + const statusCode = await new Promise((resolve) => { + oobaboogaSocket.onopen = () => resolve(0) + oobaboogaSocket.onerror = () => resolve(1001) + oobaboogaSocket.onclose = ({ code }) => resolve(code) + }) + if(abortSignal.aborted || statusCode !== 0) { + oobaboogaSocket.close() + return ({ + type: "fail", + result: abortSignal.reason || `WebSocket connection failed to '${streamUrl}' failed!`, + }) + } + + const close = () => { + oobaboogaSocket.close() + } + const stream = new ReadableStream({ + start(controller){ + let readed = ""; + oobaboogaSocket.onmessage = async (event) => { + const json = JSON.parse(event.data); + if (json.event === "stream_end") { + close() + controller.close() + return + } + if (json.event !== "text_stream") return + readed += json.text + controller.enqueue(readed) + }; + oobaboogaSocket.send(JSON.stringify(bodyTemplate)); + }, + cancel(){ + close() + } + }) + oobaboogaSocket.onerror = close + oobaboogaSocket.onclose = close + abortSignal.addEventListener("abort", close) + + return { + type: 'streaming', + result: stream + } + } + + const res = await globalFetch(blockingUrl, { + body: bodyTemplate, + headers: headers, + abortSignal, + chatId: arg.chatId + }) + + const dat = res.data as any + if(res.ok){ + try { + let result:string = dat.results[0].text + + return { + type: 'success', + result: unstringlizeChat(result, formated, currentChar?.name ?? '') + } + } catch (error) { + return { + type: 'fail', + result: (language.errors.httpError + `${error}`) + } + } + } + else{ + return { + type: 'fail', + result: (language.errors.httpError + `${JSON.stringify(res.data)}`) + } + } +} + +async function requestOoba(arg:RequestDataArgumentExtended):Promise { + const formated = arg.formated + const db = getDatabase() + const aiModel = arg.aiModel + const maxTokens = arg.maxTokens + const temperature = arg.temperature + const prompt = applyChatTemplate(formated) + let stopStrings = getStopStrings(false) + if(db.localStopStrings){ + stopStrings = db.localStopStrings.map((v) => { + return risuChatParser(v.replace(/\\n/g, "\n")) + }) + } + let bodyTemplate:Record = { + 'prompt': prompt, + presence_penalty: arg.PresensePenalty || (db.PresensePenalty / 100), + frequency_penalty: arg.frequencyPenalty || (db.frequencyPenalty / 100), + logit_bias: {}, + max_tokens: maxTokens, + stop: stopStrings, + temperature: temperature, + top_p: db.top_p, + } + + const url = new URL(db.textgenWebUIBlockingURL) + url.pathname = "/v1/completions" + const urlStr = url.toString() + + const OobaBodyTemplate = db.reverseProxyOobaArgs + const keys = Object.keys(OobaBodyTemplate) + for(const key of keys){ + if(OobaBodyTemplate[key] !== undefined && OobaBodyTemplate[key] !== null && OobaParams.includes(key)){ + bodyTemplate[key] = OobaBodyTemplate[key] + } + else if(bodyTemplate[key]){ + delete bodyTemplate[key] + } + } + + const response = await globalFetch(urlStr, { + body: bodyTemplate, + chatId: arg.chatId + }) + + if(!response.ok){ + return { + type: 'fail', + result: (language.errors.httpError + `${JSON.stringify(response.data)}`) + } + } + const text:string = response.data.choices[0].text + return { + type: 'success', + result: text.replace(/##\n/g, '') + } + +} + +async function requestPlugin(arg:RequestDataArgumentExtended):Promise { + const formated = arg.formated + const db = getDatabase() + const maxTokens = arg.maxTokens + const bias = arg.biasString + const d = await pluginProcess({ + bias: bias, + prompt_chat: formated, + temperature: (db.temperature / 100), + max_tokens: maxTokens, + presence_penalty: (db.PresensePenalty / 100), + frequency_penalty: (db.frequencyPenalty / 100) + }) + if(!d){ + return { + type: 'fail', + result: (language.errors.unknownModel) + } + } + else if(!d.success){ + return { + type: 'fail', + result: d.content + } + } + else{ + return { + type: 'success', + result: d.content + } + } +} + +async function requestGoogleCloudVertex(arg:RequestDataArgumentExtended):Promise { + + const formated = arg.formated + const db = getDatabase() + const maxTokens = arg.maxTokens + + interface GeminiPart{ + text?:string + "inlineData"?: { + "mimeType": string, + "data": string + }, + } + + interface GeminiChat { + role: "USER"|"MODEL" + parts:|GeminiPart[] + } + + + let reformatedChat:GeminiChat[] = [] + let pendingImage = '' + + for(let i=0;i { + if(data?.candidates?.[0]?.content?.parts?.[0]?.text){ + fullRes += data.candidates[0].content.parts[0].text + } + else if(data?.errors){ + return { + type: 'fail', + result: `${JSON.stringify(data.errors)}` + } + } + else{ + return { + type: 'fail', + result: `${JSON.stringify(data)}` + } + } + } + + // traverse responded data if it contains multipart contents + if (typeof (res.data)[Symbol.iterator] === 'function') { + for(const data of res.data){ + processDataItem(data) + } + } else { + processDataItem(res.data) + } + + return { + type: 'success', + result: fullRes + } +} + +async function requestKobold(arg:RequestDataArgumentExtended):Promise { + const formated = arg.formated + const db = getDatabase() + const maxTokens = arg.maxTokens + const abortSignal = arg.abortSignal + + const prompt = applyChatTemplate(formated) + const url = new URL(db.koboldURL) + if(url.pathname.length < 3){ + url.pathname = 'api/v1/generate' + } + + const body = applyParameters({ + "prompt": prompt, + max_length: maxTokens, + max_context_length: db.maxContext, + n: 1 + }, [ + 'temperature', + 'top_p', + 'repetition_penalty', + 'top_k', + 'top_a' + ], { + 'repetition_penalty': 'rep_pen' + }) as KoboldGenerationInputSchema + + const da = await globalFetch(url.toString(), { + method: "POST", + body: body, + headers: { + "content-type": "application/json", + }, + abortSignal, + chatId: arg.chatId + }) + + if(!da.ok){ + return { + type: "fail", + result: da.data, + noRetry: true + } + } + + const data = da.data + return { + type: 'success', + result: data.results[0].text + } +} + +async function requestNovelList(arg:RequestDataArgumentExtended):Promise { + + const formated = arg.formated + const db = getDatabase() + const maxTokens = arg.maxTokens + const temperature = arg.temperature + const biasString = arg.biasString + const currentChar = getCurrentCharacter() + const aiModel = arg.aiModel + const auth_key = db.novellistAPI; + const api_server_url = 'https://api.tringpt.com/'; + const logit_bias:string[] = [] + const logit_bias_values:string[] = [] + for(let i=0;i>") + db.ainconfig.stoptokens, + logit_bias: (logit_bias.length > 0) ? logit_bias.join("<<|>>") : undefined, + logit_bias_values: (logit_bias_values.length > 0) ? logit_bias_values.join("|") : undefined, + }; + const response = await globalFetch(api_server_url + '/api', { + method: 'POST', + headers: headers, + body: send_body, + chatId: arg.chatId + }); + + if(!response.ok){ + return { + type: 'fail', + result: response.data + } + } + + if(response.data.error){ + return { + 'type': 'fail', + 'result': `${response.data.error.replace("token", "api key")}` + } + } + + const result = response.data.data[0]; + const unstr = unstringlizeAIN(result, formated, currentChar?.name ?? '') + return { + 'type': 'multiline', + 'result': unstr + } +} + +async function requestOllama(arg:RequestDataArgumentExtended):Promise { + const formated = arg.formated + const db = getDatabase() + + const ollama = new Ollama({host: db.ollamaURL}) + + const response = await ollama.chat({ + model: db.ollamaModel, + messages: formated.map((v) => { + return { + role: v.role, + content: v.content + } + }).filter((v) => { + return v.role === 'assistant' || v.role === 'user' || v.role === 'system' + }), + stream: true + }) + + const readableStream = new ReadableStream({ + async start(controller){ + for await(const chunk of response){ + controller.enqueue({ + "0": chunk.message.content + }) + } + controller.close() + } + }) + + return { + type: 'streaming', + result: readableStream + } +} + +async function requestCohere(arg:RequestDataArgumentExtended):Promise { + const formated = arg.formated + const db = getDatabase() + const aiModel = arg.aiModel + + let lastChatPrompt = '' + let preamble = '' + + let lastChat = formated[formated.length-1] + if(lastChat.role === 'user'){ + lastChatPrompt = lastChat.content + formated.pop() + } + else{ + while(lastChat.role !== 'user'){ + lastChat = formated.pop() + if(!lastChat){ + return { + type: 'fail', + result: 'Cohere requires a user message to generate a response' + } + } + lastChatPrompt = (lastChat.role === 'user' ? '' : `${lastChat.role}: `) + '\n' + lastChat.content + lastChatPrompt + } + } + + const firstChat = formated[0] + if(firstChat.role === 'system'){ + preamble = firstChat.content + formated.shift() + } + + //reformat chat + + let body = applyParameters({ + message: lastChatPrompt, + chat_history: formated.map((v) => { + if(v.role === 'assistant'){ + return { + role: 'CHATBOT', + message: v.content + } + } + if(v.role === 'system'){ + return { + role: 'SYSTEM', + message: v.content + } + } + if(v.role === 'user'){ + return { + role: 'USER', + message: v.content + } + } + return null + }).filter((v) => v !== null).filter((v) => { + return v.message + }), + }, [ + 'temperature', 'top_k', 'top_p', 'presence_penalty', 'frequency_penalty' + ], { + 'top_k': 'k', + 'top_p': 'p', + }) + + if(aiModel !== 'cohere-command-r-03-2024' && aiModel !== 'cohere-command-r-plus-04-2024'){ + body.safety_mode = "NONE" + } + + if(preamble){ + if(body.chat_history.length > 0){ + // @ts-ignore + body.preamble = preamble + } + else{ + body.message = `system: ${preamble}` + } + } + + console.log(body) + + const res = await globalFetch('https://api.cohere.com/v1/chat', { + method: "POST", + headers: { + "Authorization": "Bearer " + db.cohereAPIKey, + "Content-Type": "application/json" + }, + body: body + }) + + if(!res.ok){ + return { + type: 'fail', + result: JSON.stringify(res.data) + } + } + + const result = res.data.text + if(!result){ + return { + type: 'fail', + result: JSON.stringify(res.data) + } + } + + return { + type: 'success', + result: result + } + +} + +async function requestClaude(arg:RequestDataArgumentExtended):Promise { + const formated = arg.formated + const db = getDatabase() + const aiModel = arg.aiModel + const useStreaming = arg.useStreaming + let replacerURL = (aiModel === 'reverse_proxy') ? (db.forceReplaceUrl) : ('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){ + if(replacerURL.endsWith('v1')){ + replacerURL += '/messages' + } + else if(replacerURL.endsWith('v1/')){ + replacerURL += 'messages' + } + else if(!(replacerURL.endsWith('messages') || replacerURL.endsWith('messages/'))){ + if(replacerURL.endsWith('/')){ + replacerURL += 'v1/messages' + } + else{ + replacerURL += '/v1/messages' + } + } + } + + interface Claude3TextBlock { + type: 'text', + text: string, + cache_control?: {"type": "ephemeral"} + } + + interface Claude3ImageBlock { + type: 'image', + source: { + type: 'base64' + media_type: string, + data: string + } + cache_control?: {"type": "ephemeral"} + } + + type Claude3ContentBlock = Claude3TextBlock|Claude3ImageBlock + + interface Claude3Chat { + role: 'user'|'assistant' + content: Claude3ContentBlock[] + } + + interface Claude3ExtendedChat { + role: 'user'|'assistant' + content: Claude3ContentBlock[]|string + } + + let claudeChat: Claude3Chat[] = [] + let systemPrompt:string = '' + + const addClaudeChat = (chat:{ + role: 'user'|'assistant' + content: string + }, multimodals?:MultiModal[]) => { + if(claudeChat.length > 0 && claudeChat[claudeChat.length-1].role === chat.role){ + let content = claudeChat[claudeChat.length-1].content + if(multimodals && multimodals.length > 0 && !Array.isArray(content)){ + content = [{ + type: 'text', + text: content + }] + } + + if(Array.isArray(content)){ + let lastContent = content[content.length-1] + if( lastContent?.type === 'text'){ + lastContent.text += "\n\n" + chat.content + content[content.length-1] = lastContent + } + else{ + content.push({ + type: 'text', + text: chat.content + }) + } + + if(multimodals && multimodals.length > 0){ + for(const modal of multimodals){ + if(modal.type === 'image'){ + const dataurl = modal.base64 + const base64 = dataurl.split(',')[1] + const mediaType = dataurl.split(';')[0].split(':')[1] + + content.unshift({ + type: 'image', + source: { + type: 'base64', + media_type: mediaType, data: base64 } - }] - }) - } - else if(chat.role === 'assistant' || chat.role === 'user'){ - reformatedChat.push({ - role: chat.role === 'user' ? 'USER' : 'MODEL', - parts: [{ - text: chat.content - }] - }) - } - else{ - reformatedChat.push({ - role: "USER", - parts: [{ - text: chat.role + ':' + chat.content - }] - }) - } - } - } - - const uncensoredCatagory = [ - { - "category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", - "threshold": "BLOCK_NONE" - }, - { - "category": "HARM_CATEGORY_HATE_SPEECH", - "threshold": "BLOCK_NONE" - }, - { - "category": "HARM_CATEGORY_HARASSMENT", - "threshold": "BLOCK_NONE" - }, - { - "category": "HARM_CATEGORY_DANGEROUS_CONTENT", - "threshold": "BLOCK_NONE" - }, - ] - - - const body = { - contents: reformatedChat, - generation_config: applyParameters({ - "maxOutputTokens": maxTokens, - }, ['temperature', 'top_p'], { - 'top_p': "topP" - }), - safetySettings: uncensoredCatagory - } - - let headers:{[key:string]:string} = {} - - const PROJECT_ID=db.google.projectId - const REGION="us-central1" - if(PROJECT_ID !== 'aigoogle'){ - headers['Authorization'] = "Bearer " + db.google.accessToken - } - - const url = PROJECT_ID !== 'aigoogle' ? - `https://${REGION}-aiplatform.googleapis.com/v1/projects/${PROJECT_ID}/locations/us-central1/publishers/google/models/${aiModel}:streamGenerateContent` - : `https://generativelanguage.googleapis.com/v1beta/models/${aiModel}:generateContent?key=${db.google.accessToken}` - const res = await globalFetch(url, { - headers: headers, - body: body, - chatId: arg.chatId - }) - - if(!res.ok){ - return { - type: 'fail', - result: `${JSON.stringify(res.data)}` - } - } - - let fullRes = '' - - const processDataItem = (data:any) => { - if(data?.candidates?.[0]?.content?.parts?.[0]?.text){ - fullRes += data.candidates[0].content.parts[0].text - } - else if(data?.errors){ - return { - type: 'fail', - result: `${JSON.stringify(data.errors)}` - } - } - else{ - return { - type: 'fail', - result: `${JSON.stringify(data)}` - } - } - } - - // traverse responded data if it contains multipart contents - if (typeof (res.data)[Symbol.iterator] === 'function') { - for(const data of res.data){ - processDataItem(data) - } - } else { - processDataItem(res.data) - } - - return { - type: 'success', - result: fullRes - } - - } - case "kobold":{ - const prompt = applyChatTemplate(formated) - const url = new URL(db.koboldURL) - if(url.pathname.length < 3){ - url.pathname = 'api/v1/generate' - } - - const body = applyParameters({ - "prompt": prompt, - max_length: maxTokens, - max_context_length: db.maxContext, - n: 1 - }, [ - 'temperature', - 'top_p', - 'repetition_penalty', - 'top_k', - 'top_a' - ], { - 'repetition_penalty': 'rep_pen' - }) as KoboldGenerationInputSchema - - const da = await globalFetch(url.toString(), { - method: "POST", - body: body, - headers: { - "content-type": "application/json", - }, - abortSignal, - chatId: arg.chatId - }) - - if(!da.ok){ - return { - type: "fail", - result: da.data, - noRetry: true - } - } - - const data = da.data - return { - type: 'success', - result: data.results[0].text - } - } - case "novellist": - case "novellist_damsel":{ - const auth_key = db.novellistAPI; - const api_server_url = 'https://api.tringpt.com/'; - const logit_bias:string[] = [] - const logit_bias_values:string[] = [] - for(let i=0;i>") + db.ainconfig.stoptokens, - logit_bias: (logit_bias.length > 0) ? logit_bias.join("<<|>>") : undefined, - logit_bias_values: (logit_bias_values.length > 0) ? logit_bias_values.join("|") : undefined, - }; - const response = await globalFetch(api_server_url + '/api', { - method: 'POST', - headers: headers, - body: send_body, - chatId: arg.chatId - }); - - if(!response.ok){ - return { - type: 'fail', - result: response.data - } - } - - if(response.data.error){ - return { - 'type': 'fail', - 'result': `${response.data.error.replace("token", "api key")}` - } - } - - const result = response.data.data[0]; - const unstr = unstringlizeAIN(result, formated, currentChar?.name ?? '') - return { - 'type': 'multiline', - 'result': unstr - } - } - case 'risullm-proto':{ - const res = await globalFetch('https://sv.risuai.xyz/risullm', { - body: { - messages: formated.map((v) => { - if(v.role === 'system'){ - return { - role: "user", - content: "System: " + v.content - } - } - if(v.role === 'function'){ - return { - role: "user", - content: "Function: " + v.content - - } - } - return { - role: v.role, - content: v.content - } - }) - }, - headers: { - "X-Api-Key": db.proxyKey - } - }) - - const resp:string = res?.data?.response - - if(!resp){ - return { - type: 'fail', - result: JSON.stringify(res.data) - } - } - - return { - type: 'success', - result: resp.replace(/\\n/g, '\n') - } - } - case 'ollama-hosted':{ - const ollama = new Ollama({host: db.ollamaURL}) - - const response = await ollama.chat({ - model: db.ollamaModel, - messages: formated.map((v) => { - return { - role: v.role, - content: v.content - } - }).filter((v) => { - return v.role === 'assistant' || v.role === 'user' || v.role === 'system' - }), - stream: true - }) - - const readableStream = new ReadableStream({ - async start(controller){ - for await(const chunk of response){ - controller.enqueue({ - "0": chunk.message.content - }) - } - controller.close() - } - }) - - return { - type: 'streaming', - result: readableStream - } - } - case 'cohere-command-r': - case 'cohere-command-r-plus': - case 'cohere-command-r-08-2024': - case 'cohere-command-r-03-2024': - case 'cohere-command-r-plus-04-2024': - case 'cohere-command-r-plus-08-2024':{ - const modelName = aiModel.replace('cohere-', '') - let lastChatPrompt = '' - let preamble = '' - - let lastChat = formated[formated.length-1] - if(lastChat.role === 'user'){ - lastChatPrompt = lastChat.content - formated.pop() - } - else{ - while(lastChat.role !== 'user'){ - lastChat = formated.pop() - if(!lastChat){ - return { - type: 'fail', - result: 'Cohere requires a user message to generate a response' - } - } - lastChatPrompt = (lastChat.role === 'user' ? '' : `${lastChat.role}: `) + '\n' + lastChat.content + lastChatPrompt - } - } - - const firstChat = formated[0] - if(firstChat.role === 'system'){ - preamble = firstChat.content - formated.shift() - } - - //reformat chat - - let body = applyParameters({ - message: lastChatPrompt, - chat_history: formated.map((v) => { - if(v.role === 'assistant'){ - return { - role: 'CHATBOT', - message: v.content - } - } - if(v.role === 'system'){ - return { - role: 'SYSTEM', - message: v.content - } - } - if(v.role === 'user'){ - return { - role: 'USER', - message: v.content - } - } - return null - }).filter((v) => v !== null).filter((v) => { - return v.message - }), - }, [ - 'temperature', 'top_k', 'top_p', 'presence_penalty', 'frequency_penalty' - ], { - 'top_k': 'k', - 'top_p': 'p', - }) - - if(aiModel !== 'cohere-command-r-03-2024' && aiModel !== 'cohere-command-r-plus-04-2024'){ - body.safety_mode = "NONE" - } - - if(preamble){ - if(body.chat_history.length > 0){ - // @ts-ignore - body.preamble = preamble - } - else{ - body.message = `system: ${preamble}` - } - } - - console.log(body) - - const res = await globalFetch('https://api.cohere.com/v1/chat', { - method: "POST", - headers: { - "Authorization": "Bearer " + db.cohereAPIKey, - "Content-Type": "application/json" - }, - body: body - }) - - if(!res.ok){ - return { - type: 'fail', - result: JSON.stringify(res.data) - } - } - - const result = res.data.text - if(!result){ - return { - type: 'fail', - result: JSON.stringify(res.data) - } - } - - return { - type: 'success', - result: result - } - } - - default:{ - if(raiModel.startsWith('claude-3')){ - let replacerURL = (aiModel === 'reverse_proxy') ? (db.forceReplaceUrl) : ('https://api.anthropic.com/v1/messages') - let apiKey = (aiModel === 'reverse_proxy') ? db.proxyKey : db.claudeAPIKey - if(aiModel === 'reverse_proxy' && db.autofillRequestUrl){ - if(replacerURL.endsWith('v1')){ - replacerURL += '/messages' - } - else if(replacerURL.endsWith('v1/')){ - replacerURL += 'messages' - } - else if(!(replacerURL.endsWith('messages') || replacerURL.endsWith('messages/'))){ - if(replacerURL.endsWith('/')){ - replacerURL += 'v1/messages' - } - else{ - replacerURL += '/v1/messages' - } - } - } - - interface Claude3TextBlock { - type: 'text', - text: string, - cache_control?: {"type": "ephemeral"} - } - - interface Claude3ImageBlock { - type: 'image', - source: { - type: 'base64' - media_type: string, - data: string - } - cache_control?: {"type": "ephemeral"} - } - - type Claude3ContentBlock = Claude3TextBlock|Claude3ImageBlock - - interface Claude3Chat { - role: 'user'|'assistant' - content: Claude3ContentBlock[] - } - - interface Claude3ExtendedChat { - role: 'user'|'assistant' - content: Claude3ContentBlock[]|string - } - - let claudeChat: Claude3Chat[] = [] - let systemPrompt:string = '' - - const addClaudeChat = (chat:{ - role: 'user'|'assistant' - content: string - }, multimodals?:MultiModal[]) => { - if(claudeChat.length > 0 && claudeChat[claudeChat.length-1].role === chat.role){ - let content = claudeChat[claudeChat.length-1].content - if(multimodals && multimodals.length > 0 && !Array.isArray(content)){ - content = [{ - type: 'text', - text: content - }] - } - - if(Array.isArray(content)){ - let lastContent = content[content.length-1] - if( lastContent?.type === 'text'){ - lastContent.text += "\n\n" + chat.content - content[content.length-1] = lastContent - } - else{ - content.push({ - type: 'text', - text: chat.content - }) - } - - if(multimodals && multimodals.length > 0){ - for(const modal of multimodals){ - if(modal.type === 'image'){ - const dataurl = modal.base64 - const base64 = dataurl.split(',')[1] - const mediaType = dataurl.split(';')[0].split(':')[1] - - content.unshift({ - type: 'image', - source: { - type: 'base64', - media_type: mediaType, - data: base64 - } - }) - } - } - } - } - claudeChat[claudeChat.length-1].content = content - } - else{ - let formatedChat:Claude3Chat = { - role: chat.role, - content: [{ - type: 'text', - text: chat.content - }] - } - if(multimodals && multimodals.length > 0){ - formatedChat.content = [{ - type: 'text', - text: chat.content - }] - for(const modal of multimodals){ - if(modal.type === 'image'){ - const dataurl = modal.base64 - const base64 = dataurl.split(',')[1] - const mediaType = dataurl.split(';')[0].split(':')[1] - - formatedChat.content.unshift({ - type: 'image', - source: { - type: 'base64', - media_type: mediaType, - data: base64 - } - }) - } - } - - } - claudeChat.push(formatedChat) - } - } - for(const chat of formated){ - switch(chat.role){ - case 'user':{ - addClaudeChat({ - role: 'user', - content: chat.content - }, chat.multimodals) - break - } - case 'assistant':{ - addClaudeChat({ - role: 'assistant', - content: chat.content - }, chat.multimodals) - break - } - case 'system':{ - if(claudeChat.length === 0){ - systemPrompt += '\n\n' + chat.content - } - else{ - addClaudeChat({ - role: 'user', - content: "System: " + chat.content - }) - } - break - } - case 'function':{ - //ignore function for now - break - } - } - } - if(claudeChat.length === 0 && systemPrompt === ''){ - return { - type: 'fail', - result: 'No input' - } - } - if(claudeChat.length === 0 && systemPrompt !== ''){ - claudeChat.push({ - role: 'user', - content: [{ - type: 'text', - text: 'Start' - }] - }) - systemPrompt = '' - } - if(claudeChat[0].role !== 'user'){ - claudeChat.unshift({ - role: 'user', - content: [{ - type: 'text', - text: 'Start' - }] - }) - } - if(db.claudeCachingExperimental){ - for(let i = 0;i<4;i++){ - const ind = claudeChat.findLastIndex((v) => { - if(v.role !== 'user'){ - return false - } - if(v.content.length === 0){ - return false - } - if(v.content[0].cache_control){ // if it already has cache control, skip - return false - } - return true - }) - console.log(ind) - if(ind === -1){ - break - } - claudeChat[ind].content[0].cache_control = { - type: 'ephemeral' - } - } - } - - let finalChat:Claude3ExtendedChat[] = claudeChat - - if(aiModel === 'reverse_proxy'){ - finalChat = claudeChat.map((v) => { - if(v.content.length > 0 && v.content[0].type === 'text'){ - return { - role: v.role, - content: v.content[0].text - } - } - }) - } - - - let body = applyParameters({ - model: raiModel, - messages: finalChat, - system: systemPrompt.trim(), - max_tokens: maxTokens, - stream: useStreaming ?? false - }, ['temperature', 'top_k', 'top_p']) - - if(systemPrompt === ''){ - delete body.system - } - - const bedrock = db.claudeAws - - if(bedrock && aiModel !== 'reverse_proxy'){ - function getCredentialParts(key:string) { - const [accessKeyId, secretAccessKey, region] = key.split(":"); - - if (!accessKeyId || !secretAccessKey || !region) { - throw new Error("The key assigned to this request is invalid."); - } - - return { accessKeyId, secretAccessKey, region }; - } - const { accessKeyId, secretAccessKey, region } = getCredentialParts(apiKey); - - const AMZ_HOST = "bedrock-runtime.%REGION%.amazonaws.com"; - const host = AMZ_HOST.replace("%REGION%", region); - const stream = false; // todo? - - // https://docs.aws.amazon.com/bedrock/latest/userguide/model-ids.html - const modelIDs = [ - "anthropic.claude-v2", - "anthropic.claude-v2:1", - "anthropic.claude-3-haiku-20240307-v1:0", - "anthropic.claude-3-sonnet-20240229-v1:0", - "anthropic.claude-3-opus-20240229-v1:0", - "anthropic.claude-3-5-sonnet-20240620-v1:0", - "anthropic.claude-3-5-sonnet-20241022-v2:0" - ]; - - const awsModel = "us." + ( - raiModel.includes("3-5-sonnet-20241022") ? modelIDs[6] : - raiModel.includes("3-5-sonnet-20240620") ? modelIDs[5] : - raiModel.includes("3-opus") ? modelIDs[4] : - raiModel.includes("3-sonnet") ? modelIDs[3] : - modelIDs[2]); - const url = `https://${host}/model/${awsModel}/invoke${stream ? "-with-response-stream" : ""}` - - const params = { - messages : claudeChat, - system: systemPrompt.trim(), - max_tokens: maxTokens, - // stop_sequences: null, - temperature: temperature, - top_p: db.top_p, - top_k: db.top_k, - anthropic_version: "bedrock-2023-05-31", - } - - const rq = new HttpRequest({ - method: "POST", - protocol: "https:", - hostname: host, - path: `/model/${awsModel}/invoke${stream ? "-with-response-stream" : ""}`, - headers: { - ["Host"]: host, - ["Content-Type"]: "application/json", - ["accept"]: "application/json", - }, - body: JSON.stringify(params), - }); - - const signer = new SignatureV4({ - sha256: Sha256, - credentials: { accessKeyId, secretAccessKey }, - region, - service: "bedrock", - }); - - const signed = await signer.sign(rq); - - const res = await globalFetch(url, { - method: "POST", - body: params, - headers: signed.headers, - plainFetchForce: true, - chatId: arg.chatId - }) - - if(!res.ok){ - return { - type: 'fail', - result: JSON.stringify(res.data) - } - } - if(res.data.error){ - return { - type: 'fail', - result: JSON.stringify(res.data.error) - } - } - return { - type: 'success', - result: res.data.content[0].text - - } - } - - - let headers:{ - [key:string]:string - } = { - "Content-Type": "application/json", - "x-api-key": apiKey, - "anthropic-version": "2023-06-01", - "accept": "application/json", - } - - if(db.claudeCachingExperimental){ - headers['anthropic-beta'] = 'prompt-caching-2024-07-31' - } - - if(db.usePlainFetch){ - headers['anthropic-dangerous-direct-browser-access'] = 'true' - } - - if(useStreaming){ - - const res = await fetchNative(replacerURL, { - body: JSON.stringify(body), - headers: headers, - method: "POST", - chatId: arg.chatId - }) - - if(res.status !== 200){ - return { - type: 'fail', - result: await textifyReadableStream(res.body) - } - } - let rerequesting = false - let breakError = '' - - - const stream = new ReadableStream({ - async start(controller){ - let text = '' - let reader = res.body.getReader() - const decoder = new TextDecoder() - const parser = createParser(async (e) => { - try { - if(e.type === 'event'){ - switch(e.event){ - case 'content_block_delta': { - if(e.data){ - text += JSON.parse(e.data).delta?.text - controller.enqueue({ - "0": text - }) - } - break - } - case 'error': { - if(e.data){ - const errormsg:string = JSON.parse(e.data).error?.message - if(errormsg && errormsg.toLocaleLowerCase().includes('overload') && db.antiClaudeOverload){ - console.log('Overload detected, retrying...') - reader.cancel() - rerequesting = true - await sleep(2000) - body.max_tokens -= await tokenize(text) - if(body.max_tokens < 0){ - body.max_tokens = 0 - } - if(body.messages.at(-1)?.role !== 'assistant'){ - body.messages.push({ - role: 'assistant', - content: [{ - type: 'text', - text: '' - }] - }) - } - let block = body.messages[body.messages.length-1].content - if(typeof block === 'string'){ - body.messages[body.messages.length-1].content += text - } - else if(block[0].type === 'text'){ - block[0].text += text - } - const res = await fetchNative(replacerURL, { - body: JSON.stringify(body), - headers: { - "Content-Type": "application/json", - "x-api-key": apiKey, - "anthropic-version": "2023-06-01", - "accept": "application/json", - }, - method: "POST", - chatId: arg.chatId - }) - if(res.status !== 200){ - breakError = 'Error: ' + await textifyReadableStream(res.body) - break - } - reader = res.body.getReader() - rerequesting = false - break - } - text += "Error:" + JSON.parse(e.data).error?.message - if(db.extractJson && db.jsonSchemaEnabled){ - controller.enqueue({ - "0": extractJSON(text, db.jsonSchema) - }) - } - else{ - controller.enqueue({ - "0": text - }) - } - } - break - } - } - } - } catch (error) {} }) - while(true){ - if(rerequesting){ - if(breakError){ - controller.enqueue({ - "0": breakError - }) - break - } - await sleep(1000) - continue - } - try { - const {done, value} = await reader.read() - if(done){ - if(rerequesting){ - continue - } - break - } - parser.feed(decoder.decode(value)) - } catch (error) { - await sleep(1) - } - } - controller.close() - }, - cancel(){ } - }) - - return { - type: 'streaming', - result: stream } - - } - const res = await globalFetch(replacerURL, { - body: body, - headers: headers, - method: "POST", - chatId: arg.chatId - }) - - if(!res.ok){ - return { - type: 'fail', - result: JSON.stringify(res.data) - } - } - if(res.data.error){ - return { - type: 'fail', - result: JSON.stringify(res.data.error) - } - } - const resText = res?.data?.content?.[0]?.text - if(!resText){ - return { - type: 'fail', - result: JSON.stringify(res.data) - } - } - if(db.extractJson && db.jsonSchemaEnabled){ - return { - type: 'success', - result: extractJSON(resText, db.jsonSchema) - } - } - return { - type: 'success', - result: resText } } - else if(raiModel.startsWith('claude')){ + claudeChat[claudeChat.length-1].content = content + } + else{ + let formatedChat:Claude3Chat = { + role: chat.role, + content: [{ + type: 'text', + text: chat.content + }] + } + if(multimodals && multimodals.length > 0){ + formatedChat.content = [{ + type: 'text', + text: chat.content + }] + for(const modal of multimodals){ + if(modal.type === 'image'){ + const dataurl = modal.base64 + const base64 = dataurl.split(',')[1] + const mediaType = dataurl.split(';')[0].split(':')[1] - let replacerURL = (aiModel === 'reverse_proxy') ? (db.forceReplaceUrl) : ('https://api.anthropic.com/v1/complete') - let apiKey = (aiModel === 'reverse_proxy') ? db.proxyKey : db.claudeAPIKey - if(aiModel === 'reverse_proxy'){ - if(replacerURL.endsWith('v1')){ - replacerURL += '/complete' - } - else if(replacerURL.endsWith('v1/')){ - replacerURL += 'complete' - } - else if(!(replacerURL.endsWith('complete') || replacerURL.endsWith('complete/'))){ - if(replacerURL.endsWith('/')){ - replacerURL += 'v1/complete' - } - else{ - replacerURL += '/v1/complete' - } + formatedChat.content.unshift({ + type: 'image', + source: { + type: 'base64', + media_type: mediaType, + data: base64 + } + }) } } - for(let i=0;i { - let prefix = '' - switch (v.role){ - case "assistant": - prefix = "\n\nAssistant: " - break - case "user": - prefix = "\n\nHuman: " - break - case "system": - prefix = "\n\nSystem: " - break - } - latestRole = v.role - if(raiModel.startsWith('claude-2') && (!raiModel.startsWith('claude-2.0'))){ - if(v.role === 'system' && i === 0){ - prefix = '' - } - } - return prefix + v.content - }).join('') - - if(latestRole !== 'assistant'){ - requestPrompt += '\n\nAssistant: ' - } - - - const bedrock = db.claudeAws - - if(bedrock && aiModel !== 'reverse_proxy'){ - function getCredentialParts(key:string) { - const [accessKeyId, secretAccessKey, region] = key.split(":"); - - if (!accessKeyId || !secretAccessKey || !region) { - throw new Error("The key assigned to this request is invalid."); - } - - return { accessKeyId, secretAccessKey, region }; - } - const { accessKeyId, secretAccessKey, region } = getCredentialParts(apiKey); - - const AMZ_HOST = "bedrock-runtime.%REGION%.amazonaws.com"; - const host = AMZ_HOST.replace("%REGION%", region); - - const stream = false - - const LATEST_AWS_V2_MINOR_VERSION = 1; - let awsModel = `anthropic.claude-v2:${LATEST_AWS_V2_MINOR_VERSION}`; - - const pattern = /^(claude-)?(instant-)?(v)?(\d+)(\.(\d+))?(-\d+k)?$/i; - const match = raiModel.match(pattern); - - if (match) { - const [, , instant, v, major, dot, minor] = match; - - if (instant) { - awsModel = "anthropic.claude-instant-v1"; - } - - // There's only one v1 model - else if (major === "1") { - awsModel = "anthropic.claude-v1"; - } - - // Try to map Anthropic API v2 models to AWS v2 models - else if (major === "2") { - if (minor === "0") { - awsModel = "anthropic.claude-v2"; - } else if (!v && !dot && !minor) { - awsModel = "anthropic.claude-v2"; - } - } - } - - const url = `https://${host}/model/${awsModel}/invoke${stream ? "-with-response-stream" : ""}` - const params = { - prompt : requestPrompt.startsWith("\n\nHuman: ") ? requestPrompt : "\n\nHuman: " + requestPrompt, - max_tokens_to_sample: maxTokens, - stop_sequences: ["\n\nHuman:", "\n\nSystem:", "\n\nAssistant:"], - temperature: temperature, - top_p: db.top_p, - //top_k: db.top_k, - } - const rq = new HttpRequest({ - method: "POST", - protocol: "https:", - hostname: host, - path: `/model/${awsModel}/invoke${stream ? "-with-response-stream" : ""}`, - headers: { - ["Host"]: host, - ["Content-Type"]: "application/json", - ["accept"]: "application/json", - //"anthropic-version": "2023-06-01", - }, - body: JSON.stringify(params), - }); - - - const signer = new SignatureV4({ - sha256: Sha256, - credentials: { accessKeyId, secretAccessKey }, - region, - service: "bedrock", - }); - - const signed = await signer.sign(rq); - - const da = await globalFetch(url, { - method: "POST", - body: params, - headers: signed.headers, - plainFetchForce: true, - chatId: arg.chatId + else{ + addClaudeChat({ + role: 'user', + content: "System: " + chat.content }) + } + break + } + case 'function':{ + //ignore function for now + break + } + } + } + if(claudeChat.length === 0 && systemPrompt === ''){ + return { + type: 'fail', + result: 'No input' + } + } + if(claudeChat.length === 0 && systemPrompt !== ''){ + claudeChat.push({ + role: 'user', + content: [{ + type: 'text', + text: 'Start' + }] + }) + systemPrompt = '' + } + if(claudeChat[0].role !== 'user'){ + claudeChat.unshift({ + role: 'user', + content: [{ + type: 'text', + text: 'Start' + }] + }) + } + if(db.claudeCachingExperimental){ + for(let i = 0;i<4;i++){ + const ind = claudeChat.findLastIndex((v) => { + if(v.role !== 'user'){ + return false + } + if(v.content.length === 0){ + return false + } + if(v.content[0].cache_control){ // if it already has cache control, skip + return false + } + return true + }) + console.log(ind) + if(ind === -1){ + break + } + claudeChat[ind].content[0].cache_control = { + type: 'ephemeral' + } + } + } - - if((!da.ok) || (da.data.error)){ - return { - type: 'fail', - result: `${JSON.stringify(da.data)}` + let finalChat:Claude3ExtendedChat[] = claudeChat + + if(aiModel === 'reverse_proxy'){ + finalChat = claudeChat.map((v) => { + if(v.content.length > 0 && v.content[0].type === 'text'){ + return { + role: v.role, + content: v.content[0].text + } + } + }) + } + + + let body = applyParameters({ + model: arg.modelInfo.internalID, + messages: finalChat, + system: systemPrompt.trim(), + max_tokens: maxTokens, + stream: useStreaming ?? false + }, ['temperature', 'top_k', 'top_p']) + + if(systemPrompt === ''){ + delete body.system + } + + const bedrock = arg.modelInfo.format === LLMFormat.AWSBedrockClaude + + if(bedrock && aiModel !== 'reverse_proxy'){ + function getCredentialParts(key:string) { + const [accessKeyId, secretAccessKey, region] = key.split(":"); + + if (!accessKeyId || !secretAccessKey || !region) { + throw new Error("The key assigned to this request is invalid."); + } + + return { accessKeyId, secretAccessKey, region }; + } + const { accessKeyId, secretAccessKey, region } = getCredentialParts(apiKey); + + const AMZ_HOST = "bedrock-runtime.%REGION%.amazonaws.com"; + const host = AMZ_HOST.replace("%REGION%", region); + const stream = false; // todo? + + const awsModel = "us." + arg.modelInfo.internalID; + const url = `https://${host}/model/${awsModel}/invoke${stream ? "-with-response-stream" : ""}` + + const params = { + messages : claudeChat, + system: systemPrompt.trim(), + max_tokens: maxTokens, + // stop_sequences: null, + temperature: arg.temperature, + top_p: db.top_p, + top_k: db.top_k, + anthropic_version: "bedrock-2023-05-31", + } + + const rq = new HttpRequest({ + method: "POST", + protocol: "https:", + hostname: host, + path: `/model/${awsModel}/invoke${stream ? "-with-response-stream" : ""}`, + headers: { + ["Host"]: host, + ["Content-Type"]: "application/json", + ["accept"]: "application/json", + }, + body: JSON.stringify(params), + }); + + const signer = new SignatureV4({ + sha256: Sha256, + credentials: { accessKeyId, secretAccessKey }, + region, + service: "bedrock", + }); + + const signed = await signer.sign(rq); + + const res = await globalFetch(url, { + method: "POST", + body: params, + headers: signed.headers, + plainFetchForce: true, + chatId: arg.chatId + }) + + if(!res.ok){ + return { + type: 'fail', + result: JSON.stringify(res.data) + } + } + if(res.data.error){ + return { + type: 'fail', + result: JSON.stringify(res.data.error) + } + } + return { + type: 'success', + result: res.data.content[0].text + + } + } + + + let headers:{ + [key:string]:string + } = { + "Content-Type": "application/json", + "x-api-key": apiKey, + "anthropic-version": "2023-06-01", + "accept": "application/json", + } + + if(db.claudeCachingExperimental){ + headers['anthropic-beta'] = 'prompt-caching-2024-07-31' + } + + if(db.usePlainFetch){ + headers['anthropic-dangerous-direct-browser-access'] = 'true' + } + + if(useStreaming){ + + const res = await fetchNative(replacerURL, { + body: JSON.stringify(body), + headers: headers, + method: "POST", + chatId: arg.chatId + }) + + if(res.status !== 200){ + return { + type: 'fail', + result: await textifyReadableStream(res.body) + } + } + let rerequesting = false + let breakError = '' + + + const stream = new ReadableStream({ + async start(controller){ + let text = '' + let reader = res.body.getReader() + const decoder = new TextDecoder() + const parser = createParser(async (e) => { + try { + if(e.type === 'event'){ + switch(e.event){ + case 'content_block_delta': { + if(e.data){ + text += JSON.parse(e.data).delta?.text + controller.enqueue({ + "0": text + }) + } + break + } + case 'error': { + if(e.data){ + const errormsg:string = JSON.parse(e.data).error?.message + if(errormsg && errormsg.toLocaleLowerCase().includes('overload') && db.antiClaudeOverload){ + console.log('Overload detected, retrying...') + reader.cancel() + rerequesting = true + await sleep(2000) + body.max_tokens -= await tokenize(text) + if(body.max_tokens < 0){ + body.max_tokens = 0 + } + if(body.messages.at(-1)?.role !== 'assistant'){ + body.messages.push({ + role: 'assistant', + content: [{ + type: 'text', + text: '' + }] + }) + } + let block = body.messages[body.messages.length-1].content + if(typeof block === 'string'){ + body.messages[body.messages.length-1].content += text + } + else if(block[0].type === 'text'){ + block[0].text += text + } + const res = await fetchNative(replacerURL, { + body: JSON.stringify(body), + headers: { + "Content-Type": "application/json", + "x-api-key": apiKey, + "anthropic-version": "2023-06-01", + "accept": "application/json", + }, + method: "POST", + chatId: arg.chatId + }) + if(res.status !== 200){ + breakError = 'Error: ' + await textifyReadableStream(res.body) + break + } + reader = res.body.getReader() + rerequesting = false + break + } + text += "Error:" + JSON.parse(e.data).error?.message + if(db.extractJson && db.jsonSchemaEnabled){ + controller.enqueue({ + "0": extractJSON(text, db.jsonSchema) + }) + } + else{ + controller.enqueue({ + "0": text + }) + } + } + break + } + } } - } - - const res = da.data - - return { - type: "success", - result: res.completion, - } - } - - const da = await globalFetch(replacerURL, { - method: "POST", - body: { - prompt : "\n\nHuman: " + requestPrompt, - model: raiModel, - max_tokens_to_sample: maxTokens, - stop_sequences: ["\n\nHuman:", "\n\nSystem:", "\n\nAssistant:"], - temperature: temperature, - }, - headers: { - "Content-Type": "application/json", - "x-api-key": apiKey, - "anthropic-version": "2023-06-01", - "accept": "application/json" - }, - useRisuToken: aiModel === 'reverse_proxy', - chatId: arg.chatId + } catch (error) {} }) - - if((!da.ok) || (da.data.error)){ - return { - type: 'fail', - result: `${JSON.stringify(da.data)}` + while(true){ + if(rerequesting){ + if(breakError){ + controller.enqueue({ + "0": breakError + }) + break + } + await sleep(1000) + continue + } + try { + const {done, value} = await reader.read() + if(done){ + if(rerequesting){ + continue + } + break + } + parser.feed(decoder.decode(value)) + } catch (error) { + await sleep(1) } } + controller.close() + }, + cancel(){ + } + }) - const res = da.data + return { + type: 'streaming', + result: stream + } + } + const res = await globalFetch(replacerURL, { + body: body, + headers: headers, + method: "POST", + chatId: arg.chatId + }) + + if(!res.ok){ + return { + type: 'fail', + result: JSON.stringify(res.data) + } + } + if(res.data.error){ + return { + type: 'fail', + result: JSON.stringify(res.data.error) + } + } + const resText = res?.data?.content?.[0]?.text + if(!resText){ + return { + type: 'fail', + result: JSON.stringify(res.data) + } + } + if(db.extractJson && db.jsonSchemaEnabled){ + return { + type: 'success', + result: extractJSON(resText, db.jsonSchema) + } + } + return { + type: 'success', + result: resText + } +} + +async function requestHorde(arg:RequestDataArgumentExtended):Promise { + const formated = arg.formated + const db = getDatabase() + const aiModel = arg.aiModel + const currentChar = getCurrentCharacter() + const abortSignal = arg.abortSignal + + const prompt = applyChatTemplate(formated) + + const realModel = aiModel.split(":::")[1] + + const argument = { + "prompt": prompt, + "params": { + "n": 1, + "max_context_length": db.maxContext + 100, + "max_length": db.maxResponse, + "singleline": false, + "temperature": db.temperature / 100, + "top_k": db.top_k, + "top_p": db.top_p, + }, + "trusted_workers": false, + "workerslow_workers": true, + "_blacklist": false, + "dry_run": false, + "models": [realModel, realModel.trim(), ' ' + realModel, realModel + ' '] + } + + if(realModel === 'auto'){ + delete argument.models + } + + let apiKey = '0000000000' + if(db.hordeConfig.apiKey.length > 2){ + apiKey = db.hordeConfig.apiKey + } + + const da = await fetch("https://stablehorde.net/api/v2/generate/text/async", { + body: JSON.stringify(argument), + method: "POST", + headers: { + "content-type": "application/json", + "apikey": apiKey + }, + signal: abortSignal + }) + + if(da.status !== 202){ + return { + type: "fail", + result: await da.text() + } + } + + const json:{ + id:string, + kudos:number, + message:string + } = await da.json() + + let warnMessage = "" + if(json.message){ + warnMessage = "with " + json.message + } + + while(true){ + await sleep(2000) + const data = await (await fetch("https://stablehorde.net/api/v2/generate/text/status/" + json.id)).json() + if(!data.is_possible){ + fetch("https://stablehorde.net/api/v2/generate/text/status/" + json.id, { + method: "DELETE" + }) + return { + type: 'fail', + result: "Response not possible" + warnMessage, + noRetry: true + } + } + if(data.done && Array.isArray(data.generations) && data.generations.length > 0){ + const generations:{text:string}[] = data.generations + if(generations && generations.length > 0){ return { type: "success", - result: res.completion, - } - - } - if(aiModel.startsWith("horde:::")){ - const prompt = applyChatTemplate(formated) - - const realModel = aiModel.split(":::")[1] - - const argument = { - "prompt": prompt, - "params": { - "n": 1, - "max_context_length": db.maxContext + 100, - "max_length": db.maxResponse, - "singleline": false, - "temperature": db.temperature / 100, - "top_k": db.top_k, - "top_p": db.top_p, - }, - "trusted_workers": false, - "workerslow_workers": true, - "_blacklist": false, - "dry_run": false, - "models": [realModel, realModel.trim(), ' ' + realModel, realModel + ' '] - } - - if(realModel === 'auto'){ - delete argument.models - } - - let apiKey = '0000000000' - if(db.hordeConfig.apiKey.length > 2){ - apiKey = db.hordeConfig.apiKey - } - - const da = await fetch("https://stablehorde.net/api/v2/generate/text/async", { - body: JSON.stringify(argument), - method: "POST", - headers: { - "content-type": "application/json", - "apikey": apiKey - }, - signal: abortSignal - }) - - if(da.status !== 202){ - return { - type: "fail", - result: await da.text() - } - } - - const json:{ - id:string, - kudos:number, - message:string - } = await da.json() - - let warnMessage = "" - if(json.message){ - warnMessage = "with " + json.message - } - - while(true){ - await sleep(2000) - const data = await (await fetch("https://stablehorde.net/api/v2/generate/text/status/" + json.id)).json() - if(!data.is_possible){ - fetch("https://stablehorde.net/api/v2/generate/text/status/" + json.id, { - method: "DELETE" - }) - return { - type: 'fail', - result: "Response not possible" + warnMessage, - noRetry: true - } - } - if(data.done && Array.isArray(data.generations) && data.generations.length > 0){ - const generations:{text:string}[] = data.generations - if(generations && generations.length > 0){ - return { - type: "success", - result: unstringlizeChat(generations[0].text, formated, currentChar?.name ?? '') - } - } - return { - type: 'fail', - result: "No Generations when done", - noRetry: true - } - } - } - - - } - if(aiModel.startsWith('hf:::')){ - const realModel = aiModel.split(":::")[1] - const suggesting = model === "submodel" - const prompt = applyChatTemplate(formated) - const v = await runTransformers(prompt, realModel, { - temperature: temperature, - max_new_tokens: maxTokens, - top_k: db.ooba.top_k, - top_p: db.ooba.top_p, - repetition_penalty: db.ooba.repetition_penalty, - typical_p: db.ooba.typical_p, - }) - return { - type: 'success', - result: unstringlizeChat(v.generated_text as string, formated, currentChar?.name ?? '') - } - } - if(aiModel.startsWith('local_')){ - console.log('running local model') - const suggesting = model === "submodel" - const prompt = applyChatTemplate(formated) - const stopStrings = getStopStrings(suggesting) - console.log(stopStrings) - const modelPath = aiModel.replace('local_', '') - const res = await runGGUFModel({ - prompt: prompt, - modelPath: modelPath, - temperature: temperature, - top_p: db.top_p, - top_k: db.top_k, - maxTokens: maxTokens, - presencePenalty: arg.PresensePenalty || (db.PresensePenalty / 100), - frequencyPenalty: arg.frequencyPenalty || (db.frequencyPenalty / 100), - repeatPenalty: 0, - maxContext: db.maxContext, - stop: stopStrings, - }) - let decoded = '' - const transtream = new TransformStream({ - async transform(chunk, control) { - const decodedChunk = new TextDecoder().decode(chunk) - decoded += decodedChunk - control.enqueue({ - "0": decoded - }) - } - }) - res.pipeTo(transtream.writable) - - return { - type: 'streaming', - result: transtream.readable + result: unstringlizeChat(generations[0].text, formated, currentChar?.name ?? '') } } return { type: 'fail', - result: (language.errors.unknownModel) + result: "No Generations when done", + noRetry: true } } } } - -let userString = '' -let requestedTimes = 999 -let refreshTime = 0 -function getOpenUserString(){ - if(refreshTime < Date.now() && requestedTimes > 2 ){ - refreshTime = Date.now() + (300000 * Math.random()) + 60000 - userString = v4() - requestedTimes = 0 +async function requestWebLLM(arg:RequestDataArgumentExtended):Promise { + const formated = arg.formated + const db = getDatabase() + const aiModel = arg.aiModel + const currentChar = getCurrentCharacter() + const maxTokens = arg.maxTokens + const temperature = arg.temperature + const realModel = aiModel.split(":::")[1] + const prompt = applyChatTemplate(formated) + const v = await runTransformers(prompt, realModel, { + temperature: temperature, + max_new_tokens: maxTokens, + top_k: db.ooba.top_k, + top_p: db.ooba.top_p, + repetition_penalty: db.ooba.repetition_penalty, + typical_p: db.ooba.typical_p, + }) + return { + type: 'success', + result: unstringlizeChat(v.generated_text as string, formated, currentChar?.name ?? '') } - requestedTimes += 1 - console.log(userString) - return userString } - - export interface KoboldSamplerSettingsSchema { rep_pen?: number; rep_pen_range?: number; diff --git a/src/ts/storage/database.svelte.ts b/src/ts/storage/database.svelte.ts index 782ecfab..33c152da 100644 --- a/src/ts/storage/database.svelte.ts +++ b/src/ts/storage/database.svelte.ts @@ -721,6 +721,8 @@ export interface Database{ google: { accessToken: string projectId: string + privateKey: string + clientEmail: string } mistralKey?:string chainOfThought?:boolean From cc8d753dc8cdfb3a9660de6945b3f0614a47d92d Mon Sep 17 00:00:00 2001 From: kwaroran Date: Mon, 25 Nov 2024 23:04:32 +0900 Subject: [PATCH 145/175] Rework custom API --- src/lib/Setting/Pages/BotSettings.svelte | 53 ++++++++++-------------- src/ts/process/request.ts | 43 ++++++++----------- src/ts/storage/database.svelte.ts | 3 ++ src/ts/tokenizer.ts | 7 +--- 4 files changed, 42 insertions(+), 64 deletions(-) diff --git a/src/lib/Setting/Pages/BotSettings.svelte b/src/lib/Setting/Pages/BotSettings.svelte index d5decb19..36431235 100644 --- a/src/lib/Setting/Pages/BotSettings.svelte +++ b/src/lib/Setting/Pages/BotSettings.svelte @@ -27,6 +27,7 @@ import PromptSettings from "./PromptSettings.svelte"; import { openPresetList } from "src/ts/stores.svelte"; import { selectSingleFile } from "src/ts/util"; + import { LLMFormat } from "src/ts/model/modellist"; let tokens = $state({ mainPrompt: 0, @@ -133,39 +134,27 @@ {language.proxyAPIKey} {language.proxyRequestModel} - - None - GPT 3.5 - GPT 3.5 16k - GPT-4 - GPT-4o - GPT-4 32k - GPT-4 Turbo - GPT-4 Turbo 1106 - GPT-4 Turbo 1106 Vision - GPT-3.5 0301 - GPT-4 0301 - GPT-3.5 0613 - GPT-4 0613 - claude-2.1 - claude-2.0 - claude-2 - claude-v1.3 - claude-v1.3-100k - claude-v1.2 - claude-instant-v1.1 - claude-instant-v1.1-100k - claude-3-opus-20240229 - claude-3-sonnet-20240229 - claude-3-5-sonnet-20240620 - claude-3-5-sonnet-20241022 - Custom + + {language.format} + { + DBState.db.customAPIFormat = parseInt(e.currentTarget.value) + }}> + + OpenAI Compatible + + + Anthropic Claude + + + Mistral + + + Google Cloud + + + Cohere + - {#if DBState.db.proxyRequestModel === 'custom'} - - {:else} -
- {/if} {/if} {#if DBState.db.aiModel.startsWith('risullm')} Risu {language.apiKey} diff --git a/src/ts/process/request.ts b/src/ts/process/request.ts index 0932b9c5..d9e88bf8 100644 --- a/src/ts/process/request.ts +++ b/src/ts/process/request.ts @@ -1,19 +1,16 @@ -import { get } from "svelte/store"; import type { MultiModal, OpenAIChat, OpenAIChatFull } from "./index.svelte"; import { getCurrentCharacter, getDatabase, type character } from "../storage/database.svelte"; import { pluginProcess } from "../plugins/plugins"; import { language } from "../../lang"; -import { stringlizeAINChat, stringlizeChat, getStopStrings, unstringlizeAIN, unstringlizeChat } from "./stringlize"; +import { stringlizeAINChat, getStopStrings, unstringlizeAIN, unstringlizeChat } from "./stringlize"; import { addFetchLog, fetchNative, globalFetch, isNodeServer, isTauri, textifyReadableStream } from "../globalApi.svelte"; import { sleep } from "../util"; import { NovelAIBadWordIds, stringlizeNAIChat } from "./models/nai"; import { strongBan, tokenize, tokenizeNum } from "../tokenizer"; -import { runGGUFModel } from "./models/local"; import { risuChatParser } from "../parser.svelte"; import { SignatureV4 } from "@smithy/signature-v4"; import { HttpRequest } from "@smithy/protocol-http"; import { Sha256 } from "@aws-crypto/sha256-js"; -import { v4 } from "uuid"; import { supportsInlayImage } from "./files/image"; import { Capacitor } from "@capacitor/core"; import { getFreeOpenRouterModel } from "../model/openrouter"; @@ -47,9 +44,9 @@ interface requestDataArgument{ interface RequestDataArgumentExtended extends requestDataArgument{ aiModel?:string multiGen?:boolean - realAIModel?:string abortSignal?:AbortSignal modelInfo?:LLMModel + customURL?:string } type requestDataResponse = { @@ -199,19 +196,12 @@ export async function requestChatDataMain(arg:requestDataArgument, model:'model' targ.biasString = arg.biasString ?? [] targ.aiModel = (model === 'model' ? db.aiModel : db.subModel) targ.multiGen = ((db.genTime > 1 && targ.aiModel.startsWith('gpt') && (!arg.continue)) && (!arg.noMultiGen)) - targ.realAIModel = targ.aiModel targ.abortSignal = abortSignal targ.modelInfo = getModelInfo(targ.aiModel) if(targ.aiModel === 'reverse_proxy'){ - if(db.proxyRequestModel === 'custom' && db.customProxyRequestModel.startsWith('claude')){ - targ.realAIModel = db.customProxyRequestModel - } - if(db.proxyRequestModel.startsWith('claude')){ - targ.realAIModel = db.proxyRequestModel - } - if(db.forceProxyAsOpenAI){ - targ.realAIModel = 'reverse_proxy' - } + targ.modelInfo.internalID = db.customProxyRequestModel + targ.modelInfo.format = db.customAPIFormat + targ.customURL = db.forceReplaceUrl } const format = targ.modelInfo.format @@ -384,7 +374,7 @@ async function requestOpenAI(arg:RequestDataArgumentExtended):Promise 0) ? logit_bias.join("<<|>>") : undefined, logit_bias_values: (logit_bias_values.length > 0) ? logit_bias_values.join("|") : undefined, }; - const response = await globalFetch(api_server_url + '/api', { + const response = await globalFetch(arg.customURL ?? api_server_url + '/api', { method: 'POST', headers: headers, body: send_body, @@ -1771,7 +1762,7 @@ async function requestCohere(arg:RequestDataArgumentExtended):Promise Date: Wed, 27 Nov 2024 04:33:12 +0900 Subject: [PATCH 146/175] Add systemContentReplacement and Flags --- src/lang/en.ts | 6 +- src/lib/Setting/Pages/PromptSettings.svelte | 7 + src/ts/model/modellist.ts | 375 +++++++++++++++----- src/ts/process/request.ts | 72 +++- src/ts/storage/database.svelte.ts | 4 + 5 files changed, 364 insertions(+), 100 deletions(-) diff --git a/src/lang/en.ts b/src/lang/en.ts index 50473fbf..aeac7f88 100644 --- a/src/lang/en.ts +++ b/src/lang/en.ts @@ -164,7 +164,9 @@ export const languageEnglish = { translatorNote: "Here, you can add a unique translation prompt for each character. This option only applies when using the Ax. model for translation. To apply it, include `{{slot::tnote}}` in the language settings. It doesn't work in group chats.", groupInnerFormat: "This defines a format that is used in group chat for characters that isn't speaker. if it is not blank, it will use this format instead of the default format. if `Group Other Bot Role` is `assistant`, it will also be applied to the speaker.", groupOtherBotRole: "This defines a role that is used in group chat for characters that isn't speaker.", - chatHTML: "A HTML that would be inserted as each chat.\n\nYou can use CBS and special tags.\n- ``: a textbox that would be used to render text\n- ``: an icon for user or assistant\n- ``: icon buttons for chat edit, translations and etc.\n- ``: generation information button." + chatHTML: "A HTML that would be inserted as each chat.\n\nYou can use CBS and special tags.\n- ``: a textbox that would be used to render text\n- ``: an icon for user or assistant\n- ``: icon buttons for chat edit, translations and etc.\n- ``: generation information button.", + systemContentReplacement: "The prompt format that replaces system prompt if the model doesn't support system prompt.", + systemRoleReplacement: "The role that replaces system role if the model doesn't support system role.", }, setup: { chooseProvider: "Choose AI Provider", @@ -797,4 +799,6 @@ export const languageEnglish = { recommended: "Recommended", newChat: "New Chat", predictedOutput: "Predicted Output", + systemContentReplacement: "System Content Replacement", + systemRoleReplacement: "System Role Replacement", } \ No newline at end of file diff --git a/src/lib/Setting/Pages/PromptSettings.svelte b/src/lib/Setting/Pages/PromptSettings.svelte index d52cb0dd..bb32378d 100644 --- a/src/lib/Setting/Pages/PromptSettings.svelte +++ b/src/lib/Setting/Pages/PromptSettings.svelte @@ -149,6 +149,13 @@ {language.groupInnerFormat} \n{{slot}}\n`} bind:value={DBState.db.groupTemplate}/> + {language.systemContentReplacement} + + {language.systemRoleReplacement} + + User + assistant + {#if DBState.db.jsonSchemaEnabled} {language.jsonSchema} diff --git a/src/ts/model/modellist.ts b/src/ts/model/modellist.ts index 462b3e79..32454f2e 100644 --- a/src/ts/model/modellist.ts +++ b/src/ts/model/modellist.ts @@ -1,3 +1,5 @@ +import type { Parameter } from "../process/request" + export enum LLMFlags{ hasImageInput, hasImageOutput, @@ -7,6 +9,8 @@ export enum LLMFlags{ hasCache, hasFullSystemPrompt, hasFirstSystemPrompt, + requiresAlternateRole, + mustStartWithUserInput, } export enum LLMProvider{ @@ -54,6 +58,7 @@ export interface LLMModel{ provider: LLMProvider flags: LLMFlags[] format: LLMFormat + parameters: Parameter[] recommended?: boolean } @@ -72,6 +77,9 @@ const ProviderNames = new Map([ [LLMProvider.AWS, 'AWS'], ]) +const OpenAIParameters:Parameter[] = ['temperature', 'top_p', 'frequency_penalty', 'presence_penalty'] +const ClaudeParameters:Parameter[] = ['temperature', 'top_k', 'top_p'] + export const LLMModels: LLMModel[] = [ { id: 'gpt35', @@ -79,7 +87,8 @@ export const LLMModels: LLMModel[] = [ name: 'GPT-3.5', provider: LLMProvider.OpenAI, format: LLMFormat.OpenAICompatible, - flags: [], + flags: [LLMFlags.hasFullSystemPrompt], + parameters: OpenAIParameters, }, { id: 'instructgpt35', @@ -87,7 +96,8 @@ export const LLMModels: LLMModel[] = [ name: 'InstructGPT-3.5', provider: LLMProvider.OpenAI, format: LLMFormat.OpenAILegacyInstruct, - flags: [], + flags: [LLMFlags.hasFullSystemPrompt], + parameters: OpenAIParameters, }, { id: 'gpt4_turbo', @@ -95,7 +105,8 @@ export const LLMModels: LLMModel[] = [ name: 'GPT-4 Turbo', provider: LLMProvider.OpenAI, format: LLMFormat.OpenAICompatible, - flags: [], + flags: [LLMFlags.hasFullSystemPrompt], + parameters: OpenAIParameters, }, { id: 'gpt4o', @@ -104,9 +115,11 @@ export const LLMModels: LLMModel[] = [ provider: LLMProvider.OpenAI, format: LLMFormat.OpenAICompatible, flags: [ - LLMFlags.hasImageInput + LLMFlags.hasImageInput, + LLMFlags.hasFullSystemPrompt ], - recommended: true + recommended: true, + parameters: OpenAIParameters, }, { id: 'gpt4om', @@ -115,9 +128,11 @@ export const LLMModels: LLMModel[] = [ provider: LLMProvider.OpenAI, format: LLMFormat.OpenAICompatible, flags: [ - LLMFlags.hasImageInput + LLMFlags.hasImageInput, + LLMFlags.hasFullSystemPrompt ], - recommended: true + recommended: true, + parameters: OpenAIParameters, }, { id: 'gpt4', @@ -125,7 +140,10 @@ export const LLMModels: LLMModel[] = [ name: 'GPT-4', provider: LLMProvider.OpenAI, format: LLMFormat.OpenAICompatible, - flags: [], + flags: [ + LLMFlags.hasFullSystemPrompt + ], + parameters: OpenAIParameters, }, { id: 'gpt4_32k', @@ -133,7 +151,10 @@ export const LLMModels: LLMModel[] = [ name: 'GPT-4 32k', provider: LLMProvider.OpenAI, format: LLMFormat.OpenAICompatible, - flags: [], + flags: [ + LLMFlags.hasFullSystemPrompt + ], + parameters: OpenAIParameters, }, { id: 'gpt35_16k', @@ -141,7 +162,10 @@ export const LLMModels: LLMModel[] = [ name: 'GPT-3.5 Turbo 16k', provider: LLMProvider.OpenAI, format: LLMFormat.OpenAICompatible, - flags: [], + flags: [ + LLMFlags.hasFullSystemPrompt + ], + parameters: OpenAIParameters, }, { id: 'gpt4_0314', @@ -149,7 +173,10 @@ export const LLMModels: LLMModel[] = [ name: 'GPT-4 0314', provider: LLMProvider.OpenAI, format: LLMFormat.OpenAICompatible, - flags: [], + flags: [ + LLMFlags.hasFullSystemPrompt + ], + parameters: OpenAIParameters, }, { id: 'gpt4_0613', @@ -157,7 +184,10 @@ export const LLMModels: LLMModel[] = [ name: 'GPT-4 0613', provider: LLMProvider.OpenAI, format: LLMFormat.OpenAICompatible, - flags: [], + flags: [ + LLMFlags.hasFullSystemPrompt + ], + parameters: OpenAIParameters, }, { id: 'gpt4_32k_0613', @@ -165,7 +195,10 @@ export const LLMModels: LLMModel[] = [ name: 'GPT-4 32k 0613', provider: LLMProvider.OpenAI, format: LLMFormat.OpenAICompatible, - flags: [], + flags: [ + LLMFlags.hasFullSystemPrompt + ], + parameters: OpenAIParameters, }, { id: 'gpt4_1106', @@ -173,7 +206,10 @@ export const LLMModels: LLMModel[] = [ name: 'GPT-4 1106', provider: LLMProvider.OpenAI, format: LLMFormat.OpenAICompatible, - flags: [], + flags: [ + LLMFlags.hasFullSystemPrompt + ], + parameters: OpenAIParameters, }, { id: 'gpt35_0125', @@ -181,7 +217,10 @@ export const LLMModels: LLMModel[] = [ name: 'GPT-3.5 Turbo 0125', provider: LLMProvider.OpenAI, format: LLMFormat.OpenAICompatible, - flags: [], + flags: [ + LLMFlags.hasFullSystemPrompt + ], + parameters: OpenAIParameters, }, { id: 'gpt35_1106', @@ -189,7 +228,10 @@ export const LLMModels: LLMModel[] = [ name: 'GPT-3.5 Turbo 1106', provider: LLMProvider.OpenAI, format: LLMFormat.OpenAICompatible, - flags: [], + flags: [ + LLMFlags.hasFullSystemPrompt + ], + parameters: OpenAIParameters, }, { id: 'gpt35_0613', @@ -197,7 +239,10 @@ export const LLMModels: LLMModel[] = [ name: 'GPT-3.5 Turbo 0613', provider: LLMProvider.OpenAI, format: LLMFormat.OpenAICompatible, - flags: [], + flags: [ + LLMFlags.hasFullSystemPrompt + ], + parameters: OpenAIParameters, }, { id: 'gpt35_16k_0613', @@ -205,7 +250,10 @@ export const LLMModels: LLMModel[] = [ name: 'GPT-3.5 Turbo 16k', provider: LLMProvider.OpenAI, format: LLMFormat.OpenAICompatible, - flags: [], + flags: [ + LLMFlags.hasFullSystemPrompt + ], + parameters: OpenAIParameters, }, { id: 'gpt35_0301', @@ -213,7 +261,10 @@ export const LLMModels: LLMModel[] = [ name: 'GPT-3.5 Turbo 0301', provider: LLMProvider.OpenAI, format: LLMFormat.OpenAICompatible, - flags: [], + flags: [ + LLMFlags.hasFullSystemPrompt + ], + parameters: OpenAIParameters, }, { id: 'gpt4_0125', @@ -221,7 +272,10 @@ export const LLMModels: LLMModel[] = [ name: 'GPT-4 0125', provider: LLMProvider.OpenAI, format: LLMFormat.OpenAICompatible, - flags: [], + flags: [ + LLMFlags.hasFullSystemPrompt + ], + parameters: OpenAIParameters, }, { id: 'gptvi4_1106', @@ -230,6 +284,7 @@ export const LLMModels: LLMModel[] = [ provider: LLMProvider.OpenAI, format: LLMFormat.OpenAICompatible, flags: [LLMFlags.hasImageInput], + parameters: OpenAIParameters, }, { id: 'gpt4_turbo_20240409', @@ -237,7 +292,10 @@ export const LLMModels: LLMModel[] = [ name: 'GPT-4 Turbo 2024-04-09', provider: LLMProvider.OpenAI, format: LLMFormat.OpenAICompatible, - flags: [], + flags: [ + LLMFlags.hasFullSystemPrompt + ], + parameters: OpenAIParameters, }, { id: 'gpt4o-2024-05-13', @@ -246,8 +304,10 @@ export const LLMModels: LLMModel[] = [ provider: LLMProvider.OpenAI, format: LLMFormat.OpenAICompatible, flags: [ - LLMFlags.hasImageInput + LLMFlags.hasImageInput, + LLMFlags.hasFullSystemPrompt ], + parameters: OpenAIParameters, }, { id: 'gpt4o-2024-08-06', @@ -256,8 +316,10 @@ export const LLMModels: LLMModel[] = [ provider: LLMProvider.OpenAI, format: LLMFormat.OpenAICompatible, flags: [ - LLMFlags.hasImageInput + LLMFlags.hasImageInput, + LLMFlags.hasFullSystemPrompt ], + parameters: OpenAIParameters, }, { id: 'gpt4o-2024-11-20', @@ -266,8 +328,10 @@ export const LLMModels: LLMModel[] = [ provider: LLMProvider.OpenAI, format: LLMFormat.OpenAICompatible, flags: [ - LLMFlags.hasImageInput + LLMFlags.hasImageInput, + LLMFlags.hasFullSystemPrompt ], + parameters: OpenAIParameters, }, { id: 'gpt4o-chatgpt', @@ -276,8 +340,10 @@ export const LLMModels: LLMModel[] = [ provider: LLMProvider.OpenAI, format: LLMFormat.OpenAICompatible, flags: [ - LLMFlags.hasImageInput + LLMFlags.hasImageInput, + LLMFlags.hasFullSystemPrompt ], + parameters: OpenAIParameters, }, { id: 'gpt4o1-preview', @@ -285,7 +351,10 @@ export const LLMModels: LLMModel[] = [ name: 'o1 Preview', provider: LLMProvider.OpenAI, format: LLMFormat.OpenAICompatible, - flags: [], + flags: [ + LLMFlags.hasFullSystemPrompt + ], + parameters: OpenAIParameters, }, { id: 'gpt4o1-mini', @@ -293,7 +362,10 @@ export const LLMModels: LLMModel[] = [ name: 'o1 Mini', provider: LLMProvider.OpenAI, format: LLMFormat.OpenAICompatible, - flags: [], + flags: [ + LLMFlags.hasFullSystemPrompt + ], + parameters: OpenAIParameters, }, { name: "Claude 3.5 Sonnet", @@ -301,8 +373,13 @@ export const LLMModels: LLMModel[] = [ shortName: "3.5 Sonnet", provider: LLMProvider.Anthropic, format: LLMFormat.Anthropic, - flags: [LLMFlags.hasPrefill, LLMFlags.hasImageInput], - recommended: true + flags: [ + LLMFlags.hasPrefill, + LLMFlags.hasImageInput, + LLMFlags.hasFirstSystemPrompt + ], + recommended: true, + parameters: ClaudeParameters, }, { name: "Claude 3.5 Haiku", @@ -310,8 +387,13 @@ export const LLMModels: LLMModel[] = [ shortName: "3.5 Haiku", provider: LLMProvider.Anthropic, format: LLMFormat.Anthropic, - flags: [LLMFlags.hasPrefill, LLMFlags.hasImageInput], - recommended: true + flags: [ + LLMFlags.hasPrefill, + LLMFlags.hasImageInput, + LLMFlags.hasFirstSystemPrompt + ], + recommended: true, + parameters: ClaudeParameters, }, { name: 'Claude 3.5 Sonnet (20241022)', @@ -319,7 +401,12 @@ export const LLMModels: LLMModel[] = [ shortName: "3.5 Sonnet 1022", provider: LLMProvider.Anthropic, format: LLMFormat.Anthropic, - flags: [LLMFlags.hasPrefill, LLMFlags.hasImageInput], + flags: [ + LLMFlags.hasPrefill, + LLMFlags.hasImageInput, + LLMFlags.hasFirstSystemPrompt + ], + parameters: ClaudeParameters, }, { name: "Claude 3.5 Haiku (20241022)", @@ -327,7 +414,12 @@ export const LLMModels: LLMModel[] = [ shortName: "3.5 Haiku 1022", provider: LLMProvider.Anthropic, format: LLMFormat.Anthropic, - flags: [LLMFlags.hasPrefill, LLMFlags.hasImageInput], + flags: [ + LLMFlags.hasPrefill, + LLMFlags.hasImageInput, + LLMFlags.hasFirstSystemPrompt + ], + parameters: ClaudeParameters, }, { name: 'Claude 3 Haiku (20240307)', @@ -335,7 +427,12 @@ export const LLMModels: LLMModel[] = [ shortName: "3 Haiku 0307", provider: LLMProvider.Anthropic, format: LLMFormat.Anthropic, - flags: [LLMFlags.hasPrefill, LLMFlags.hasImageInput], + flags: [ + LLMFlags.hasPrefill, + LLMFlags.hasImageInput, + LLMFlags.hasFirstSystemPrompt + ], + parameters: ClaudeParameters, }, { name: 'Claude 3.5 Sonnet (20240620)', @@ -343,7 +440,12 @@ export const LLMModels: LLMModel[] = [ shortName: "3.5 Sonnet 0620", provider: LLMProvider.Anthropic, format: LLMFormat.Anthropic, - flags: [LLMFlags.hasPrefill, LLMFlags.hasImageInput], + flags: [ + LLMFlags.hasPrefill, + LLMFlags.hasImageInput, + LLMFlags.hasFirstSystemPrompt + ], + parameters: ClaudeParameters, }, { name: 'Claude 3 Opus (20240229)', @@ -351,7 +453,12 @@ export const LLMModels: LLMModel[] = [ shortName: "3 Opus 0229", provider: LLMProvider.Anthropic, format: LLMFormat.Anthropic, - flags: [LLMFlags.hasPrefill, LLMFlags.hasImageInput], + flags: [ + LLMFlags.hasPrefill, + LLMFlags.hasImageInput, + LLMFlags.hasFirstSystemPrompt + ], + parameters: ClaudeParameters, }, { name: 'Claude 3 Sonnet (20240229)', @@ -359,14 +466,22 @@ export const LLMModels: LLMModel[] = [ shortName: "3 Sonnet 0229", provider: LLMProvider.Anthropic, format: LLMFormat.Anthropic, - flags: [LLMFlags.hasPrefill, LLMFlags.hasImageInput], + flags: [ + LLMFlags.hasPrefill, + LLMFlags.hasImageInput, + LLMFlags.hasFirstSystemPrompt + ], + parameters: ClaudeParameters, }, { name: 'Claude 2.1', id: 'claude-2.1', provider: LLMProvider.Anthropic, format: LLMFormat.AnthropicLegacy, - flags: [LLMFlags.hasPrefill], + flags: [ + LLMFlags.hasPrefill, + ], + parameters: ClaudeParameters, }, { name: 'Claude 2', @@ -374,6 +489,7 @@ export const LLMModels: LLMModel[] = [ provider: LLMProvider.Anthropic, format: LLMFormat.AnthropicLegacy, flags: [LLMFlags.hasPrefill], + parameters: ClaudeParameters, }, { name: 'Claude 2 100k', @@ -381,6 +497,7 @@ export const LLMModels: LLMModel[] = [ provider: LLMProvider.Anthropic, format: LLMFormat.AnthropicLegacy, flags: [LLMFlags.hasPrefill], + parameters: ClaudeParameters, }, { name: 'Claude v1', @@ -388,6 +505,7 @@ export const LLMModels: LLMModel[] = [ provider: LLMProvider.Anthropic, format: LLMFormat.AnthropicLegacy, flags: [LLMFlags.hasPrefill], + parameters: ClaudeParameters, }, { name: 'Claude v1 100k', @@ -395,6 +513,7 @@ export const LLMModels: LLMModel[] = [ provider: LLMProvider.Anthropic, format: LLMFormat.AnthropicLegacy, flags: [LLMFlags.hasPrefill], + parameters: ClaudeParameters, }, { name: 'Claude Instant v1', @@ -402,6 +521,7 @@ export const LLMModels: LLMModel[] = [ provider: LLMProvider.Anthropic, format: LLMFormat.AnthropicLegacy, flags: [LLMFlags.hasPrefill], + parameters: ClaudeParameters, }, { name: 'Claude Instant v1 100k', @@ -409,6 +529,7 @@ export const LLMModels: LLMModel[] = [ provider: LLMProvider.Anthropic, format: LLMFormat.AnthropicLegacy, flags: [LLMFlags.hasPrefill], + parameters: ClaudeParameters, }, { name: 'Claude v1.2', @@ -416,6 +537,7 @@ export const LLMModels: LLMModel[] = [ provider: LLMProvider.Anthropic, format: LLMFormat.AnthropicLegacy, flags: [LLMFlags.hasPrefill], + parameters: ClaudeParameters, }, { name: 'Claude v1.0', @@ -423,49 +545,56 @@ export const LLMModels: LLMModel[] = [ provider: LLMProvider.Anthropic, format: LLMFormat.AnthropicLegacy, flags: [LLMFlags.hasPrefill], + parameters: ClaudeParameters, }, { name: 'Claude 3.5 Sonnet (20241022) v2', id: 'anthropic.claude-3-5-sonnet-20241022-v2:0', provider: LLMProvider.AWS, format: LLMFormat.AWSBedrockClaude, - flags: [LLMFlags.hasPrefill, LLMFlags.hasImageInput], + flags: [LLMFlags.hasPrefill, LLMFlags.hasImageInput, LLMFlags.hasFirstSystemPrompt], + parameters: ClaudeParameters, }, { name: 'Claude 3.5 Sonnet (20240620) v1', id: 'anthropic.claude-3-5-sonnet-20240620-v1:0', provider: LLMProvider.AWS, format: LLMFormat.AWSBedrockClaude, - flags: [LLMFlags.hasPrefill, LLMFlags.hasImageInput], + flags: [LLMFlags.hasPrefill, LLMFlags.hasImageInput, LLMFlags.hasFirstSystemPrompt], + parameters: ClaudeParameters, }, { name: 'Claude 3 Opus (20240229) v1', id: 'anthropic.claude-3-opus-20240229-v1:0', provider: LLMProvider.AWS, format: LLMFormat.AWSBedrockClaude, - flags: [LLMFlags.hasPrefill, LLMFlags.hasImageInput], + flags: [LLMFlags.hasPrefill, LLMFlags.hasImageInput, LLMFlags.hasFirstSystemPrompt], + parameters: ClaudeParameters, }, { name: 'Ooba', id: 'ooba', provider: LLMProvider.AsIs, format: LLMFormat.Ooba, - flags: [], - recommended: true + flags: [LLMFlags.hasFirstSystemPrompt], + recommended: true, + parameters: [] }, { name: 'Mancer', id: 'mancer', provider: LLMProvider.AsIs, format: LLMFormat.OobaLegacy, - flags: [], + flags: [LLMFlags.hasFirstSystemPrompt], + parameters: [] }, { name: 'OpenRouter', id: 'openrouter', provider: LLMProvider.AsIs, format: LLMFormat.OpenAICompatible, - flags: [], + flags: [LLMFlags.hasFullSystemPrompt, LLMFlags.hasImageInput], + parameters: ['temperature', 'top_p', 'frequency_penalty', 'presence_penalty', 'repetition_penalty', 'min_p', 'top_a', 'top_k'], recommended: true }, { @@ -474,8 +603,9 @@ export const LLMModels: LLMModel[] = [ shortName: 'Mistral S', provider: LLMProvider.Mistral, format: LLMFormat.Mistral, - flags: [], - recommended: true + flags: [LLMFlags.hasFirstSystemPrompt, LLMFlags.mustStartWithUserInput, LLMFlags.requiresAlternateRole], + recommended: true, + parameters: ['temperature', 'presence_penalty', 'frequency_penalty'] }, { name: 'Mistral Medium Latest', @@ -483,8 +613,9 @@ export const LLMModels: LLMModel[] = [ shortName: 'Mistral M', provider: LLMProvider.Mistral, format: LLMFormat.Mistral, - flags: [], - recommended: true + flags: [LLMFlags.hasFirstSystemPrompt, LLMFlags.mustStartWithUserInput, LLMFlags.requiresAlternateRole], + recommended: true, + parameters: ['temperature', 'presence_penalty', 'frequency_penalty'] }, { name: 'Mistral Large 2411', @@ -492,7 +623,8 @@ export const LLMModels: LLMModel[] = [ shortName: 'Mistral L 2411', provider: LLMProvider.Mistral, format: LLMFormat.Mistral, - flags: [], + flags: [LLMFlags.hasFirstSystemPrompt, LLMFlags.mustStartWithUserInput, LLMFlags.requiresAlternateRole], + parameters: ['temperature', 'presence_penalty', 'frequency_penalty'] }, { name: 'Mistral Nemo', @@ -500,7 +632,8 @@ export const LLMModels: LLMModel[] = [ shortName: 'Mistral Nemo', provider: LLMProvider.Mistral, format: LLMFormat.Mistral, - flags: [], + flags: [LLMFlags.hasFirstSystemPrompt, LLMFlags.mustStartWithUserInput, LLMFlags.requiresAlternateRole], + parameters: ['temperature', 'presence_penalty', 'frequency_penalty'] }, { name: 'Mistral Large Latest', @@ -508,7 +641,8 @@ export const LLMModels: LLMModel[] = [ shortName: 'Mistral L', provider: LLMProvider.Mistral, format: LLMFormat.Mistral, - flags: [], + flags: [LLMFlags.hasFirstSystemPrompt, LLMFlags.mustStartWithUserInput, LLMFlags.requiresAlternateRole], + parameters: ['temperature', 'presence_penalty', 'frequency_penalty'], recommended: true }, { @@ -516,31 +650,35 @@ export const LLMModels: LLMModel[] = [ id: 'gemini-1.5-pro-exp-0827', provider: LLMProvider.GoogleCloud, format: LLMFormat.GoogleCloud, - flags: [LLMFlags.hasImageInput], + flags: [LLMFlags.hasImageInput, LLMFlags.hasFirstSystemPrompt], + parameters: ['temperature', 'top_k', 'top_p'] }, { name: "Gemini Exp 1121", id: 'gemini-exp-1121', provider: LLMProvider.GoogleCloud, format: LLMFormat.GoogleCloud, - flags: [LLMFlags.hasImageInput], - recommended: true + flags: [LLMFlags.hasImageInput, LLMFlags.hasFirstSystemPrompt], + recommended: true, + parameters: ['temperature', 'top_k', 'top_p'] }, { name: "Gemini Pro 1.5", id: 'gemini-1.5-pro-latest', provider: LLMProvider.GoogleCloud, format: LLMFormat.GoogleCloud, - flags: [LLMFlags.hasImageInput], - recommended: true + flags: [LLMFlags.hasImageInput, LLMFlags.hasFirstSystemPrompt], + recommended: true, + parameters: ['temperature', 'top_k', 'top_p'] }, { name: "Gemini Flash 1.5", id: 'gemini-1.5-flash', provider: LLMProvider.GoogleCloud, format: LLMFormat.GoogleCloud, - flags: [LLMFlags.hasImageInput], - recommended: true + flags: [LLMFlags.hasImageInput, LLMFlags.hasFirstSystemPrompt], + recommended: true, + parameters: ['temperature', 'top_k', 'top_p'] }, { name: "Gemini Exp 1121", @@ -548,7 +686,8 @@ export const LLMModels: LLMModel[] = [ internalID: 'gemini-exp-1121', provider: LLMProvider.GoogleCloud, format: LLMFormat.VertexAIGemini, - flags: [LLMFlags.hasImageInput], + flags: [LLMFlags.hasImageInput, LLMFlags.hasFirstSystemPrompt], + parameters: ['temperature', 'top_k', 'top_p'] }, { name: "Gemini Pro 1.5", @@ -556,7 +695,8 @@ export const LLMModels: LLMModel[] = [ internalID: 'gemini-1.5-pro-latest', provider: LLMProvider.GoogleCloud, format: LLMFormat.VertexAIGemini, - flags: [LLMFlags.hasImageInput], + flags: [LLMFlags.hasImageInput, LLMFlags.hasFirstSystemPrompt], + parameters: ['temperature', 'top_k', 'top_p'] }, { name: "Gemini Flash 1.5", @@ -564,64 +704,79 @@ export const LLMModels: LLMModel[] = [ internalID: 'gemini-1.5-flash', provider: LLMProvider.GoogleCloud, format: LLMFormat.VertexAIGemini, - flags: [LLMFlags.hasImageInput], + flags: [LLMFlags.hasImageInput, LLMFlags.hasFirstSystemPrompt], + parameters: ['temperature', 'top_k', 'top_p'] }, { name: "Gemini Exp 1114", id: 'gemini-exp-1114', provider: LLMProvider.GoogleCloud, format: LLMFormat.GoogleCloud, - flags: [LLMFlags.hasImageInput], + flags: [LLMFlags.hasImageInput, LLMFlags.hasFirstSystemPrompt], + parameters: ['temperature', 'top_k', 'top_p'] }, { name: "Gemini Pro 1.5 002", id: 'gemini-1.5-pro-002', provider: LLMProvider.GoogleCloud, format: LLMFormat.GoogleCloud, - flags: [LLMFlags.hasImageInput], + flags: [LLMFlags.hasImageInput, LLMFlags.hasFirstSystemPrompt], + parameters: ['temperature', 'top_k', 'top_p'] }, { name: "Gemini Flash 1.5 002", id: 'gemini-1.5-flash-002', provider: LLMProvider.GoogleCloud, format: LLMFormat.GoogleCloud, - flags: [LLMFlags.hasImageInput], + flags: [LLMFlags.hasImageInput, LLMFlags.hasFirstSystemPrompt], + parameters: ['temperature', 'top_k', 'top_p'] }, { name: "Gemini Pro", id: 'gemini-pro', provider: LLMProvider.GoogleCloud, format: LLMFormat.GoogleCloud, - flags: [LLMFlags.hasImageInput], + flags: [LLMFlags.hasImageInput, LLMFlags.hasFirstSystemPrompt], + parameters: ['temperature', 'top_k', 'top_p'] }, { name: "Gemini Pro Vision", id: 'gemini-pro-vision', provider: LLMProvider.GoogleCloud, format: LLMFormat.GoogleCloud, - flags: [LLMFlags.hasImageInput], + flags: [LLMFlags.hasImageInput, LLMFlags.hasFirstSystemPrompt], + parameters: ['temperature', 'top_k', 'top_p'] }, { name: "Gemini Ultra", id: 'gemini-ultra', provider: LLMProvider.GoogleCloud, format: LLMFormat.GoogleCloud, - flags: [LLMFlags.hasImageInput], + flags: [LLMFlags.hasImageInput, LLMFlags.hasFirstSystemPrompt], + parameters: ['temperature', 'top_k', 'top_p'] }, { name: "Gemini Ultra Vision", id: 'gemini-ultra-vision', provider: LLMProvider.GoogleCloud, format: LLMFormat.GoogleCloud, - flags: [LLMFlags.hasImageInput], + flags: [LLMFlags.hasImageInput, LLMFlags.hasFirstSystemPrompt], + parameters: ['temperature', 'top_k', 'top_p'] }, { name: 'Kobold', id: 'kobold', provider: LLMProvider.AsIs, format: LLMFormat.Kobold, - flags: [], - recommended: true + flags: [LLMFlags.hasFirstSystemPrompt], + recommended: true, + parameters: [ + 'temperature', + 'top_p', + 'repetition_penalty', + 'top_k', + 'top_a' + ] }, { name: "SuperTrin", @@ -629,6 +784,7 @@ export const LLMModels: LLMModel[] = [ provider: LLMProvider.NovelList, format: LLMFormat.NovelList, flags: [], + parameters: [] }, { name: "Damsel", @@ -636,6 +792,7 @@ export const LLMModels: LLMModel[] = [ provider: LLMProvider.NovelList, format: LLMFormat.NovelList, flags: [], + parameters: [] }, { name: "Command R", @@ -643,8 +800,11 @@ export const LLMModels: LLMModel[] = [ internalID: 'command-r', provider: LLMProvider.Cohere, format: LLMFormat.Cohere, - flags: [], - recommended: true + flags: [LLMFlags.hasFirstSystemPrompt, LLMFlags.requiresAlternateRole, LLMFlags.mustStartWithUserInput], + recommended: true, + parameters: [ + 'temperature', 'top_k', 'top_p', 'presence_penalty', 'frequency_penalty' + ] }, { name: "Command R Plus", @@ -652,8 +812,11 @@ export const LLMModels: LLMModel[] = [ internalID: 'command-r-plus', provider: LLMProvider.Cohere, format: LLMFormat.Cohere, - flags: [], - recommended: true + flags: [LLMFlags.hasFirstSystemPrompt, LLMFlags.requiresAlternateRole, LLMFlags.mustStartWithUserInput], + recommended: true, + parameters: [ + 'temperature', 'top_k', 'top_p', 'presence_penalty', 'frequency_penalty' + ] }, { name: "Command R 08-2024", @@ -661,7 +824,10 @@ export const LLMModels: LLMModel[] = [ internalID: 'command-r-08-2024', provider: LLMProvider.Cohere, format: LLMFormat.Cohere, - flags: [], + flags: [LLMFlags.hasFirstSystemPrompt, LLMFlags.requiresAlternateRole, LLMFlags.mustStartWithUserInput], + parameters: [ + 'temperature', 'top_k', 'top_p', 'presence_penalty', 'frequency_penalty' + ] }, { name: "Command R 03-2024", @@ -669,7 +835,10 @@ export const LLMModels: LLMModel[] = [ internalID: 'command-r-03-2024', provider: LLMProvider.Cohere, format: LLMFormat.Cohere, - flags: [], + flags: [LLMFlags.hasFirstSystemPrompt, LLMFlags.requiresAlternateRole, LLMFlags.mustStartWithUserInput], + parameters: [ + 'temperature', 'top_k', 'top_p', 'presence_penalty', 'frequency_penalty' + ] }, { name: "Command R Plus 08-2024", @@ -677,7 +846,10 @@ export const LLMModels: LLMModel[] = [ internalID: 'command-r-plus-08-2024', provider: LLMProvider.Cohere, format: LLMFormat.Cohere, - flags: [], + flags: [LLMFlags.hasFirstSystemPrompt, LLMFlags.requiresAlternateRole, LLMFlags.mustStartWithUserInput], + parameters: [ + 'temperature', 'top_k', 'top_p', 'presence_penalty', 'frequency_penalty' + ] }, { name: "Command R Plus 04-2024", @@ -685,67 +857,82 @@ export const LLMModels: LLMModel[] = [ internalID: 'command-r-plus-04-2024', provider: LLMProvider.Cohere, format: LLMFormat.Cohere, - flags: [], + flags: [LLMFlags.hasFirstSystemPrompt, LLMFlags.requiresAlternateRole, LLMFlags.mustStartWithUserInput], + parameters: [ + 'temperature', 'top_k', 'top_p', 'presence_penalty', 'frequency_penalty' + ] }, { name: "Clio", id: 'novelai', provider: LLMProvider.NovelAI, format: LLMFormat.NovelAI, - flags: [], - recommended: true + flags: [LLMFlags.hasFullSystemPrompt], + recommended: true, + parameters: [ + 'temperature', 'top_k', 'top_p', 'presence_penalty', 'frequency_penalty' + ] }, { name: "Kayra", id: 'novelai_kayra', provider: LLMProvider.NovelAI, format: LLMFormat.NovelAI, - flags: [], - recommended: true + flags: [LLMFlags.hasFullSystemPrompt], + recommended: true, + parameters: [ + 'temperature', 'top_k', 'top_p', 'presence_penalty', 'frequency_penalty' + ] }, { id: 'ollama-hosted', name: 'Ollama', provider: LLMProvider.AsIs, format: LLMFormat.Ollama, - flags: [], + flags: [LLMFlags.hasFullSystemPrompt], + parameters: OpenAIParameters }, { id: 'hf:::Xenova/opt-350m', name: 'opt-350m', provider: LLMProvider.WebLLM, format: LLMFormat.WebLLM, - flags: [], + flags: [LLMFlags.hasFullSystemPrompt], + parameters: OpenAIParameters }, { id: 'hf:::Xenova/tiny-random-mistral', name: 'tiny-random-mistral', provider: LLMProvider.WebLLM, format: LLMFormat.WebLLM, - flags: [], + flags: [LLMFlags.hasFullSystemPrompt], + parameters: OpenAIParameters }, { id: 'hf:::Xenova/gpt2-large-conversational', name: 'gpt2-large-conversational', provider: LLMProvider.WebLLM, format: LLMFormat.WebLLM, - flags: [], + flags: [LLMFlags.hasFullSystemPrompt], + parameters: OpenAIParameters }, { id: 'custom', name: "Plugin", provider: LLMProvider.AsIs, format: LLMFormat.Plugin, - flags: [], - recommended: true + flags: [LLMFlags.hasFullSystemPrompt], + recommended: true, + parameters: ['temperature', 'top_p', 'frequency_penalty', 'presence_penalty', 'repetition_penalty', 'min_p', 'top_a', 'top_k'] }, { id: 'reverse_proxy', name: "Custom API", provider: LLMProvider.AsIs, format: LLMFormat.OpenAICompatible, - flags: [], - recommended: true + flags: [LLMFlags.hasFullSystemPrompt], + recommended: true, + parameters: ['temperature', 'top_p', 'frequency_penalty', 'presence_penalty', 'repetition_penalty', 'min_p', 'top_a', 'top_k'] } ] @@ -757,12 +944,13 @@ for(let model of LLMModels){ export function getModelInfo(id: string): LLMModel{ - const found = LLMModels.find(model => model.id === id) ?? { + const found:LLMModel = LLMModels.find(model => model.id === id) ?? { id, name: id, provider: LLMProvider.AsIs, format: LLMFormat.OpenAICompatible, flags: [], + parameters: OpenAIParameters } if(found) return found @@ -778,6 +966,7 @@ export function getModelInfo(id: string): LLMModel{ provider: LLMProvider.WebLLM, format: LLMFormat.WebLLM, flags: [], + parameters: OpenAIParameters } } if(id.startsWith('horde:::')){ @@ -791,6 +980,7 @@ export function getModelInfo(id: string): LLMModel{ provider: LLMProvider.Horde, format: LLMFormat.Horde, flags: [], + parameters: OpenAIParameters } } @@ -803,6 +993,7 @@ export function getModelInfo(id: string): LLMModel{ provider: LLMProvider.AsIs, format: LLMFormat.OpenAICompatible, flags: [], + parameters: OpenAIParameters } } diff --git a/src/ts/process/request.ts b/src/ts/process/request.ts index d9e88bf8..dfc3f9a1 100644 --- a/src/ts/process/request.ts +++ b/src/ts/process/request.ts @@ -20,7 +20,7 @@ import {Ollama} from 'ollama/dist/browser.mjs' import { applyChatTemplate } from "./templates/chatTemplate"; import { OobaParams } from "./prompt"; import { extractJSON, getOpenAIJSONSchema } from "./templates/jsonSchema"; -import { getModelInfo, LLMFormat, type LLMModel } from "../model/modellist"; +import { getModelInfo, LLMFlags, LLMFormat, type LLMModel } from "../model/modellist"; @@ -88,7 +88,7 @@ interface OaiFunctions { } -type Parameter = 'temperature'|'top_k'|'repetition_penalty'|'min_p'|'top_a'|'top_p'|'frequency_penalty'|'presence_penalty' +export type Parameter = 'temperature'|'top_k'|'repetition_penalty'|'min_p'|'top_a'|'top_p'|'frequency_penalty'|'presence_penalty' type ParameterMap = { [key in Parameter]?: string; }; @@ -182,6 +182,63 @@ export interface OpenAIChatExtra { multimodals?:MultiModal[] } +function reformater(formated:OpenAIChat[],modelInfo:LLMModel){ + + const db = getDatabase() + let systemPrompt:OpenAIChat|null = null + + if(!modelInfo.flags.includes(LLMFlags.hasFullSystemPrompt)){ + if(modelInfo.flags.includes(LLMFlags.hasFirstSystemPrompt)){ + if(formated[0].role === 'system'){ + systemPrompt = formated[0] + formated = formated.slice(1) + } + } + + for(let i=0;i { const db = getDatabase() @@ -206,6 +263,8 @@ export async function requestChatDataMain(arg:requestDataArgument, model:'model' const format = targ.modelInfo.format + targ.formated = reformater(targ.formated, targ.modelInfo) + switch(format){ case LLMFormat.OpenAICompatible: case LLMFormat.Mistral: @@ -437,14 +496,13 @@ async function requestOpenAI(arg:RequestDataArgumentExtended):Promise Date: Wed, 27 Nov 2024 04:44:25 +0900 Subject: [PATCH 147/175] Fix background embedding --- src/lib/ChatScreens/BackgroundDom.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/ChatScreens/BackgroundDom.svelte b/src/lib/ChatScreens/BackgroundDom.svelte index 0af7ff15..6611b5ff 100644 --- a/src/lib/ChatScreens/BackgroundDom.svelte +++ b/src/lib/ChatScreens/BackgroundDom.svelte @@ -5,7 +5,7 @@ import { moduleBackgroundEmbedding, ReloadGUIPointer, selIdState } from "src/ts/stores.svelte"; let backgroundHTML = $derived(DBState.db?.characters?.[selIdState.selId]?.backgroundHTML) - let currentChar:character|groupChat = $state() + let currentChar:character|groupChat = $derived(DBState.db?.characters?.[selIdState.selId]) From 9ab7dd3091c50365b197a41b3469085ce94d7808 Mon Sep 17 00:00:00 2001 From: Kwaroran Date: Wed, 27 Nov 2024 04:47:05 +0900 Subject: [PATCH 148/175] Remove AWS Claude check from BotSettings --- src/lib/Setting/Pages/BotSettings.svelte | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/lib/Setting/Pages/BotSettings.svelte b/src/lib/Setting/Pages/BotSettings.svelte index 36431235..9db153c4 100644 --- a/src/lib/Setting/Pages/BotSettings.svelte +++ b/src/lib/Setting/Pages/BotSettings.svelte @@ -233,9 +233,6 @@ } }}/> {/if} - {#if DBState.db.aiModel.startsWith('claude-') || DBState.db.subModel.startsWith('claude-')} - - {/if} {#if DBState.db.aiModel === 'reverse_proxy' || DBState.db.subModel === 'reverse_proxy'} {/if} From 73b7fd915656b0ac537e5a0ccc95bad746bb6cf0 Mon Sep 17 00:00:00 2001 From: Kwaroran Date: Wed, 27 Nov 2024 04:48:30 +0900 Subject: [PATCH 149/175] Update LLM model provider from GoogleCloud to VertexAI --- src/ts/model/modellist.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ts/model/modellist.ts b/src/ts/model/modellist.ts index 32454f2e..616aba74 100644 --- a/src/ts/model/modellist.ts +++ b/src/ts/model/modellist.ts @@ -684,7 +684,7 @@ export const LLMModels: LLMModel[] = [ name: "Gemini Exp 1121", id: 'gemini-exp-1121-vertex', internalID: 'gemini-exp-1121', - provider: LLMProvider.GoogleCloud, + provider: LLMProvider.VertexAI, format: LLMFormat.VertexAIGemini, flags: [LLMFlags.hasImageInput, LLMFlags.hasFirstSystemPrompt], parameters: ['temperature', 'top_k', 'top_p'] @@ -693,7 +693,7 @@ export const LLMModels: LLMModel[] = [ name: "Gemini Pro 1.5", id: 'gemini-1.5-pro-latest-vertex', internalID: 'gemini-1.5-pro-latest', - provider: LLMProvider.GoogleCloud, + provider: LLMProvider.VertexAI, format: LLMFormat.VertexAIGemini, flags: [LLMFlags.hasImageInput, LLMFlags.hasFirstSystemPrompt], parameters: ['temperature', 'top_k', 'top_p'] @@ -702,7 +702,7 @@ export const LLMModels: LLMModel[] = [ name: "Gemini Flash 1.5", id: 'gemini-1.5-flash-vertex', internalID: 'gemini-1.5-flash', - provider: LLMProvider.GoogleCloud, + provider: LLMProvider.VertexAI, format: LLMFormat.VertexAIGemini, flags: [LLMFlags.hasImageInput, LLMFlags.hasFirstSystemPrompt], parameters: ['temperature', 'top_k', 'top_p'] From 981ec3921eb69e8aaf9f33457f83f4c7a96157b0 Mon Sep 17 00:00:00 2001 From: Kwaroran Date: Wed, 27 Nov 2024 06:01:42 +0900 Subject: [PATCH 150/175] Add plenty of features --- src/lang/en.ts | 8 + src/lib/ChatScreens/Chat.svelte | 11 +- src/lib/Setting/Pages/BotSettings.svelte | 141 ++++++++++-------- src/lib/Setting/Pages/LanguageSettings.svelte | 10 ++ src/lib/Setting/Pages/OtherBotSettings.svelte | 5 +- src/lib/UI/GUI/SliderInput.svelte | 4 +- src/ts/model/modellist.ts | 107 ++++++++----- src/ts/process/index.svelte.ts | 3 +- src/ts/process/memory/hypav2.ts | 8 +- src/ts/process/memory/supaMemory.ts | 6 +- src/ts/process/request.ts | 61 ++++++-- src/ts/storage/database.svelte.ts | 37 ++++- src/ts/translator/translator.ts | 17 ++- 13 files changed, 285 insertions(+), 133 deletions(-) diff --git a/src/lang/en.ts b/src/lang/en.ts index aeac7f88..452f46a0 100644 --- a/src/lang/en.ts +++ b/src/lang/en.ts @@ -167,6 +167,9 @@ export const languageEnglish = { chatHTML: "A HTML that would be inserted as each chat.\n\nYou can use CBS and special tags.\n- ``: a textbox that would be used to render text\n- ``: an icon for user or assistant\n- ``: icon buttons for chat edit, translations and etc.\n- ``: generation information button.", systemContentReplacement: "The prompt format that replaces system prompt if the model doesn't support system prompt.", systemRoleReplacement: "The role that replaces system role if the model doesn't support system role.", + summarizationPrompt: "The prompt that is used for summarization. if it is blank, it will use the default prompt. you can also use ChatML formating with {{slot}} for the chat data.", + translatorPrompt: "The prompt that is used for translation. if it is blank, it will use the default prompt. you can also use ChatML formating with {{slot}} for the dest language, {{solt::content}} for the content, and {{slot::tnote}} for the translator note.", + translateBeforeHTMLFormatting: "If enabled, it will translate the text before Regex scripts and HTML formatting. this could make the token lesser but could break the formatting.", }, setup: { chooseProvider: "Choose AI Provider", @@ -801,4 +804,9 @@ export const languageEnglish = { predictedOutput: "Predicted Output", systemContentReplacement: "System Content Replacement", systemRoleReplacement: "System Role Replacement", + seperateParameters: "Seperate Parameters", + seperateParametersEnabled: "Enable Seperate Parameters", + summarizationPrompt: "Summarization Prompt", + translatorPrompt: "Translation Prompt", + translateBeforeHTMLFormatting: "Translate Before HTML Formatting", } \ No newline at end of file diff --git a/src/lib/ChatScreens/Chat.svelte b/src/lib/ChatScreens/Chat.svelte index a29cd519..a4216761 100644 --- a/src/lib/ChatScreens/Chat.svelte +++ b/src/lib/ChatScreens/Chat.svelte @@ -148,7 +148,16 @@ } } if(translateText){ - if(!DBState.db.legacyTranslation){ + if(DBState.db.translator === 'llm' && DBState.db.translateBeforeHTMLFormatting){ + translating = true + data = await translateHTML(data, false, charArg, chatID) + translating = false + const marked = await ParseMarkdown(data, charArg, mode, chatID, getCbsCondition()) + lastParsedQueue = marked + lastCharArg = charArg + return marked + } + else if(!DBState.db.legacyTranslation){ const marked = await ParseMarkdown(data, charArg, 'pretranslate', chatID, getCbsCondition()) translating = true const translated = await postTranslationParse(await translateHTML(marked, false, charArg, chatID)) diff --git a/src/lib/Setting/Pages/BotSettings.svelte b/src/lib/Setting/Pages/BotSettings.svelte index 9db153c4..7e95cb12 100644 --- a/src/lib/Setting/Pages/BotSettings.svelte +++ b/src/lib/Setting/Pages/BotSettings.svelte @@ -27,7 +27,8 @@ import PromptSettings from "./PromptSettings.svelte"; import { openPresetList } from "src/ts/stores.svelte"; import { selectSingleFile } from "src/ts/util"; - import { LLMFormat } from "src/ts/model/modellist"; + import { getModelInfo, LLMFlags, LLMFormat, LLMProvider } from "src/ts/model/modellist"; + import CheckInput from "src/lib/UI/GUI/CheckInput.svelte"; let tokens = $state({ mainPrompt: 0, @@ -54,6 +55,8 @@ }); let submenu = $state(DBState.db.useLegacyGUI ? -1 : 0) + let modelInfo = $derived(getModelInfo(DBState.db.aiModel)) + let subModelInfo = $derived(getModelInfo(DBState.db.subModel))

{language.chatBot}

@@ -89,26 +92,21 @@ {language.submodel} - {#if DBState.db.aiModel.startsWith('palm2') || DBState.db.subModel.startsWith('palm2') || DBState.db.aiModel.startsWith('gemini') || DBState.db.subModel.startsWith('gemini')} - - {#if DBState.db.google.projectId === 'aigoogle'} - GoogleAI API Key - {:else} - Google Bearer Token - {/if} - + {#if modelInfo.provider === LLMProvider.GoogleCloud || subModelInfo.provider === LLMProvider.GoogleCloud} + GoogleAI API Key - - {#if DBState.db.google.projectId !== 'aigoogle'} - Google Project ID - - {/if} {/if} - {#if DBState.db.aiModel.startsWith('jamba') || DBState.db.subModel.startsWith('jamba')} + {#if modelInfo.provider === LLMProvider.VertexAI || subModelInfo.provider === LLMProvider.VertexAI} + Vertex Client Email + + Vertex Private Key + + {/if} + {#if modelInfo.provider === LLMProvider.AI21 || subModelInfo.provider === LLMProvider.AI21} AI21 {language.apiKey} {/if} - {#if DBState.db.aiModel.startsWith('novellist') || DBState.db.subModel.startsWith('novellist')} + {#if modelInfo.provider === LLMProvider.NovelList || subModelInfo.provider === LLMProvider.NovelList} NovelList {language.apiKey} {/if} @@ -116,15 +114,15 @@ Mancer {language.apiKey} {/if} - {#if DBState.db.aiModel.startsWith('claude-') || DBState.db.subModel.startsWith('claude-')} + {#if modelInfo.provider === LLMProvider.Anthropic || subModelInfo.provider === LLMProvider.Anthropic} Claude {language.apiKey} {/if} - {#if DBState.db.aiModel.startsWith('mistral') || DBState.db.subModel.startsWith('mistral')} + {#if modelInfo.provider === LLMProvider.Mistral || subModelInfo.provider === LLMProvider.Mistral} Mistral {language.apiKey} {/if} - {#if DBState.db.aiModel.startsWith('novelai') || DBState.db.subModel.startsWith('novelai')} + {#if modelInfo.provider === LLMProvider.NovelAI || subModelInfo.provider === LLMProvider.NovelAI} NovelAI Bearer Token {/if} @@ -156,11 +154,7 @@
{/if} - {#if DBState.db.aiModel.startsWith('risullm')} - Risu {language.apiKey} - - {/if} - {#if DBState.db.aiModel.startsWith('cohere')} + {#if modelInfo.provider === LLMProvider.Cohere || subModelInfo.provider === LLMProvider.Cohere} Cohere {language.apiKey} {/if} @@ -211,32 +205,21 @@ {/each} {/if} - {#if DBState.db.aiModel.startsWith('gpt') || DBState.db.subModel.startsWith('gpt') - || DBState.db.aiModel.startsWith('instructgpt') || DBState.db.subModel.startsWith('instructgpt')} + {#if modelInfo.provider === LLMProvider.OpenAI || subModelInfo.provider === LLMProvider.OpenAI} OpenAI {language.apiKey} {/if}
- {#if DBState.db.aiModel.startsWith('gpt') || DBState.db.aiModel === 'reverse_proxy' || DBState.db.aiModel === 'openrouter' || DBState.db.aiModel.startsWith('claude-3')} + {#if modelInfo.flags.includes(LLMFlags.hasStreaming) || subModelInfo.flags.includes(LLMFlags.hasStreaming)} {/if} - {#if DBState.db.aiModel.startsWith('palm2') || DBState.db.subModel.startsWith('palm2') || DBState.db.aiModel.startsWith('gemini') || DBState.db.subModel.startsWith('gemini')} - { - if(!v){ - DBState.db.google.projectId = 'aigoogle' - } - else{ - DBState.db.google.projectId = '' - } - }}/> - {/if} {#if DBState.db.aiModel === 'reverse_proxy' || DBState.db.subModel === 'reverse_proxy'} {/if} - {#if DBState.db.aiModel === "novelai" || DBState.db.subModel === "novelai" || DBState.db.aiModel === 'novelai_kayra' || DBState.db.subModel === 'novelai_kayra'} + {#if modelInfo.provider === LLMProvider.NovelAI || subModelInfo.provider === LLMProvider.NovelAI} @@ -299,25 +282,21 @@ {/if} {language.temperature} - - {#if DBState.db.aiModel.startsWith("novelai")} - - {:else} - - {/if} - {#if DBState.db.aiModel.startsWith('openrouter') || DBState.db.aiModel.startsWith('claude-3') || DBState.db.aiModel.startsWith('cohere-')|| DBState.db.aiModel === 'kobold'} + + {#if modelInfo.parameters.includes('top_k')} Top K {/if} - {#if DBState.db.aiModel.startsWith('openrouter')} + {#if modelInfo.parameters.includes('min_p')} Min P {/if} - {#if DBState.db.aiModel.startsWith('openrouter') || DBState.db.aiModel === 'kobold'} + {#if modelInfo.parameters.includes('top_a')} Top A - + {/if} + {#if modelInfo.parameters.includes('repetition_penalty')} Repetition penalty @@ -389,7 +368,7 @@
- {:else if DBState.db.aiModel.startsWith('novelai')} + {:else if modelInfo.format === LLMFormat.NovelAI}
Starter @@ -423,7 +402,7 @@ Cfg Scale - {:else if DBState.db.aiModel.startsWith('novellist')} + {:else if modelInfo.format === LLMFormat.NovelList} Top P Reputation Penalty @@ -438,22 +417,19 @@ Typical P - {:else if DBState.db.aiModel.startsWith('claude')} - Top P - - {:else if DBState.db.aiModel.startsWith('kobold')} - Top P - {:else} - - - Top P - - - {language.frequencyPenalty} - - {language.presensePenalty} - + {#if modelInfo.parameters.includes('top_p')} + Top P + + {/if} + {#if modelInfo.parameters.includes('frequency_penalty')} + {language.frequencyPenalty} + + {/if} + {#if modelInfo.parameters.includes('presence_penalty')} + {language.presensePenalty} + + {/if} {/if} {#if (DBState.db.reverseProxyOobaMode && DBState.db.aiModel === 'reverse_proxy') || (DBState.db.aiModel === 'ooba')} @@ -464,6 +440,41 @@ {/if} + + + {#if DBState.db.seperateParametersEnabled} + {#each Object.keys(DBState.db.seperateParameters) as param, i} + + {language.temperature} + + Top K + + Repetition penalty + + Min P + + Top A + + Top P + + Frequency Penalty + + Presence Penalty + + + {/each} + + {/if} + + {/if} {#if submenu === 3 || submenu === -1} diff --git a/src/lib/Setting/Pages/LanguageSettings.svelte b/src/lib/Setting/Pages/LanguageSettings.svelte index 97c70f26..182afb6a 100644 --- a/src/lib/Setting/Pages/LanguageSettings.svelte +++ b/src/lib/Setting/Pages/LanguageSettings.svelte @@ -115,6 +115,8 @@ {#if DBState.db.translatorType === 'llm'} {language.translationResponseSize} + {language.translatorPrompt} + {/if} @@ -149,4 +151,12 @@
+ + {#if DBState.db.translatorType === 'llm'} +
+ + + +
+ {/if} {/if} \ No newline at end of file diff --git a/src/lib/Setting/Pages/OtherBotSettings.svelte b/src/lib/Setting/Pages/OtherBotSettings.svelte index 598bda89..a6935c8e 100644 --- a/src/lib/Setting/Pages/OtherBotSettings.svelte +++ b/src/lib/Setting/Pages/OtherBotSettings.svelte @@ -15,6 +15,7 @@ import { getCharImage } from "src/ts/characters"; import Arcodion from "src/lib/UI/Arcodion.svelte"; import CheckInput from "src/lib/UI/GUI/CheckInput.svelte"; + import TextAreaInput from "src/lib/UI/GUI/TextAreaInput.svelte"; $effect.pre(() => { DBState.db.NAIImgConfig ??= { width: 512, @@ -423,8 +424,8 @@ {language.SuperMemory} OpenAI Key {/if} - {language.SuperMemory} Prompt - + {language.summarizationPrompt} + {language.HypaMemory} Model MiniLM-L6-v2 (Free / Local) diff --git a/src/lib/UI/GUI/SliderInput.svelte b/src/lib/UI/GUI/SliderInput.svelte index 3901521e..c41b74d0 100644 --- a/src/lib/UI/GUI/SliderInput.svelte +++ b/src/lib/UI/GUI/SliderInput.svelte @@ -13,7 +13,7 @@ {#if disableable}
- { + { onchange?.() if(c) { value = min; @@ -58,7 +58,7 @@ - {customText === undefined ? (value === -1000 ? language.disabled : (value * multiple).toFixed(fixed)) : customText} + {customText === undefined ? ((value === -1000 || value === undefined) ? language.disabled : (value * multiple).toFixed(fixed)) : customText}
diff --git a/src/ts/model/modellist.ts b/src/ts/model/modellist.ts index 616aba74..96dd5a58 100644 --- a/src/ts/model/modellist.ts +++ b/src/ts/model/modellist.ts @@ -9,6 +9,7 @@ export enum LLMFlags{ hasCache, hasFullSystemPrompt, hasFirstSystemPrompt, + hasStreaming, requiresAlternateRole, mustStartWithUserInput, } @@ -26,6 +27,7 @@ export enum LLMProvider{ WebLLM, Horde, AWS, + AI21 } export enum LLMFormat{ @@ -87,7 +89,7 @@ export const LLMModels: LLMModel[] = [ name: 'GPT-3.5', provider: LLMProvider.OpenAI, format: LLMFormat.OpenAICompatible, - flags: [LLMFlags.hasFullSystemPrompt], + flags: [LLMFlags.hasFullSystemPrompt, LLMFlags.hasStreaming], parameters: OpenAIParameters, }, { @@ -96,7 +98,7 @@ export const LLMModels: LLMModel[] = [ name: 'InstructGPT-3.5', provider: LLMProvider.OpenAI, format: LLMFormat.OpenAILegacyInstruct, - flags: [LLMFlags.hasFullSystemPrompt], + flags: [LLMFlags.hasFullSystemPrompt, LLMFlags.hasStreaming], parameters: OpenAIParameters, }, { @@ -105,7 +107,7 @@ export const LLMModels: LLMModel[] = [ name: 'GPT-4 Turbo', provider: LLMProvider.OpenAI, format: LLMFormat.OpenAICompatible, - flags: [LLMFlags.hasFullSystemPrompt], + flags: [LLMFlags.hasFullSystemPrompt, LLMFlags.hasStreaming], parameters: OpenAIParameters, }, { @@ -116,7 +118,8 @@ export const LLMModels: LLMModel[] = [ format: LLMFormat.OpenAICompatible, flags: [ LLMFlags.hasImageInput, - LLMFlags.hasFullSystemPrompt + LLMFlags.hasFullSystemPrompt, + LLMFlags.hasStreaming ], recommended: true, parameters: OpenAIParameters, @@ -129,7 +132,8 @@ export const LLMModels: LLMModel[] = [ format: LLMFormat.OpenAICompatible, flags: [ LLMFlags.hasImageInput, - LLMFlags.hasFullSystemPrompt + LLMFlags.hasFullSystemPrompt, + LLMFlags.hasStreaming ], recommended: true, parameters: OpenAIParameters, @@ -141,7 +145,8 @@ export const LLMModels: LLMModel[] = [ provider: LLMProvider.OpenAI, format: LLMFormat.OpenAICompatible, flags: [ - LLMFlags.hasFullSystemPrompt + LLMFlags.hasFullSystemPrompt, + LLMFlags.hasStreaming ], parameters: OpenAIParameters, }, @@ -152,7 +157,8 @@ export const LLMModels: LLMModel[] = [ provider: LLMProvider.OpenAI, format: LLMFormat.OpenAICompatible, flags: [ - LLMFlags.hasFullSystemPrompt + LLMFlags.hasFullSystemPrompt, + LLMFlags.hasStreaming ], parameters: OpenAIParameters, }, @@ -163,7 +169,8 @@ export const LLMModels: LLMModel[] = [ provider: LLMProvider.OpenAI, format: LLMFormat.OpenAICompatible, flags: [ - LLMFlags.hasFullSystemPrompt + LLMFlags.hasFullSystemPrompt, + LLMFlags.hasStreaming ], parameters: OpenAIParameters, }, @@ -174,7 +181,8 @@ export const LLMModels: LLMModel[] = [ provider: LLMProvider.OpenAI, format: LLMFormat.OpenAICompatible, flags: [ - LLMFlags.hasFullSystemPrompt + LLMFlags.hasFullSystemPrompt, + LLMFlags.hasStreaming ], parameters: OpenAIParameters, }, @@ -185,7 +193,8 @@ export const LLMModels: LLMModel[] = [ provider: LLMProvider.OpenAI, format: LLMFormat.OpenAICompatible, flags: [ - LLMFlags.hasFullSystemPrompt + LLMFlags.hasFullSystemPrompt, + LLMFlags.hasStreaming ], parameters: OpenAIParameters, }, @@ -196,7 +205,8 @@ export const LLMModels: LLMModel[] = [ provider: LLMProvider.OpenAI, format: LLMFormat.OpenAICompatible, flags: [ - LLMFlags.hasFullSystemPrompt + LLMFlags.hasFullSystemPrompt, + LLMFlags.hasStreaming ], parameters: OpenAIParameters, }, @@ -207,7 +217,8 @@ export const LLMModels: LLMModel[] = [ provider: LLMProvider.OpenAI, format: LLMFormat.OpenAICompatible, flags: [ - LLMFlags.hasFullSystemPrompt + LLMFlags.hasFullSystemPrompt, + LLMFlags.hasStreaming ], parameters: OpenAIParameters, }, @@ -218,7 +229,8 @@ export const LLMModels: LLMModel[] = [ provider: LLMProvider.OpenAI, format: LLMFormat.OpenAICompatible, flags: [ - LLMFlags.hasFullSystemPrompt + LLMFlags.hasFullSystemPrompt, + LLMFlags.hasStreaming ], parameters: OpenAIParameters, }, @@ -229,7 +241,8 @@ export const LLMModels: LLMModel[] = [ provider: LLMProvider.OpenAI, format: LLMFormat.OpenAICompatible, flags: [ - LLMFlags.hasFullSystemPrompt + LLMFlags.hasFullSystemPrompt, + LLMFlags.hasStreaming ], parameters: OpenAIParameters, }, @@ -240,7 +253,8 @@ export const LLMModels: LLMModel[] = [ provider: LLMProvider.OpenAI, format: LLMFormat.OpenAICompatible, flags: [ - LLMFlags.hasFullSystemPrompt + LLMFlags.hasFullSystemPrompt, + LLMFlags.hasStreaming ], parameters: OpenAIParameters, }, @@ -251,7 +265,8 @@ export const LLMModels: LLMModel[] = [ provider: LLMProvider.OpenAI, format: LLMFormat.OpenAICompatible, flags: [ - LLMFlags.hasFullSystemPrompt + LLMFlags.hasFullSystemPrompt, + LLMFlags.hasStreaming ], parameters: OpenAIParameters, }, @@ -262,7 +277,8 @@ export const LLMModels: LLMModel[] = [ provider: LLMProvider.OpenAI, format: LLMFormat.OpenAICompatible, flags: [ - LLMFlags.hasFullSystemPrompt + LLMFlags.hasFullSystemPrompt, + LLMFlags.hasStreaming ], parameters: OpenAIParameters, }, @@ -273,7 +289,8 @@ export const LLMModels: LLMModel[] = [ provider: LLMProvider.OpenAI, format: LLMFormat.OpenAICompatible, flags: [ - LLMFlags.hasFullSystemPrompt + LLMFlags.hasFullSystemPrompt, + LLMFlags.hasStreaming ], parameters: OpenAIParameters, }, @@ -283,7 +300,10 @@ export const LLMModels: LLMModel[] = [ name: 'GPT-4 Vision 1106', provider: LLMProvider.OpenAI, format: LLMFormat.OpenAICompatible, - flags: [LLMFlags.hasImageInput], + flags: [ + LLMFlags.hasImageInput, + LLMFlags.hasStreaming + ], parameters: OpenAIParameters, }, { @@ -293,7 +313,8 @@ export const LLMModels: LLMModel[] = [ provider: LLMProvider.OpenAI, format: LLMFormat.OpenAICompatible, flags: [ - LLMFlags.hasFullSystemPrompt + LLMFlags.hasFullSystemPrompt, + LLMFlags.hasStreaming ], parameters: OpenAIParameters, }, @@ -305,7 +326,8 @@ export const LLMModels: LLMModel[] = [ format: LLMFormat.OpenAICompatible, flags: [ LLMFlags.hasImageInput, - LLMFlags.hasFullSystemPrompt + LLMFlags.hasFullSystemPrompt, + LLMFlags.hasStreaming ], parameters: OpenAIParameters, }, @@ -317,7 +339,8 @@ export const LLMModels: LLMModel[] = [ format: LLMFormat.OpenAICompatible, flags: [ LLMFlags.hasImageInput, - LLMFlags.hasFullSystemPrompt + LLMFlags.hasFullSystemPrompt, + LLMFlags.hasStreaming ], parameters: OpenAIParameters, }, @@ -329,7 +352,8 @@ export const LLMModels: LLMModel[] = [ format: LLMFormat.OpenAICompatible, flags: [ LLMFlags.hasImageInput, - LLMFlags.hasFullSystemPrompt + LLMFlags.hasFullSystemPrompt, + LLMFlags.hasStreaming ], parameters: OpenAIParameters, }, @@ -341,7 +365,8 @@ export const LLMModels: LLMModel[] = [ format: LLMFormat.OpenAICompatible, flags: [ LLMFlags.hasImageInput, - LLMFlags.hasFullSystemPrompt + LLMFlags.hasFullSystemPrompt, + LLMFlags.hasStreaming ], parameters: OpenAIParameters, }, @@ -352,7 +377,8 @@ export const LLMModels: LLMModel[] = [ provider: LLMProvider.OpenAI, format: LLMFormat.OpenAICompatible, flags: [ - LLMFlags.hasFullSystemPrompt + LLMFlags.hasFullSystemPrompt, + LLMFlags.hasStreaming ], parameters: OpenAIParameters, }, @@ -363,7 +389,8 @@ export const LLMModels: LLMModel[] = [ provider: LLMProvider.OpenAI, format: LLMFormat.OpenAICompatible, flags: [ - LLMFlags.hasFullSystemPrompt + LLMFlags.hasFullSystemPrompt, + LLMFlags.hasStreaming ], parameters: OpenAIParameters, }, @@ -376,7 +403,8 @@ export const LLMModels: LLMModel[] = [ flags: [ LLMFlags.hasPrefill, LLMFlags.hasImageInput, - LLMFlags.hasFirstSystemPrompt + LLMFlags.hasFirstSystemPrompt, + LLMFlags.hasStreaming ], recommended: true, parameters: ClaudeParameters, @@ -390,7 +418,8 @@ export const LLMModels: LLMModel[] = [ flags: [ LLMFlags.hasPrefill, LLMFlags.hasImageInput, - LLMFlags.hasFirstSystemPrompt + LLMFlags.hasFirstSystemPrompt, + LLMFlags.hasStreaming ], recommended: true, parameters: ClaudeParameters, @@ -404,7 +433,8 @@ export const LLMModels: LLMModel[] = [ flags: [ LLMFlags.hasPrefill, LLMFlags.hasImageInput, - LLMFlags.hasFirstSystemPrompt + LLMFlags.hasFirstSystemPrompt, + LLMFlags.hasStreaming ], parameters: ClaudeParameters, }, @@ -417,7 +447,8 @@ export const LLMModels: LLMModel[] = [ flags: [ LLMFlags.hasPrefill, LLMFlags.hasImageInput, - LLMFlags.hasFirstSystemPrompt + LLMFlags.hasFirstSystemPrompt, + LLMFlags.hasStreaming ], parameters: ClaudeParameters, }, @@ -430,7 +461,8 @@ export const LLMModels: LLMModel[] = [ flags: [ LLMFlags.hasPrefill, LLMFlags.hasImageInput, - LLMFlags.hasFirstSystemPrompt + LLMFlags.hasFirstSystemPrompt, + LLMFlags.hasStreaming ], parameters: ClaudeParameters, }, @@ -443,7 +475,8 @@ export const LLMModels: LLMModel[] = [ flags: [ LLMFlags.hasPrefill, LLMFlags.hasImageInput, - LLMFlags.hasFirstSystemPrompt + LLMFlags.hasFirstSystemPrompt, + LLMFlags.hasStreaming ], parameters: ClaudeParameters, }, @@ -456,7 +489,8 @@ export const LLMModels: LLMModel[] = [ flags: [ LLMFlags.hasPrefill, LLMFlags.hasImageInput, - LLMFlags.hasFirstSystemPrompt + LLMFlags.hasFirstSystemPrompt, + LLMFlags.hasStreaming ], parameters: ClaudeParameters, }, @@ -469,7 +503,8 @@ export const LLMModels: LLMModel[] = [ flags: [ LLMFlags.hasPrefill, LLMFlags.hasImageInput, - LLMFlags.hasFirstSystemPrompt + LLMFlags.hasFirstSystemPrompt, + LLMFlags.hasStreaming ], parameters: ClaudeParameters, }, @@ -593,7 +628,7 @@ export const LLMModels: LLMModel[] = [ id: 'openrouter', provider: LLMProvider.AsIs, format: LLMFormat.OpenAICompatible, - flags: [LLMFlags.hasFullSystemPrompt, LLMFlags.hasImageInput], + flags: [LLMFlags.hasFullSystemPrompt, LLMFlags.hasImageInput, LLMFlags.hasStreaming], parameters: ['temperature', 'top_p', 'frequency_penalty', 'presence_penalty', 'repetition_penalty', 'min_p', 'top_a', 'top_k'], recommended: true }, @@ -930,7 +965,7 @@ export const LLMModels: LLMModel[] = [ name: "Custom API", provider: LLMProvider.AsIs, format: LLMFormat.OpenAICompatible, - flags: [LLMFlags.hasFullSystemPrompt], + flags: [LLMFlags.hasFullSystemPrompt, LLMFlags.hasStreaming], recommended: true, parameters: ['temperature', 'top_p', 'frequency_penalty', 'presence_penalty', 'repetition_penalty', 'min_p', 'top_a', 'top_k'] } diff --git a/src/ts/process/index.svelte.ts b/src/ts/process/index.svelte.ts index 74f067ba..47dcfd44 100644 --- a/src/ts/process/index.svelte.ts +++ b/src/ts/process/index.svelte.ts @@ -1465,9 +1465,8 @@ export async function sendChat(chatProcessIndex = -1,arg:{ formated: promptbody, bias: emobias, currentChar: currentChar, - temperature: 0.4, maxTokens: 30, - }, 'submodel', abortSignal) + }, 'emotion', abortSignal) if(rq.type === 'fail' || rq.type === 'streaming' || rq.type === 'multiline'){ if(abortSignal.aborted){ diff --git a/src/ts/process/memory/hypav2.ts b/src/ts/process/memory/hypav2.ts index a0758b9b..ba68e160 100644 --- a/src/ts/process/memory/hypav2.ts +++ b/src/ts/process/memory/hypav2.ts @@ -5,6 +5,7 @@ import { requestChatData } from "../request"; import { HypaProcesser } from "./hypamemory"; import { globalFetch } from "src/ts/globalApi.svelte"; import { runSummarizer } from "../transformers"; +import { parseChatML } from "src/ts/parser.svelte"; export interface HypaV2Data { chunks: { @@ -83,7 +84,10 @@ async function summary(stringlizedChat: string): Promise<{ success: boolean; dat }; } } else { - const promptbody: OpenAIChat[] = [ + + let parsedPrompt = parseChatML(supaPrompt.replaceAll('{{slot}}', stringlizedChat)) + + const promptbody: OpenAIChat[] = parsedPrompt ?? [ { role: "user", content: stringlizedChat @@ -99,7 +103,7 @@ async function summary(stringlizedChat: string): Promise<{ success: boolean; dat bias: {}, useStreaming: false, noMultiGen: true - }, 'submodel'); + }, 'memory'); if (da.type === 'fail' || da.type === 'streaming' || da.type === 'multiline') { return { success: false, diff --git a/src/ts/process/memory/supaMemory.ts b/src/ts/process/memory/supaMemory.ts index 189358c4..ffa507ec 100644 --- a/src/ts/process/memory/supaMemory.ts +++ b/src/ts/process/memory/supaMemory.ts @@ -7,6 +7,7 @@ import { stringlizeChat } from "../stringlize"; import { globalFetch } from "src/ts/globalApi.svelte"; import { runSummarizer } from "../transformers"; import { getUserName } from "src/ts/util"; +import { parseChatML } from "src/ts/parser.svelte"; export async function supaMemory( chats:OpenAIChat[], @@ -252,7 +253,8 @@ export async function supaMemory( } } else { - const promptbody:OpenAIChat[] = [ + let parsedPrompt = parseChatML(supaPrompt.replaceAll('{{slot}}', stringlizedChat)) + const promptbody:OpenAIChat[] = parsedPrompt ?? [ { role: "user", content: stringlizedChat @@ -267,7 +269,7 @@ export async function supaMemory( bias: {}, useStreaming: false, noMultiGen: true - }, 'submodel') + }, 'memory') if(da.type === 'fail' || da.type === 'streaming' || da.type === 'multiline'){ return { currentTokens: currentTokens, diff --git a/src/ts/process/request.ts b/src/ts/process/request.ts index dfc3f9a1..a4d8432f 100644 --- a/src/ts/process/request.ts +++ b/src/ts/process/request.ts @@ -1,5 +1,5 @@ import type { MultiModal, OpenAIChat, OpenAIChatFull } from "./index.svelte"; -import { getCurrentCharacter, getDatabase, type character } from "../storage/database.svelte"; +import { getCurrentCharacter, getDatabase, setDatabase, type character } from "../storage/database.svelte"; import { pluginProcess } from "../plugins/plugins"; import { language } from "../../lang"; import { stringlizeAINChat, getStopStrings, unstringlizeAIN, unstringlizeChat } from "./stringlize"; @@ -47,6 +47,7 @@ interface RequestDataArgumentExtended extends requestDataArgument{ abortSignal?:AbortSignal modelInfo?:LLMModel customURL?:string + mode?:ModelModeExtended } type requestDataResponse = { @@ -89,12 +90,31 @@ interface OaiFunctions { export type Parameter = 'temperature'|'top_k'|'repetition_penalty'|'min_p'|'top_a'|'top_p'|'frequency_penalty'|'presence_penalty' +export type ModelModeExtended = 'model'|'submodel'|'memory'|'emotion'|'otherAx'|'translate' type ParameterMap = { [key in Parameter]?: string; }; -function applyParameters(data: { [key: string]: any }, parameters: Parameter[], rename: ParameterMap = {}) { +function applyParameters(data: { [key: string]: any }, parameters: Parameter[], rename: ParameterMap, ModelMode:ModelModeExtended): { [key: string]: any } { const db = getDatabase() + if(db.seperateParametersEnabled && ModelMode !== 'model'){ + if(ModelMode === 'submodel'){ + ModelMode = 'otherAx' + } + + for(const parameter of parameters){ + let value = db.seperateParameters[ModelMode][parameter] + + if(value === -1000 || value === undefined){ + continue + } + + data[rename[parameter] ?? parameter] = value + } + return data + } + + for(const parameter of parameters){ let value = 0 switch(parameter){ @@ -141,7 +161,7 @@ function applyParameters(data: { [key: string]: any }, parameters: Parameter[], return data } -export async function requestChatData(arg:requestDataArgument, model:'model'|'submodel', abortSignal:AbortSignal=null):Promise { +export async function requestChatData(arg:requestDataArgument, model:ModelModeExtended, abortSignal:AbortSignal=null):Promise { const db = getDatabase() let trys = 0 while(true){ @@ -240,7 +260,7 @@ function reformater(formated:OpenAIChat[],modelInfo:LLMModel){ } -export async function requestChatDataMain(arg:requestDataArgument, model:'model'|'submodel', abortSignal:AbortSignal=null):Promise { +export async function requestChatDataMain(arg:requestDataArgument, model:ModelModeExtended, abortSignal:AbortSignal=null):Promise { const db = getDatabase() const targ:RequestDataArgumentExtended = arg targ.formated = safeStructuredClone(arg.formated) @@ -255,6 +275,7 @@ export async function requestChatDataMain(arg:requestDataArgument, model:'model' targ.multiGen = ((db.genTime > 1 && targ.aiModel.startsWith('gpt') && (!arg.continue)) && (!arg.noMultiGen)) targ.abortSignal = abortSignal targ.modelInfo = getModelInfo(targ.aiModel) + targ.mode = model if(targ.aiModel === 'reverse_proxy'){ targ.modelInfo.internalID = db.customProxyRequestModel targ.modelInfo.format = db.customAPIFormat @@ -502,7 +523,7 @@ async function requestOpenAI(arg:RequestDataArgumentExtended):Promise() -async function translateLLM(text:string, arg:{to:string}){ - if(llmCache.has(text)){ - return llmCache.get(text) +async function translateLLM(text:string, arg:{to:string}):Promise{ + const cacheMatch = await LLMCacheStorage.getItem(text) + if(cacheMatch){ + return cacheMatch as string } const styleDecodeRegex = /\(.+?)\<\/risu-style\>/gms let styleDecodes:string[] = [] @@ -489,7 +494,7 @@ async function translateLLM(text:string, arg:{to:string}){ useStreaming: false, noMultiGen: true, maxTokens: db.translatorMaxResponse, - }, 'submodel') + }, 'translate') if(rq.type === 'fail' || rq.type === 'streaming' || rq.type === 'multiline'){ alertError(`${rq.result}`) @@ -498,6 +503,6 @@ async function translateLLM(text:string, arg:{to:string}){ const result = rq.result.replace(//g, (match, p1) => { return styleDecodes[parseInt(p1)] ?? '' }).replace(/<\/style-data>/g, '') - llmCache.set(text, result) + await LLMCacheStorage.setItem(text, result) return result } \ No newline at end of file From 2b50abc09cf5df8642e2ec64a81366f688229b7b Mon Sep 17 00:00:00 2001 From: Kwaroran Date: Wed, 27 Nov 2024 06:31:59 +0900 Subject: [PATCH 151/175] Add retranslate --- src/lang/en.ts | 1 + src/lib/ChatScreens/Chat.svelte | 65 ++++++++++++++++++++++----------- src/ts/translator/translator.ts | 14 ++++--- 3 files changed, 52 insertions(+), 28 deletions(-) diff --git a/src/lang/en.ts b/src/lang/en.ts index 452f46a0..9601d18c 100644 --- a/src/lang/en.ts +++ b/src/lang/en.ts @@ -809,4 +809,5 @@ export const languageEnglish = { summarizationPrompt: "Summarization Prompt", translatorPrompt: "Translation Prompt", translateBeforeHTMLFormatting: "Translate Before HTML Formatting", + retranslate: "Retranslate", } \ No newline at end of file diff --git a/src/lib/ChatScreens/Chat.svelte b/src/lib/ChatScreens/Chat.svelte index a4216761..6ed2da91 100644 --- a/src/lib/ChatScreens/Chat.svelte +++ b/src/lib/ChatScreens/Chat.svelte @@ -12,7 +12,7 @@ import { type Unsubscriber } from "svelte/store"; import { isEqual } from "lodash"; import { sayTTS } from "src/ts/process/tts"; - import { capitalize } from "src/ts/util"; + import { capitalize, sleep } from "src/ts/util"; import { longpress } from "src/ts/gui/longtouch"; import { ColorSchemeTypeStore } from "src/ts/gui/colorscheme"; import { ConnectionOpenStore } from "src/ts/sync/multiuser"; @@ -21,6 +21,7 @@ let translating = $state(false) let editMode = $state(false) let statusMessage:string = $state('') + let retranslate = false interface Props { message?: string; name?: string; @@ -148,9 +149,13 @@ } } if(translateText){ - if(DBState.db.translator === 'llm' && DBState.db.translateBeforeHTMLFormatting){ + let doRetranslate = retranslate + retranslate = false + console.log(`retranslating: ${doRetranslate}`) + if(DBState.db.translatorType === 'llm' && DBState.db.translateBeforeHTMLFormatting){ + await sleep(100) translating = true - data = await translateHTML(data, false, charArg, chatID) + data = await translateHTML(data, false, charArg, chatID, doRetranslate) translating = false const marked = await ParseMarkdown(data, charArg, mode, chatID, getCbsCondition()) lastParsedQueue = marked @@ -160,7 +165,7 @@ else if(!DBState.db.legacyTranslation){ const marked = await ParseMarkdown(data, charArg, 'pretranslate', chatID, getCbsCondition()) translating = true - const translated = await postTranslationParse(await translateHTML(marked, false, charArg, chatID)) + const translated = await postTranslationParse(await translateHTML(marked, false, charArg, chatID, doRetranslate)) translating = false lastParsedQueue = translated lastCharArg = charArg @@ -169,7 +174,7 @@ else{ const marked = await ParseMarkdown(data, charArg, mode, chatID, getCbsCondition()) translating = true - const translated = await translateHTML(marked, false, charArg, chatID) + const translated = await translateHTML(marked, false, charArg, chatID, doRetranslate) translating = false lastParsedQueue = translated lastCharArg = charArg @@ -228,24 +233,40 @@ {#snippet genInfo()} - {#if messageGenerationInfo && DBState.db.requestInfoInsideChat} -
- +
+ {#if messageGenerationInfo && DBState.db.requestInfoInsideChat} + + + {/if} + {#if DBState.db.translatorType === 'llm' && translated && !lastParsed.startsWith(`div class="flex justify-center items-center">
{ + lastParsed = `
` + retranslate = true + $ReloadGUIPointer = $ReloadGUIPointer + 1 + }} + > + + + {language.retranslate} + + + {/if}
- {/if} {/snippet} {#snippet textBox()} diff --git a/src/ts/translator/translator.ts b/src/ts/translator/translator.ts index 41c4de84..72e8a35d 100644 --- a/src/ts/translator/translator.ts +++ b/src/ts/translator/translator.ts @@ -216,7 +216,7 @@ export function isExpTranslator(){ return db.translatorType === 'llm' || db.translatorType === 'deepl' || db.translatorType === 'deeplX' } -export async function translateHTML(html: string, reverse:boolean, charArg:simpleCharacterArgument|string = '', chatID:number): Promise { +export async function translateHTML(html: string, reverse:boolean, charArg:simpleCharacterArgument|string = '', chatID:number, regenerate = false): Promise { let alwaysExistChar: character | groupChat | simpleCharacterArgument; if(charArg !== ''){ if(typeof(charArg) === 'string'){ @@ -245,7 +245,7 @@ export async function translateHTML(html: string, reverse:boolean, charArg:simpl } if(db.translatorType === 'llm'){ const tr = db.translator || 'en' - return translateLLM(html, {to: tr}) + return translateLLM(html, {to: tr, regenerate}) } const dom = new DOMParser().parseFromString(html, 'text/html'); console.log(html) @@ -447,10 +447,12 @@ function needSuperChunkedTranslate(){ return getDatabase().translatorType === 'deeplX' } -async function translateLLM(text:string, arg:{to:string}):Promise{ - const cacheMatch = await LLMCacheStorage.getItem(text) - if(cacheMatch){ - return cacheMatch as string +async function translateLLM(text:string, arg:{to:string, regenerate?:boolean}):Promise{ + if(!arg.regenerate){ + const cacheMatch = await LLMCacheStorage.getItem(text) + if(cacheMatch){ + return cacheMatch as string + } } const styleDecodeRegex = /\(.+?)\<\/risu-style\>/gms let styleDecodes:string[] = [] From 2acbc00548c2a18b2e51cfaba57aa9f9aa9cb7c5 Mon Sep 17 00:00:00 2001 From: Kwaroran Date: Wed, 27 Nov 2024 07:53:35 +0900 Subject: [PATCH 152/175] Improve chat copy --- src/lang/en.ts | 1 + src/lib/ChatScreens/Chat.svelte | 104 ++++++++++++++++++++++++++++++-- 2 files changed, 100 insertions(+), 5 deletions(-) diff --git a/src/lang/en.ts b/src/lang/en.ts index 9601d18c..5dc39a0e 100644 --- a/src/lang/en.ts +++ b/src/lang/en.ts @@ -810,4 +810,5 @@ export const languageEnglish = { translatorPrompt: "Translation Prompt", translateBeforeHTMLFormatting: "Translate Before HTML Formatting", retranslate: "Retranslate", + loading: "Loading", } \ No newline at end of file diff --git a/src/lib/ChatScreens/Chat.svelte b/src/lib/ChatScreens/Chat.svelte index 6ed2da91..2ae609ef 100644 --- a/src/lib/ChatScreens/Chat.svelte +++ b/src/lib/ChatScreens/Chat.svelte @@ -2,7 +2,7 @@ import { ArrowLeft, Sparkles, ArrowRight, PencilIcon, LanguagesIcon, RefreshCcwIcon, TrashIcon, CopyIcon, Volume2Icon, BotIcon, ArrowLeftRightIcon, UserIcon } from "lucide-svelte"; import { type CbsConditions, ParseMarkdown, postTranslationParse, type simpleCharacterArgument } from "../../ts/parser.svelte"; import AutoresizeArea from "../UI/GUI/TextAreaResizable.svelte"; - import { alertConfirm, alertError, alertRequestData } from "../../ts/alert"; + import { alertConfirm, alertError, alertNormal, alertRequestData, alertWait } from "../../ts/alert"; import { language } from "../../lang"; import { type MessageGenerationInfo } from "../../ts/storage/database.svelte"; import { alertStore, DBState } from 'src/ts/stores.svelte'; @@ -10,7 +10,7 @@ import { translateHTML } from "../../ts/translator/translator"; import { risuChatParser } from "src/ts/process/scripts"; import { type Unsubscriber } from "svelte/store"; - import { isEqual } from "lodash"; + import { get, isEqual, startsWith } from "lodash"; import { sayTTS } from "src/ts/process/tts"; import { capitalize, sleep } from "src/ts/util"; import { longpress } from "src/ts/gui/longtouch"; @@ -18,6 +18,8 @@ import { ConnectionOpenStore } from "src/ts/sync/multiuser"; import { onDestroy, onMount } from "svelte"; import { getModelInfo } from "src/ts/model/modellist"; + import { getCharImage } from "src/ts/characters"; + import { getFileSrc } from "src/ts/globalApi.svelte"; let translating = $state(false) let editMode = $state(false) let statusMessage:string = $state('') @@ -151,7 +153,6 @@ if(translateText){ let doRetranslate = retranslate retranslate = false - console.log(`retranslating: ${doRetranslate}`) if(DBState.db.translatorType === 'llm' && DBState.db.translateBeforeHTMLFormatting){ await sleep(100) translating = true @@ -221,7 +222,6 @@ try { const parser = new DOMParser() const doc = parser.parseFromString(risuChatParser(html ?? '', {cbsConditions: getCbsCondition()}), 'text/html') - console.log(doc.body) return doc.body } catch (error) { const placeholder = document.createElement('div') @@ -314,7 +314,101 @@ {/if} {#if DBState.db.useChatCopy && !blankMessage} - - {/if} {#if DBState.db.useChatCopy && !blankMessage} - {/if} {/if} {#if DBState.db.translator !== '' && !blankMessage} - - {:else} - {/if} @@ -672,7 +672,7 @@
{#if editMode} - + {:else}
{@render textBox()} diff --git a/src/lib/ChatScreens/DefaultChatScreen.svelte b/src/lib/ChatScreens/DefaultChatScreen.svelte index 96aa1dbe..a59760d5 100644 --- a/src/lib/ChatScreens/DefaultChatScreen.svelte +++ b/src/lib/ChatScreens/DefaultChatScreen.svelte @@ -447,7 +447,7 @@ {/if} {#if !DBState.db.useAdvancedEditor} - \ No newline at end of file diff --git a/src/ts/alert.ts b/src/ts/alert.ts index 71787bae..411f0bb1 100644 --- a/src/ts/alert.ts +++ b/src/ts/alert.ts @@ -165,7 +165,7 @@ export function alertMd(msg:string){ } export function doingAlert(){ - return get(alertStoreImported).type !== 'none' && get(alertStoreImported).type !== 'toast' + return get(alertStoreImported).type !== 'none' && get(alertStoreImported).type !== 'toast' && get(alertStoreImported).type !== 'wait' } export function alertToast(msg:string){ diff --git a/src/ts/hotkey.ts b/src/ts/hotkey.ts index d79f3ab6..aacbe240 100644 --- a/src/ts/hotkey.ts +++ b/src/ts/hotkey.ts @@ -1,13 +1,62 @@ import { get } from "svelte/store" import { alertSelect, alertToast, doingAlert } from "./alert" import { changeToPreset as changeToPreset2, getDatabase } from "./storage/database.svelte" -import { MobileGUIStack, MobileSideBar, openPersonaList, openPresetList, SafeModeStore, selectedCharID, settingsOpen } from "./stores.svelte" +import { alertStore, MobileGUIStack, MobileSideBar, openPersonaList, openPresetList, SafeModeStore, selectedCharID, settingsOpen } from "./stores.svelte" import { language } from "src/lang" import { updateTextThemeAndCSS } from "./gui/colorscheme" export function initHotkey(){ document.addEventListener('keydown', (ev) => { if(ev.ctrlKey){ + + if(ev.altKey){ + switch(ev.key){ + case "r":{ + ev.preventDefault() + clickQuery('.button-icon-reroll') + return + } + case "f":{ + ev.preventDefault() + clickQuery('.button-icon-unreroll') + return + } + case "t":{ + ev.preventDefault() + clickQuery('.button-icon-translate') + return + } + case "r":{ + ev.preventDefault() + clickQuery('.button-icon-remove') + return + } + case 'e':{ + ev.preventDefault() + clickQuery('.button-icon-edit') + setTimeout(() => { + focusQuery('.message-edit-area') + }, 100) + return + } + case 'c':{ + ev.preventDefault() + clickQuery('.button-icon-copy') + return + } + case 'i':{ + ev.preventDefault() + focusQuery('.text-input-area') + return + } + case 'Enter':{ + ev.preventDefault() + clickQuery('.button-icon-send') + return + } + } + } + switch (ev.key){ case "1":{ changeToPreset(0) @@ -105,6 +154,15 @@ export function initHotkey(){ } ev.preventDefault() } + if(ev.key === 'Enter'){ + const alertType = get(alertStore).type + if(alertType === 'ask' || alertType === 'normal' || alertType === 'error'){ + alertStore.set({ + type: 'none', + msg: 'yes' + }) + } + } }) @@ -143,6 +201,23 @@ export function initHotkey(){ }) } +function clickQuery(query:string){ + let ele = document.querySelector(query) as HTMLElement + console.log(ele) + if(ele){ + ele.click() + } +} + +function focusQuery(query:string){ + let ele = document.querySelector(query) as HTMLElement + if(ele){ + ele.focus() + } +} + + + export function initMobileGesture(){ let pressingPointers = new Map() From 32bb0430e237246714e682178a2ef89fa0efbda4 Mon Sep 17 00:00:00 2001 From: Kwaroran Date: Wed, 27 Nov 2024 08:43:26 +0900 Subject: [PATCH 159/175] Bump version to 140.0.0 --- src-tauri/tauri.conf.json | 2 +- src/ts/hotkey.ts | 2 +- src/ts/storage/database.svelte.ts | 2 +- version.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index a4f17187..03a2176b 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -29,7 +29,7 @@ }, "productName": "RisuAI", "mainBinaryName": "RisuAI", - "version": "139.2.0", + "version": "140.0.0", "identifier": "co.aiclient.risu", "plugins": { "updater": { diff --git a/src/ts/hotkey.ts b/src/ts/hotkey.ts index aacbe240..fff01c3e 100644 --- a/src/ts/hotkey.ts +++ b/src/ts/hotkey.ts @@ -26,7 +26,7 @@ export function initHotkey(){ clickQuery('.button-icon-translate') return } - case "r":{ + case "d":{ ev.preventDefault() clickQuery('.button-icon-remove') return diff --git a/src/ts/storage/database.svelte.ts b/src/ts/storage/database.svelte.ts index 55fe72f3..1192c3a8 100644 --- a/src/ts/storage/database.svelte.ts +++ b/src/ts/storage/database.svelte.ts @@ -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 = "139.2.0" +export let appVer = "140.0.0" export let webAppSubVer = '' diff --git a/version.json b/version.json index 19e98dba..a56302da 100644 --- a/version.json +++ b/version.json @@ -1 +1 @@ -{"version":"139.2.0"} \ No newline at end of file +{"version":"140.0.0"} \ No newline at end of file From fef96fefd4ec760fb801bd5d2346c6080a321334 Mon Sep 17 00:00:00 2001 From: Kwaroran Date: Wed, 27 Nov 2024 09:05:03 +0900 Subject: [PATCH 160/175] Fix botpreset --- src/ts/storage/database.svelte.ts | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/ts/storage/database.svelte.ts b/src/ts/storage/database.svelte.ts index 1192c3a8..98e1c258 100644 --- a/src/ts/storage/database.svelte.ts +++ b/src/ts/storage/database.svelte.ts @@ -1154,6 +1154,17 @@ export interface botPreset{ extractJson?:string groupTemplate?:string groupOtherBotRole?:string + seperateParametersEnabled?:boolean + seperateParameters?:{ + memory: SeparateParameters, + emotion: SeparateParameters, + translate: SeparateParameters, + otherAx: SeparateParameters + } + customAPIFormat?:LLMFormat + systemContentReplacement?: string + systemRoleReplacement?: 'user'|'assistant' + openAIPrediction?: string } @@ -1446,6 +1457,12 @@ export function saveCurrentPreset(){ extractJson:db.extractJson ?? '', groupOtherBotRole: db.groupOtherBotRole ?? 'user', groupTemplate: db.groupTemplate ?? '', + seperateParametersEnabled: db.seperateParametersEnabled ?? false, + seperateParameters: safeStructuredClone(db.seperateParameters), + openAIPrediction: db.OAIPrediction, + customAPIFormat: safeStructuredClone(db.customAPIFormat), + systemContentReplacement: db.systemContentReplacement, + systemRoleReplacement: db.systemRoleReplacement, } db.botPresets = pres setDatabase(db) @@ -1540,6 +1557,17 @@ export function setPreset(db:Database, newPres: botPreset){ db.extractJson = newPres.extractJson ?? '' db.groupOtherBotRole = newPres.groupOtherBotRole ?? 'user' db.groupTemplate = newPres.groupTemplate ?? '' + db.seperateParametersEnabled = newPres.seperateParametersEnabled ?? false + db.seperateParameters = newPres.seperateParameters ? safeStructuredClone(newPres.seperateParameters) : { + memory: {}, + emotion: {}, + translate: {}, + otherAx: {} + } + db.OAIPrediction = newPres.openAIPrediction ?? '' + db.customAPIFormat = safeStructuredClone(newPres.customAPIFormat) ?? LLMFormat.OpenAICompatible + db.systemContentReplacement = newPres.systemContentReplacement ?? '' + db.systemRoleReplacement = newPres.systemRoleReplacement ?? 'user' return db } From 842258a0f5c125b69ba942666fb58f4989d91a6b Mon Sep 17 00:00:00 2001 From: Kwaroran Date: Wed, 27 Nov 2024 09:05:28 +0900 Subject: [PATCH 161/175] Bump version to 140.0.1 --- src-tauri/tauri.conf.json | 2 +- src/ts/storage/database.svelte.ts | 2 +- version.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 03a2176b..5a1d900d 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -29,7 +29,7 @@ }, "productName": "RisuAI", "mainBinaryName": "RisuAI", - "version": "140.0.0", + "version": "140.0.1", "identifier": "co.aiclient.risu", "plugins": { "updater": { diff --git a/src/ts/storage/database.svelte.ts b/src/ts/storage/database.svelte.ts index 98e1c258..2a81d41b 100644 --- a/src/ts/storage/database.svelte.ts +++ b/src/ts/storage/database.svelte.ts @@ -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 = "140.0.0" +export let appVer = "140.0.1" export let webAppSubVer = '' diff --git a/version.json b/version.json index a56302da..780e5aa9 100644 --- a/version.json +++ b/version.json @@ -1 +1 @@ -{"version":"140.0.0"} \ No newline at end of file +{"version":"140.0.1"} \ No newline at end of file From eaf429a61f827983f0595dcc4cb1c34847fabf44 Mon Sep 17 00:00:00 2001 From: Kwaroran Date: Wed, 27 Nov 2024 09:17:57 +0900 Subject: [PATCH 162/175] Optimize image loading by using canvas to convert fetched images to data URLs --- src/lib/ChatScreens/Chat.svelte | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/src/lib/ChatScreens/Chat.svelte b/src/lib/ChatScreens/Chat.svelte index 99940f0b..2a6882ef 100644 --- a/src/lib/ChatScreens/Chat.svelte +++ b/src/lib/ChatScreens/Chat.svelte @@ -345,12 +345,23 @@ if(url.startsWith('http://asset.localhost') || url.startsWith('https://asset.localhost') || url.startsWith('https://sv.risuai')){ try { const data = await fetch(url) - img.setAttribute('src', `${await data.blob().then((b) => new Promise((resolve, reject) => { + const canvas = document.createElement('canvas') + const ctx = canvas.getContext('2d') + const img = new Image() + img.src = await data.blob().then((b) => new Promise((resolve, reject) => { const reader = new FileReader() reader.onload = () => resolve(reader.result as string) reader.onerror = reject reader.readAsDataURL(b) - }))}`) + })) + await new Promise((resolve) => { + img.onload = resolve + }) + canvas.width = img.width + canvas.height = img.height + ctx.drawImage(img, 0, 0) + const dataURL = canvas.toDataURL('image/jpeg') + img.setAttribute('src', dataURL) } catch (error) { console.error(error) } @@ -363,12 +374,22 @@ if(iconImage.startsWith('http://asset.localhost') || iconImage.startsWith('https://asset.localhost') || iconImage.startsWith('https://sv.risuai')){ try { const data = await fetch(iconImage) - iconDataUrl = await data.blob().then((b) => new Promise((resolve, reject) => { + const canvas = document.createElement('canvas') + const ctx = canvas.getContext('2d') + const img = new Image() + img.src = await data.blob().then((b) => new Promise((resolve, reject) => { const reader = new FileReader() reader.onload = () => resolve(reader.result as string) reader.onerror = reject reader.readAsDataURL(b) })) + await new Promise((resolve) => { + img.onload = resolve + }) + canvas.width = img.width + canvas.height = img.height + ctx.drawImage(img, 0, 0) + iconDataUrl = canvas.toDataURL('image/jpeg') } catch (error) { console.error(error) } From 171db25f554d4e374fe1a905776a9f23358f9ea0 Mon Sep 17 00:00:00 2001 From: Kwaroran Date: Wed, 27 Nov 2024 09:19:22 +0900 Subject: [PATCH 163/175] Add try-catch --- src/lib/ChatScreens/Chat.svelte | 179 ++++++++++++++++---------------- 1 file changed, 92 insertions(+), 87 deletions(-) diff --git a/src/lib/ChatScreens/Chat.svelte b/src/lib/ChatScreens/Chat.svelte index 2a6882ef..3a48eb7c 100644 --- a/src/lib/ChatScreens/Chat.svelte +++ b/src/lib/ChatScreens/Chat.svelte @@ -306,45 +306,75 @@ {#if DBState.db.useChatCopy && !blankMessage}
+ +
+ + + +
{/if} {/if} \ No newline at end of file diff --git a/src/ts/storage/database.svelte.ts b/src/ts/storage/database.svelte.ts index caf92699..6224c911 100644 --- a/src/ts/storage/database.svelte.ts +++ b/src/ts/storage/database.svelte.ts @@ -846,6 +846,7 @@ export interface Database{ otherAx: SeparateParameters } translateBeforeHTMLFormatting:boolean + autoTranslateCachedOnly:boolean } interface SeparateParameters{ diff --git a/src/ts/translator/translator.ts b/src/ts/translator/translator.ts index 72e8a35d..156db65d 100644 --- a/src/ts/translator/translator.ts +++ b/src/ts/translator/translator.ts @@ -507,4 +507,8 @@ async function translateLLM(text:string, arg:{to:string, regenerate?:boolean}):P }).replace(/<\/style-data>/g, '') await LLMCacheStorage.setItem(text, result) return result +} + +export async function getLLMCache(text:string):Promise{ + return await LLMCacheStorage.getItem(text) } \ No newline at end of file From 7bad240d852a2213d95cbb5e447145b6cac4b565 Mon Sep 17 00:00:00 2001 From: kwaroran Date: Sat, 30 Nov 2024 01:30:06 +0900 Subject: [PATCH 171/175] Refactor character card processing by consolidating logic into convertCharbook function --- src/ts/characterCards.ts | 246 ++++++++++++++++++++------------------- 1 file changed, 125 insertions(+), 121 deletions(-) diff --git a/src/ts/characterCards.ts b/src/ts/characterCards.ts index 6c018430..0cb309fe 100644 --- a/src/ts/characterCards.ts +++ b/src/ts/characterCards.ts @@ -494,36 +494,16 @@ function convertOffSpecCards(charaData:OldTavernChar|CharacterCardV2Risu, imgp:s let loresettings:undefined|loreSettings = undefined let loreExt:undefined|any = undefined if(charbook){ - if((!checkNullish(charbook.recursive_scanning)) && - (!checkNullish(charbook.scan_depth)) && - (!checkNullish(charbook.token_budget))){ - loresettings = { - tokenBudget:charbook.token_budget, - scanDepth:charbook.scan_depth, - recursiveScanning: charbook.recursive_scanning, - fullWordMatching: charbook?.extensions?.risu_fullWordMatching ?? false, - } - } + const a = convertCharbook({ + lorebook, + charbook, + loresettings, + loreExt + }) - loreExt = charbook.extensions - - for(const book of charbook.entries){ - lorebook.push({ - key: book.keys.join(', '), - secondkey: book.secondary_keys?.join(', ') ?? '', - insertorder: book.insertion_order, - comment: book.name ?? book.comment ?? "", - content: book.content, - mode: "normal", - alwaysActive: book.constant ?? false, - selective: book.selective ?? false, - extentions: {...book.extensions, risu_case_sensitive: book.case_sensitive}, - activationPercent: book.extensions?.risu_activationPercent, - loreCache: book.extensions?.risu_loreCache ?? null, - //@ts-ignore - useRegex: book.use_regex ?? false - }) - } + lorebook = a.lorebook + loresettings = a.loresettings + loreExt = a.loreExt } return { @@ -794,99 +774,16 @@ async function importCharacterCardSpec(card:CharacterCardV2Risu|CharacterCardV3, let loresettings:undefined|loreSettings = undefined let loreExt:undefined|any = undefined if(charbook){ - if((!checkNullish(charbook.recursive_scanning)) && - (!checkNullish(charbook.scan_depth)) && - (!checkNullish(charbook.token_budget))){ - loresettings = { - tokenBudget:charbook.token_budget, - scanDepth:charbook.scan_depth, - recursiveScanning: charbook.recursive_scanning, - fullWordMatching: charbook?.extensions?.risu_fullWordMatching ?? false, - } - } - - loreExt = charbook.extensions - - for(const book of charbook.entries){ - let content = book.content - - if(book.use_regex && !book.keys?.[0]?.startsWith('/')){ - book.use_regex = false - } - - //extention migration - const extensions = book.extensions ?? {} - - if(extensions.useProbability && extensions.probability !== undefined && extensions.probability !== 100){ - content = `@@probability ${extensions.probability}\n` + content - delete extensions.useProbability - delete extensions.probability - } - if(extensions.position === 4 && typeof extensions.depth === 'number' && typeof(extensions.role) === 'number'){ - content = `@@depth ${extensions.depth}\n@@role ${['system','user','assistant'][extensions.role]}\n` + content - delete extensions.position - delete extensions.depth - delete extensions.role - } - if(typeof(extensions.selectiveLogic) === 'number' && book.secondary_keys && book.secondary_keys.length > 0){ - switch(extensions.selectiveLogic){ - case 0:{ - if(!book.secondary_keys || book.secondary_keys.length === 0){ - book.selective = false - } - break - } - case 1:{ - book.selective = false - content = `@@exclude_keys_all ${book.secondary_keys.join(',')}\n` + content - break - } - case 2:{ - book.selective = false - for(const secKey of book.secondary_keys){ - content = `@@exclude_keys ${secKey}\n` + content - } - break - } - case 3:{ - book.selective = false - for(const secKey of book.secondary_keys){ - content = `@@additional_keys ${secKey}\n` + content - } - break - } - } - } - if(typeof extensions.delay === 'number' && extensions.delay > 0){ - content = `@@activate_only_after ${extensions.delay}\n` + content - delete extensions.delay - } - if(extensions.match_whole_words === true){ - content = `@@match_full_word\n` + content - delete extensions.match_whole_words - } - if(extensions.match_whole_words === false){ - content = `@@match_partial_word\n` + content - delete extensions.match_whole_words - } - - lorebook.push({ - key: book.keys.join(', '), - secondkey: book.secondary_keys?.join(', ') ?? '', - insertorder: book.insertion_order, - comment: book.name ?? book.comment ?? "", - content: content, - mode: "normal", - alwaysActive: book.constant ?? false, - selective: book.selective ?? false, - extentions: {...extensions, risu_case_sensitive: book.case_sensitive}, - activationPercent: book.extensions?.risu_activationPercent, - loreCache: book.extensions?.risu_loreCache ?? null, - //@ts-ignore - useRegex: book.use_regex ?? false - }) - } + const a = convertCharbook({ + lorebook, + charbook, + loresettings, + loreExt + }) + lorebook = a.lorebook + loresettings = a.loresettings + loreExt = a.loreExt } let ext = safeStructuredClone(data?.extensions ?? {}) @@ -980,6 +877,113 @@ async function importCharacterCardSpec(card:CharacterCardV2Risu|CharacterCardV3, } +function convertCharbook(arg:{ + lorebook:loreBook[] + charbook:CharacterBook + loresettings:loreSettings + loreExt:any +}){ + let {lorebook, loresettings, loreExt, charbook} = arg + if((!checkNullish(charbook.recursive_scanning)) && + (!checkNullish(charbook.scan_depth)) && + (!checkNullish(charbook.token_budget))){ + loresettings = { + tokenBudget:charbook.token_budget, + scanDepth:charbook.scan_depth, + recursiveScanning: charbook.recursive_scanning, + fullWordMatching: charbook?.extensions?.risu_fullWordMatching ?? false, + } + } + + loreExt = charbook.extensions + + for(const book of charbook.entries){ + let content = book.content + + if(book.use_regex && !book.keys?.[0]?.startsWith('/')){ + book.use_regex = false + } + + //extention migration + const extensions = book.extensions ?? {} + + if(extensions.useProbability && extensions.probability !== undefined && extensions.probability !== 100){ + content = `@@probability ${extensions.probability}\n` + content + delete extensions.useProbability + delete extensions.probability + } + if(extensions.position === 4 && typeof extensions.depth === 'number' && typeof(extensions.role) === 'number'){ + content = `@@depth ${extensions.depth}\n@@role ${['system','user','assistant'][extensions.role]}\n` + content + delete extensions.position + delete extensions.depth + delete extensions.role + } + if(typeof(extensions.selectiveLogic) === 'number' && book.secondary_keys && book.secondary_keys.length > 0){ + switch(extensions.selectiveLogic){ + case 0:{ + if(!book.secondary_keys || book.secondary_keys.length === 0){ + book.selective = false + } + break + } + case 1:{ + book.selective = false + content = `@@exclude_keys_all ${book.secondary_keys.join(',')}\n` + content + break + } + case 2:{ + book.selective = false + for(const secKey of book.secondary_keys){ + content = `@@exclude_keys ${secKey}\n` + content + } + break + } + case 3:{ + book.selective = false + for(const secKey of book.secondary_keys){ + content = `@@additional_keys ${secKey}\n` + content + } + break + } + } + } + if(typeof extensions.delay === 'number' && extensions.delay > 0){ + content = `@@activate_only_after ${extensions.delay}\n` + content + delete extensions.delay + } + if(extensions.match_whole_words === true){ + content = `@@match_full_word\n` + content + delete extensions.match_whole_words + } + if(extensions.match_whole_words === false){ + content = `@@match_partial_word\n` + content + delete extensions.match_whole_words + } + + lorebook.push({ + key: book.keys.join(', '), + secondkey: book.secondary_keys?.join(', ') ?? '', + insertorder: book.insertion_order, + comment: book.name ?? book.comment ?? "", + content: content, + mode: "normal", + alwaysActive: book.constant ?? false, + selective: book.selective ?? false, + extentions: {...extensions, risu_case_sensitive: book.case_sensitive}, + activationPercent: book.extensions?.risu_activationPercent, + loreCache: book.extensions?.risu_loreCache ?? null, + //@ts-ignore + useRegex: book.use_regex ?? false + }) + } + + return { + lorebook, + loresettings, + loreExt + } +} + async function createBaseV2(char:character) { From 02b007155b97afcf2c99ceb892f089c19e289b11 Mon Sep 17 00:00:00 2001 From: Rivelle Date: Sat, 30 Nov 2024 00:31:02 +0800 Subject: [PATCH 172/175] Update Simplified Chinese Translations in cn.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extended Description: I have updated the word choices in Simplified Chinese translations to better align with the preferences of Simplified Chinese users. This update also includes some unfinished translations from the previous version. With the help of several Simplified Chinese-speaking friends, I performed a thorough review of the translations. They pointed out that I occasionally used phrasing that reflects Traditional Chinese grammar habits. However, they confirmed that the current version is still clear and widely acceptable among Simplified Chinese users. The main correction is translating "lorebook" to "世界书" in most cases, as this is the common term used by the Simplified Chinese community. Additionally, I decided to retain some technical terms, such as "Enable Schema," in English. Since there is no widely accepted Chinese translation for these terms, keeping them in English provides greater clarity and professionalism. Lastly, this file was created by copying and adapting the translations from the RisuAI .json translation file. I have carefully checked the formatting and hope there are no issues. --- src/lang/cn.ts | 1485 +++++++++++++++++++++++++----------------------- 1 file changed, 772 insertions(+), 713 deletions(-) diff --git a/src/lang/cn.ts b/src/lang/cn.ts index c63b1157..f24a5f20 100644 --- a/src/lang/cn.ts +++ b/src/lang/cn.ts @@ -1,715 +1,774 @@ export const languageChinese = { "formating": { - "main": "主控提示词", - "jailbreak": "越狱提示词", - "chats": "历史聊天", - "lorebook": "Lorebook", - "globalNote": "全局备注", - "authorNote": "作者备注", - "lastChat": "前一次聊天", - "description": "角色描述", - "personaPrompt": "用户提示词", - "plain": "基础提示词", - "memory": "Supa/HypaMemory", - "postEverything": "系统提示词结束" - }, - "errors": { - "toomuchtoken": "错误:所需的 Token 数超过了可用的最大上下文大小", - "unknownModel": "错误:无法识别所选的模型", - "httpError": "错误:请求发生错误:", - "noData": "无法找到文件中的数据,或者文件已经损毁", - "onlyOneChat": "至少需要一个聊天室", - "alreadyCharInGroup": "该群组中已经有一个同名角色。", - "noUserIcon": "请先设置你的个人头像。", - "emptyText": "文字内容为空。", - "wrongPassword": "密码错误", - "networkFetch": "这通常是由于网络连接不稳定或服务器故障引起的。", - "networkFetchWeb": "这可能是由 CORS 错误引起的。这种情况只会在使用网页版时发生,因为浏览器存在一些限制。请尝试使用本地版或其他版本的叡苏。", - "networkFetchPlain": "这可能是一次数据抓取(Fetch)错误。请尝试在设置中关闭强制抓取选项。", - "requestLogRemoved": "该请求记录已被删除。", - "requestLogRemovedDesc": "当客户端刷新或加载时,该请求记录会被删除。" - }, - "showHelp": "显示帮助", - "help": { - "model": "此模型是指聊天中使用的主控模型。", - "submodel": "辅助模型是一个用于分析情感图像、产生自动建议等的模型,推荐使用 GPT-3.5。", - "oaiapikey": "OpenAI 的 API 密钥(Key),可在 https://platform.openai.com/account/api-keys 获取。", - "mainprompt": "主控提示词设置用于决定模型的默认行为。", - "jailbreak": "当角色中的越狱开关被激活后,越狱提示词将被使用。", - "globalNote": "一个对模型行为有强烈影响的备注(也称为 UJB),适用于所有角色。", - "autoSuggest": "用于自动建议用户回应时生成选项的提示词。", - "formatOrder": "提示词的排列顺序:越靠下的区块对模型的影响越大。", - "forceUrl": "此字段不为空时,请求将被发送到你所输入的网址。", - "tempature": "较低的数值会使角色更紧密地遵循提示词,但会使回应更制式与机械化。\n较高的数值则会增强角色的创意表现,但回应可能会变得不稳定。", - "frequencyPenalty": "较高的数值可以避免角色在个别回应中重复使用相同的词汇,但回应也更容易出现语义混乱。", - "presensePenalty": "较高的数值可以避免角色在整体对话中重复使用相同的词汇,但这也可能导致回答失去一致性和稳定性。", - "sdProvider": "图像生成提供者。", - "msgSound": "当角色回应时,播放 *叮* 的提示音", - "charDesc": "角色的简要描述。这会影响角色的回应方式。", - "charFirstMessage": "角色的初始消息,这会极大地影响角色的回应方式。", - "charNote": "对模型行为有强烈影响的备注,嵌入到当前角色中,也称为 UJB。", - "toggleNsfw": "切换越狱提示词的开关。", - "lorebook": "Lorebook 是由用户创建的 AI 辞典,只有当上下文中包含关键词时 AI 才能看到它。", - "loreName": "Lorebook 的名称,不影响 AI。", - "loreActivationKey": "当上下文中包含任一关键词时,该条目将被激活,并激活相应的提示词。使用逗号分隔。", - "loreorder": "插入顺序越高,对模型的影响力越大。在激活大量条目时,也更不容易被截断。", - "bias": "Bias 是一组键值数据,用于修改某些字符串出现的概率。\n其数值范围可以是 -100 到 100。较高的数值会使该字符串更可能出现,较低的数值则降低出现概率。\n另外,在某些模型中,若将数值设为 -101,该字符串将被标记为「强制禁止词」。\n警告:若 Tokenizer 设置有误,可能无法正常运作。", - "emotion": "「情绪影像会根据角色情绪显示对应的图片。具体的情绪由角色的回应进行分析。你必须输入情绪名称作为词汇*(如:joy, happy, fear 等)*。若存在名为 **neutral** 的情绪,将作为默认情绪。至少需要三张图片才能正常运作。」", - "imggen": "分析聊天内容后,将提示套用至 {{slot}}。", - "regexScript": "正则表达式(Regex Script)是一个自定义工具,用于将符合条件的字符串由「IN」替换为「OUT」。\n\n有四种类型选项:\n\n- **修改输入(Modify Input)**:修改用户的输入内容\n\n- **修改输出(Modify Output)**:修改角色的输出内容\n\n- **修改请求数据(Modify Request Data)**:在当前聊天数据发送时进行修改\n\n- **修改显示(Modify Display)**:仅修改显示的文字,不更改聊天数据\n\n「IN」必须是一个不带标志(flags)的正则表达式,且开头和结尾不包含斜线。\n\n「OUT」是一个可以包含替换模式的字符串。这些替换模式如下:\n\n- $$\n\n - 插入符号「$」\n\n- $&\n\n - 插入匹配到的子字符串\n\n- $`\n\n - 插入匹配子字符串前的部分\n\n- $1\n\n - 插入第一个匹配群组,可以替换为其他数字(例如 2、3...)\n\n- $(name)\n\n - 插入命名群组\n\n针对标志(flags),除了原生支持的标志之外,还可以使用下列专为高级用户设计的标志:\n\n- ``:将结果注入当前字符串中\n- ``:将结果移到字符串的顶部\n- ``:将结果移到字符串的底部\n- ``:如果找不到匹配,则使用上一个匹配的结果\n- ``:设置结果的顺序,数值越高显示越靠前。「n」代表一个数字(例如 ``)。如果未设置,默认为 0。\n- ``:解析「IN」中的大括号语法\n\n若要与原生标志结合使用,可以像这样使用:`gi`。", - "experimental": "此为实验性功能,可能不稳定。", - "oogaboogaURL": "如果你的 WebUI 支持旧版 API,你的 URL 应类似于 *https://.../run/textgen*。\n\n如果你的 WebUI 支持新版 API,你的 URL 应类似于 *https://.../api/v1/generate*,且将使用 API 服务器作为主机,并在参数中添加 —api。", - "exampleMessage": "示范对话会影响角色的回应,但不会永久占用 Token。\n\n对话格式示例:\n\n```\n\n{{user}}: hi\n{{char}}: hello\n\n{{user}}: hi\nHaruhi: hello\n```\n\n`````` 标记了一段新对话的开始。", - "creatorQuotes": "说明将显示在初始消息之上,用于向用户提供角色说明。此内容不会进入提示词中。", - "systemPrompt": "此字段不为空时,将替换设置中的主控提示词为此内容。", - "chatNote": "这是一个强烈影响模型行为的备注,嵌入于当前聊天中,也称为记忆或 UJB。", - "personality": "对角色性格的简要描述。\n\n**不建议使用此字段,请填写在角色描述中。**", - "scenario": "对角色情境的简要描述。\n\n**不建议使用此字段,请填写在角色描述中。**", - "utilityBot": "激活后,将忽略主控提示词、越狱提示词和其他提示词。适用于工具型机器人,而非用于角色扮演。", - "loreSelective": "激活选择性模式后,需同时匹配关键词与次要关键詞,方可激活该条目。", - "loreRandomActivation": "激活「使用概率条件」后,若同时符合激活条目的其他条件,则在每次发送聊天时,该条目将依照设置的概率被使用。", - "additionalAssets": "在聊天中显示的额外资源。\n\n - 使用 `{{raw::<资源名称>}}` 作为路径。\n - 使用 `{{image::<资源名称>}}` 作为图片。\n - 使用 `{{video::<资源名称>}}` 作为影片。\n - 使用 `{{audio::<资源名称>}}` 作为音频。\n - 建议放置在背景 HTML 中。", - "superMemory": "SuperMemory 通过向 AI 提供摘要数据来增强角色的记忆能力。\n\nSuperMemory 是一个文本摘要功能,推荐使用 davinci 模型。不建议使用辅助模型,除非它是未经过滤、最大上下文长度超过 2000 Tokens,且具有良好摘要能力的模型。\n\nSuperMemory 提示词决定了模型如何撰写摘要。留空将使用默认提示词,建议保持留空。\n\n完成所有设置后,你可以在角色的设置中激活此功能。", - "replaceGlobalNote": "此字段不为空时,将替换当前的全局备注为此内容。", - "backgroundHTML": "将 Markdown/HTML 注入到聊天画面的背景中。\n\n你也可以使用额外资源。例如,你可以使用 {{audio::<资源名称}} 作为背景音乐。\n\n此外,你还可以与额外资源搭配使用以下格式:\n - {{bg::<资源名称>}}:将资源设为背景。", - "additionalText": "只有当 AI 认为有必要时,才会将该段文本添加到角色描述中。你可以在此处放置较长的文本。使用双换行进内联容分隔。", - "charjs": "这是一段会与角色一同运行的 JavaScript。详情请查看:https://github.com/kwaroran/RisuAI/blob/main/src/etc/example-char.js\n**出于安全原因,目前不建议使用。这些代码不会被包含在导出中。**", - "romanizer": "Romanizer 是一个将非罗马字母转换为罗马字母的插件,用于减少请求数据时的 Token。这可能会导致输出结果与原始模型不同。如果已在聊天中使用罗马字母,不建议激活。", - "oaiRandomUser": "激活后,请求中的用户参数将被随机 UUID 替代,并在刷新时修改。这可以用来防止 AI 识别用户。", - "inlayImages": "激活后,图片可嵌入聊天中,并且支持此功能的 AI 将能够看到它们。", - "metrica": "Metric Systemizer 是一个插件,会在请求阶段将公制单位转换为英制单位,而在输出时转回公制,让用户在接口上显示公制单位,但性能上使用英制单位。如果在聊天中已经使用英制单位,不建议激活。", - "lorePlus": "LoreBook+ 是一项实验性功能。使用向量数据库(VectorDB),而不仅是字符串匹配。目的在于提供更好的机器人创建体验和更好的匹配性能。", - "topP": "Top P 是内核采样(Nucleus Sampling)的概率阈值,模型只会考虑总概率质量达到 top_p 的 Token 结果。", - "openAIFixer": "OpenAI Fixer 是一个用于修复 OpenAI 部分问题的插件。", - "sayNothing": "激活后,如果用户没有输入聊天内容,系统会自动填入 ’say nothing‘。", - "showUnrecommended": "激活后,将显示不建议使用的过时设置。不建议使用这些设置。", - "imageCompression": "激活后,在导出角色时会压缩略图片。如果动画图片无法显示,请尝试关闭此选项。", - "useExperimental": "激活后,将显示部分实验性功能。", - "forceProxyAsOpenAI": "激活后,使用反向代理(Reverse proxy)时将强制使用 OpenAI 格式。", - "forcePlainFetch": "激活后,将使用浏览器的 Fetch API 来替代原生 HTTP 请求。这可能会导致 CORS 错误。", - "autoFillRequestURL": "激活后,将自动填入请求 URL 以匹配当前模型。", - "chainOfThought": "激活后,将在提示词中添加思维链(CoT, Chain-of-Thought)提示。", - "gptVisionQuality": "此选项用于设置图像检测模型的质量。质量越高,检测越准确,但会使用更多的 Token。", - "genTimes": "此设置支持模型上的重滚(reroll)回应数量。除第一则回应外,其他回应将作为缓存使用,以降低成本。但若未多次重滚回应,可能增加成本。", - "requestretrys": "此选项用于设置请求失败时的重试次数。", - "emotionPrompt": "此选项用于设置情绪检测的提示词。留空将使用默认提示词。", - "removePunctuationHypa": "激活后,将在执行 HypaMemory 前移除标点符号。", - "additionalParams": "此选项允许将附加的参数添加到请求主体(Request body)中。若要排除某些参数,可以将值设为 `{{none}}`。若要添加包头(Request header)而非主体,可以在键前加上 `header::`,如 `header::Authorization`。若要将值作为 JSON,可以在值前加上 `json::`,如 `json::{\"key\":\"value\"}`。其他情况下,系统将自动判定值的类型。", - "antiClaudeOverload": "若 Claude 过载发生,叡苏会透过继续相同的提示来阻止它,以减少过载的概率。此功能仅适用于流式回应(Streamed Responses),对非官方 API 端点可能无效。", - "triggerScript": "触发式(Trigger)是一个自定义脚本,在符合条件时执行。可用于修改聊天数据、执行命令、更改变量等。类型取决于触发时的情况,也可由按钮触发,如 {{button::Display::TriggerName}} 或带有 risu-trigger=\"\" 属性的 HTML 按钮。", - "autoContinueChat": "激活后,当聊天不以标点符号结束时,系统将尝试继续对话。请勿在不使用标点符号的语言中激活此功能。", - "combineTranslation": "激活后,将把被 HTML 标签分隔但属于同一句的文本合并后进行翻译,并在翻译结果上重新套用「修改显示」(Modify Display)。这有助于提高翻译的准确性。若激活此后接口出现异常,请关闭此选项并回报问题。", - "dynamicAssets": "激活后,若在处理数据时找不到资源名称,系统将使用向量搜索(Vector Search)尝试查找最接近的资源名称并进行替换。", - "dynamicAssetsEditDisplay": "激活后,动态资源将同样应用于「修改显示」阶段,但这可能会影响性能。", - "nickname": "设置后,将在聊天中以此昵称取代角色名称,并显示于 {{char}} 和 。", - "useRegexLorebook": "激活后,Lorebook 将改用正则表达式(Regex)搜索,而不再使用字符串匹配。格式为 /regex/flags。", - "customChainOfThought": "警告:不再建议使用思维链(CoT, Chain-of-Thought)切换功能。请将相关提示词移至其他提示词字段。", - "customPromptTemplateToggle": "可在此处设置自定义提示词切换功能。使用 `=` 格式,每行一个,例如:`cot=Toggle COT`。你可以在提示词中透过 `{{getglobalvar::toggle_}}` 语法来使用这些切换功能,如:`{{getglobalvar::toggle_cot}}`。", - "defaultVariables": "可在此处设置自定义默认变量。使用 `=` 格式,每行一个。例如:`name=叡苏`,可在触发式和 CBS 变量中使用,如:`{{getvar::A}}`、`{{setvar::A::B}}` 或 `{{? $A + 1}}`。若提示词模板的默认变量与角色的默认变量名称相同,系统将使用角色的默认变量。", - "lowLevelAccess": "激活后,将开放需要高计算能力的功能,并允许通过角色中的触发式执行 AI 模型。除非确实需要这些功能,否则不要激活此选项。", - "triggerLLMPrompt": "这是将发送到模型的提示词。你可以使用 `@@role user`、`@@role system`、`@@role assistant` 来设置多轮对话及角色。例如:\n```\n@@role system\nrespond as hello\n@@role assistant\nhello\n@@role user\nhi\n```", - "legacyTranslation": "激活后,将使用旧版翻译方法,在翻译前对 Markdown 和引号进行预处理,而非在翻译后处理。", - "luaHelp": "可使用 Lua 作为触发式,并可定义 onInput、onOutput 和 onStart 函数。当用户发送消息时,调用 onInput;当角色发送消息时,调用 onOutput;当对话开始时,调用 onStart。详情请参阅说明文档。", - "claudeCachingExperimental": "Claude 缓存是实验性功能,可减少模型成本。但若在不使用重滚(reroll)回应的情况下激活,则可能增加成本。实验性功能可能不稳定,且未来可能会有所变动。", - "urllora": "可使用模型文档的直接下载链接。通过类似 https://sites.google.com/site/gdocs2direct/ 的网站,从 Google Drive 等平台产生直接连接。或者使用 Civitai URL,复制 AIR(格式如 `urn:air:flux1:lora:civitai:180891@776656` 或 `civitai:180891@776656`),并粘贴。", - "namespace": "命名空间(Namespace)是模块的唯一标识符,用于防止模块冲突,并与默认和其他模块等进行交互。若不确定如何填写,建议留空。", - "moduleIntergration": "可在模块集成区域中输入模块的命名空间(Namespace)来激活模块。若要激活多个模块,可用逗号分隔,例如:`module1,module2,module3`。此功能便于高级用户通过默认敏捷运用模块。", - "customCSS": "自定义 CSS 样式。若出现问题,可使用 (Ctrl + .) 激活或禁用。", - "betaMobileGUI": "激活后,将在小于 800px 的屏幕上使用测试版行动接口,需刷新页面。", - "unrecommended": "这是一个不建议使用的设置。建议关闭。", - "jsonSchema": "JSON Schema 将在 AI 模型支持时发送给模型。\n\n然而,由于 JSON Schema 学习难度较高,在叡苏中,你可以使用 TypeScript 接口的子集来代替 JSON Schema。叡苏将在运行时进行转换。例如,如果你想发送如下的JSON:\n\n```js\n{\n \"name\": \"叡苏\", // name 必须是叡苏,\n \"age\": 1, // age 必须是数字,\n \"icon\": \"slim\", // icon 必须是 ’slim‘ 或 ’rounded‘\n \"thoughts\": [\"Good View!\", \"Lorem\"] // thoughts 必须是字符串数组\n}\n```\n\n你可以使用以下 TypeScript 接口:\n\n```typescript\ninterface Schema {\n name: string;\n age: number;\n icon: ’slim‘|’rounded‘\n thoughts: string[]\n}\n```\n\n接口名称不重要。欲了解更多信息,请参阅 TypeScript 说明文档:https://www.typescriptlang.org/docs/handbook/interfaces.html 。要检查支持的 TypeScript 子集,请查看以下内容。
支持的 TypeScript 子集\n\n支持的类型包括 `boolean`、`number`、`string` 和 `Array`。高级类型不被支持(如:单元类型、交集类型、联合类型、可选类型、字面量类型等),除了以下几种情况:\n\n - 原始数据型别(Primitive Type)的数组(Array):(如 `string[]`、`Array`)\n - 字符串之间的单值类型(Unit Types):(例如 `’slim‘|’rounded‘`)\n\n 属性必须在同一内联定义。若一行中有多个属性,将会产生错误。属性和接口名称仅可使用拉丁字符,并在 ASCII 范围内。属性名称不得以单引号或双引号包裹。接口内部不支持嵌套。在定义属性的行中,不能包含 `{` 或 `}`。如果想使用更高级的类型,请使用 JSON Schema。\n
", - "strictJsonSchema": "激活后,某些模型将严格遵循提供的 JSON Schema。若禁用,可能会忽略 JSON Schema。", - "extractJson": "此字段不为空时,将从回应中提取特定的 JSON 数据。例如:想从回应 `{\"response\": {\"text\": [\"hello\"]}}` 中提取 `response.text[0]`,可以填写 `response.text.0`。", - "translatorNote": "可在此处为每个角色添加独特的翻译提示,但仅适用于使用 Ax. 模型进行翻译。要激活此功能,请在语言设置中包含 `{{slot::tnote}}`。此功能不适用群组聊天。", - "groupInnerFormat": "用于定义群组聊天中非发言者角色的格式。此字段不为空时,将使用此格式替代默认格式。若 `Group Other Bot Role` 设置为 `assistant`,该格式也将应用于发言者。", - "groupOtherBotRole": "用于定义群组聊天中非发言者的角色。", - "chatHTML": "每个聊天插入的 HTML。\n\n可以使用CBS和特殊标签。\n- ``:用于呈现文字的文本框\n- ``:用于显示用户或助理的头像\n- ``:用于聊天编辑、翻译等图标按钮\n- ``:生成消息按钮。" - }, - "setup": { - "chooseProvider": "选择 AI 提供者", - "openaikey": "使用 OpenAI API 密钥(推荐)", - "openaiProxy": "OpenAI 反向代理", - "setupmodelself": "其他 / 自行设置", - "inputApiKey": "请在此输入 API 密钥", - "apiKeyhelp": "可在以下获取 API 密钥:", - "setupSelfHelp": "欢迎画面结束后,可前往设置中自行配置。", - "theme": "选择接口主题", - "themeDescWifulike": "不适合在行动设备上使用", - "themeDescWifuCut": "适合在行动设备上使用", - "themeDescClassic": "适用于所有设备", - "texttheme": "设置文字色彩", - "inputName": "最后,请输入你的昵称。", - "welcome": "欢迎使用叡苏!我会引导你进行设置。请问我该如何称呼你?", - "welcome2": "你好,{username}!在开始之前,我会问你一些问题,稍后可在设置中进行修改。\n\n首先,请选择 AI 提供者。", - "openAIProvider": "OpenAI GPT 是高质量的 AI 模型,但属于付费并经内容审核。", - "openrouterProvider": "Openrouter 提供许多模型,部分免费且未经内容过滤,但质量不如 OpenAI。", - "hordeProvider": "Horde 提供免费服务,但回应时间较长且质量较低。", - "setProviderLater": "还有其他提供者,你可以稍后在设置中配置。如想稍后设置,请选择此选项。", - "setupOpenAI": "使用 OpenAI 需要获取 API 密钥(Key)。\n1. 前往 https://beta.openai.com/ \n2. 使用账号登录 \n3. 前往 https://beta.openai.com/account/api-keys \n4. 点击「Create New API Key」,并命名密钥。 \n5. 复制该密钥。 \n6. 返回叡苏\n7. 粘贴密钥并点击「发送」。", - "setupOpenrouter": "使用 Openrouter 需要获取 API 密钥(Key)。 \n1. 前往 https://openrouter.ai/keys\n2. 点击「Create Key」\n3. 任意命名密钥名称。\n4. 复制该密钥。\n5. 返回叡苏\n6. 粘贴密钥并点击「发送」。", - "allDone": "完成所有设置!请稍待片刻。" - }, - "confirm": "确定", - "goback": "返回", - "botSettings": "机器人设置", - "model": "模型", - "apiKey": "API 密钥", - "providerURL": "请求地址(URL)", - "providerJSON": "请求主体(JSON)", - "mainPrompt": "主控提示词", - "jailbreakPrompt": "越狱提示词", - "globalNote": "全局备注", - "autoSuggest": "自动建议", - "tokens": "Tokens", - "maxContextSize": "最大上下文长度(Max Context Size)", - "maxResponseSize": "最大回应长度(Max Response Size)", - "temperature": "温度(Temperature)", - "frequencyPenalty": "频率惩罚(Frequency Penalty)", - "presensePenalty": "存在惩罚(Presence Penalty)", - "advancedSettings": "高级设置", - "advancedSettingsWarn": "警告:若不确定该选项的作用,请勿进行修改!", - "formatingOrder": "格式顺序", - "authorNote": "作者备注", - "firstMessage": "初始消息", - "description": "描述", - "jailbreakToggle": "使用越狱提示词", - "charIcon": "角色头像", - "characterDisplay": "角色演示", - "viewScreen": "额外角色画面", - "none": "无", - "emotionImage": "情绪影像", - "noImages": "没有图片", - "noBias": "No Bias", - "image": "图片", - "name": "名称", - "emotion": "情绪名称", - "value": "值", - "reroll": "重新生成", - "chatList": "聊天列表", - "removeChat": "确定要移除此消息吗?", - "loreBook": "Lorebook", - "character": "角色", - "Chat": "聊天", - "globalLoreInfo": "角色 Lorebook 适用于该角色的所有对话。", - "group": "群组", - "groupLoreInfo": "群组 Lorebook 适用于该群组的所有对话。", - "localLoreInfo": "聊天 Lorebook 仅用于此对话。", - "removeConfirm": "你确定要删除:", - "removeConfirm2": "你**真的**确定要删除:", - "exportConfirm": "你想要导出此数据吗?", - "insertOrder": "插入顺序", - "activationKeys": "关键词", - "activationKeysInfo": "使用逗号分隔", - "prompt": "提示词", - "loreBookDepth": "Lorebook 搜索深度", - "loreBookToken": "Lorebook 最大 Token 数", - "removeCharacter": "删除角色", - "removeGroup": "删除群组", - "exportCharacter": "导出角色", - "userSetting": "用户设置", - "username": "你的名称", - "userIcon": "你的头像", - "successExport": "已成功导出并保存至你的下载数据夹", - "successImport": "成功导入", - "importedCharacter": "导入角色", - "alwaysActive": "始终激活", - "additionalPrompt": "附加提示词", - "descriptionPrefix": "描述前缀", - "forceReplaceUrl": "反向代理", - "emotionWarn": "正在使用辅助模型。", - "plugin": "插件", - "language": "语言", - "UiLanguage": "接口语言", - "createfromScratch": "自行创建", - "importCharacter": "导入角色", - "translator": "翻译器", - "disabled": "关闭", - "noPluginSelected": "已选择模型为插件,但未选择插件。", - "text": "文字", - "UISize": "聊天文字大小", - "newVersion": "发现更新,是否进行安装?", - "display": "显示 & 音频", - "useCustomBackground": "自定义背景", - "translateInput": "翻译输入", - "autoTranslation": "自动翻译", - "fullscreen": "全屏幕", - "playMessage": "播放消息音效", - "iconSize": "头像大小", - "createGroup": "创建群组", - "groupIcon": "群组头像", - "single": "单个", - "multiple": "多个", - "useCharLorebook": "使用角色 Lorebook", - "selectChar": "选择角色", - "askLoadFirstMsg": "是否加载初始消息?", - "theme": "接口主题", - "editOrder": "编辑顺序", - "autoMode": "自动模式", - "submodel": "辅助模型", - "timeOutinSec": "超时时间(秒)", - "emotionPrompt": "情绪提示词", - "singleView": "单角色模式", - "SpacedView": "多角色模式", - "emphasizedView": "双角色模式", - "pluginWarn": "插件可在隔离环境中运行,但安装恶意插件可能导致问题。", - "createGroupImg": "产生群组头像", - "waifuWidth": "角色对话框宽度", - "savebackup": "备份至 Google", - "loadbackup": "从 Google 读取备份", - "files": "文件", - "backupConfirm": "你确定要保存备份吗?", - "backupLoadConfirm": "你确定要读取备份吗?所有数据将被覆盖!", - "backupLoadConfirm2": "你**真的、真的**确定要加载备份吗?这将会清除所有数据!", - "pasteAuthCode": "请从弹出窗口复制鉴别码并贴入:", - "others": "其他", - "presets": "默认设置", - "imageGeneration": "图像生成", - "provider": "提供者", - "key": "密钥(Key)", - "noData": "没有数据", - "currentImageGeneration": "当前图像生成数据", - "promptPreprocess": "使用提示词预处理", - "SwipeRegenerate": "使用滑动箭头重新产生消息", - "instantRemove": "删除消息时连带删除后续消息", - "instantRemoveConfirm": "你想只删除一条消息吗?若选择「否」,后续消息也将被删除。", - "textColor": "文字颜色", - "classicRisu": "经典叡苏", - "highcontrast": "高对比度", - "quickPreset": "可通过 Ctrl +(默认顺序编号)快速切换默认。", - "requestretrys": "请求失败时重试", - "utilityBot": "工具机器人", - "ShowLog": "显示请求记录", - "waifuWidth2": "角色显示宽度", - "sayNothing": "无输入内容时自动填入 ’say nothing‘", - "regexScript": "正则表达式(Regex)", - "type": "类型", - "editInput": "修改输入", - "editOutput": "修改输出", - "editProcess": "修改请求数据", - "loadLatest": "读取最新的备份", - "loadOthers": "读取其他备份", - "exampleMessage": "示范消息", - "creatorNotes": "创建者备注", - "systemPrompt": "系统提示词", - "characterNotes": "角色备注", - "personality": "性格", - "scenario": "场景", - "alternateGreetings": "Alternate Greetings", - "unrecommended": "不建议", - "chatNotes": "聊天备注", - "showUnrecommended": "显示不建议的设置", - "altGreet": "替代初始消息", - "scripts": "脚本", - "settings": "设置", - "selective": "选择性", - "SecondaryKeys": "次要关键词", - "useGlobalSettings": "使用全局设置", - "recursiveScanning": "递归扫描", - "creator": "创建者", - "CharVersion": "角色版本", - "Speech": "语音", - "ToggleSuperMemory": "激活 SupaMemory", - "SuperMemory": "SupaMemory", - "useExperimental": "激活实验性功能", - "showMemoryLimit": "显示记忆上限", - "roundIcons": "圆形头像", - "streaming": "即时流式传输", - "chatBot": "聊天机器人", - "otherBots": "其他机器人", - "user": "用户", - "additionalAssets": "额外资源", - "editDisplay": "修改显示", - "community": "社区", - "textBackgrounds": "自定义文本窗口颜色", - "textBorder": "文字边框", - "textScreenRound": "圆角化文本窗口", - "textScreenBorder": "文本窗口边框", - "ttsReadOnlyQuoted": "仅朗读引号内容", - "ttsStop": "停止语音合成", - "askRemoval": "请求删除", - "replaceGlobalNote": "替换全局备注", - "charLoreBook": "角色 Lorebook", - "globalLoreBook": "全局 Lorebook", - "globalRegexScript": "全局正则表达式", - "accessibility": "辅助功能", - "sendWithEnter": "使用 Enter 键发送", - "clickToEdit": "点击文字进行编辑", - "setNodePassword": "设置密码以提升安全性", - "inputNodePassword": "输入密码。如果忘记密码,请删除服务器文档中的 save/__password.txt 并重启服务器。", - "simple": "基本", - "advanced": "高级", - "askReRollAutoSuggestions": "自动滚动建议", - "creatingSuggestions": "正在产生建议⋯", - "orderByOrder": "按顺序对话", - "removeFromGroup": "确定要将 {{char}} 移出群组吗?", - "talkness": "健谈度", - "active": "活跃", - "loreRandomActivation": "激活概率条件", - "activationProbability": "概率", - "shareCloud": "分享至 RisuRealm", - "hub": "RisuRealm", - "tags": "标签", - "backgroundHTML": "背景嵌入", - "copied": "已复制", - "useChatCopy": "激活聊天消息复制", - "useChatSticker": "激活聊天贴图", - "useAdditionalAssetsPreview": "使用额外资源预览", - "autoTranslateInput": "自动输入翻译", - "enterMessageForTranslateToEnglish": "输入消息以翻译成英文", - "recent": "最新", - "downloads": "下载", - "trending": "热门", - "imageCompression": "图片压缩", - "notLoggedIn": "尚未登录 Risu 账号", - "googleDriveInfo": "连接至 Google Drive 以同步数据。", - "googleDriveConnection": "连接 Google Drive", - "googleDriveConnected": "已连接 Google Drive", - "SaveDataInAccount": "保存数据到账号", - "dataSavingInAccount": "正在将数据保存到账号", - "logout": "注销", - "loadDataFromAccount": "从账号读取数据", - "saveCurrentDataToAccount": "保存目前数据到账号", - "chatAssumed": "", - "proxyAPIKey": "代理密钥/密码", - "proxyRequestModel": "请求模型", - "officialWiki": "官方 Wiki", - "officialWikiDesc": "欢迎查看叡苏官方的 Wiki。", - "officialDiscord": "官方 Discord", - "officialDiscordDesc": "叡苏官方的 Discord 服务器", - "confirmRecommendedPreset": "此模型有建议设置,是否更改为建议设置?(可在辅助功能中关闭提示)", - "toggleConfirmRecommendedPreset": "模型变更时询问是否使用建议设置", - "recommendedPreset": "使用建议设置", - "persona": "用户信息", - "icon": "头像", - "account": "账号", - "remove": "删除", - "creationSuccess": "创建成功", - "noweb": "此功能无法于网页版使用。", - "createBotInternet": "使用 AI 从网络创建机器人", - "createBotInternetAlert": "请提供角色名称及对应的系列或游戏。", - "able": "激活", - "assetWidth": "额外资源图片最大宽度", - "animationSpeed": "动画速度", - "screenshot": "截图", - "screenshotSaved": "截图已保存", - "inputBotGenerationPrompt": "输入机器人生成提示词", - "createBotAI": "使用 AI 创建原创机器人", - "createBotwithAI": "使用 AI 创建机器人", - "changeFolderName": "输入新数据夹名称(留空以取消)", - "cancel": "取消", - "renameFolder": "重命名数据夹", - "changeFolderColor": "更改数据夹颜色", - "fullWordMatching": "完整单词匹配", - "botSettingAtStart": "激活时显示机器人菜单", - "triggerStart": "聊天发送时触发", - "triggerInput": "用户输出时触发", - "triggerOutput": "角色输出时触发", - "triggerManual": "仅限手动触发", - "triggerCondVar": "如果变量为", - "triggerCondExists": "如果聊天中存在文字", - "triggerScript": "触发式(Trigger)", - "triggerMatchRegex": "与正则表达式匹配", - "triggerMatchLoose": "宽松匹配", - "triggerMatchStrict": "严格匹配", - "searchDepth": "搜索深度", - "equal": "等于", - "notEqual": "不等于", - "greater": "大于", - "less": "小于", - "greaterEqual": "大于或等于", - "lessEqual": "小于或等于", - "triggerEffSysPrompt": "添加系统提示词", - "triggerEffSetVar": "修改变量", - "triggerEffImperson": "发送聊天消息", - "triggerEffCommand": "执行命令", - "triggerEffRunTrigger": "执行触发式", - "triggerEffStop": "停止发送提示词", - "triggerEffCall": "调用触发式", - "varableName": "变量名", - "role": "身份", - "location": "位置", - "promptstart": "提示词开始", - "promptend": "提示词结束", - "historyend": "聊天历史结束", - "always": "总是", - "noEffect": "无效果", - "invaildTriggerEffect": "此效果不适用于该触发类型。", - "operator": "运算符", - "TriggerSetToVar": "设置为变量", - "TriggerAddToVar": "增加为变量", - "TriggerSubToVar": "从变量中减去", - "TriggerMulToVar": "将变量乘以", - "TriggerDivToVar": "将变量除以", - "isNull": "尚未设置", - "ifChatIndex": "如果对话索引", - "ifRandom": "如果随机", - "ifValue": "如果值", - "hideRealm": "隐藏 RisuRealm", - "popularityLevel": "{} 人气", - "colorScheme": "配色方案", - "rangeStart": "范围开始", - "rangeEnd": "范围结束", - "untilChatEnd": "直到聊天结束", - "usePromptTemplate": "使用提示词模板", - "specialType": "特殊类型", - "noSpecialType": "无特殊类型", - "forceProxyAsOpenAI": "强制代理格式为 OpenAI", - "promptTemplate": "提示词模板", - "customInnerFormat": "自定义内部格式", - "innerFormat": "内部格式", - "HypaMemory": "HypaMemory", - "ToggleHypaMemory": "激活 HypaMemory", - "resetPromptTemplateConfirm": "你真的确定要重置提示词模板吗?", - "emotionMethod": "情绪检测方式", - "continueResponse": "继续回应", - "showMenuChatList": "在菜单中显示聊天列表", - "translatorLanguage": "翻译目标语言", - "translatorType": "翻译器类型", - "deeplKey": "DeepL API 密钥", - "deeplFreeKey": "DeepL 免费 API 密钥", - "deeplXUrl": "DeepLX URL", - "deeplXToken": "DeepLX Token", - "exportPersona": "导出用户设置", - "importPersona": "导入用户设置", - "export": "导出", - "import": "导入", - "supporterThanks": "支持者感谢", - "supporterThanksDesc": "感谢您的支持!", - "donatorPatreonDesc": "为保护隐私,默认不会显示在名单中。若想显示您的昵称,请前往叡苏的 Patreon 页面并点击链接按钮。", - "useNamePrefix": "使用名称前缀", - "textAdventureNAI": "以文字冒险形式运行", - "appendNameNAI": "附加名称至 NAI", - "customStopWords": "自定义停止词", - "defaultPrompt": "默认提示词", - "additionalText": "额外提示", - "seed": "Seed", - "charjs": "角色 JavaScript", - "depthPrompt": "提示词深度", - "largePortrait": "肖像", - "lorePlus": "LoreBook+", - "reverseProxyOobaMode": "Ooba Mode", - "joinMultiUserRoom": "加入用户多人聊天室", - "exactTokens": "精确 Tokens", - "fixedTokens": "估算 Tokens", - "inlayViewScreen": "内嵌窗口", - "imgGenPrompt": "图像生成提示词", - "imgGenNegatives": "图像生成负面提示词", - "imgGenInstructions": "系统提示词", - "usePlainFetchWarn": "使用 NovelAI 时请关闭此选项,避免出现 CORS 错误。", - "translationPrompt": "翻译提示词", - "translationResponseSize": "翻译回应长度", - "webdeeplwarn": "此功能不不建议于网页版使用,避免出现 CORS 错误。", - "saveBackupLocal": "本地保存备份", - "loadBackupLocal": "本机读取备份", - "topP": "Top P", - "genTimes": "生成次数", - "cot": "思维链(CoT)", - "forcePlainFetch": "强制简单抓取", - "autoFillRequestURL": "自动填入请求 URL", - "newOAIHandle": "新的 OpenAI 处理方式", - "oaiRandomUser": "放置 OAI 随机用户", - "inlayImage": "内嵌图片功能", - "nativeAutomark": "实验性本地自动标记", - "assistantPrefill": "助理预填充", - "postEndInnerFormat": "结尾内部格式", - "sendChatAsSystem": "以系统身份发送消息", - "sendName": "非群聊时发送名称", - "utilOverride": "实用程序改写", - "template": "模板", - "chatAsOriginalOnSystem": "以原始身份发送", - "exportAsDataset": "导出保存数据为数据集", - "editTranslationDisplay": "修改翻译显示", - "selectModel": "选择模型", - "autoRemoveThoughtTag": "删除思维标记", - "customChainOfThought": "自定义思维链", - "maxThoughtTagDepth": "思维标记最大深度", - "openrouterFallback": "Openrouter 回退", - "openrouterMiddleOut": "Openrouter 中间输出", - "geminiApiKey": "Gemini API 密钥", - "removePunctuationHypa": "移除记忆标记", - "memoryLimitThickness": "记忆上限厚度", - "inputCardPassword": "输入角色卡密码", - "ccv2Desc": "V2 角色卡是广泛用于聊天机器人前端的格式。", - "ccv3Desc": "V3 角色卡是用于聊天机器人前端的新型格式。", - "realmDesc": "RisuRealm 是叡苏的内容分享平台,你可以将角色分享给其他用户。", - "rccDesc": "Risu Refined 角色卡具有密码保护、完整性验证等附加功能。", - "password": "密码", - "license": "授权", - "licenseDesc": "你可以设置下载授权,限制角色卡对提示词的使用。", - "passwordDesc": "你可以为角色卡设置密码,防止未经授权的访问。", - "largePersonaPortrait": "用户肖像", - "module": "模块", - "modules": "模块", - "noModules": "尚未安装任何模块。", - "createModule": "创建模块", - "basicInfo": "基本数据", - "moduleContent": "模块内容", - "confirmRemoveModuleFeature": "确定要移除此功能吗?此操作无法还原。", - "editModule": "编辑模块", - "importModule": "导入模块", - "download": "下载", - "edit": "编辑", - "enableGlobal": "全局激活", - "chatModulesInfo": "可选择开启或关闭此对话的模块。", - "sideMenuRerollButton": "侧栏菜单重新加载", - "persistentStorage": "永久存储", - "persistentStorageSuccess": "存储已成功永久化", - "persistentStorageFail": "存储未能永久化。你是否拒绝了请求,或浏览器不支持?", - "persistentStorageRecommended": "建议使用永久存储", - "persistentStorageDesc": "你的浏览器支持永久存储,建议激活以提升性能和用户体验。", - "enable": "激活", - "postFile": "上传文件", - "requestInfoInsideChat": "在聊天中显示请求数据", - "inputTokens": "输入 Tokens", - "outputTokens": "输出 Tokens", - "tokenWarning": "Token 计算可能不精确,仅作参考。", - "log": "记录", - "popularityLevelDesc": "人气会随着下载量等增加。3.7 人气约等于 1 次下载。", - "additionalParams": "额外参数", - "heightMode": "高度模式", - "useAdvancedEditor": "使用高级编辑器", - "noWaitForTranslate": "不等待翻译", - "updateRealm": "更新至 RisuRealm", - "updateRealmDesc": "你正试图将角色更新至 RisuRealm。此操作将使角色更新至 RisuRealm,且无法还原。", - "antiClaudeOverload": "防止 Claude 超载", - "activeTabChange": "目前的标签已停用,因其他标签处于活动中。若要激活此标签,请按「确定」。", - "maxSupaChunkSize": "最大 SupaMemory 组块大小", - "addCharacter": "新增角色", - "importFromRealm": "从 RisuRealm 选择", - "importFromRealmDesc": "RisuRealm 提供超过 1000 位角色", - "random": "随机", - "metaData": "Metadata", - "autoContinueMinTokens": "目标 Tokens(自动继续)", - "autoContinueChat": "防止不完整回复(自动继续)", - "removeIncompleteResponse": "移除不完整句子", - "tokenizer": "Tokenizer", - "chatFormating": "聊天格式", - "useInstructPrompt": "激活命令提示词", - "hanuraiMemory": "HanuraiMemory", - "playground": "Playground", - "textAreaSize": "输入栏大小", - "textAreaTextSize": "输入栏文字大小", - "sideBarSize": "侧边栏大小", - "embedding": "嵌入", - "syntax": "语法", - "run": "执行", - "noMessage": "输入内容以开始聊天。", - "combineTranslation": "合并翻译", - "dynamicAssets": "动态资源", - "dynamicAssetsEditDisplay": "在接口上使用动态资源", - "longTermMemory": "长期记忆", - "grid": "网格", - "list": "列表", - "trash": "垃圾桶", - "trashDesc": "删除的角色将移至垃圾桶,可选择还原或永久删除。删除的角色将在 3 天后自动永久删除。", - "shareExport": "分享/导出", - "risupresetDesc": "Risupreset 是专为叡苏默认设置设计的格式。", - "risuMDesc": "RisuM 是专为叡苏模块设计的格式。", - "jsonDesc": "JSON 是一种人机都易于读写的格式。", - "nickname": "昵称", - "useRegexLorebook": "使用正则表达式", - "customPromptTemplateToggle": "自定义开关", - "defaultVariables": "默认变量", - "hypaAllocatedTokens": "已分配的 Tokens", - "hypaChunkSize": "组块大小", - "hypaV2Desc": "HypaMemory V2 是一种结合摘要数据和向量搜索的长期记忆系统。", - "supaDesc": "SupaMemory 是一种使用摘要数据的长期记忆系统。", - "hanuraiDesc": "HanuraiMemory 是一个使用向量搜索的记忆系统。", - "lowLevelAccess": "低层级访问", - "resultStoredVar": "存储结果的变量", - "triggerEffRunLLM": "执行主控模型", - "triggerEffectSendAI": "重新发送 AI", - "triggerEffCheckSim": "检查相似度", - "triggerEffShowAlert": "显示警告", - "normal": "正常", - "error": "错误", - "input": "输入", - "select": "选择", - "lowLevelAccessConfirm": "此内容使用低层级访问,可直接访问 AI 模型和你的存储数据。你确定要导入吗?", - "triggerLowLevelOnly": "此触发仅适用于低层级访问,需在角色或模块的高级设置中激活低层级访问。", - "truthy": "真值", - "extractRegex": "使用正则表达式提取文字", - "runImgGen": "执行图像生成", - "cutChat": "分割聊天", - "modifyChat": "修改聊天", - "regex": "正则表达式", - "flags": "修饰符", - "resultFormat": "结果格式", - "negPrompt": "负面提示词", - "start": "开始", - "end": "结束", - "index": "索引", - "search": "搜索", - "goCharacterOnImport": "导入后跳转至角色页面", - "format": "格式", - "v2Warning": "警告:已停止支持 V2 角色卡,可能会缺失部分数据。", - "applyModule": "套用模块", - "successApplyModule": "已成功套用模块", - "font": "字体", - "lineHeight": "行距", - "loadAutoServerBackup": "读取服务器自动备份", - "notCharxWarn": "角色使用多项资源,建议导出为 CharX 格式以提升兼容性。", - "noPlugins": "未安装插件", - "legacyTranslation": "旧版翻译", - "clipboardSuccess": "已复制到剪贴板", - "translateContent": "翻译内容", - "doNotTranslate": "不进行翻译", - "includePersonaName": "包含用户名称", - "hidePersonaName": "隐藏用户名称", - "triggerSwitchWarn": "更改触发类型后,现有触发将被清除。你确定要继续吗?", - "codeMode": "程序码", - "blockMode": "区块", - "helpBlock": "帮助", - "hideChatIcon": "隐藏头像", - "loadInternalBackup": "读取内部备份", - "createCopy": "新建副本", - "bindPersona": "绑定用户", - "chatOptions": "聊天选项", - "doYouWantToBindCurrentPersona": "是否绑定当前用户至此聊天?", - "doYouWantToUnbindCurrentPersona": "是否解除此对话中的用户绑定?", - "personaBindedSuccess": "用户绑定已完成", - "personaUnbindedSuccess": "用户绑定已解除", - "parameters": "参数", - "sizeAndSpeed": "大小与速度", - "useLegacyGUI": "使用旧版接口", - "claudeCachingExperimental": "Claude 缓存", - "openClose": "开启/关闭", - "hideApiKeys": "隐藏 API 密钥", - "unformatQuotes": "禁用引号格式", - "enableDevTools": "激活开发者工具", - "selectFile": "选择文件", - "namespace": "命名空间", - "moduleIntergration": "模块集成", - "previewInfo": "此预览显示模型处理前的提示词。", - "miscTools": "其他工具", - "promptConvertion": "提示词转换", - "convertionStep1": "选择与提示词相关的文件(支持 Context、Instruct 及 Sampler JSON)", - "customCSS": "自定义 CSS", - "betaMobileGUI": "测试版行动接口", - "menu": "菜单", - "connectionOpen": "已开启连接", - "connectionOpenInfo": "多人聊天室已开启,你可以将聊天室代码分享给其他用户。其他用户可在 Playground > 加入多人聊天室 > 使用代码加入。", - "createMultiuserRoom": "新建多人聊天室", - "connectionHost": "你是聊天室的主持人。", - "connectionGuest": "你是聊天室的访客。", - "otherUserRequesting": "其他用户正在请求中,请稍后重试。", - "jsonSchema": "JSON Schema", - "enableJsonSchema": "激活 Schema", - "strictJsonSchema": "严谨 Schema", - "extractJson": "提取 JSON", - "reloadSession": "发现更新版本的存储数据,正在重新加载对话⋯", - "fixMarkdownNewline": "修正 Markdown 换行", - "customQuotes": "自定义引号", - "leadingSingleQuote": "上单引号", - "leadingDoubleQuote": "上双引号", - "trailingSingleQuote": "下单引号", - "trailingDoubleQuote": "下双引号", - "translatorNote": "翻译器备注", - "formatGroupInSingle": "单一群组格式", - "groupInnerFormat": "非发言者内部格式", - "groupOtherBotRole": "群组内非发言者的身份", - "defineCustomGUI": "自定义界面", - "chatHTML": "聊天介面 HTML", - "logShare": "显示分享记录按钮", - "preview": "预览" - } \ No newline at end of file + "main": "主提示词", + "jailbreak": "越狱提示词", + "chats": "历史聊天", + "lorebook": "世界书", + "globalNote": "全局备注", + "authorNote": "作者备注", + "lastChat": "前一次聊天", + "description": "角色描述", + "personaPrompt": "用户提示词", + "plain": "基础提示词", + "memory": "Supa/HypaMemory", + "postEverything": "系统提示词结束" + }, + "errors": { + "toomuchtoken": "错误:所需的 Token 数超过了可用的最大上下文大小", + "unknownModel": "错误:无法识别所选的模型", + "httpError": "错误:请求发生错误:", + "noData": "无法找到文件中的数据,或者文件已经损毁", + "onlyOneChat": "至少需要一个聊天室", + "alreadyCharInGroup": "该群组中已经有一个同名角色。", + "noUserIcon": "请先设置你的个人头像。", + "emptyText": "文字内容为空。", + "wrongPassword": "密码错误", + "networkFetch": "这通常是由于网络连接不稳定或服务器故障引起的。", + "networkFetchWeb": "这可能是由 CORS 错误引起的。这种情况只会在使用网页版时发生,因为浏览器存在一些限制。请尝试使用本地版或其他版本的叡苏。", + "networkFetchPlain": "这可能是一次数据抓取(Fetch)错误。请尝试在设置中关闭强制抓取选项。", + "requestLogRemoved": "该请求记录已被删除。", + "requestLogRemovedDesc": "当客户端刷新或加载时,该请求记录会被删除。" + }, + "showHelp": "显示帮助", + "help": { + "model": "此模型是指聊天中使用的主控模型。", + "submodel": "辅助模型是一个用于分析情感图像、产生自动建议等的模型,推荐使用 GPT-3.5。", + "oaiapikey": "OpenAI 的 API 密钥(Key),可在 https://platform.openai.com/account/api-keys 获取。", + "mainprompt": "主提示词设置用于决定模型的默认行为。", + "jailbreak": "当角色中的越狱开关被激活后,越狱提示词将被使用。", + "globalNote": "一个对模型行为有强烈影响的备注(也称为 UJB),适用于所有角色。", + "autoSuggest": "用于自动建议用户回应时生成选项的提示词。", + "formatOrder": "提示词的排列顺序:越靠下的区块对模型的影响越大。", + "forceUrl": "此字段不为空时,请求将被发送到你所输入的网址。", + "tempature": "较低的数值会使角色更紧密地遵循提示词,但会使回应更制式与机械化。\n较高的数值则会增强角色的创意表现,但回应可能会变得不稳定。", + "frequencyPenalty": "较高的数值可以避免角色在个别回应中重复使用相同的词汇,但回应也更容易出现语义混乱。", + "presensePenalty": "较高的数值可以避免角色在整体对话中重复使用相同的词汇,但这也可能导致回答失去一致性和稳定性。", + "sdProvider": "图像生成提供者。", + "msgSound": "当角色回应时,播放 *叮* 的提示音", + "charDesc": "角色的简要描述。这会影响角色的回应方式。", + "charFirstMessage": "角色的初始消息,这会极大地影响角色的回应方式。", + "charNote": "对模型行为有强烈影响的备注,嵌入到当前角色中,也称为 UJB。", + "toggleNsfw": "切换越狱提示词的开关。", + "lorebook": "世界书(Lorebook)是由用户创建的 AI 辞典,只有当上下文中包含关键词时 AI 才能看到它。", + "loreName": "世界书的名称,不影响 AI。", + "loreActivationKey": "当上下文中包含任一关键词时,该条目将被激活,并激活相应的提示词。使用逗号分隔。", + "loreorder": "插入顺序越高,对模型的影响力越大。在激活大量条目时,也更不容易被截断。", + "bias": "Bias 是一组键值数据,用于修改某些字符串出现的概率。\n其数值范围可以是 -100 到 100。较高的数值会使该字符串更可能出现,较低的数值则降低出现概率。\n另外,在某些模型中,若将数值设为 -101,该字符串将被标记为“强制禁止词”。\n警告:若 Tokenizer 设置有误,可能无法正常运作。", + "emotion": "“表情立绘会根据角色情绪显示对应的图片。具体的情绪由角色的回应进行分析。你必须输入情绪名称作为词汇*(如:joy, happy, fear 等)*。若存在名为 **neutral** 的情绪,将作为默认情绪。至少需要三张图片才能正常运作。”", + "imggen": "分析聊天内容后,将提示套用至 {{slot}}。", + "regexScript": "正则表达式(Regex Script)是一个自定义工具,用于将符合条件的字符串由“IN”替换为“OUT”。\n\n有四种类型选项:\n\n- **修改输入(Modify Input)**:修改用户的输入内容\n\n- **修改输出(Modify Output)**:修改角色的输出内容\n\n- **修改请求数据(Modify Request Data)**:在当前聊天数据发送时进行修改\n\n- **修改显示(Modify Display)**:仅修改显示的文字,不更改聊天数据\n\n“IN”必须是一个不带标志(flags)的正则表达式,且开头和结尾不包含斜线。\n\n“OUT”是一个可以包含替换模式的字符串。这些替换模式如下:\n\n- $$\n\n - 插入符号“$”\n\n- $&\n\n - 插入匹配到的子字符串\n\n- $`\n\n - 插入匹配子字符串前的部分\n\n- $1\n\n - 插入第一个匹配群组,可以替换为其他数字(例如 2、3...)\n\n- $(name)\n\n - 插入命名群组\n\n针对标志(flags),除了原生支持的标志之外,还可以使用下列专为高级用户设计的标志:\n\n- ``:将结果注入当前字符串中\n- ``:将结果移到字符串的顶部\n- ``:将结果移到字符串的底部\n- ``:如果找不到匹配,则使用上一个匹配的结果\n- ``:设置结果的顺序,数值越高显示越靠前。“n”代表一个数字(例如 ``)。如果未设置,默认为 0。\n- ``:解析“IN”中的大括号语法\n\n若要与原生标志结合使用,可以像这样使用:`gi`。", + "experimental": "此为实验性功能,可能不稳定。", + "oogaboogaURL": "如果你的 WebUI 支持旧版 API,你的 URL 应类似于 *https://.../run/textgen*。\n\n如果你的 WebUI 支持新版 API,你的 URL 应类似于 *https://.../api/v1/generate*,且将使用 API 服务器作为主机,并在参数中添加 —api。", + "exampleMessage": "示范对话会影响角色的回应,但不会永久占用 Token。\n\n对话格式示例:\n\n```\n\n{{user}}: hi\n{{char}}: hello\n\n{{user}}: hi\nHaruhi: hello\n```\n\n`````` 标记了一段新对话的开始。", + "creatorQuotes": "说明将显示在初始消息之上,用于向用户提供角色说明。此内容不会进入提示词中。", + "systemPrompt": "此字段不为空时,将替换设置中的主提示词为此内容。", + "chatNote": "这是一个强烈影响模型行为的备注,嵌入于当前聊天中,也称为记忆或 UJB。", + "personality": "对角色性格的简要描述。\n\n**不建议使用此字段,请填写在角色描述中。**", + "scenario": "对角色情境的简要描述。\n\n**不建议使用此字段,请填写在角色描述中。**", + "utilityBot": "激活后,将忽略主提示词、越狱提示词和其他提示词。适用于工具型机器人,而非用于角色扮演。", + "loreSelective": "激活选择性模式后,需同时匹配关键词与次要关键詞,方可激活该条目。", + "loreRandomActivation": "激活“使用概率条件”后,若同时符合激活条目的其他条件,则在每次发送聊天时,该条目将依照设置的概率被使用。", + "additionalAssets": "在聊天中显示的额外资源。\n\n - 使用 `{{raw::<资源名称>}}` 作为路径。\n - 使用 `{{image::<资源名称>}}` 作为图片。\n - 使用 `{{video::<资源名称>}}` 作为影片。\n - 使用 `{{audio::<资源名称>}}` 作为音频。\n - 建议放置在背景 HTML 中。", + "superMemory": "SuperMemory 通过向 AI 提供摘要数据来增强角色的记忆能力。\n\nSuperMemory 是一个文本摘要功能,推荐使用 davinci 模型。不建议使用辅助模型,除非它是未经过滤、最大上下文长度超过 2000 Tokens,且具有良好摘要能力的模型。\n\nSuperMemory 提示词决定了模型如何撰写摘要。留空将使用默认提示词,建议保持留空。\n\n完成所有设置后,你可以在角色的设置中激活此功能。", + "replaceGlobalNote": "此字段不为空时,将替换当前的全局备注为此内容。", + "backgroundHTML": "将 Markdown/HTML 注入到聊天画面的背景中。\n\n你也可以使用额外资源。例如,你可以使用 {{audio::<资源名称}} 作为背景音乐。\n\n此外,你还可以与额外资源搭配使用以下格式:\n - {{bg::<资源名称>}}:将资源设为背景。", + "additionalText": "只有当 AI 认为有必要时,才会将该段文本添加到角色描述中。你可以在此处放置较长的文本。使用双换行进内联容分隔。", + "charjs": "这是一段会与角色一同运行的 JavaScript。详情请查看:https://github.com/kwaroran/RisuAI/blob/main/src/etc/example-char.js\n**出于安全原因,目前不建议使用。这些代码不会被包含在导出中。**", + "romanizer": "Romanizer 是一个将非罗马字母转换为罗马字母的插件,用于减少请求数据时的 Token。这可能会导致输出结果与原始模型不同。如果已在聊天中使用罗马字母,不建议激活。", + "oaiRandomUser": "激活后,请求中的用户参数将被随机 UUID 替代,并在刷新时修改。这可以用来防止 AI 识别用户。", + "inlayImages": "激活后,图片可嵌入聊天中,并且支持此功能的 AI 将能够看到它们。", + "metrica": "Metric Systemizer 是一个插件,会在请求阶段将公制单位转换为英制单位,而在输出时转回公制,让用户在接口上显示公制单位,但性能上使用英制单位。如果在聊天中已经使用英制单位,不建议激活。", + "lorePlus": "LoreBook+ 是一项实验性功能。使用向量数据库(VectorDB),而不仅是字符串匹配。目的在于提供更好的机器人创建体验和更好的匹配性能。", + "topP": "Top P 是内核采样(Nucleus Sampling)的概率阈值,模型只会考虑总概率质量达到 top_p 的 Token 结果。", + "openAIFixer": "OpenAI Fixer 是一个用于修复 OpenAI 部分问题的插件。", + "sayNothing": "激活后,如果用户没有输入聊天内容,系统会自动填入 ’say nothing‘。", + "showUnrecommended": "激活后,将显示不建议使用的过时设置。不建议使用这些设置。", + "imageCompression": "激活后,在导出角色时会压缩略图片。如果动画图片无法显示,请尝试关闭此选项。", + "useExperimental": "激活后,将显示部分实验性功能。", + "forceProxyAsOpenAI": "激活后,使用反向代理(Reverse proxy)时将强制使用 OpenAI 格式。", + "forcePlainFetch": "激活后,将使用浏览器的 Fetch API 来替代原生 HTTP 请求。这可能会导致 CORS 错误。", + "autoFillRequestURL": "激活后,将自动填入请求 URL 以匹配当前模型。", + "chainOfThought": "激活后,将在提示词中添加思维链(CoT)提示。", + "gptVisionQuality": "此选项用于设置图像检测模型的质量。质量越高,检测越准确,但会使用更多的 Token。", + "genTimes": "此设置支持模型上的重滚(reroll)回应数量。除第一则回应外,其他回应将作为缓存使用,以降低成本。但若未多次重滚回应,可能增加成本。", + "requestretrys": "此选项用于设置请求失败时的重试次数。", + "emotionPrompt": "此选项用于设置情绪检测的提示词。留空将使用默认提示词。", + "removePunctuationHypa": "激活后,将在执行 HypaMemory 前移除标点符号。", + "additionalParams": "此选项允许将附加的参数添加到请求主体(Request body)中。若要排除某些参数,可以将值设为 `{{none}}`。若要添加包头(Request header)而非主体,可以在键前加上 `header::`,如 `header::Authorization`。若要将值作为 JSON,可以在值前加上 `json::`,如 `json::{\"key\":\"value\"}`。其他情况下,系统将自动判定值的类型。", + "antiClaudeOverload": "若 Claude 过载发生,叡苏会透过继续相同的提示来阻止它,以减少过载的概率。此功能仅适用于串流回应(Streamed Responses),对非官方 API 端点可能无效。", + "triggerScript": "触发器(Trigger)是一个自定义脚本,在符合条件时执行。可用于修改聊天数据、执行命令、更改变量等。类型取决于触发时的情况,也可由按钮触发,如 {{button::Display::TriggerName}} 或带有 risu-trigger=\"\" 属性的 HTML 按钮。", + "autoContinueChat": "激活后,当聊天不以标点符号结束时,系统将尝试继续对话。请勿在不使用标点符号的语言中激活此功能。", + "combineTranslation": "激活后,将把被 HTML 标签分隔但属于同一句的文本合并后进行翻译,并在翻译结果上重新套用“修改显示”(Modify Display)。这有助于提高翻译的准确性。若激活此后接口出现异常,请关闭此选项并回报问题。", + "dynamicAssets": "激活后,若在处理数据时找不到资源名称,系统将使用向量搜索(Vector Search)尝试查找最接近的资源名称并进行替换。", + "dynamicAssetsEditDisplay": "激活后,动态资源将同样应用于“修改显示”阶段,但这可能会影响性能。", + "nickname": "设置后,将在聊天中以此昵称取代角色名称,并显示于 {{char}} 和 。", + "useRegexLorebook": "激活后,世界书将改用正则表达式(Regex)搜索,而不再使用字符串匹配。格式为 /regex/flags。", + "customChainOfThought": "警告:不再建议使用思维链(CoT)切换功能。请将相关提示词移至其他提示词字段。", + "customPromptTemplateToggle": "可在此处设置自定义提示词切换功能。使用 `=` 格式,每行一个,例如:`cot=Toggle COT`。你可以在提示词中透过 `{{getglobalvar::toggle_}}` 语法来使用这些切换功能,如:`{{getglobalvar::toggle_cot}}`。", + "defaultVariables": "可在此处设置自定义默认变量。使用 `=` 格式,每行一个。例如:`name=叡苏`,可在触发式和 CBS 变量中使用,如:`{{getvar::A}}`、`{{setvar::A::B}}` 或 `{{? $A + 1}}`。若提示词模板的默认变量与角色的默认变量名称相同,系统将使用角色的默认变量。", + "lowLevelAccess": "激活后,将开放需要高计算能力的功能,并允许通过角色中的触发式执行 AI 模型。除非确实需要这些功能,否则不要激活此选项。", + "triggerLLMPrompt": "这是将发送到模型的提示词。你可以使用 `@@role user`、`@@role system`、`@@role assistant` 来设置多轮对话及角色。例如:\n```\n@@role system\nrespond as hello\n@@role assistant\nhello\n@@role user\nhi\n```", + "legacyTranslation": "激活后,将使用旧版翻译方法,在翻译前对 Markdown 和引号进行预处理,而非在翻译后处理。", + "luaHelp": "可使用 Lua 作为触发式,并可定义 onInput、onOutput 和 onStart 函数。当用户发送消息时,调用 onInput;当角色发送消息时,调用 onOutput;当对话开始时,调用 onStart。详情请参阅说明文档。", + "claudeCachingExperimental": "Claude 缓存是实验性功能,可减少模型成本。但若在不使用重滚(reroll)回应的情况下激活,则可能增加成本。实验性功能可能不稳定,且未来可能会有所变动。", + "urllora": "可使用模型文档的直接下载链接。通过类似 https://sites.google.com/site/gdocs2direct/ 的网站,从 Google Drive 等平台产生直接连接。或者使用 Civitai URL,复制 AIR(格式如 `urn:air:flux1:lora:civitai:180891@776656` 或 `civitai:180891@776656`),并粘贴。", + "namespace": "命名空间(Namespace)是模块的唯一标识符,用于防止模块冲突,并与默认和其他模块等进行交互。若不确定如何填写,建议留空。", + "moduleIntergration": "可在模块集成区域中输入模块的命名空间(Namespace)来激活模块。若要激活多个模块,可用逗号分隔,例如:`module1,module2,module3`。此功能便于高级用户通过默认敏捷运用模块。", + "customCSS": "自定义 CSS 样式。若出现问题,可使用 (Ctrl + .) 激活或禁用。", + "betaMobileGUI": "激活后,将在小于 800px 的屏幕上使用测试版行动接口,需刷新页面。", + "unrecommended": "这是一个不建议使用的设置。建议关闭。", + "jsonSchema": "JSON Schema 将在 AI 模型支持时发送给模型。\n\n然而,由于 JSON Schema 学习难度较高,在叡苏中,你可以使用 TypeScript 接口的子集来代替 JSON Schema。叡苏将在运行时进行转换。例如,如果你想发送如下的JSON:\n\n```js\n{\n \"name\": \"叡苏\", // name 必须是叡苏,\n \"age\": 1, // age 必须是数字,\n \"icon\": \"slim\", // icon 必须是 ’slim‘ 或 ’rounded‘\n \"thoughts\": [\"Good View!\", \"Lorem\"] // thoughts 必须是字符串数组\n}\n```\n\n你可以使用以下 TypeScript 接口:\n\n```typescript\ninterface Schema {\n name: string;\n age: number;\n icon: ’slim‘|’rounded‘\n thoughts: string[]\n}\n```\n\n接口名称不重要。欲了解更多信息,请参阅 TypeScript 说明文档:https://www.typescriptlang.org/docs/handbook/interfaces.html 。要检查支持的 TypeScript 子集,请查看以下内容。
支持的 TypeScript 子集\n\n支持的类型包括 `boolean`、`number`、`string` 和 `Array`。高级类型不被支持(如:单元类型、交集类型、联合类型、可选类型、字面量类型等),除了以下几种情况:\n\n - 原始数据型别(Primitive Type)的数组(Array):(如 `string[]`、`Array`)\n - 字符串之间的单值类型(Unit Types):(例如 `’slim‘|’rounded‘`)\n\n 属性必须在同一内联定义。若一行中有多个属性,将会产生错误。属性和接口名称仅可使用拉丁字符,并在 ASCII 范围内。属性名称不得以单引号或双引号包裹。接口内部不支持嵌套。在定义属性的行中,不能包含 `{` 或 `}`。如果想使用更高级的类型,请使用 JSON Schema。\n
", + "strictJsonSchema": "激活后,某些模型将严格遵循提供的 JSON Schema。若禁用,可能会忽略 JSON Schema。", + "extractJson": "此字段不为空时,将从回应中提取特定的 JSON 数据。例如:想从回应 `{\"response\": {\"text\": [\"hello\"]}}` 中提取 `response.text[0]`,可以填写 `response.text.0`。", + "translatorNote": "可在此处为每个角色添加独特的翻译提示,但仅适用于使用 Ax. 模型进行翻译。要激活此功能,请在语言设置中包含 `{{slot::tnote}}`。此功能不适用群组聊天。", + "groupInnerFormat": "用于定义群组聊天中非发言者角色的格式。此字段不为空时,将使用此格式替代默认格式。若 `Group Other Bot Role` 设置为 `assistant`,该格式也将应用于发言者。", + "groupOtherBotRole": "用于定义群组聊天中非发言者的角色。", + "chatHTML": "每个聊天插入的 HTML。\n\n可以使用CBS和特殊标签。\n- ``:用于呈现文字的文本框\n- ``:用于显示用户或助理的头像\n- ``:用于聊天编辑、翻译等图标按钮\n- ``:生成消息按钮。", + "systemContentReplacement": "若模型不支持系统提示词,将使用此格式替换系统提示词。", + "systemRoleReplacement": "若模型不支持系统角色,将使用此角色替换系统角色。", + "summarizationPrompt": "用于摘要的提示词。留空将使用默认提示。你还可以使用带有 {{slot}} 的 ChatML 格式来处理聊天数据。", + "translatorPrompt": "用于翻译的提示词。留空将使用默认提示。你还可以使用带有 {{slot}} 的 ChatML 格式表示目标语言:用 {{slot::content}} 表示内容,用 {{slot::tnote}} 表示翻译注释。", + "translateBeforeHTMLFormatting": "激活后,将在正则脚本和 HTML 格式化之前翻译文本。这可能减少 Token 数,但可能破坏格式。" + }, + "setup": { + "chooseProvider": "选择 AI 提供者", + "openaikey": "使用 OpenAI API 密钥(推荐)", + "openaiProxy": "OpenAI 反向代理", + "setupmodelself": "其他/自行设置", + "inputApiKey": "请在此输入 API 密钥", + "apiKeyhelp": "可在以下获取 API 密钥:", + "setupSelfHelp": "欢迎画面结束后,可前往设置中自行配置。", + "theme": "选择介面主题", + "themeDescWifulike": "不适合在行动设备上使用", + "themeDescWifuCut": "适合在行动设备上使用", + "themeDescClassic": "适用于所有设备", + "texttheme": "设置文字颜色", + "inputName": "最后,请输入你的昵称。", + "welcome": "欢迎使用 Risu(叡苏)!我将引导你进行设置。请问我该如何称呼你?", + "welcome2": "你好,{username}!在开始之前,我会问你一些问题,稍后可在设置中进行修改。\n\n首先,请选择 AI 提供者。", + "openrouterProvider": "Openrouter 提供许多模型,部分免费且未经内容过滤,但质量不如 OpenAI。", + "hordeProvider": "Horde 提供免费服务,但回应时间较长且质量较低。", + "setProviderLater": "还有其他提供者,你可以稍后在设置中配置。如想稍后设置,请选择此选项。", + "setupOpenAI": "使用 OpenAI 需要获取 API 密钥(Key)。\n1. 前往 https://beta.openai.com/ \n2. 使用账号登录 \n3. 前往 https://beta.openai.com/account/api-keys \n4. 点击“Create New API Key”,并命名密钥。 \n5. 复制该密钥。 \n6. 返回叡苏\n7. 粘贴密钥并点击“发送”。", + "setupClaude": "使用 Claude,你需要获取一个 API 密钥。", + "setupClaudeSteps": [ + "访问此链接并使用 Google 帐户登录", + "输入您的信息并点击“继续”(Continue)", + "在组织页面,仅在第一个框中输入任意名称,然后点击“创建账户”", + "点击“购买积分”(Buy Credits)", + "点击“完成设置”(Complete Setup)", + "输入如上图所示的内容,但国家/地区部分需自行输入。完成后点击“继续”(Continue)", + "添加您的支付方式并点击“继续”(Continue)", + "支付完成后,点击“添加资金”(Add Funds),最低需添加 5 美元或设置自动充值", + "添加金额后,转到仪表板页面并点击“获取 API 密钥”(Get API Keys)", + "点击“创建 API 密钥”(Create API Key)", + "按照上图输入信息,然后点击“添加”(Add)", + "複製密钥,将其粘贴在此并发送。" + ], + "setupOpenrouter": "使用 Openrouter 需要获取 API 密钥(Key)。 \n1. 前往 https://openrouter.ai/keys\n2. 点击“Create Key”\n3. 任意命名密钥名称。\n4. 复制该密钥。\n5. 返回叡苏\n6. 粘贴密钥并点击“发送”。", + "allDone": "完成所有设置!请稍待片刻。", + "setupLaterMessage": "欢迎,{username}!你希望我引导你完成设置还是自行设置?", + "setupMessageOption1": "引导我完成设置", + "setupMessageOption1Desc": "推荐新用户使用", + "setupMessageOption2": "由我自己完成设置", + "claudeDesc": "Claude 是由 Antropic 开发的 AI 模型,是 OpenAI 的竞争对手。如果你希望使用非英语语言,它优于 GPT。", + "openAIDesc": "OpenAI GPT 是高质量的 AI 模型,但它付费且存在内容过滤,在非英语环境下表现较弱。", + "chooseChatType": "很好!现在请选择聊天语言。", + "chooseChatTypeOption1": "全英语", + "chooseChatTypeOption1Desc": "推荐英语使用者。AI 将使用英语进行输入和输出。", + "chooseChatTypeOption2": "英语处理", + "chooseChatTypeOption2Desc": "推荐非英语使用者。AI 内部使用英语处理,但输入输出为您的语言。", + "chooseChatTypeOption3": "无语言侧重", + "chooseChatTypeOption3Desc": "AI 将使用你的语言处理,但可能会降低回应质量。", + "chooseCheapOrMemory": "除此之外,你更倾向于记忆功能还是节约成本?", + "chooseCheapOrMemoryOption1": "记忆功能", + "chooseCheapOrMemoryOption1Desc": "AI 会记住更多内容,但费用较高。", + "chooseCheapOrMemoryOption2": "省钱模式", + "chooseCheapOrMemoryOption2Desc": "AI 会记住较少内容,但费用更低。", + "chooseCheapOrMemoryOption3": "平衡", + "chooseCheapOrMemoryOption3Desc": "AI 记住的内容多于省钱模式,但不及使用记忆功能。", + "chooseCheapOrMemoryOption4": "无限制", + "chooseCheapOrMemoryOption4Desc": "AI 会记住几乎所有内容,但费用极高。", + "finally": "最后,你是否希望使用进阶工具?", + "finallyOption1": "是", + "finallyOption1Desc": "使用进阶工具会使界面变得更複杂。推荐高级用户使用。", + "finallyOption2": "否", + "finallyOption2Desc": "不使用高级工具将使界面更简洁。推荐新使用者使用。", + "openAIProvider": "OpenAI GPT 是高质量的 AI 模型,但属于付费并经内容审核。" + }, + "confirm": "确定", + "goback": "返回", + "botSettings": "机器人设置", + "model": "模型", + "apiKey": "API 密钥", + "providerURL": "请求地址(URL)", + "providerJSON": "请求主体(JSON)", + "mainPrompt": "主提示词", + "jailbreakPrompt": "越狱提示词", + "globalNote": "全局备注", + "autoSuggest": "自动建议", + "tokens": "Tokens", + "maxContextSize": "最大上下文长度(Max Context Size)", + "maxResponseSize": "最大回应长度(Max Response Size)", + "temperature": "温度(Temperature)", + "frequencyPenalty": "频率惩罚(Frequency Penalty)", + "presensePenalty": "存在惩罚(Presence Penalty)", + "advancedSettings": "高级设置", + "advancedSettingsWarn": "警告:若不确定该选项的作用,请勿进行修改!", + "formatingOrder": "格式顺序", + "authorNote": "作者备注", + "firstMessage": "初始消息", + "description": "描述", + "jailbreakToggle": "使用越狱提示词", + "charIcon": "角色头像", + "characterDisplay": "角色演示", + "viewScreen": "额外角色画面", + "none": "无", + "emotionImage": "表情立绘", + "noImages": "没有图片", + "noBias": "No Bias", + "image": "图片", + "name": "名称", + "emotion": "情绪名称", + "value": "值", + "reroll": "重新生成", + "chatList": "聊天列表", + "removeChat": "确定要移除此消息吗?", + "loreBook": "世界书", + "character": "角色", + "Chat": "聊天", + "globalLoreInfo": "角色世界书适用于该角色的所有对话。", + "group": "群组", + "groupLoreInfo": "群组世界书适用于该群组的所有对话。", + "localLoreInfo": "聊天世界书仅用于此对话。", + "removeConfirm": "你确定要删除:", + "removeConfirm2": "你**真的**确定要删除:", + "exportConfirm": "你想要导出此数据吗?", + "insertOrder": "插入顺序", + "activationKeys": "关键词", + "activationKeysInfo": "使用逗号分隔", + "prompt": "提示词", + "loreBookDepth": "世界书搜索深度", + "loreBookToken": "世界书最大 Token 数", + "removeCharacter": "删除角色", + "removeGroup": "删除群组", + "exportCharacter": "导出角色", + "userSetting": "用户设置", + "username": "你的名称", + "userIcon": "你的头像", + "successExport": "已成功导出并保存至你的下载数据夹", + "successImport": "成功导入", + "importedCharacter": "导入角色", + "alwaysActive": "始终激活", + "additionalPrompt": "附加提示词", + "descriptionPrefix": "描述前缀", + "forceReplaceUrl": "反向代理", + "emotionWarn": "正在使用辅助模型。", + "plugin": "插件", + "language": "语言", + "UiLanguage": "介面语言", + "createfromScratch": "自行创建", + "importCharacter": "导入角色", + "translator": "翻译器", + "disabled": "关闭", + "noPluginSelected": "已选择模型为插件,但未选择插件。", + "text": "文字", + "UISize": "聊天文字大小", + "newVersion": "发现更新,是否进行安装?", + "display": "显示 & 音频", + "useCustomBackground": "自定义背景", + "translateInput": "翻译输入", + "autoTranslation": "自动翻译", + "fullscreen": "全屏幕", + "playMessage": "播放消息音效", + "iconSize": "头像大小", + "createGroup": "创建群组", + "groupIcon": "群组头像", + "single": "单个", + "multiple": "多个", + "useCharLorebook": "使用角色世界书", + "selectChar": "选择角色", + "askLoadFirstMsg": "是否加载初始消息?", + "theme": "介面主题", + "editOrder": "编辑顺序", + "autoMode": "自动模式", + "submodel": "辅助模型", + "timeOutinSec": "超时时间(秒)", + "emotionPrompt": "情绪提示词", + "singleView": "单角色模式", + "SpacedView": "多角色模式", + "emphasizedView": "双角色模式", + "pluginWarn": "插件可在隔离环境中运行,但安装恶意插件可能导致问题。", + "createGroupImg": "产生群组头像", + "waifuWidth": "角色对话框宽度", + "savebackup": "备份至 Google", + "loadbackup": "从 Google 读取备份", + "files": "文件", + "backupConfirm": "你确定要保存备份吗?", + "backupLoadConfirm": "你确定要读取备份吗?所有数据将被覆盖!", + "backupLoadConfirm2": "你**真的、真的**确定要加载备份吗?这将会清除所有数据!", + "pasteAuthCode": "请从弹出窗口复制鉴别码并贴入:", + "others": "其他", + "presets": "默认设置", + "imageGeneration": "图像生成", + "provider": "提供者", + "key": "密钥(Key)", + "noData": "没有数据", + "currentImageGeneration": "当前图像生成数据", + "promptPreprocess": "使用提示词预处理", + "SwipeRegenerate": "使用滑动箭头重新产生消息", + "instantRemove": "删除消息时连带删除后续消息", + "instantRemoveConfirm": "你想只删除一条消息吗?若选择“否”,后续消息也将被删除。", + "textColor": "文字颜色", + "classicRisu": "经典叡苏", + "highcontrast": "高对比度", + "quickPreset": "可通过 Ctrl +(默认顺序编号)快速切换默认。", + "requestretrys": "请求失败时重试", + "utilityBot": "工具机器人", + "ShowLog": "显示请求记录", + "waifuWidth2": "角色显示宽度", + "sayNothing": "无输入内容时自动填入 ’say nothing‘", + "regexScript": "正则表达式", + "type": "类型", + "editInput": "修改输入", + "editOutput": "修改输出", + "editProcess": "修改请求数据", + "loadLatest": "读取最新的备份", + "loadOthers": "读取其他备份", + "exampleMessage": "示范消息", + "creatorNotes": "创建者备注", + "systemPrompt": "系统提示词", + "characterNotes": "角色备注", + "personality": "性格", + "scenario": "场景", + "alternateGreetings": "备选问候语", + "unrecommended": "不建议", + "chatNotes": "聊天备注", + "showUnrecommended": "显示不建议的设置", + "altGreet": "备选初始消息", + "scripts": "脚本", + "settings": "设置", + "selective": "选择性", + "SecondaryKeys": "次要关键词", + "useGlobalSettings": "使用全局设置", + "recursiveScanning": "递归扫描", + "creator": "创建者", + "CharVersion": "角色版本", + "Speech": "语音", + "ToggleSuperMemory": "激活 SupaMemory", + "SuperMemory": "SupaMemory", + "useExperimental": "激活实验性功能", + "showMemoryLimit": "显示记忆上限", + "roundIcons": "圆形头像", + "streaming": "即时串流传输", + "chatBot": "聊天机器人", + "otherBots": "其他机器人", + "user": "用户", + "additionalAssets": "额外资源", + "editDisplay": "修改显示", + "community": "社区", + "textBackgrounds": "自定义文本窗口颜色", + "textBorder": "文字边框", + "textScreenRound": "圆角化文本窗口", + "textScreenBorder": "文本窗口边框", + "ttsReadOnlyQuoted": "仅朗读引号内容", + "ttsStop": "停止语音合成", + "askRemoval": "请求删除", + "replaceGlobalNote": "替换全局备注", + "charLoreBook": "角色世界书", + "globalLoreBook": "全局世界书", + "globalRegexScript": "全局正则表达式", + "accessibility": "辅助功能", + "sendWithEnter": "使用 Enter 键发送", + "clickToEdit": "点击文字进行编辑", + "setNodePassword": "设置密码以提升安全性", + "inputNodePassword": "输入密码。如果忘记密码,请删除服务器文档中的 save/__password.txt 并重启服务器。", + "simple": "基本", + "advanced": "高级", + "askReRollAutoSuggestions": "自动滚动建议", + "creatingSuggestions": "正在产生建议⋯", + "orderByOrder": "按顺序对话", + "removeFromGroup": "确定要将 {{char}} 移出群组吗?", + "talkness": "健谈度", + "active": "活跃", + "loreRandomActivation": "激活概率条件", + "activationProbability": "概率", + "shareCloud": "分享至 RisuRealm", + "hub": "RisuRealm", + "tags": "标签", + "backgroundHTML": "背景嵌入", + "copied": "已复制", + "useChatCopy": "激活聊天消息复制", + "useChatSticker": "激活聊天贴图", + "useAdditionalAssetsPreview": "使用额外资源预览", + "autoTranslateInput": "自动输入翻译", + "enterMessageForTranslateToEnglish": "输入消息以翻译成英文", + "recent": "最新", + "downloads": "下载", + "trending": "热门", + "imageCompression": "图片压缩", + "notLoggedIn": "尚未登录 Risu 账号", + "googleDriveInfo": "连接至 Google Drive 以同步数据。", + "googleDriveConnection": "连接 Google Drive", + "googleDriveConnected": "已连接 Google Drive", + "SaveDataInAccount": "保存数据到账号", + "dataSavingInAccount": "正在将数据保存到账号", + "logout": "注销", + "loadDataFromAccount": "从账号读取数据", + "saveCurrentDataToAccount": "保存目前数据到账号", + "chatAssumed": "", + "proxyAPIKey": "代理密钥/密码", + "proxyRequestModel": "请求模型", + "officialWiki": "官方 Wiki", + "officialWikiDesc": "欢迎查看叡苏官方的 Wiki。", + "officialDiscord": "官方 Discord", + "officialDiscordDesc": "叡苏官方的 Discord 服务器", + "confirmRecommendedPreset": "此模型有建议设置,是否更改为建议设置?(可在辅助功能中关闭提示)", + "toggleConfirmRecommendedPreset": "模型变更时询问是否使用建议设置", + "recommendedPreset": "使用建议设置", + "persona": "用户信息", + "icon": "头像", + "account": "账号", + "remove": "删除", + "creationSuccess": "创建成功", + "noweb": "此功能无法于网页版使用。", + "createBotInternet": "使用 AI 从网络创建机器人", + "createBotInternetAlert": "请提供角色名称及对应的系列或游戏。", + "able": "激活", + "assetWidth": "额外资源图片最大宽度", + "animationSpeed": "动画速度", + "screenshot": "截图", + "screenshotSaved": "截图已保存", + "inputBotGenerationPrompt": "输入机器人生成提示词", + "createBotAI": "使用 AI 创建原创机器人", + "createBotwithAI": "使用 AI 创建机器人", + "changeFolderName": "输入新数据夹名称(留空以取消)", + "cancel": "取消", + "renameFolder": "重命名数据夹", + "changeFolderColor": "更改数据夹颜色", + "fullWordMatching": "完整单词匹配", + "botSettingAtStart": "激活时显示机器人菜单", + "triggerStart": "聊天发送时触发", + "triggerInput": "用户输出时触发", + "triggerOutput": "角色输出时触发", + "triggerManual": "仅限手动触发", + "triggerCondVar": "如果变量为", + "triggerCondExists": "如果聊天中存在文字", + "triggerScript": "触发器", + "triggerMatchRegex": "与正则表达式匹配", + "triggerMatchLoose": "宽松匹配", + "triggerMatchStrict": "严格匹配", + "searchDepth": "搜索深度", + "equal": "等于", + "notEqual": "不等于", + "greater": "大于", + "less": "小于", + "greaterEqual": "大于或等于", + "lessEqual": "小于或等于", + "triggerEffSysPrompt": "添加系统提示词", + "triggerEffSetVar": "修改变量", + "triggerEffImperson": "发送聊天消息", + "triggerEffCommand": "执行命令", + "triggerEffRunTrigger": "执行触发器", + "triggerEffStop": "停止发送提示词", + "triggerEffCall": "调用触发器", + "varableName": "变量名", + "role": "身份", + "location": "位置", + "promptstart": "提示词开始", + "promptend": "提示词结束", + "historyend": "聊天历史结束", + "always": "总是", + "noEffect": "无效果", + "invaildTriggerEffect": "此效果不适用于该触发类型。", + "operator": "运算符", + "TriggerSetToVar": "设置为变量", + "TriggerAddToVar": "增加为变量", + "TriggerSubToVar": "从变量中减去", + "TriggerMulToVar": "将变量乘以", + "TriggerDivToVar": "将变量除以", + "isNull": "尚未设置", + "ifChatIndex": "如果对话索引", + "ifRandom": "如果随机", + "ifValue": "如果值", + "hideRealm": "隐藏 RisuRealm", + "popularityLevel": "{} 人气", + "colorScheme": "配色方案", + "rangeStart": "范围开始", + "rangeEnd": "范围结束", + "untilChatEnd": "直到聊天结束", + "usePromptTemplate": "使用提示词模板", + "specialType": "特殊类型", + "noSpecialType": "无特殊类型", + "forceProxyAsOpenAI": "强制代理格式为 OpenAI", + "promptTemplate": "提示词模板", + "customInnerFormat": "自定义内部格式", + "innerFormat": "内部格式", + "HypaMemory": "HypaMemory", + "ToggleHypaMemory": "激活 HypaMemory", + "resetPromptTemplateConfirm": "你真的确定要重置提示词模板吗?", + "emotionMethod": "情绪检测方式", + "continueResponse": "继续回应", + "showMenuChatList": "在菜单中显示聊天列表", + "translatorLanguage": "翻译目标语言", + "translatorType": "翻译器类型", + "deeplKey": "DeepL API 密钥", + "deeplFreeKey": "DeepL 免费 API 密钥", + "deeplXUrl": "DeepLX URL", + "deeplXToken": "DeepLX Token", + "exportPersona": "导出用户设置", + "importPersona": "导入用户设置", + "export": "导出", + "import": "导入", + "supporterThanks": "支持者感谢", + "supporterThanksDesc": "感谢您的支持!", + "donatorPatreonDesc": "为保护隐私,默认不会显示在名单中。若想显示您的昵称,请前往叡苏的 Patreon 页面并点击链接按钮。", + "useNamePrefix": "使用名称前缀", + "textAdventureNAI": "以文字冒险形式运行", + "appendNameNAI": "附加名称至 NAI", + "customStopWords": "自定义停止词", + "defaultPrompt": "默认提示词", + "additionalText": "额外提示", + "seed": "Seed", + "charjs": "角色 JavaScript", + "depthPrompt": "提示词深度", + "largePortrait": "肖像", + "lorePlus": "Lorebook+", + "reverseProxyOobaMode": "Ooba Mode", + "joinMultiUserRoom": "加入多用户聊天室", + "exactTokens": "精确 Tokens", + "fixedTokens": "估算 Tokens", + "inlayViewScreen": "内嵌窗口", + "imgGenPrompt": "图像生成提示词", + "imgGenNegatives": "图像生成负面提示词", + "imgGenInstructions": "系统提示词", + "usePlainFetchWarn": "使用 NovelAI 时请关闭此选项,避免出现 CORS 错误。", + "translationPrompt": "翻译提示词", + "translationResponseSize": "翻译回应长度", + "webdeeplwarn": "此功能不不建议于网页版使用,避免出现 CORS 错误。", + "saveBackupLocal": "本地保存备份", + "loadBackupLocal": "本机读取备份", + "topP": "Top P", + "genTimes": "生成次数", + "cot": "思维链(CoT)", + "forcePlainFetch": "强制简单抓取", + "autoFillRequestURL": "自动填入请求 URL", + "newOAIHandle": "新的 OpenAI 处理方式", + "oaiRandomUser": "放置 OAI 随机用户", + "inlayImage": "内嵌图片功能", + "nativeAutomark": "实验性本地自动标记", + "assistantPrefill": "助理预填充", + "postEndInnerFormat": "结尾内部格式", + "sendChatAsSystem": "以系统身份发送消息", + "sendName": "非群聊时发送名称", + "utilOverride": "实用程序改写", + "template": "模板", + "chatAsOriginalOnSystem": "以原始身份发送", + "exportAsDataset": "导出保存数据为数据集", + "editTranslationDisplay": "修改翻译显示", + "selectModel": "选择模型", + "autoRemoveThoughtTag": "删除思维标记", + "customChainOfThought": "自定义思维链", + "maxThoughtTagDepth": "思维标记最大深度", + "openrouterFallback": "Openrouter 回退", + "openrouterMiddleOut": "Openrouter 中间输出", + "geminiApiKey": "Gemini API 密钥", + "removePunctuationHypa": "移除记忆标记", + "memoryLimitThickness": "记忆上限厚度", + "inputCardPassword": "输入角色卡密码", + "ccv2Desc": "V2 角色卡是广泛用于聊天机器人前端的格式。", + "ccv3Desc": "V3 角色卡是用于聊天机器人前端的新型格式。", + "realmDesc": "RisuRealm 是叡苏的内容分享平台,你可以将角色分享给其他用户。", + "rccDesc": "Risu Refined 角色卡具有密码保护、完整性验证等附加功能。", + "password": "密码", + "license": "授权", + "licenseDesc": "你可以设置下载授权,限制角色卡对提示词的使用。", + "passwordDesc": "你可以为角色卡设置密码,防止未经授权的访问。", + "largePersonaPortrait": "用户肖像", + "module": "模块", + "modules": "模块", + "noModules": "尚未安装任何模块。", + "createModule": "创建模块", + "basicInfo": "基本数据", + "moduleContent": "模块内容", + "confirmRemoveModuleFeature": "确定要移除此功能吗?此操作无法还原。", + "editModule": "编辑模块", + "importModule": "导入模块", + "download": "下载", + "edit": "编辑", + "enableGlobal": "全局激活", + "chatModulesInfo": "可选择开启或关闭此对话的模块。", + "sideMenuRerollButton": "侧栏菜单重新加载", + "persistentStorage": "永久存储", + "persistentStorageSuccess": "存储已成功永久化", + "persistentStorageFail": "存储未能永久化。你是否拒绝了请求,或浏览器不支持?", + "persistentStorageRecommended": "建议使用永久存储", + "persistentStorageDesc": "你的浏览器支持永久存储,建议激活以提升性能和用户体验。", + "enable": "激活", + "postFile": "上传文件", + "requestInfoInsideChat": "在聊天中显示请求数据", + "inputTokens": "输入 Tokens", + "outputTokens": "输出 Tokens", + "tokenWarning": "Token 计算可能不精确,仅作参考。", + "log": "记录", + "popularityLevelDesc": "人气会随着下载量等增加。3.7 人气约等于 1 次下载。", + "additionalParams": "额外参数", + "heightMode": "高度模式", + "useAdvancedEditor": "使用高级编辑器", + "noWaitForTranslate": "不等待翻译", + "updateRealm": "更新至 RisuRealm", + "updateRealmDesc": "你正试图将角色更新至 RisuRealm。此操作将使角色更新至 RisuRealm,且无法还原。", + "antiClaudeOverload": "防止 Claude 超载", + "activeTabChange": "目前的标签已停用,因其他标签处于活动中。若要激活此标签,请按“确定”。", + "maxSupaChunkSize": "最大 SupaMemory Chunk 大小", + "addCharacter": "新增角色", + "importFromRealm": "从 RisuRealm 选择", + "importFromRealmDesc": "RisuRealm 提供超过 1000 位角色", + "random": "随机", + "metaData": "Metadata", + "autoContinueMinTokens": "目标 Tokens(自动继续)", + "autoContinueChat": "防止不完整回复(自动继续)", + "removeIncompleteResponse": "移除不完整句子", + "tokenizer": "Tokenizer", + "chatFormating": "聊天格式", + "useInstructPrompt": "激活命令提示词", + "hanuraiMemory": "HanuraiMemory", + "playground": "Playground", + "textAreaSize": "输入栏大小", + "textAreaTextSize": "输入栏文字大小", + "sideBarSize": "侧边栏大小", + "embedding": "嵌入", + "syntax": "语法", + "run": "执行", + "noMessage": "输入内容以开始聊天。", + "combineTranslation": "合并翻译", + "dynamicAssets": "动态资源", + "dynamicAssetsEditDisplay": "在接口上使用动态资源", + "longTermMemory": "长期记忆", + "grid": "网格", + "list": "列表", + "trash": "垃圾桶", + "trashDesc": "删除的角色将移至垃圾桶,可选择还原或永久删除。删除的角色将在 3 天后自动永久删除。", + "shareExport": "分享/导出", + "risupresetDesc": "Risupreset 是专为叡苏默认设置设计的格式。", + "risuMDesc": "RisuM 是专为叡苏模块设计的格式。", + "jsonDesc": "JSON 是一种人机都易于读写的格式。", + "nickname": "昵称", + "useRegexLorebook": "使用正则表达式", + "customPromptTemplateToggle": "自定义开关", + "defaultVariables": "默认变量", + "hypaAllocatedTokens": "已分配的 Tokens", + "hypaChunkSize": "Chunk 大小", + "hypaV2Desc": "HypaMemory V2 是一种结合摘要数据和向量搜索的长期记忆系统。", + "supaDesc": "SupaMemory 是一种使用摘要数据的长期记忆系统。", + "hanuraiDesc": "HanuraiMemory 是一个使用向量搜索的记忆系统。", + "lowLevelAccess": "低层级访问", + "resultStoredVar": "存储结果的变量", + "triggerEffRunLLM": "执行主控模型", + "triggerEffectSendAI": "重新发送 AI", + "triggerEffCheckSim": "检查相似度", + "triggerEffShowAlert": "显示警告", + "normal": "正常", + "error": "错误", + "input": "输入", + "select": "选择", + "lowLevelAccessConfirm": "此内容使用低层级访问,可直接访问 AI 模型和你的存储数据。你确定要导入吗?", + "triggerLowLevelOnly": "此触发仅适用于低层级访问,需在角色或模块的高级设置中激活低层级访问。", + "truthy": "真值", + "extractRegex": "使用正则表达式提取文字", + "runImgGen": "执行图像生成", + "cutChat": "分割聊天", + "modifyChat": "修改聊天", + "regex": "正则表达式", + "flags": "修饰符", + "resultFormat": "结果格式", + "negPrompt": "负面提示词", + "start": "开始", + "end": "结束", + "index": "索引", + "search": "搜索", + "goCharacterOnImport": "导入后跳转至角色页面", + "format": "格式", + "v2Warning": "警告:已停止支持 V2 角色卡,可能会缺失部分数据。", + "applyModule": "套用模块", + "successApplyModule": "已成功套用模块", + "font": "字体", + "lineHeight": "行距", + "loadAutoServerBackup": "读取服务器自动备份", + "notCharxWarn": "角色使用多项资源,建议导出为 CharX 格式以提升兼容性。", + "noPlugins": "未安装插件", + "legacyTranslation": "旧版翻译", + "clipboardSuccess": "已复制到剪贴板", + "translateContent": "翻译内容", + "doNotTranslate": "不进行翻译", + "includePersonaName": "包含用户名称", + "hidePersonaName": "隐藏用户名称", + "triggerSwitchWarn": "更改触发类型后,现有触发将被清除。你确定要继续吗?", + "codeMode": "程序码", + "blockMode": "区块", + "helpBlock": "帮助", + "hideChatIcon": "隐藏头像", + "loadInternalBackup": "读取内部备份", + "createCopy": "新建副本", + "bindPersona": "绑定用户", + "chatOptions": "聊天选项", + "doYouWantToBindCurrentPersona": "是否绑定当前用户至此聊天?", + "doYouWantToUnbindCurrentPersona": "是否解除此对话中的用户绑定?", + "personaBindedSuccess": "用户绑定已完成", + "personaUnbindedSuccess": "用户绑定已解除", + "parameters": "参数", + "sizeAndSpeed": "大小与速度", + "useLegacyGUI": "使用旧版接口", + "claudeCachingExperimental": "Claude 缓存", + "openClose": "开启/关闭", + "hideApiKeys": "隐藏 API 密钥", + "unformatQuotes": "禁用引号格式", + "enableDevTools": "激活开发者工具", + "selectFile": "选择文件", + "namespace": "命名空间", + "moduleIntergration": "模块集成", + "previewInfo": "此预览显示模型处理前的提示词。", + "miscTools": "其他工具", + "promptConvertion": "提示词转换", + "convertionStep1": "选择与提示词相关的文件(支持 Context、Instruct 及 Sampler JSON)", + "customCSS": "自定义 CSS", + "betaMobileGUI": "测试版行动接口", + "menu": "菜单", + "connectionOpen": "已开启连接", + "connectionOpenInfo": "多人聊天室已开启,你可以将聊天室代码分享给其他用户。其他用户可在 Playground > 加入多人聊天室 > 使用代码加入。", + "createMultiuserRoom": "新建多人聊天室", + "connectionHost": "你是聊天室的主持人。", + "connectionGuest": "你是聊天室的访客。", + "otherUserRequesting": "其他用户正在请求中,请稍后重试。", + "jsonSchema": "JSON Schema", + "enableJsonSchema": "Enable Schema", + "strictJsonSchema": "Strict Schema", + "extractJson": "提取 JSON", + "reloadSession": "发现更新版本的存储数据,正在重新加载对话⋯", + "fixMarkdownNewline": "修正 Markdown 换行", + "customQuotes": "自定义引号", + "leadingSingleQuote": "上单引号", + "leadingDoubleQuote": "上双引号", + "trailingSingleQuote": "下单引号", + "trailingDoubleQuote": "下双引号", + "translatorNote": "翻译器备注", + "formatGroupInSingle": "单一群组格式", + "groupInnerFormat": "非发言者内部格式", + "groupOtherBotRole": "群组内非发言者的身份", + "defineCustomGUI": "自定义界面", + "chatHTML": "聊天介面 HTML", + "logShare": "显示分享记录按钮", + "preview": "预览", + "recommended": "推荐", + "newChat": "新对话", + "predictedOutput": "预测输出", + "systemContentReplacement": "替换系统内容", + "systemRoleReplacement": "替换系统角色", + "seperateParameters": "分离参数", + "seperateParametersEnabled": "激活分离参数", + "summarizationPrompt": "摘要提示词", + "translatorPrompt": "翻译提示词", + "translateBeforeHTMLFormatting": "於 HTML 格式化前翻译", + "retranslate": "重新翻译", + "loading": "加载中" + } From be6bb983e6bb93c206ff86ef237df1b9a2951422 Mon Sep 17 00:00:00 2001 From: Rivelle Date: Sat, 30 Nov 2024 01:16:58 +0800 Subject: [PATCH 173/175] Update Traditional Chinese Translations in zh-Hant.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I updated some wording in the Traditional Chinese translations and completed some unfinished translations in this version. This update was completed independently by me. For "lorebook," I chose to retain the original term, as it is still commonly used among Traditional Chinese users (at least based on my understanding). I also fine-tuned some phrases, such as changing "頭貼" to "頭像." I discovered that "頭像" has been commonly used by Traditional Chinese users as early as 2008. Professional translators also confirmed that they generally use this translation. Similar to the Simplified Chinese version, I decided to retain some technical terms like "Enable Schema" in English. Since there are no established Traditional Chinese equivalents for these terms, keeping them in English is considered more intuitive by many users. This file was created by copying and adapting the translations from the RisuAI .json translation file. I have carefully checked the formatting and hope there are no issues. Additionally, this is my first time uploading a branch... I didn’t initially realize the importance of preserving version comparisons on GitHub. However, since the previous submissions for both the Traditional and Simplified Chinese translations were also made by me, I believe this won’t cause significant problems. --- src/lang/zh-Hant.ts | 1483 ++++++++++++++++++++++--------------------- 1 file changed, 771 insertions(+), 712 deletions(-) diff --git a/src/lang/zh-Hant.ts b/src/lang/zh-Hant.ts index 18ca8b86..c36f6570 100644 --- a/src/lang/zh-Hant.ts +++ b/src/lang/zh-Hant.ts @@ -1,715 +1,774 @@ export const languageChineseTraditional = { - "formating": { - "main": "主要提示詞", - "jailbreak": "越獄提示詞", - "chats": "歷史聊天", - "lorebook": "Lorebook", + "formating": { + "main": "主要提示詞", + "jailbreak": "越獄提示詞", + "chats": "歷史聊天", + "lorebook": "Lorebook", + "globalNote": "全域備註", + "authorNote": "作者備註", + "lastChat": "前一次聊天", + "description": "角色描述", + "personaPrompt": "使用者提示詞", + "plain": "基礎提示詞", + "memory": "Supa/HypaMemory", + "postEverything": "系統提示詞結束" + }, + "errors": { + "toomuchtoken": "錯誤:所需的 Token 數超過了可用的最大上下文大小", + "unknownModel": "錯誤:無法識別所選的模型", + "httpError": "錯誤:請求發生錯誤:", + "noData": "無法找到檔案中的資料,或者檔案已經損毀", + "onlyOneChat": "至少需要一個聊天室", + "alreadyCharInGroup": "該群組中已經有一個同名角色。", + "noUserIcon": "請先設定你的個人頭像。", + "emptyText": "文字內容為空。", + "wrongPassword": "密碼錯誤", + "networkFetch": "這通常是由於網路連線不穩定或伺服器故障引起的。", + "networkFetchWeb": "這可能是由 CORS 錯誤引起的。這種情況只會在使用網頁版時發生,因為瀏覽器存在一些限制。請嘗試使用本地版或其他版本的叡甦。", + "networkFetchPlain": "這可能是一次資料抓取(Fetch)錯誤。請嘗試在設定中關閉強制抓取選項。", + "requestLogRemoved": "該請求記錄已被刪除。", + "requestLogRemovedDesc": "當用戶端重新整理或加載時,該請求記錄會被刪除。" + }, + "showHelp": "顯示幫助", + "help": { + "model": "此模型是指聊天中使用的主要模型。", + "submodel": "輔助模型是一個用於分析情感圖像、產生自動建議等的模型,推薦使用 GPT-3.5。", + "oaiapikey": "OpenAI 的 API 金鑰(Key),可在 https://platform.openai.com/account/api-keys 取得。", + "mainprompt": "主要提示詞設定用於決定模型的預設行為。", + "jailbreak": "當角色中的越獄開關被啟動後,越獄提示詞將被使用。", + "globalNote": "一個對模型行為有強烈影響的備註(也稱為 UJB),適用於所有角色。", + "autoSuggest": "用於自動建議使用者回應時生成選項的提示詞。", + "formatOrder": "提示詞的排列順序:越靠下的區塊對模型的影響越大。", + "forceUrl": "此欄位不為空時,請求將被發送到你所輸入的網址。", + "tempature": "較低的數值會使角色更緊密地遵循提示詞,但會使回應更制式與機械化。\n較高的數值則會增強角色的創意表現,但回應可能會變得不穩定。", + "frequencyPenalty": "較高的數值可以避免角色在個別回應中重複使用相同的詞彙,但回應也更容易出現語意混亂。", + "presensePenalty": "較高的數值可以避免角色在整體對話中重複使用相同的詞彙,但這也可能導致回答失去一致性和穩定性。", + "sdProvider": "圖像生成提供者。", + "msgSound": "當角色回應時,播放 *叮* 的提示音", + "charDesc": "角色的簡要描述。這會影響角色的回應方式。", + "charFirstMessage": "角色的初始訊息,這會極大地影響角色的回應方式。", + "charNote": "對模型行為有強烈影響的備註,嵌入到當前角色中,也稱為 UJB。", + "toggleNsfw": "切換越獄提示詞的開關。", + "lorebook": "Lorebook 是由使用者創建的 AI 辭典,只有當上下文中包含關鍵字時 AI 才能看到它。", + "loreName": "Lorebook 的名稱,不影響 AI。", + "loreActivationKey": "當上下文中包含任一關鍵字時,該條目將被啟用,並啟動相應的提示詞。使用逗號分隔。", + "loreorder": "插入順序越高,對模型的影響力越大。在啟動大量條目時,也更不容易被截斷。", + "bias": "Bias 是一組鍵值數據,用於修改某些字串出現的機率。\n其數值範圍可以是 -100 到 100。較高的數值會使該字串更可能出現,較低的數值則降低出現機率。\n另外,在某些模型中,若將數值設為 -101,該字串將被標記為「強制禁止詞」。\n警告:若 Tokenizer 設定有誤,可能無法正常運作。", + "emotion": "「表情立繪會根據角色情緒顯示對應的圖片。具體的情緒由角色的回應進行分析。你必須輸入情緒名稱作為詞彙*(如:joy, happy, fear 等)*。若存在名為 **neutral** 的情緒,將作為默認情緒。至少需要三張圖片才能正常運作。」", + "imggen": "分析聊天內容後,將提示套用至 {{slot}}。", + "regexScript": "正規表達式(Regex Script)是一個自定義工具,用於將符合條件的字串由「IN」替換為「OUT」。\n\n有四種類型選項:\n\n- **修改輸入(Modify Input)**:修改使用者的輸入內容\n\n- **修改輸出(Modify Output)**:修改角色的輸出內容\n\n- **修改請求資料(Modify Request Data)**:在當前聊天資料發送時進行修改\n\n- **修改顯示(Modify Display)**:僅修改顯示的文字,不更改聊天資料\n\n「IN」必須是一個不帶標誌(flags)的正規表達式,且開頭和結尾不包含斜線。\n\n「OUT」是一個可以包含替換模式的字串。這些替換模式如下:\n\n- $$\n\n - 插入符號「$」\n\n- $&\n\n - 插入匹配到的子字串\n\n- $`\n\n - 插入匹配子字串前的部分\n\n- $1\n\n - 插入第一個匹配群組,可以替換為其他數字(例如 2、3...)\n\n- $(name)\n\n - 插入命名群組\n\n針對標誌(flags),除了原生支援的標誌之外,還可以使用下列專為進階使用者設計的標誌:\n\n- ``:將結果注入當前字串中\n- ``:將結果移到字串的頂部\n- ``:將結果移到字串的底部\n- ``:如果找不到匹配,則使用上一個匹配的結果\n- ``:設定結果的順序,數值越高顯示越靠前。「n」代表一個數字(例如 ``)。如果未設定,默認為 0。\n- ``:解析「IN」中的大括號語法\n\n若要與原生標誌結合使用,可以像這樣使用:`gi`。", + "experimental": "此為實驗性功能,可能不穩定。", + "oogaboogaURL": "如果你的 WebUI 支援舊版 API,你的 URL 應類似於 *https://.../run/textgen*。\n\n如果你的 WebUI 支援新版 API,你的 URL 應類似於 *https://.../api/v1/generate*,且將使用 API 伺服器作為主機,並在參數中添加 —api。", + "exampleMessage": "示範對話會影響角色的回應,但不會永久佔用 Token。\n\n對話格式示例:\n\n```\n\n{{user}}: hi\n{{char}}: hello\n\n{{user}}: hi\nHaruhi: hello\n```\n\n`````` 標記了一段新對話的開始。", + "creatorQuotes": "說明將顯示在初始訊息之上,用於向使用者提供角色說明。此內容不會進入提示詞中。", + "systemPrompt": "此欄位不為空時,將替換設定中的主要提示詞為此內容。", + "chatNote": "這是一個強烈影響模型行為的備註,嵌入於當前聊天中,也稱為記憶或 UJB。", + "personality": "對角色性格的簡要描述。\n\n**不建議使用此欄位,請填寫在角色描述中。**", + "scenario": "對角色情境的簡要描述。\n\n**不建議使用此欄位,請填寫在角色描述中。**", + "utilityBot": "啟用後,將忽略主要提示詞、越獄提示詞和其他提示詞。適用於工具型機器人,而非用於角色扮演。", + "loreSelective": "啟用選擇性模式後,需同時匹配關鍵字與次要關鍵字,方可啟用該條目。", + "loreRandomActivation": "啟用「使用機率條件」後,若同時符合啟用條目的其他條件,則在每次發送聊天時,該條目將依照設定的機率被使用。", + "additionalAssets": "在聊天中顯示的額外資源。\n\n - 使用 `{{raw::<資源名稱>}}` 作為路徑。\n - 使用 `{{image::<資源名稱>}}` 作為圖片。\n - 使用 `{{video::<資源名稱>}}` 作為影片。\n - 使用 `{{audio::<資源名稱>}}` 作為音訊。\n - 建議放置在背景 HTML 中。", + "superMemory": "SuperMemory 通過向 AI 提供摘要資料來增強角色的記憶能力。\n\nSuperMemory 是一個文本摘要功能,推薦使用 davinci 模型。不建議使用輔助模型,除非它是未經過濾、最大上下文長度超過 2000 Tokens,且具有良好摘要能力的模型。\n\nSuperMemory 提示詞決定了模型如何撰寫摘要。留空將使用預設提示詞,建議保持留空。\n\n完成所有設定後,你可以在角色的設定中啟用此功能。", + "replaceGlobalNote": "此欄位不為空時,將替換當前的全域備註為此內容。", + "backgroundHTML": "將 Markdown/HTML 注入到聊天畫面的背景中。\n\n你也可以使用額外資源。例如,你可以使用 {{audio::<資源名稱}} 作為背景音樂。\n\n此外,你還可以與額外資源搭配使用以下格式:\n - {{bg::<資源名稱>}}:將資源設為背景。", + "additionalText": "只有當 AI 認為有必要時,才會將該段文本添加到角色描述中。你可以在此處放置較長的文本。使用雙換行進行內容分隔。", + "charjs": "這是一段會與角色一同運行的 JavaScript。詳情請查看:https://github.com/kwaroran/RisuAI/blob/main/src/etc/example-char.js\n**出於安全原因,目前不建議使用。這些代碼不會被包含在匯出中。**", + "romanizer": "Romanizer 是一個將非羅馬字母轉換為羅馬字母的外掛程式,用於減少請求資料時的 Token。這可能會導致輸出結果與原始模型不同。如果已在聊天中使用羅馬字母,不建議啟用。", + "oaiRandomUser": "啟用後,請求中的使用者參數將被隨機 UUID 替代,並在重新整理時修改。這可以用來防止 AI 識別使用者。", + "inlayImages": "啟用後,圖片可嵌入聊天中,並且支援此功能的 AI 將能夠看到它們。", + "metrica": "Metric Systemizer 是一個外掛程式,會在請求階段將公制單位轉換為英制單位,而在輸出時轉回公制,讓使用者在介面上顯示公制單位,但效能上使用英制單位。如果在聊天中已經使用英制單位,不建議啟用。", + "lorePlus": "LoreBook+ 是一項實驗性功能。使用向量資料庫(VectorDB),而不僅是字串匹配。目的在於提供更好的機器人創建體驗和更好的匹配效能。", + "topP": "Top P 是核心採樣(Nucleus Sampling)的機率閾值,模型只會考慮總機率品質達到 top_p 的 Token 結果。", + "openAIFixer": "OpenAI Fixer 是一個用於修復 OpenAI 部分問題的外掛程式。", + "sayNothing": "啟用後,如果使用者沒有輸入聊天內容,系統會自動填入 ’say nothing‘。", + "showUnrecommended": "啟用後,將顯示不建議使用的過時設定。不建議使用這些設定。", + "imageCompression": "啟用後,在匯出角色時會壓縮圖片。如果動畫圖片無法顯示,請嘗試關閉此選項。", + "useExperimental": "啟用後,將顯示部分實驗性功能。", + "forceProxyAsOpenAI": "啟用後,使用反向代理(Reverse proxy)時將強制使用 OpenAI 格式。", + "forcePlainFetch": "啟用後,將使用瀏覽器的 Fetch API 來替代原生 HTTP 請求。這可能會導致 CORS 錯誤。", + "autoFillRequestURL": "啟用後,將自動填入請求 URL 以匹配當前模型。", + "chainOfThought": "啟用後,將在提示詞中添加思維鏈(CoT, Chain-of-Thought)提示。", + "gptVisionQuality": "此選項用於設定圖像檢測模型的品質。品質越高,檢測越準確,但會使用更多的 Token。", + "genTimes": "此設定支援模型上的重滾(reroll)回應數量。除第一則回應外,其他回應將作為快取使用,以降低成本。但若未多次重滾回應,可能增加成本。", + "requestretrys": "此選項用於設定請求失敗時的重試次數。", + "emotionPrompt": "此選項用於設定情緒檢測的提示詞。留空將使用預設提示詞。", + "removePunctuationHypa": "啟用後,將在執行 HypaMemory 前移除標點符號。", + "additionalParams": "此選項允許將附加的參數添加到請求主體(Request body)中。若要排除某些參數,可以將值設為 `{{none}}`。若要添加標頭(Request header)而非主體,可以在鍵前加上 `header::`,如 `header::Authorization`。若要將值作為 JSON,可以在值前加上 `json::`,如 `json::{\"key\":\"value\"}`。其他情況下,系統將自動判定值的類型。", + "antiClaudeOverload": "若 Claude 過載發生,叡甦會透過繼續相同的提示來阻止它,以減少過載的機率。此功能僅適用於流式回應(Streamed Responses),對非官方 API 端點可能無效。", + "triggerScript": "觸發式(Trigger)是一個自定義指令碼,在符合條件時執行。可用於修改聊天資料、執行指令、更改變數等。類型取決於觸發時的情況,也可由按鈕觸發,如 {{button::Display::TriggerName}} 或帶有 risu-trigger=\"\" 屬性的 HTML 按鈕。", + "autoContinueChat": "啟用後,當聊天不以標點符號結束時,系統將嘗試繼續對話。請勿在不使用標點符號的語言中啟用此功能。", + "combineTranslation": "啟用後,將把被 HTML 標籤分隔但屬於同一句的文本合併後進行翻譯,並在翻譯結果上重新套用「修改顯示」(Modify Display)。這有助於提高翻譯的準確性。若啟用此後介面出現異常,請關閉此選項並回報問題。", + "dynamicAssets": "啟用後,若在處理資料時找不到資源名稱,系統將使用向量搜尋(Vector Search)嘗試尋找最接近的資源名稱並進行替換。", + "dynamicAssetsEditDisplay": "啟用後,動態資源將同樣應用於「修改顯示」階段,但這可能會影響效能。", + "nickname": "設定後,將在聊天中以此暱稱取代角色名稱,並顯示於 {{char}} 和 。", + "useRegexLorebook": "啟用後,Lorebook 將改用正規表達式(Regex)搜尋,而不再使用字串匹配。格式為 /regex/flags。", + "customChainOfThought": "警告:不再建議使用思維鏈(CoT, Chain-of-Thought)切換功能。請將相關提示詞移至其他提示詞欄位。", + "customPromptTemplateToggle": "可在此處設定自定義提示詞切換功能。使用 `=` 格式,每行一個,例如:`cot=Toggle COT`。你可以在提示詞中透過 `{{getglobalvar::toggle_}}` 語法來使用這些切換功能,如:`{{getglobalvar::toggle_cot}}`。", + "defaultVariables": "可在此處設定自訂預設變數。使用 `=` 格式,每行一個。例如:`name=叡甦`,可在觸發式和 CBS 變數中使用,如:`{{getvar::A}}`、`{{setvar::A::B}}` 或 `{{? $A + 1}}`。若提示詞範本的預設變數與角色的預設變數名稱相同,系統將使用角色的預設變數。", + "lowLevelAccess": "啟用後,將開放需要高計算能力的功能,並允許通過角色中的觸發式執行 AI 模型。除非確實需要這些功能,否則不要啟用此選項。", + "triggerLLMPrompt": "這是將發送到模型的提示詞。你可以使用 `@@role user`、`@@role system`、`@@role assistant` 來設定多輪對話及角色。例如:\n```\n@@role system\nrespond as hello\n@@role assistant\nhello\n@@role user\nhi\n```", + "legacyTranslation": "啟用後,將使用舊版翻譯方法,在翻譯前對 Markdown 和引號進行預處理,而非在翻譯後處理。", + "luaHelp": "可使用 Lua 作為觸發式,並可定義 onInput、onOutput 和 onStart 函數。當使用者發送消息時,調用 onInput;當角色發送消息時,調用 onOutput;當對話開始時,調用 onStart。詳情請參閱說明文檔。", + "claudeCachingExperimental": "Claude 快取是實驗性功能,可減少模型成本。但若在不使用重滾(reroll)回應的情況下啟用,則可能增加成本。實驗性功能可能不穩定,且未來可能會有所變動。", + "urllora": "可使用模型文件的直接下載鏈接。通過類似 https://sites.google.com/site/gdocs2direct/ 的網站,從 Google Drive 等平台產生直接連接。或者使用 Civitai URL,複製 AIR(格式如 `urn:air:flux1:lora:civitai:180891@776656` 或 `civitai:180891@776656`),並貼上。", + "namespace": "命名空間(Namespace)是模組的唯一標識符,用於防止模組衝突,並與預設和其他模組等進行互動。若不確定如何填寫,建議留空。", + "moduleIntergration": "可在模組整合區域中輸入模組的命名空間(Namespace)來啟用模組。若要啟用多個模組,可用逗號分隔,例如:`module1,module2,module3`。此功能便於高級使用者通過預設靈活運用模組。", + "customCSS": "自訂 CSS 樣式。若出現問題,可使用 (Ctrl + .) 啟用或禁用。", + "betaMobileGUI": "啟用後,將在小於 800px 的螢幕上使用測試版行動介面,需重新整理頁面。", + "unrecommended": "這是一個不建議使用的設定。建議關閉。", + "jsonSchema": "JSON Schema 將在 AI 模型支援時發送給模型。\n\n然而,由於 JSON Schema 學習難度較高,在叡甦中,你可以使用 TypeScript 接口的子集來代替 JSON Schema。叡甦將在運行時進行轉換。例如,如果你想發送如下的JSON:\n\n```js\n{\n \"name\": \"叡甦\", // name 必須是叡甦,\n \"age\": 1, // age 必須是數字,\n \"icon\": \"slim\", // icon 必須是 ’slim‘ 或 ’rounded‘\n \"thoughts\": [\"Good View!\", \"Lorem\"] // thoughts 必須是字符串數組\n}\n```\n\n你可以使用以下 TypeScript 接口:\n\n```typescript\ninterface Schema {\n name: string;\n age: number;\n icon: ’slim‘|’rounded‘\n thoughts: string[]\n}\n```\n\n接口名稱不重要。欲了解更多資訊,請參閱 TypeScript 說明文件:https://www.typescriptlang.org/docs/handbook/interfaces.html 。要檢查支持的 TypeScript 子集,請查看以下內容。
支持的 TypeScript 子集\n\n支援的類型包括 `boolean`、`number`、`string` 和 `Array`。高級類型不被支援(如:單元類型、交集類型、聯合類型、可選類型、字面量類型等),除了以下幾種情況:\n\n - 原始資料型別(Primitive Type)的陣列(Array):(如 `string[]`、`Array`)\n - 字符串之間的單值類型(Unit Types):(例如 `’slim‘|’rounded‘`)\n\n 屬性必須在同一行內定義。若一行中有多個屬性,將會產生錯誤。屬性和接口名稱僅可使用拉丁字符,並在 ASCII 範圍內。屬性名稱不得以單引號或雙引號包裹。接口內部不支持嵌套。在定義屬性的行中,不能包含 `{` 或 `}`。如果想使用更高級的類型,請使用 JSON Schema。\n
", + "strictJsonSchema": "啟用後,某些模型將嚴格遵循提供的 JSON Schema。若禁用,可能會忽略 JSON Schema。", + "extractJson": "此欄位不為空時,將從回應中提取特定的 JSON 資料。例如:想從回應 `{\"response\": {\"text\": [\"hello\"]}}` 中提取 `response.text[0]`,可以填寫 `response.text.0`。", + "translatorNote": "可在此處為每個角色添加獨特的翻譯提示,但僅適用於使用 Ax. 模型進行翻譯。要啟用此功能,請在語言設定中包含 `{{slot::tnote}}`。此功能不適用群組聊天。", + "groupInnerFormat": "用於定義群組聊天中非發言者角色的格式。此欄位不為空時,將使用此格式替代預設格式。若 `Group Other Bot Role` 設定為 `assistant`,該格式也將應用於發言者。", + "groupOtherBotRole": "用於定義群組聊天中非發言者的角色。", + "chatHTML": "每個聊天插入的 HTML。\n\n可以使用CBS和特殊標籤。\n- ``:用於呈現文字的文本框\n- ``:用於顯示使用者或助理的頭像\n- ``:用於聊天編輯、翻譯等圖示按鈕\n- ``:生成訊息按鈕。", + "systemContentReplacement": "若模型不支援系統提示詞,則使用此格式取代系統提示詞內容。", + "systemRoleReplacement": "若模型不支援系統角色,將使用此角色取代系統角色。", + "summarizationPrompt": "用於摘要的提示詞。留空將使用預設提示詞。你也可以使用包含 {{slot}} 的 ChatML 格式來處理聊天數據。", + "translatorPrompt": "用於翻譯的提示詞。留空將使用默認提示。你還可以使用帶有 {{slot}} 的 ChatML 格式表示目標語言:用 {{slot::content}} 表示內容,用 {{slot::tnote}} 表示翻譯註釋。", + "translateBeforeHTMLFormatting": "啟用後,將在正規表達式和 HTML 格式化之前翻譯文本。這可能減少 Token 數,但可能破壞格式。" + }, + "setup": { + "chooseProvider": "選擇 AI 提供者", + "openaikey": "使用 OpenAI API 金鑰(推薦)", + "openaiProxy": "OpenAI 反向代理", + "setupmodelself": "其他/自行設定", + "inputApiKey": "請在此輸入 API 金鑰", + "apiKeyhelp": "可在以下取得 API 金鑰:", + "setupSelfHelp": "歡迎畫面結束後,可前往設定中自行配置。", + "theme": "選擇介面主題", + "themeDescWifulike": "不適合在行動裝置上使用", + "themeDescWifuCut": "適合在行動裝置上使用", + "themeDescClassic": "適用於所有裝置", + "texttheme": "設定文字顏色", + "inputName": "最後,請輸入你的暱稱。", + "welcome": "歡迎使用 Risu(叡甦)!我將引導你進行設定。請問我該如何稱呼你?", + "welcome2": "你好,{username}!在開始之前,我會問你一些問題,稍後可在設定中進行修改。\n\n首先,請選擇 AI 提供者。", + "openrouterProvider": "Openrouter 提供許多模型,部分免費且未經內容過濾,但品質不如 OpenAI。", + "hordeProvider": "Horde 提供免費服務,但回應時間較長且品質較低。", + "setProviderLater": "還有其他提供者,你可以稍後在設定中配置。如想稍後設定,請選擇此選項。", + "setupOpenAI": "使用 OpenAI,需要取得 API 金鑰(Key)。\n1. 前往 https://beta.openai.com/ \n2. 使用帳號登入 \n3. 前往 https://beta.openai.com/account/api-keys \n4. 點擊「Create New API Key」,並命名金鑰。 \n5. 複製該金鑰。 \n6. 返回叡甦\n7. 貼上金鑰並點擊「發送」。", + "setupClaude": "使用 Claude,需要取得一個 API 金鑰(Key)。", + "setupClaudeSteps": [ + "點擊此連結並使用 Google 帳號登錄", + "輸入你的資料並點擊「繼續」(Continue)", + "僅在第一個框中輸入任意名稱,然後點擊「建立帳號」(Create Account)", + "點擊「購買點數」(Buy Credits)", + "點擊「完成設定」(Complete Setup)", + "輸入如上圖所示的內容,但國家/地區部分需自行輸入。完成後點擊「繼續」(Continue)", + "新增付款方式後點擊「繼續」(Continue)", + "付款完成後,點擊「新增資金」(Add Funds)。最低需新增 5 美元,或設定自動儲值功能", + "新增資金後,前往控制台頁面,點擊「取得 API 金鑰」(Get API Keys)", + "點擊「建立 API 金鑰」(Create API Key)", + "輸入如上圖所示的內容後,點擊「新增」(Add)", + "複製金鑰,將其貼到此處並發送。" + ], + "setupOpenrouter": "使用 Openrouter 需要獲取 API 金鑰(Key)。 \n1. 前往 https://openrouter.ai/keys\n2. 點擊「Create Key」\n3. 任意命名金鑰名稱。\n4. 複製該金鑰。\n5. 返回叡甦\n6. 貼上金鑰並點擊「發送」。", + "allDone": "完成所有設定!請稍待片刻。", + "setupLaterMessage": "歡迎,{username}!你希望我引導你完成設定還是自行設定?", + "setupMessageOption1": "引導我完成設定", + "setupMessageOption1Desc": "推薦新使用者使用", + "setupMessageOption2": "由我自己完成設定", + "claudeDesc": "Claude 是由 Antropic 開發的 AI 模型,是 OpenAI 的競爭對手。若你希望使用非英語語言,它優於 GPT。", + "openAIDesc": "OpenAI GPT 是高品質的 AI 模型,但它付費且存在內容審核,在非英語環境下表現較弱。", + "chooseChatType": "很好!現在請選擇聊天語言。", + "chooseChatTypeOption1": "全英語", + "chooseChatTypeOption1Desc": "推薦英語使用者。AI 將使用英語進行輸入和輸出。", + "chooseChatTypeOption2": "英語處理", + "chooseChatTypeOption2Desc": "推薦非英語使用者。AI 內部使用英語處理,但輸入輸出為你的語言。", + "chooseChatTypeOption3": "無語言側重", + "chooseChatTypeOption3Desc": "AI 將使用你的語言處理,但可能會降低回應品質。", + "chooseCheapOrMemory": "除此之外,你更傾向於記憶功能還是節省成本?", + "chooseCheapOrMemoryOption1": "記憶功能", + "chooseCheapOrMemoryOption1Desc": "AI 會記住更多內容,但費用較高。", + "chooseCheapOrMemoryOption2": "省錢模式", + "chooseCheapOrMemoryOption2Desc": "AI 會記住較少內容,但費用更低。", + "chooseCheapOrMemoryOption3": "平衡", + "chooseCheapOrMemoryOption3Desc": "AI 記住的內容多於省錢模式,但不及使用記憶功能。", + "chooseCheapOrMemoryOption4": "無限制", + "chooseCheapOrMemoryOption4Desc": "AI 會記住幾乎所有內容,但費用極高。", + "finally": "最後,你是否希望使用進階工具?", + "finallyOption1": "是", + "finallyOption1Desc": "使用進階工具會使界面變得更複雜。推薦進階使用者使用。", + "finallyOption2": "否", + "finallyOption2Desc": "不使用高級工具將使界面更簡潔。推薦新使用者使用。", + "openAIProvider": "OpenAI GPT 是高品質的 AI 模型,但需付費且經內容審核。" + }, + "confirm": "確定", + "goback": "返回", + "botSettings": "機器人設定", + "model": "模型", + "apiKey": "API 金鑰", + "providerURL": "請求地址(URL)", + "providerJSON": "請求主體(JSON)", + "mainPrompt": "主要提示詞", + "jailbreakPrompt": "越獄提示詞", "globalNote": "全域備註", + "autoSuggest": "自動建議", + "tokens": "Tokens", + "maxContextSize": "最大上下文長度(Max Context Size)", + "maxResponseSize": "最大回應長度(Max Response Size)", + "temperature": "溫度(Temperature)", + "frequencyPenalty": "頻率懲罰(Frequency Penalty)", + "presensePenalty": "存在懲罰(Presence Penalty)", + "advancedSettings": "進階設定", + "advancedSettingsWarn": "警告:若不確定該選項的作用,請勿進行修改!", + "formatingOrder": "格式順序", "authorNote": "作者備註", - "lastChat": "前一次聊天", - "description": "角色描述", - "personaPrompt": "使用者提示詞", - "plain": "基礎提示詞", - "memory": "Supa/HypaMemory", - "postEverything": "系統提示詞結束" - }, - "errors": { - "toomuchtoken": "錯誤:所需的 Token 數超過了可用的最大上下文大小", - "unknownModel": "錯誤:無法識別所選的模型", - "httpError": "錯誤:請求發生錯誤:", - "noData": "無法找到檔案中的資料,或者檔案已經損毀", - "onlyOneChat": "至少需要一個聊天室", - "alreadyCharInGroup": "該群組中已經有一個同名角色。", - "noUserIcon": "請先設定你的個人頭貼。", - "emptyText": "文字內容為空。", - "wrongPassword": "密碼錯誤", - "networkFetch": "這通常是由於網路連線不穩定或伺服器故障引起的。", - "networkFetchWeb": "這可能是由 CORS 錯誤引起的。這種情況只會在使用網頁版時發生,因為瀏覽器存在一些限制。請嘗試使用本地版或其他版本的叡甦。", - "networkFetchPlain": "這可能是一次資料抓取(Fetch)錯誤。請嘗試在設定中關閉強制抓取選項。", - "requestLogRemoved": "該請求記錄已被刪除。", - "requestLogRemovedDesc": "當用戶端重新整理或加載時,該請求記錄會被刪除。" - }, - "showHelp": "顯示幫助", - "help": { - "model": "此模型是指聊天中使用的主要模型。", - "submodel": "輔助模型是一個用於分析情感圖像、產生自動建議等的模型,推薦使用 GPT-3.5。", - "oaiapikey": "OpenAI 的 API 金鑰(Key),可在 https://platform.openai.com/account/api-keys 取得。", - "mainprompt": "主要提示詞設定用於決定模型的預設行為。", - "jailbreak": "當角色中的越獄開關被啟動後,越獄提示詞將被使用。", - "globalNote": "一個對模型行為有強烈影響的備註(也稱為 UJB),適用於所有角色。", - "autoSuggest": "用於自動建議使用者回應時生成選項的提示詞。", - "formatOrder": "提示詞的排列順序:越靠下的區塊對模型的影響越大。", - "forceUrl": "此欄位不為空時,請求將被發送到你所輸入的網址。", - "tempature": "較低的數值會使角色更緊密地遵循提示詞,但會使回應更制式與機械化。\n較高的數值則會增強角色的創意表現,但回應可能會變得不穩定。", - "frequencyPenalty": "較高的數值可以避免角色在個別回應中重複使用相同的詞彙,但回應也更容易出現語意混亂。", - "presensePenalty": "較高的數值可以避免角色在整體對話中重複使用相同的詞彙,但這也可能導致回答失去一致性和穩定性。", - "sdProvider": "圖像生成提供者。", - "msgSound": "當角色回應時,播放 *叮* 的提示音", - "charDesc": "角色的簡要描述。這會影響角色的回應方式。", - "charFirstMessage": "角色的初始訊息,這會極大地影響角色的回應方式。", - "charNote": "對模型行為有強烈影響的備註,嵌入到當前角色中,也稱為 UJB。", - "toggleNsfw": "切換越獄提示詞的開關。", - "lorebook": "Lorebook 是由使用者創建的 AI 辭典,只有當上下文中包含關鍵字時 AI 才能看到它。", - "loreName": "Lorebook 的名稱,不影響 AI。", - "loreActivationKey": "當上下文中包含任一關鍵字時,該條目將被啟用,並啟動相應的提示詞。使用逗號分隔。", - "loreorder": "插入順序越高,對模型的影響力越大。在啟動大量條目時,也更不容易被截斷。", - "bias": "Bias 是一組鍵值數據,用於修改某些字串出現的機率。\n其數值範圍可以是 -100 到 100。較高的數值會使該字串更可能出現,較低的數值則降低出現機率。\n另外,在某些模型中,若將數值設為 -101,該字串將被標記為「強制禁止詞」。\n警告:若 Tokenizer 設定有誤,可能無法正常運作。", - "emotion": "「情緒影像會根據角色情緒顯示對應的圖片。具體的情緒由角色的回應進行分析。你必須輸入情緒名稱作為詞彙*(如:joy, happy, fear 等)*。若存在名為 **neutral** 的情緒,將作為默認情緒。至少需要三張圖片才能正常運作。」", - "imggen": "分析聊天內容後,將提示套用至 {{slot}}。", - "regexScript": "正規表達式(Regex Script)是一個自定義工具,用於將符合條件的字串由「IN」替換為「OUT」。\n\n有四種類型選項:\n\n- **修改輸入(Modify Input)**:修改使用者的輸入內容\n\n- **修改輸出(Modify Output)**:修改角色的輸出內容\n\n- **修改請求資料(Modify Request Data)**:在當前聊天資料發送時進行修改\n\n- **修改顯示(Modify Display)**:僅修改顯示的文字,不更改聊天資料\n\n「IN」必須是一個不帶標誌(flags)的正規表達式,且開頭和結尾不包含斜線。\n\n「OUT」是一個可以包含替換模式的字串。這些替換模式如下:\n\n- $$\n\n - 插入符號「$」\n\n- $&\n\n - 插入匹配到的子字串\n\n- $`\n\n - 插入匹配子字串前的部分\n\n- $1\n\n - 插入第一個匹配群組,可以替換為其他數字(例如 2、3...)\n\n- $(name)\n\n - 插入命名群組\n\n針對標誌(flags),除了原生支援的標誌之外,還可以使用下列專為進階使用者設計的標誌:\n\n- ``:將結果注入當前字串中\n- ``:將結果移到字串的頂部\n- ``:將結果移到字串的底部\n- ``:如果找不到匹配,則使用上一個匹配的結果\n- ``:設定結果的順序,數值越高顯示越靠前。「n」代表一個數字(例如 ``)。如果未設定,默認為 0。\n- ``:解析「IN」中的大括號語法\n\n若要與原生標誌結合使用,可以像這樣使用:`gi`。", - "experimental": "此為實驗性功能,可能不穩定。", - "oogaboogaURL": "如果你的 WebUI 支援舊版 API,你的 URL 應類似於 *https://.../run/textgen*。\n\n如果你的 WebUI 支援新版 API,你的 URL 應類似於 *https://.../api/v1/generate*,且將使用 API 伺服器作為主機,並在參數中添加 —api。", - "exampleMessage": "示範對話會影響角色的回應,但不會永久佔用 Token。\n\n對話格式示例:\n\n```\n\n{{user}}: hi\n{{char}}: hello\n\n{{user}}: hi\nHaruhi: hello\n```\n\n`````` 標記了一段新對話的開始。", - "creatorQuotes": "說明將顯示在初始訊息之上,用於向使用者提供角色說明。此內容不會進入提示詞中。", - "systemPrompt": "此欄位不為空時,將替換設定中的主要提示詞為此內容。", - "chatNote": "這是一個強烈影響模型行為的備註,嵌入於當前聊天中,也稱為記憶或 UJB。", - "personality": "對角色性格的簡要描述。\n\n**不建議使用此欄位,請填寫在角色描述中。**", - "scenario": "對角色情境的簡要描述。\n\n**不建議使用此欄位,請填寫在角色描述中。**", - "utilityBot": "啟用後,將忽略主要提示詞、越獄提示詞和其他提示詞。適用於工具型機器人,而非用於角色扮演。", - "loreSelective": "啟用選擇性模式後,需同時匹配關鍵字與次要關鍵字,方可啟用該條目。", - "loreRandomActivation": "啟用「使用機率條件」後,若同時符合啟用條目的其他條件,則在每次發送聊天時,該條目將依照設定的機率被使用。", - "additionalAssets": "在聊天中顯示的額外資源。\n\n - 使用 `{{raw::<資源名稱>}}` 作為路徑。\n - 使用 `{{image::<資源名稱>}}` 作為圖片。\n - 使用 `{{video::<資源名稱>}}` 作為影片。\n - 使用 `{{audio::<資源名稱>}}` 作為音訊。\n - 建議放置在背景 HTML 中。", - "superMemory": "SuperMemory 通過向 AI 提供摘要資料來增強角色的記憶能力。\n\nSuperMemory 是一個文本摘要功能,推薦使用 davinci 模型。不建議使用輔助模型,除非它是未經過濾、最大上下文長度超過 2000 Tokens,且具有良好摘要能力的模型。\n\nSuperMemory 提示詞決定了模型如何撰寫摘要。留空將使用預設提示詞,建議保持留空。\n\n完成所有設定後,你可以在角色的設定中啟用此功能。", - "replaceGlobalNote": "此欄位不為空時,將替換當前的全域備註為此內容。", - "backgroundHTML": "將 Markdown/HTML 注入到聊天畫面的背景中。\n\n你也可以使用額外資源。例如,你可以使用 {{audio::<資源名稱}} 作為背景音樂。\n\n此外,你還可以與額外資源搭配使用以下格式:\n - {{bg::<資源名稱>}}:將資源設為背景。", - "additionalText": "只有當 AI 認為有必要時,才會將該段文本添加到角色描述中。你可以在此處放置較長的文本。使用雙換行進行內容分隔。", - "charjs": "這是一段會與角色一同運行的 JavaScript。詳情請查看:https://github.com/kwaroran/RisuAI/blob/main/src/etc/example-char.js\n**出於安全原因,目前不建議使用。這些代碼不會被包含在匯出中。**", - "romanizer": "Romanizer 是一個將非羅馬字母轉換為羅馬字母的外掛程式,用於減少請求資料時的 Token。這可能會導致輸出結果與原始模型不同。如果已在聊天中使用羅馬字母,不建議啟用。", - "oaiRandomUser": "啟用後,請求中的使用者參數將被隨機 UUID 替代,並在重新整理時修改。這可以用來防止 AI 識別使用者。", - "inlayImages": "啟用後,圖片可嵌入聊天中,並且支援此功能的 AI 將能夠看到它們。", - "metrica": "Metric Systemizer 是一個外掛程式,會在請求階段將公制單位轉換為英制單位,而在輸出時轉回公制,讓使用者在介面上顯示公制單位,但效能上使用英制單位。如果在聊天中已經使用英制單位,不建議啟用。", - "lorePlus": "LoreBook+ 是一項實驗性功能。使用向量資料庫(VectorDB),而不僅是字串匹配。目的在於提供更好的機器人創建體驗和更好的匹配效能。", - "topP": "Top P 是核心採樣(Nucleus Sampling)的機率閾值,模型只會考慮總機率品質達到 top_p 的 Token 結果。", - "openAIFixer": "OpenAI Fixer 是一個用於修復 OpenAI 部分問題的外掛程式。", - "sayNothing": "啟用後,如果使用者沒有輸入聊天內容,系統會自動填入 ’say nothing‘。", - "showUnrecommended": "啟用後,將顯示不建議使用的過時設定。不建議使用這些設定。", - "imageCompression": "啟用後,在匯出角色時會壓縮圖片。如果動畫圖片無法顯示,請嘗試關閉此選項。", - "useExperimental": "啟用後,將顯示部分實驗性功能。", - "forceProxyAsOpenAI": "啟用後,使用反向代理(Reverse proxy)時將強制使用 OpenAI 格式。", - "forcePlainFetch": "啟用後,將使用瀏覽器的 Fetch API 來替代原生 HTTP 請求。這可能會導致 CORS 錯誤。", - "autoFillRequestURL": "啟用後,將自動填入請求 URL 以匹配當前模型。", - "chainOfThought": "啟用後,將在提示詞中添加思維鏈(CoT, Chain-of-Thought)提示。", - "gptVisionQuality": "此選項用於設定圖像檢測模型的品質。品質越高,檢測越準確,但會使用更多的 Token。", - "genTimes": "此設定支援模型上的重滾(reroll)回應數量。除第一則回應外,其他回應將作為快取使用,以降低成本。但若未多次重滾回應,可能增加成本。", - "requestretrys": "此選項用於設定請求失敗時的重試次數。", - "emotionPrompt": "此選項用於設定情緒檢測的提示詞。留空將使用預設提示詞。", - "removePunctuationHypa": "啟用後,將在執行 HypaMemory 前移除標點符號。", - "additionalParams": "此選項允許將附加的參數添加到請求主體(Request body)中。若要排除某些參數,可以將值設為 `{{none}}`。若要添加標頭(Request header)而非主體,可以在鍵前加上 `header::`,如 `header::Authorization`。若要將值作為 JSON,可以在值前加上 `json::`,如 `json::{\"key\":\"value\"}`。其他情況下,系統將自動判定值的類型。", - "antiClaudeOverload": "若 Claude 過載發生,叡甦會透過繼續相同的提示來阻止它,以減少過載的機率。此功能僅適用於流式回應(Streamed Responses),對非官方 API 端點可能無效。", - "triggerScript": "觸發式(Trigger)是一個自定義指令碼,在符合條件時執行。可用於修改聊天資料、執行指令、更改變數等。類型取決於觸發時的情況,也可由按鈕觸發,如 {{button::Display::TriggerName}} 或帶有 risu-trigger=\"\" 屬性的 HTML 按鈕。", - "autoContinueChat": "啟用後,當聊天不以標點符號結束時,系統將嘗試繼續對話。請勿在不使用標點符號的語言中啟用此功能。", - "combineTranslation": "啟用後,將把被 HTML 標籤分隔但屬於同一句的文本合併後進行翻譯,並在翻譯結果上重新套用「修改顯示」(Modify Display)。這有助於提高翻譯的準確性。若啟用此後介面出現異常,請關閉此選項並回報問題。", - "dynamicAssets": "啟用後,若在處理資料時找不到資源名稱,系統將使用向量搜尋(Vector Search)嘗試尋找最接近的資源名稱並進行替換。", - "dynamicAssetsEditDisplay": "啟用後,動態資源將同樣應用於「修改顯示」階段,但這可能會影響效能。", - "nickname": "設定後,將在聊天中以此暱稱取代角色名稱,並顯示於 {{char}} 和 。", - "useRegexLorebook": "啟用後,Lorebook 將改用正規表達式(Regex)搜尋,而不再使用字串匹配。格式為 /regex/flags。", - "customChainOfThought": "警告:不再建議使用思維鏈(CoT, Chain-of-Thought)切換功能。請將相關提示詞移至其他提示詞欄位。", - "customPromptTemplateToggle": "可在此處設定自定義提示詞切換功能。使用 `=` 格式,每行一個,例如:`cot=Toggle COT`。你可以在提示詞中透過 `{{getglobalvar::toggle_}}` 語法來使用這些切換功能,如:`{{getglobalvar::toggle_cot}}`。", - "defaultVariables": "可在此處設定自訂預設變數。使用 `=` 格式,每行一個。例如:`name=叡甦`,可在觸發式和 CBS 變數中使用,如:`{{getvar::A}}`、`{{setvar::A::B}}` 或 `{{? $A + 1}}`。若提示詞範本的預設變數與角色的預設變數名稱相同,系統將使用角色的預設變數。", - "lowLevelAccess": "啟用後,將開放需要高計算能力的功能,並允許通過角色中的觸發式執行 AI 模型。除非確實需要這些功能,否則不要啟用此選項。", - "triggerLLMPrompt": "這是將發送到模型的提示詞。你可以使用 `@@role user`、`@@role system`、`@@role assistant` 來設定多輪對話及角色。例如:\n```\n@@role system\nrespond as hello\n@@role assistant\nhello\n@@role user\nhi\n```", - "legacyTranslation": "啟用後,將使用舊版翻譯方法,在翻譯前對 Markdown 和引號進行預處理,而非在翻譯後處理。", - "luaHelp": "可使用 Lua 作為觸發式,並可定義 onInput、onOutput 和 onStart 函數。當使用者發送消息時,調用 onInput;當角色發送消息時,調用 onOutput;當對話開始時,調用 onStart。詳情請參閱說明文檔。", - "claudeCachingExperimental": "Claude 快取是實驗性功能,可減少模型成本。但若在不使用重滾(reroll)回應的情況下啟用,則可能增加成本。實驗性功能可能不穩定,且未來可能會有所變動。", - "urllora": "可使用模型文件的直接下載鏈接。通過類似 https://sites.google.com/site/gdocs2direct/ 的網站,從 Google Drive 等平台產生直接連接。或者使用 Civitai URL,複製 AIR(格式如 `urn:air:flux1:lora:civitai:180891@776656` 或 `civitai:180891@776656`),並貼上。", - "namespace": "命名空間(Namespace)是模組的唯一標識符,用於防止模組衝突,並與預設和其他模組等進行互動。若不確定如何填寫,建議留空。", - "moduleIntergration": "可在模組整合區域中輸入模組的命名空間(Namespace)來啟用模組。若要啟用多個模組,可用逗號分隔,例如:`module1,module2,module3`。此功能便於高級使用者通過預設靈活運用模組。", - "customCSS": "自訂 CSS 樣式。若出現問題,可使用 (Ctrl + .) 啟用或禁用。", - "betaMobileGUI": "啟用後,將在小於 800px 的螢幕上使用測試版行動介面,需重新整理頁面。", - "unrecommended": "這是一個不建議使用的設定。建議關閉。", - "jsonSchema": "JSON Schema 將在 AI 模型支援時發送給模型。\n\n然而,由於 JSON Schema 學習難度較高,在叡甦中,你可以使用 TypeScript 接口的子集來代替 JSON Schema。叡甦將在運行時進行轉換。例如,如果你想發送如下的JSON:\n\n```js\n{\n \"name\": \"叡甦\", // name 必須是叡甦,\n \"age\": 1, // age 必須是數字,\n \"icon\": \"slim\", // icon 必須是 ’slim‘ 或 ’rounded‘\n \"thoughts\": [\"Good View!\", \"Lorem\"] // thoughts 必須是字符串數組\n}\n```\n\n你可以使用以下 TypeScript 接口:\n\n```typescript\ninterface Schema {\n name: string;\n age: number;\n icon: ’slim‘|’rounded‘\n thoughts: string[]\n}\n```\n\n接口名稱不重要。欲了解更多資訊,請參閱 TypeScript 說明文件:https://www.typescriptlang.org/docs/handbook/interfaces.html 。要檢查支持的 TypeScript 子集,請查看以下內容。
支持的 TypeScript 子集\n\n支援的類型包括 `boolean`、`number`、`string` 和 `Array`。高級類型不被支援(如:單元類型、交集類型、聯合類型、可選類型、字面量類型等),除了以下幾種情況:\n\n - 原始資料型別(Primitive Type)的陣列(Array):(如 `string[]`、`Array`)\n - 字符串之間的單值類型(Unit Types):(例如 `’slim‘|’rounded‘`)\n\n 屬性必須在同一行內定義。若一行中有多個屬性,將會產生錯誤。屬性和接口名稱僅可使用拉丁字符,並在 ASCII 範圍內。屬性名稱不得以單引號或雙引號包裹。接口內部不支持嵌套。在定義屬性的行中,不能包含 `{` 或 `}`。如果想使用更高級的類型,請使用 JSON Schema。\n
", - "strictJsonSchema": "啟用後,某些模型將嚴格遵循提供的 JSON Schema。若禁用,可能會忽略 JSON Schema。", - "extractJson": "此欄位不為空時,將從回應中提取特定的 JSON 資料。例如:想從回應 `{\"response\": {\"text\": [\"hello\"]}}` 中提取 `response.text[0]`,可以填寫 `response.text.0`。", - "translatorNote": "可在此處為每個角色添加獨特的翻譯提示,但僅適用於使用 Ax. 模型進行翻譯。要啟用此功能,請在語言設定中包含 `{{slot::tnote}}`。此功能不適用群組聊天。", - "groupInnerFormat": "用於定義群組聊天中非發言者角色的格式。此欄位不為空時,將使用此格式替代預設格式。若 `Group Other Bot Role` 設定為 `assistant`,該格式也將應用於發言者。", - "groupOtherBotRole": "用於定義群組聊天中非發言者的角色。", - "chatHTML": "每個聊天插入的 HTML。\n\n可以使用CBS和特殊標籤。\n- ``:用於呈現文字的文本框\n- ``:用於顯示使用者或助理的頭貼\n- ``:用於聊天編輯、翻譯等圖示按鈕\n- ``:生成訊息按鈕。" - }, - "setup": { - "chooseProvider": "選擇 AI 提供者", - "openaikey": "使用 OpenAI API 金鑰(推薦)", - "openaiProxy": "OpenAI 反向代理", - "setupmodelself": "其他 / 自行設定", - "inputApiKey": "請在此輸入 API 金鑰", - "apiKeyhelp": "可在以下取得 API 金鑰:", - "setupSelfHelp": "歡迎畫面結束後,可前往設定中自行配置。", - "theme": "選擇介面主題", - "themeDescWifulike": "不適合在行動裝置上使用", - "themeDescWifuCut": "適合在行動裝置上使用", - "themeDescClassic": "適用於所有裝置", - "texttheme": "設定文字色彩", - "inputName": "最後,請輸入你的暱稱。", - "welcome": "歡迎使用叡甦!我會引導你進行設定。請問我該如何稱呼你?", - "welcome2": "你好,{username}!在開始之前,我會問你一些問題,稍後可在設定中進行修改。\n\n首先,請選擇 AI 提供者。", - "openAIProvider": "OpenAI GPT 是高品質的 AI 模型,但屬於付費並經內容審核。", - "openrouterProvider": "Openrouter 提供許多模型,部分免費且未經內容過濾,但品質不如 OpenAI。", - "hordeProvider": "Horde 提供免費服務,但回應時間較長且品質較低。", - "setProviderLater": "還有其他提供者,你可以稍後在設定中配置。如想稍後設定,請選擇此選項。", - "setupOpenAI": "使用 OpenAI 需要獲取 API 金鑰(Key)。\n1. 前往 https://beta.openai.com/ \n2. 使用帳號登入 \n3. 前往 https://beta.openai.com/account/api-keys \n4. 點擊「Create New API Key」,並命名金鑰。 \n5. 複製該金鑰。 \n6. 返回叡甦\n7. 貼上金鑰並點擊「發送」。", - "setupOpenrouter": "使用 Openrouter 需要獲取 API 金鑰(Key)。 \n1. 前往 https://openrouter.ai/keys\n2. 點擊「Create Key」\n3. 任意命名金鑰名稱。\n4. 複製該金鑰。\n5. 返回叡甦\n6. 貼上金鑰並點擊「發送」。", - "allDone": "完成所有設定!請稍待片刻。" - }, - "confirm": "確定", - "goback": "返回", - "botSettings": "機器人設定", - "model": "模型", - "apiKey": "API 金鑰", - "providerURL": "請求地址(URL)", - "providerJSON": "請求主體(JSON)", - "mainPrompt": "主要提示詞", - "jailbreakPrompt": "越獄提示詞", - "globalNote": "全域備註", - "autoSuggest": "自動建議", - "tokens": "Tokens", - "maxContextSize": "最大上下文長度(Max Context Size)", - "maxResponseSize": "最大回應長度(Max Response Size)", - "temperature": "溫度(Temperature)", - "frequencyPenalty": "頻率懲罰(Frequency Penalty)", - "presensePenalty": "存在懲罰(Presence Penalty)", - "advancedSettings": "進階設定", - "advancedSettingsWarn": "警告:若不確定該選項的作用,請勿進行修改!", - "formatingOrder": "格式順序", - "authorNote": "作者備註", - "firstMessage": "初始訊息", - "description": "描述", - "jailbreakToggle": "使用越獄提示詞", - "charIcon": "角色頭貼", - "characterDisplay": "角色展示", - "viewScreen": "額外角色畫面", - "none": "無", - "emotionImage": "情緒影像", - "noImages": "沒有圖片", - "noBias": "No Bias", - "image": "圖片", - "name": "名稱", - "emotion": "情緒名稱", - "value": "值", - "reroll": "重新生成", - "chatList": "聊天列表", - "removeChat": "確定要移除此訊息嗎?", - "loreBook": "Lorebook", - "character": "角色", - "Chat": "聊天", - "globalLoreInfo": "角色 Lorebook 適用於該角色的所有對話。", - "group": "群組", - "groupLoreInfo": "群組 Lorebook 適用於該群組的所有對話。", - "localLoreInfo": "聊天 Lorebook 僅用於此對話。", - "removeConfirm": "你確定要刪除:", - "removeConfirm2": "你**真的**確定要刪除:", - "exportConfirm": "你想要匯出此資料嗎?", - "insertOrder": "插入順序", - "activationKeys": "關鍵字", - "activationKeysInfo": "使用逗號分隔", - "prompt": "提示詞", - "loreBookDepth": "Lorebook 搜尋深度", - "loreBookToken": "Lorebook 最大 Token 數", - "removeCharacter": "刪除角色", - "removeGroup": "刪除群組", - "exportCharacter": "匯出角色", - "userSetting": "使用者設定", - "username": "你的名稱", - "userIcon": "你的頭貼", - "successExport": "已成功匯出並保存至你的下載資料夾", - "successImport": "成功匯入", - "importedCharacter": "匯入角色", - "alwaysActive": "始終啟用", - "additionalPrompt": "附加提示詞", - "descriptionPrefix": "描述前綴", - "forceReplaceUrl": "反向代理", - "emotionWarn": "正在使用輔助模型。", - "plugin": "外掛程式", - "language": "語言", - "UiLanguage": "介面語言", - "createfromScratch": "自行創建", - "importCharacter": "匯入角色", - "translator": "翻譯器", - "disabled": "關閉", - "noPluginSelected": "已選擇模型為外掛程式,但未選擇外掛。", - "text": "文字", - "UISize": "聊天文字大小", - "newVersion": "發現更新,是否進行安裝?", - "display": "顯示 & 音訊", - "useCustomBackground": "自訂背景", - "translateInput": "翻譯輸入", - "autoTranslation": "自動翻譯", - "fullscreen": "全螢幕", - "playMessage": "播放訊息音效", - "iconSize": "頭貼大小", - "createGroup": "建立群組", - "groupIcon": "群組頭貼", - "single": "單個", - "multiple": "多個", - "useCharLorebook": "使用角色 Lorebook", - "selectChar": "選擇角色", - "askLoadFirstMsg": "是否載入初始訊息?", - "theme": "介面主題", - "editOrder": "編輯順序", - "autoMode": "自動模式", - "submodel": "輔助模型", - "timeOutinSec": "超時時間(秒)", - "emotionPrompt": "情緒提示詞", - "singleView": "單角色模式", - "SpacedView": "多角色模式", - "emphasizedView": "雙角色模式", - "pluginWarn": "外掛程式可在隔離環境中運行,但安裝惡意外掛可能導致問題。", - "createGroupImg": "產生群組頭貼", - "waifuWidth": "角色對話框寬度", - "savebackup": "備份至 Google", - "loadbackup": "從 Google 讀取備份", - "files": "檔案", - "backupConfirm": "你確定要保存備份嗎?", - "backupLoadConfirm": "你確定要讀取備份嗎?所有資料將被覆蓋!", - "backupLoadConfirm2": "你**真的、真的**確定要加載備份嗎?這將會清除所有資料!", - "pasteAuthCode": "請從彈出窗口複製驗證碼並貼入:", - "others": "其他", - "presets": "預設設定", - "imageGeneration": "圖像生成", - "provider": "提供者", - "key": "金鑰(Key)", - "noData": "沒有資料", - "currentImageGeneration": "當前圖像生成數據", - "promptPreprocess": "使用提示詞預處理", - "SwipeRegenerate": "使用滑動箭頭重新產生訊息", - "instantRemove": "刪除訊息時連帶刪除後續訊息", - "instantRemoveConfirm": "你想只刪除一條訊息嗎?若選擇「否」,後續訊息也將被刪除。", - "textColor": "文字顏色", - "classicRisu": "經典 Risu", - "highcontrast": "高對比度", - "quickPreset": "可通過 Ctrl +(預設順序編號)快速切換預設。", - "requestretrys": "請求失敗時重試", - "utilityBot": "工具機器人", - "ShowLog": "顯示請求記錄", - "waifuWidth2": "角色顯示寬度", - "sayNothing": "無輸入內容時自動填入 ’say nothing‘", - "regexScript": "正規表達式(Regex)", - "type": "類型", - "editInput": "修改輸入", - "editOutput": "修改輸出", - "editProcess": "修改請求資料", - "loadLatest": "讀取最新的備份", - "loadOthers": "讀取其他備份", - "exampleMessage": "示範訊息", - "creatorNotes": "創建者備註", - "systemPrompt": "系統提示詞", - "characterNotes": "角色備註", - "personality": "性格", - "scenario": "場景", - "alternateGreetings": "Alternate Greetings", - "unrecommended": "不建議", - "chatNotes": "聊天備註", - "showUnrecommended": "顯示不建議的設定", - "altGreet": "替代初始訊息", - "scripts": "指令碼", - "settings": "設定", - "selective": "選擇性", - "SecondaryKeys": "次要關鍵字", - "useGlobalSettings": "使用全域設定", - "recursiveScanning": "遞迴掃描", - "creator": "創建者", - "CharVersion": "角色版本", - "Speech": "語音", - "ToggleSuperMemory": "啟用 SupaMemory", - "SuperMemory": "SupaMemory", - "useExperimental": "啟用實驗性功能", - "showMemoryLimit": "顯示記憶上限", - "roundIcons": "圓形頭貼", - "streaming": "即時流式傳輸", - "chatBot": "聊天機器人", - "otherBots": "其他機器人", - "user": "使用者", - "additionalAssets": "額外資源", - "editDisplay": "修改顯示", - "community": "社群", - "textBackgrounds": "自訂文本視窗顏色", - "textBorder": "文字邊框", - "textScreenRound": "圓角化文本視窗", - "textScreenBorder": "文本視窗邊框", - "ttsReadOnlyQuoted": "僅朗讀引號內容", - "ttsStop": "停止語音合成", - "askRemoval": "請求刪除", - "replaceGlobalNote": "替換全域備註", - "charLoreBook": "角色 Lorebook", - "globalLoreBook": "全域 Lorebook", - "globalRegexScript": "全域正規表達式", - "accessibility": "輔助功能", - "sendWithEnter": "使用 Enter 鍵發送", - "clickToEdit": "點擊文字進行編輯", - "setNodePassword": "設定密碼以提升安全性", - "inputNodePassword": "輸入密碼。如果忘記密碼,請刪除伺服器文件中的 save/__password.txt 並重啟伺服器。", - "simple": "基本", - "advanced": "進階", - "askReRollAutoSuggestions": "自動滾動建議", - "creatingSuggestions": "正在產生建議⋯", - "orderByOrder": "按順序對話", - "removeFromGroup": "確定要將 {{char}} 移出群組嗎?", - "talkness": "健談度", - "active": "活躍", - "loreRandomActivation": "啟用機率條件", - "activationProbability": "機率", - "shareCloud": "分享至 RisuRealm", - "hub": "RisuRealm", - "tags": "標籤", - "backgroundHTML": "背景嵌入", - "copied": "已複製", - "useChatCopy": "啟用聊天訊息複製", - "useChatSticker": "啟用聊天貼圖", - "useAdditionalAssetsPreview": "使用額外資源預覽", - "autoTranslateInput": "自動輸入翻譯", - "enterMessageForTranslateToEnglish": "輸入訊息以翻譯成英文", - "recent": "最新", - "downloads": "下載", - "trending": "熱門", - "imageCompression": "圖片壓縮", - "notLoggedIn": "尚未登入 Risu 帳號", - "googleDriveInfo": "連接至 Google Drive 以同步資料。", - "googleDriveConnection": "連接 Google Drive", - "googleDriveConnected": "已連接 Google Drive", - "SaveDataInAccount": "保存資料到帳號", - "dataSavingInAccount": "正在將資料保存到帳號", - "logout": "登出", - "loadDataFromAccount": "從帳號讀取資料", - "saveCurrentDataToAccount": "保存目前資料到帳號", - "chatAssumed": "", - "proxyAPIKey": "代理金鑰/密碼", - "proxyRequestModel": "請求模型", - "officialWiki": "官方 Wiki", - "officialWikiDesc": "歡迎查看叡甦官方的 Wiki。", - "officialDiscord": "官方 Discord", - "officialDiscordDesc": "叡甦官方的 Discord 伺服器", - "confirmRecommendedPreset": "此模型有建議設定,是否更改為建議設定?(可在輔助功能中關閉提示)", - "toggleConfirmRecommendedPreset": "模型變更時詢問是否使用建議設定", - "recommendedPreset": "使用建議設定", - "persona": "使用者資訊", - "icon": "頭貼", - "account": "帳號", - "remove": "刪除", - "creationSuccess": "創建成功", - "noweb": "此功能無法於網頁版使用。", - "createBotInternet": "使用 AI 從網路創建機器人", - "createBotInternetAlert": "請提供角色名稱及對應的系列或遊戲。", - "able": "啟用", - "assetWidth": "額外資源圖片最大寬度", - "animationSpeed": "動畫速度", - "screenshot": "截圖", - "screenshotSaved": "截圖已保存", - "inputBotGenerationPrompt": "輸入機器人生成提示詞", - "createBotAI": "使用 AI 創建原創機器人", - "createBotwithAI": "使用 AI 創建機器人", - "changeFolderName": "輸入新資料夾名稱(留空以取消)", - "cancel": "取消", - "renameFolder": "重新命名資料夾", - "changeFolderColor": "更改資料夾顏色", - "fullWordMatching": "完整單詞匹配", - "botSettingAtStart": "啟動時顯示機器人選單", - "triggerStart": "聊天發送時觸發", - "triggerInput": "使用者輸出時觸發", - "triggerOutput": "角色輸出時觸發", - "triggerManual": "僅限手動觸發", - "triggerCondVar": "如果變數為", - "triggerCondExists": "如果聊天中存在文字", - "triggerScript": "觸發式(Trigger)", - "triggerMatchRegex": "與正規表達式匹配", - "triggerMatchLoose": "寬鬆匹配", - "triggerMatchStrict": "嚴格匹配", - "searchDepth": "搜尋深度", - "equal": "等於", - "notEqual": "不等於", - "greater": "大於", - "less": "小於", - "greaterEqual": "大於或等於", - "lessEqual": "小於或等於", - "triggerEffSysPrompt": "添加系統提示詞", - "triggerEffSetVar": "修改變數", - "triggerEffImperson": "發送聊天訊息", - "triggerEffCommand": "執行指令", - "triggerEffRunTrigger": "執行觸發式", - "triggerEffStop": "停止發送提示詞", - "triggerEffCall": "呼叫觸發式", - "varableName": "變數名", - "role": "身份", - "location": "位置", - "promptstart": "提示詞開始", - "promptend": "提示詞結束", - "historyend": "聊天歷史結束", - "always": "總是", - "noEffect": "無效果", - "invaildTriggerEffect": "此效果不適用於該觸發類型。", - "operator": "運算符", - "TriggerSetToVar": "設定為變數", - "TriggerAddToVar": "增加為變數", - "TriggerSubToVar": "從變數中減去", - "TriggerMulToVar": "將變數乘以", - "TriggerDivToVar": "將變數除以", - "isNull": "尚未設定", - "ifChatIndex": "如果對話索引", - "ifRandom": "如果隨機", - "ifValue": "如果值", - "hideRealm": "隱藏 RisuRealm", - "popularityLevel": "{} 人氣", - "colorScheme": "配色方案", - "rangeStart": "範圍開始", - "rangeEnd": "範圍結束", - "untilChatEnd": "直到聊天結束", - "usePromptTemplate": "使用提示詞模板", - "specialType": "特殊類型", - "noSpecialType": "無特殊類型", - "forceProxyAsOpenAI": "強制代理格式為 OpenAI", - "promptTemplate": "提示詞模板", - "customInnerFormat": "自訂內部格式", - "innerFormat": "內部格式", - "HypaMemory": "HypaMemory", - "ToggleHypaMemory": "啟動 HypaMemory", - "resetPromptTemplateConfirm": "你真的確定要重置提示詞模板嗎?", - "emotionMethod": "情緒檢測方式", - "continueResponse": "繼續回應", - "showMenuChatList": "在選單中顯示聊天列表", - "translatorLanguage": "翻譯目標語言", - "translatorType": "翻譯器類型", - "deeplKey": "DeepL API 金鑰", - "deeplFreeKey": "DeepL 免費 API 金鑰", - "deeplXUrl": "DeepLX URL", - "deeplXToken": "DeepLX Token", - "exportPersona": "匯出使用者設定", - "importPersona": "匯入使用者設定", - "export": "匯出", - "import": "匯入", - "supporterThanks": "支持者感謝", - "supporterThanksDesc": "感謝您的支持!", - "donatorPatreonDesc": "為保護隱私,默認不會顯示在名單中。若想顯示您的暱稱,請前往叡甦的 Patreon 頁面並點擊連結按鈕。", - "useNamePrefix": "使用名稱前綴", - "textAdventureNAI": "以文字冒險形式運行", - "appendNameNAI": "附加名稱至 NAI", - "customStopWords": "自訂停止詞", - "defaultPrompt": "預設提示詞", - "additionalText": "額外提示", - "seed": "Seed", - "charjs": "角色 JavaScript", - "depthPrompt": "提示詞深度", - "largePortrait": "肖像", - "lorePlus": "LoreBook+", - "reverseProxyOobaMode": "Ooba Mode", - "joinMultiUserRoom": "加入使用者多人聊天室", - "exactTokens": "精確 Tokens", - "fixedTokens": "估算 Tokens", - "inlayViewScreen": "內嵌視窗", - "imgGenPrompt": "圖像生成提示詞", - "imgGenNegatives": "圖像生成負面提示詞", - "imgGenInstructions": "系統提示詞", - "usePlainFetchWarn": "使用 NovelAI 時請關閉此選項,避免出現 CORS 錯誤。", - "translationPrompt": "翻譯提示詞", - "translationResponseSize": "翻譯回應長度", - "webdeeplwarn": "此功能不不建議於網頁版使用,避免出現 CORS 錯誤。", - "saveBackupLocal": "本地保存備份", - "loadBackupLocal": "本機讀取備份", - "topP": "Top P", - "genTimes": "生成次數", - "cot": "思維鏈(CoT)", - "forcePlainFetch": "強制簡單抓取", - "autoFillRequestURL": "自動填入請求 URL", - "newOAIHandle": "新的 OpenAI 處理方式", - "oaiRandomUser": "放置 OAI 隨機使用者", - "inlayImage": "內嵌圖片功能", - "nativeAutomark": "實驗性本地自動標記", - "assistantPrefill": "助理預填充", - "postEndInnerFormat": "結尾內部格式", - "sendChatAsSystem": "以系統身份發送訊息", - "sendName": "非群聊時發送名稱", - "utilOverride": "實用程式覆寫", - "template": "模板", - "chatAsOriginalOnSystem": "以原始身份發送", - "exportAsDataset": "匯出保存資料為數據集", - "editTranslationDisplay": "修改翻譯顯示", - "selectModel": "選擇模型", - "autoRemoveThoughtTag": "刪除思維標記", - "customChainOfThought": "自訂思維鏈", - "maxThoughtTagDepth": "思維標記最大深度", - "openrouterFallback": "Openrouter 回退", - "openrouterMiddleOut": "Openrouter 中間輸出", - "geminiApiKey": "Gemini API 金鑰", - "removePunctuationHypa": "移除記憶標記", - "memoryLimitThickness": "記憶上限厚度", - "inputCardPassword": "輸入角色卡密碼", - "ccv2Desc": "V2 角色卡是廣泛用於聊天機器人前端的格式。", - "ccv3Desc": "V3 角色卡是用於聊天機器人前端的新型格式。", - "realmDesc": "RisuRealm 是叡甦的內容分享平台,你可以將角色分享給其他使用者。", - "rccDesc": "Risu Refined 角色卡具有密碼保護、完整性驗證等附加功能。", - "password": "密碼", - "license": "授權", - "licenseDesc": "你可以設定下載授權,限制角色卡對提示詞的使用。", - "passwordDesc": "你可以為角色卡設置密碼,防止未經授權的訪問。", - "largePersonaPortrait": "使用者肖像", - "module": "模組", - "modules": "模組", - "noModules": "尚未安裝任何模組。", - "createModule": "建立模組", - "basicInfo": "基本資料", - "moduleContent": "模組內容", - "confirmRemoveModuleFeature": "確定要移除此功能嗎?此操作無法還原。", - "editModule": "編輯模組", - "importModule": "匯入模組", - "download": "下載", - "edit": "編輯", - "enableGlobal": "全域啟用", - "chatModulesInfo": "可選擇開啟或關閉此對話的模組。", - "sideMenuRerollButton": "側欄選單重新載入", - "persistentStorage": "永久儲存", - "persistentStorageSuccess": "儲存已成功永久化", - "persistentStorageFail": "儲存未能永久化。你是否拒絕了請求,或瀏覽器不支持?", - "persistentStorageRecommended": "建議使用永久儲存", - "persistentStorageDesc": "你的瀏覽器支持永久儲存,建議啟用以提升效能和使用者體驗。", - "enable": "啟用", - "postFile": "上傳檔案", - "requestInfoInsideChat": "在聊天中顯示請求資料", - "inputTokens": "輸入 Tokens", - "outputTokens": "輸出 Tokens", - "tokenWarning": "Token 計算可能不精確,僅作參考。", - "log": "記錄", - "popularityLevelDesc": "人氣會隨著下載量等增加。3.7 人氣約等於 1 次下載。", - "additionalParams": "額外參數", - "heightMode": "高度模式", - "useAdvancedEditor": "使用進階編輯器", - "noWaitForTranslate": "不等待翻譯", - "updateRealm": "更新至 RisuRealm", - "updateRealmDesc": "你正試圖將角色更新至 RisuRealm。此操作將使角色更新至 RisuRealm,且無法還原。", - "antiClaudeOverload": "防止 Claude 超載", - "activeTabChange": "目前的標籤已停用,因其他標籤處於活動中。若要啟動此標籤,請按「確定」。", - "maxSupaChunkSize": "最大 SupaMemory 組塊大小", - "addCharacter": "新增角色", - "importFromRealm": "從 RisuRealm 選擇", - "importFromRealmDesc": "RisuRealm 提供超過 1000 位角色", - "random": "隨機", - "metaData": "Metadata", - "autoContinueMinTokens": "目標 Tokens(自動繼續)", - "autoContinueChat": "防止不完整回覆(自動繼續)", - "removeIncompleteResponse": "移除不完整句子", - "tokenizer": "Tokenizer", - "chatFormating": "聊天格式", - "useInstructPrompt": "啟用指令提示詞", - "hanuraiMemory": "HanuraiMemory", - "playground": "Playground", - "textAreaSize": "輸入欄大小", - "textAreaTextSize": "輸入欄文字大小", - "sideBarSize": "側邊欄大小", - "embedding": "嵌入", - "syntax": "語法", - "run": "執行", - "noMessage": "輸入內容以開始聊天。", - "combineTranslation": "合併翻譯", - "dynamicAssets": "動態資源", - "dynamicAssetsEditDisplay": "在介面上使用動態資源", - "longTermMemory": "長期記憶", - "grid": "網格", - "list": "列表", - "trash": "垃圾桶", - "trashDesc": "刪除的角色將移至垃圾桶,可選擇還原或永久刪除。刪除的角色將在 3 天後自動永久刪除。", - "shareExport": "分享/匯出", - "risupresetDesc": "Risupreset 是專為叡甦預設設定設計的格式。", - "risuMDesc": "RisuM 是專為叡甦模組設計的格式。", - "jsonDesc": "JSON 是一種人機都易於讀寫的格式。", - "nickname": "暱稱", - "useRegexLorebook": "使用正規表達式", - "customPromptTemplateToggle": "自訂開關", - "defaultVariables": "預設變數", - "hypaAllocatedTokens": "已分配的 Tokens", - "hypaChunkSize": "組塊大小", - "hypaV2Desc": "HypaMemory V2 是一種結合摘要資料和向量搜尋的長期記憶系統。", - "supaDesc": "SupaMemory 是一種使用摘要資料的長期記憶系統。", - "hanuraiDesc": "HanuraiMemory 是一個使用向量搜尋的記憶系統。", - "lowLevelAccess": "低層級訪問", - "resultStoredVar": "儲存結果的變數", - "triggerEffRunLLM": "執行主要模型", - "triggerEffectSendAI": "重新發送 AI", - "triggerEffCheckSim": "檢查相似度", - "triggerEffShowAlert": "顯示警告", - "normal": "正常", - "error": "錯誤", - "input": "輸入", - "select": "選擇", - "lowLevelAccessConfirm": "此內容使用低層級訪問,可直接存取 AI 模型和你的儲存資料。你確定要匯入嗎?", - "triggerLowLevelOnly": "此觸發僅適用於低層級訪問,需在角色或模組的進階設定中啟用低層級訪問。", - "truthy": "真值", - "extractRegex": "使用正規表達式提取文字", - "runImgGen": "執行圖像生成", - "cutChat": "分割聊天", - "modifyChat": "修改聊天", - "regex": "正規表達式", - "flags": "修飾詞", - "resultFormat": "結果格式", - "negPrompt": "負面提示詞", - "start": "開始", - "end": "結束", - "index": "索引", - "search": "搜尋", - "goCharacterOnImport": "導入後跳轉至角色頁面", - "format": "格式", - "v2Warning": "警告:已停止支援 V2 角色卡,可能會缺失部分資料。", - "applyModule": "套用模組", - "successApplyModule": "已成功套用模組", - "font": "字型", - "lineHeight": "行距", - "loadAutoServerBackup": "讀取伺服器自動備份", - "notCharxWarn": "角色使用多項資源,建議匯出為 CharX 格式以提升相容性。", - "noPlugins": "未安裝外掛", - "legacyTranslation": "舊版翻譯", - "clipboardSuccess": "已複製到剪貼簿", - "translateContent": "翻譯內容", - "doNotTranslate": "不進行翻譯", - "includePersonaName": "包含使用者名稱", - "hidePersonaName": "隱藏使用者名稱", - "triggerSwitchWarn": "更改觸發類型後,現有觸發將被清除。你確定要繼續嗎?", - "codeMode": "程式碼", - "blockMode": "區塊", - "helpBlock": "幫助", - "hideChatIcon": "隱藏頭貼", - "loadInternalBackup": "讀取內部備份", - "createCopy": "新建副本", - "bindPersona": "綁定使用者", - "chatOptions": "聊天選項", - "doYouWantToBindCurrentPersona": "是否綁定當前使用者至此聊天?", - "doYouWantToUnbindCurrentPersona": "是否解除此對話中的使用者綁定?", - "personaBindedSuccess": "使用者綁定已完成", - "personaUnbindedSuccess": "使用者綁定已解除", - "parameters": "參數", - "sizeAndSpeed": "大小與速度", - "useLegacyGUI": "使用舊版介面", - "claudeCachingExperimental": "Claude 快取", - "openClose": "開啟/關閉", - "hideApiKeys": "隱藏 API 金鑰", - "unformatQuotes": "禁用引號格式", - "enableDevTools": "啟用開發者工具", - "selectFile": "選擇檔案", - "namespace": "命名空間", - "moduleIntergration": "模組整合", - "previewInfo": "此預覽顯示模型處理前的提示詞。", - "miscTools": "其他工具", - "promptConvertion": "提示詞轉換", - "convertionStep1": "選擇與提示詞相關的檔案(支持 Context、Instruct 及 Sampler JSON)", - "customCSS": "自訂 CSS", - "betaMobileGUI": "測試版行動介面", - "menu": "選單", - "connectionOpen": "已開啟連線", - "connectionOpenInfo": "多人聊天室已開啟,你可以將聊天室代碼分享給其他使用者。其他使用者可在 Playground > 加入多人聊天室 > 使用代碼加入。", - "createMultiuserRoom": "新建多人聊天室", - "connectionHost": "你是聊天室的主持人。", - "connectionGuest": "你是聊天室的訪客。", - "otherUserRequesting": "其他使用者正在請求中,請稍後重試。", - "jsonSchema": "JSON Schema", - "enableJsonSchema": "啟用 Schema", - "strictJsonSchema": "嚴謹 Schema", - "extractJson": "提取 JSON", - "reloadSession": "發現更新版本的儲存資料,正在重新載入對話⋯", - "fixMarkdownNewline": "修正 Markdown 換行", - "customQuotes": "自訂引號", - "leadingSingleQuote": "上單引號", - "leadingDoubleQuote": "上雙引號", - "trailingSingleQuote": "下單引號", - "trailingDoubleQuote": "下雙引號", - "translatorNote": "翻譯器備註", - "formatGroupInSingle": "單一群組格式", - "groupInnerFormat": "非發言者內部格式", - "groupOtherBotRole": "群組內非發言者的身份", - "defineCustomGUI": "自定義界面", - "chatHTML": "聊天介面 HTML", - "logShare": "顯示分享記錄按鈕", - "preview": "預覽" -} \ No newline at end of file + "firstMessage": "初始訊息", + "description": "描述", + "jailbreakToggle": "使用越獄提示詞", + "charIcon": "角色頭像", + "characterDisplay": "角色展示", + "viewScreen": "額外角色畫面", + "none": "無", + "emotionImage": "表情立繪", + "noImages": "沒有圖片", + "noBias": "No Bias", + "image": "圖片", + "name": "名稱", + "emotion": "情緒名稱", + "value": "值", + "reroll": "重新生成", + "chatList": "聊天列表", + "removeChat": "確定要移除此訊息嗎?", + "loreBook": "Lorebook", + "character": "角色", + "Chat": "聊天", + "globalLoreInfo": "角色 Lorebook 適用於該角色的所有對話。", + "group": "群組", + "groupLoreInfo": "群組 Lorebook 適用於該群組的所有對話。", + "localLoreInfo": "聊天 Lorebook 僅用於此對話。", + "removeConfirm": "你確定要刪除:", + "removeConfirm2": "你**真的**確定要刪除:", + "exportConfirm": "你想要匯出此資料嗎?", + "insertOrder": "插入順序", + "activationKeys": "關鍵字", + "activationKeysInfo": "使用逗號分隔", + "prompt": "提示詞", + "loreBookDepth": "Lorebook 搜尋深度", + "loreBookToken": "Lorebook 最大 Token 數", + "removeCharacter": "刪除角色", + "removeGroup": "刪除群組", + "exportCharacter": "匯出角色", + "userSetting": "使用者設定", + "username": "你的名稱", + "userIcon": "你的頭像", + "successExport": "已成功匯出並保存至你的下載資料夾", + "successImport": "成功匯入", + "importedCharacter": "匯入角色", + "alwaysActive": "始終啟用", + "additionalPrompt": "附加提示詞", + "descriptionPrefix": "描述前綴", + "forceReplaceUrl": "反向代理", + "emotionWarn": "正在使用輔助模型。", + "plugin": "外掛程式", + "language": "語言", + "UiLanguage": "介面語言", + "createfromScratch": "自行創建", + "importCharacter": "匯入角色", + "translator": "翻譯器", + "disabled": "關閉", + "noPluginSelected": "已選擇模型為外掛程式,但未選擇外掛。", + "text": "文字", + "UISize": "聊天文字大小", + "newVersion": "發現更新,是否進行安裝?", + "display": "顯示 & 音訊", + "useCustomBackground": "自訂背景", + "translateInput": "翻譯輸入", + "autoTranslation": "自動翻譯", + "fullscreen": "全螢幕", + "playMessage": "播放訊息音效", + "iconSize": "頭像大小", + "createGroup": "建立群組", + "groupIcon": "群組頭像", + "single": "單個", + "multiple": "多個", + "useCharLorebook": "使用角色 Lorebook", + "selectChar": "選擇角色", + "askLoadFirstMsg": "是否載入初始訊息?", + "theme": "介面主題", + "editOrder": "編輯順序", + "autoMode": "自動模式", + "submodel": "輔助模型", + "timeOutinSec": "超時時間(秒)", + "emotionPrompt": "情緒提示詞", + "singleView": "單角色模式", + "SpacedView": "多角色模式", + "emphasizedView": "雙角色模式", + "pluginWarn": "外掛程式可在隔離環境中運行,但安裝惡意外掛可能導致問題。", + "createGroupImg": "產生群組頭像", + "waifuWidth": "角色對話框寬度", + "savebackup": "備份至 Google", + "loadbackup": "從 Google 讀取備份", + "files": "檔案", + "backupConfirm": "你確定要保存備份嗎?", + "backupLoadConfirm": "你確定要讀取備份嗎?所有資料將被覆蓋!", + "backupLoadConfirm2": "你**真的、真的**確定要加載備份嗎?這將會清除所有資料!", + "pasteAuthCode": "請從彈出窗口複製驗證碼並貼入:", + "others": "其他", + "presets": "預設設定", + "imageGeneration": "圖像生成", + "provider": "提供者", + "key": "金鑰(Key)", + "noData": "沒有資料", + "currentImageGeneration": "當前圖像生成數據", + "promptPreprocess": "使用提示詞預處理", + "SwipeRegenerate": "使用滑動箭頭重新產生訊息", + "instantRemove": "刪除訊息時連帶刪除後續訊息", + "instantRemoveConfirm": "你想只刪除一條訊息嗎?若選擇「否」,後續訊息也將被刪除。", + "textColor": "文字顏色", + "classicRisu": "經典 Risu", + "highcontrast": "高對比度", + "quickPreset": "可通過 Ctrl +(預設順序編號)快速切換預設。", + "requestretrys": "請求失敗時重試", + "utilityBot": "工具機器人", + "ShowLog": "顯示請求記錄", + "waifuWidth2": "角色顯示寬度", + "sayNothing": "無輸入內容時自動填入 ’say nothing‘", + "regexScript": "正規表達式", + "type": "類型", + "editInput": "修改輸入", + "editOutput": "修改輸出", + "editProcess": "修改請求資料", + "loadLatest": "讀取最新的備份", + "loadOthers": "讀取其他備份", + "exampleMessage": "示範訊息", + "creatorNotes": "創建者備註", + "systemPrompt": "系統提示詞", + "characterNotes": "角色備註", + "personality": "性格", + "scenario": "場景", + "alternateGreetings": "備選問候語", + "unrecommended": "不建議", + "chatNotes": "聊天備註", + "showUnrecommended": "顯示不建議的設定", + "altGreet": "備選初始訊息", + "scripts": "指令碼", + "settings": "設定", + "selective": "選擇性", + "SecondaryKeys": "次要關鍵字", + "useGlobalSettings": "使用全域設定", + "recursiveScanning": "遞迴掃描", + "creator": "創建者", + "CharVersion": "角色版本", + "Speech": "語音", + "ToggleSuperMemory": "啟用 SupaMemory", + "SuperMemory": "SupaMemory", + "useExperimental": "啟用實驗性功能", + "showMemoryLimit": "顯示記憶上限", + "roundIcons": "圓形頭像", + "streaming": "即時串流傳輸", + "chatBot": "聊天機器人", + "otherBots": "其他機器人", + "user": "使用者", + "additionalAssets": "額外資源", + "editDisplay": "修改顯示", + "community": "社群", + "textBackgrounds": "自訂文本視窗顏色", + "textBorder": "文字邊框", + "textScreenRound": "圓角化文本視窗", + "textScreenBorder": "文本視窗邊框", + "ttsReadOnlyQuoted": "僅朗讀引號內容", + "ttsStop": "停止語音合成", + "askRemoval": "請求刪除", + "replaceGlobalNote": "替換全域備註", + "charLoreBook": "角色 Lorebook", + "globalLoreBook": "全域 Lorebook", + "globalRegexScript": "全域正規表達式", + "accessibility": "輔助功能", + "sendWithEnter": "使用 Enter 鍵發送", + "clickToEdit": "點擊文字進行編輯", + "setNodePassword": "設定密碼以提升安全性", + "inputNodePassword": "輸入密碼。如果忘記密碼,請刪除伺服器文件中的 save/__password.txt 並重啟伺服器。", + "simple": "基本", + "advanced": "進階", + "askReRollAutoSuggestions": "自動滾動建議", + "creatingSuggestions": "正在產生建議⋯", + "orderByOrder": "按順序對話", + "removeFromGroup": "確定要將 {{char}} 移出群組嗎?", + "talkness": "健談度", + "active": "活躍", + "loreRandomActivation": "啟用機率條件", + "activationProbability": "機率", + "shareCloud": "分享至 RisuRealm", + "hub": "RisuRealm", + "tags": "標籤", + "backgroundHTML": "背景嵌入", + "copied": "已複製", + "useChatCopy": "啟用聊天訊息複製", + "useChatSticker": "啟用聊天貼圖", + "useAdditionalAssetsPreview": "使用額外資源預覽", + "autoTranslateInput": "自動輸入翻譯", + "enterMessageForTranslateToEnglish": "輸入訊息以翻譯成英文", + "recent": "最新", + "downloads": "下載", + "trending": "熱門", + "imageCompression": "圖片壓縮", + "notLoggedIn": "尚未登入 Risu 帳號", + "googleDriveInfo": "連接至 Google Drive 以同步資料。", + "googleDriveConnection": "連接 Google Drive", + "googleDriveConnected": "已連接 Google Drive", + "SaveDataInAccount": "保存資料到帳號", + "dataSavingInAccount": "正在將資料保存到帳號", + "logout": "登出", + "loadDataFromAccount": "從帳號讀取資料", + "saveCurrentDataToAccount": "保存目前資料到帳號", + "chatAssumed": "", + "proxyAPIKey": "代理金鑰/密碼", + "proxyRequestModel": "請求模型", + "officialWiki": "官方 Wiki", + "officialWikiDesc": "歡迎查看叡甦官方的 Wiki。", + "officialDiscord": "官方 Discord", + "officialDiscordDesc": "叡甦官方的 Discord 伺服器", + "confirmRecommendedPreset": "此模型有建議設定,是否更改為建議設定?(可在輔助功能中關閉提示)", + "toggleConfirmRecommendedPreset": "模型變更時詢問是否使用建議設定", + "recommendedPreset": "使用建議設定", + "persona": "使用者資訊", + "icon": "頭像", + "account": "帳號", + "remove": "刪除", + "creationSuccess": "創建成功", + "noweb": "此功能無法於網頁版使用。", + "createBotInternet": "使用 AI 從網路創建機器人", + "createBotInternetAlert": "請提供角色名稱及對應的系列或遊戲。", + "able": "啟用", + "assetWidth": "額外資源圖片最大寬度", + "animationSpeed": "動畫速度", + "screenshot": "截圖", + "screenshotSaved": "截圖已保存", + "inputBotGenerationPrompt": "輸入機器人生成提示詞", + "createBotAI": "使用 AI 創建原創機器人", + "createBotwithAI": "使用 AI 創建機器人", + "changeFolderName": "輸入新資料夾名稱(留空以取消)", + "cancel": "取消", + "renameFolder": "重新命名資料夾", + "changeFolderColor": "更改資料夾顏色", + "fullWordMatching": "完整單詞匹配", + "botSettingAtStart": "啟動時顯示機器人選單", + "triggerStart": "聊天發送時觸發", + "triggerInput": "使用者輸出時觸發", + "triggerOutput": "角色輸出時觸發", + "triggerManual": "僅限手動觸發", + "triggerCondVar": "如果變數為", + "triggerCondExists": "如果聊天中存在文字", + "triggerScript": "觸發式", + "triggerMatchRegex": "與正規表達式匹配", + "triggerMatchLoose": "寬鬆匹配", + "triggerMatchStrict": "嚴格匹配", + "searchDepth": "搜尋深度", + "equal": "等於", + "notEqual": "不等於", + "greater": "大於", + "less": "小於", + "greaterEqual": "大於或等於", + "lessEqual": "小於或等於", + "triggerEffSysPrompt": "添加系統提示詞", + "triggerEffSetVar": "修改變數", + "triggerEffImperson": "發送聊天訊息", + "triggerEffCommand": "執行指令", + "triggerEffRunTrigger": "執行觸發式", + "triggerEffStop": "停止發送提示詞", + "triggerEffCall": "呼叫觸發式", + "varableName": "變數名", + "role": "身份", + "location": "位置", + "promptstart": "提示詞開始", + "promptend": "提示詞結束", + "historyend": "聊天歷史結束", + "always": "總是", + "noEffect": "無效果", + "invaildTriggerEffect": "此效果不適用於該觸發類型。", + "operator": "運算符", + "TriggerSetToVar": "設定為變數", + "TriggerAddToVar": "增加為變數", + "TriggerSubToVar": "從變數中減去", + "TriggerMulToVar": "將變數乘以", + "TriggerDivToVar": "將變數除以", + "isNull": "尚未設定", + "ifChatIndex": "如果對話索引", + "ifRandom": "如果隨機", + "ifValue": "如果值", + "hideRealm": "隱藏 RisuRealm", + "popularityLevel": "{} 人氣", + "colorScheme": "配色方案", + "rangeStart": "範圍開始", + "rangeEnd": "範圍結束", + "untilChatEnd": "直到聊天結束", + "usePromptTemplate": "使用提示詞模板", + "specialType": "特殊類型", + "noSpecialType": "無特殊類型", + "forceProxyAsOpenAI": "強制代理格式為 OpenAI", + "promptTemplate": "提示詞模板", + "customInnerFormat": "自訂內部格式", + "innerFormat": "內部格式", + "HypaMemory": "HypaMemory", + "ToggleHypaMemory": "啟動 HypaMemory", + "resetPromptTemplateConfirm": "你真的確定要重置提示詞模板嗎?", + "emotionMethod": "情緒檢測方式", + "continueResponse": "繼續回應", + "showMenuChatList": "在選單中顯示聊天列表", + "translatorLanguage": "翻譯目標語言", + "translatorType": "翻譯器類型", + "deeplKey": "DeepL API 金鑰", + "deeplFreeKey": "DeepL 免費 API 金鑰", + "deeplXUrl": "DeepLX URL", + "deeplXToken": "DeepLX Token", + "exportPersona": "匯出使用者設定", + "importPersona": "匯入使用者設定", + "export": "匯出", + "import": "匯入", + "supporterThanks": "支持者感謝", + "supporterThanksDesc": "感謝您的支持!", + "donatorPatreonDesc": "為保護隱私,默認不會顯示在名單中。若想顯示您的暱稱,請前往叡甦的 Patreon 頁面並點擊連結按鈕。", + "useNamePrefix": "使用名稱前綴", + "textAdventureNAI": "以文字冒險形式運行", + "appendNameNAI": "附加名稱至 NAI", + "customStopWords": "自訂停止詞", + "defaultPrompt": "預設提示詞", + "additionalText": "額外提示", + "seed": "Seed", + "charjs": "角色 JavaScript", + "depthPrompt": "提示詞深度", + "largePortrait": "肖像", + "lorePlus": "LoreBook+", + "reverseProxyOobaMode": "Ooba Mode", + "joinMultiUserRoom": "加入使用者多人聊天室", + "exactTokens": "精確 Tokens", + "fixedTokens": "估算 Tokens", + "inlayViewScreen": "內嵌視窗", + "imgGenPrompt": "圖像生成提示詞", + "imgGenNegatives": "圖像生成負面提示詞", + "imgGenInstructions": "系統提示詞", + "usePlainFetchWarn": "使用 NovelAI 時請關閉此選項,避免出現 CORS 錯誤。", + "translationPrompt": "翻譯提示詞", + "translationResponseSize": "翻譯回應長度", + "webdeeplwarn": "此功能不不建議於網頁版使用,避免出現 CORS 錯誤。", + "saveBackupLocal": "本地保存備份", + "loadBackupLocal": "本機讀取備份", + "topP": "Top P", + "genTimes": "生成次數", + "cot": "思維鏈(CoT)", + "forcePlainFetch": "強制簡單抓取", + "autoFillRequestURL": "自動填入請求 URL", + "newOAIHandle": "新的 OpenAI 處理方式", + "oaiRandomUser": "放置 OAI 隨機使用者", + "inlayImage": "內嵌圖片功能", + "nativeAutomark": "實驗性本地自動標記", + "assistantPrefill": "助理預填充", + "postEndInnerFormat": "結尾內部格式", + "sendChatAsSystem": "以系統身份發送訊息", + "sendName": "非群聊時發送名稱", + "utilOverride": "實用程式覆寫", + "template": "模板", + "chatAsOriginalOnSystem": "以原始身份發送", + "exportAsDataset": "匯出保存資料為數據集", + "editTranslationDisplay": "修改翻譯顯示", + "selectModel": "選擇模型", + "autoRemoveThoughtTag": "刪除思維標記", + "customChainOfThought": "自訂思維鏈", + "maxThoughtTagDepth": "思維標記最大深度", + "openrouterFallback": "Openrouter 回退", + "openrouterMiddleOut": "Openrouter 中間輸出", + "geminiApiKey": "Gemini API 金鑰", + "removePunctuationHypa": "移除記憶標記", + "memoryLimitThickness": "記憶上限厚度", + "inputCardPassword": "輸入角色卡密碼", + "ccv2Desc": "V2 角色卡是廣泛用於聊天機器人前端的格式。", + "ccv3Desc": "V3 角色卡是用於聊天機器人前端的新型格式。", + "realmDesc": "RisuRealm 是叡甦的內容分享平台,你可以將角色分享給其他使用者。", + "rccDesc": "Risu Refined 角色卡具有密碼保護、完整性驗證等附加功能。", + "password": "密碼", + "license": "授權", + "licenseDesc": "你可以設定下載授權,限制角色卡對提示詞的使用。", + "passwordDesc": "你可以為角色卡設置密碼,防止未經授權的訪問。", + "largePersonaPortrait": "使用者肖像", + "module": "模組", + "modules": "模組", + "noModules": "尚未安裝任何模組。", + "createModule": "建立模組", + "basicInfo": "基本資料", + "moduleContent": "模組內容", + "confirmRemoveModuleFeature": "確定要移除此功能嗎?此操作無法還原。", + "editModule": "編輯模組", + "importModule": "匯入模組", + "download": "下載", + "edit": "編輯", + "enableGlobal": "全域啟用", + "chatModulesInfo": "可選擇開啟或關閉此對話的模組。", + "sideMenuRerollButton": "側欄選單重新載入", + "persistentStorage": "永久儲存", + "persistentStorageSuccess": "儲存已成功永久化", + "persistentStorageFail": "儲存未能永久化。你是否拒絕了請求,或瀏覽器不支持?", + "persistentStorageRecommended": "建議使用永久儲存", + "persistentStorageDesc": "你的瀏覽器支持永久儲存,建議啟用以提升效能和使用者體驗。", + "enable": "啟用", + "postFile": "上傳檔案", + "requestInfoInsideChat": "在聊天中顯示請求資料", + "inputTokens": "輸入 Tokens", + "outputTokens": "輸出 Tokens", + "tokenWarning": "Token 計算可能不精確,僅作參考。", + "log": "記錄", + "popularityLevelDesc": "人氣會隨著下載量等增加。3.7 人氣約等於 1 次下載。", + "additionalParams": "額外參數", + "heightMode": "高度模式", + "useAdvancedEditor": "使用進階編輯器", + "noWaitForTranslate": "不等待翻譯", + "updateRealm": "更新至 RisuRealm", + "updateRealmDesc": "你正試圖將角色更新至 RisuRealm。此操作將使角色更新至 RisuRealm,且無法還原。", + "antiClaudeOverload": "防止 Claude 超載", + "activeTabChange": "目前的標籤已停用,因其他標籤處於活動中。若要啟動此標籤,請按「確定」。", + "maxSupaChunkSize": "最大 SupaMemory Chunk 大小", + "addCharacter": "新增角色", + "importFromRealm": "從 RisuRealm 選擇", + "importFromRealmDesc": "RisuRealm 提供超過 1000 位角色", + "random": "隨機", + "metaData": "Metadata", + "autoContinueMinTokens": "目標 Tokens(自動繼續)", + "autoContinueChat": "防止不完整回覆(自動繼續)", + "removeIncompleteResponse": "移除不完整句子", + "tokenizer": "Tokenizer", + "chatFormating": "聊天格式", + "useInstructPrompt": "啟用指令提示詞", + "hanuraiMemory": "HanuraiMemory", + "playground": "Playground", + "textAreaSize": "輸入欄大小", + "textAreaTextSize": "輸入欄文字大小", + "sideBarSize": "側邊欄大小", + "embedding": "嵌入", + "syntax": "語法", + "run": "執行", + "noMessage": "輸入內容以開始聊天。", + "combineTranslation": "合併翻譯", + "dynamicAssets": "動態資源", + "dynamicAssetsEditDisplay": "在介面上使用動態資源", + "longTermMemory": "長期記憶", + "grid": "網格", + "list": "列表", + "trash": "垃圾桶", + "trashDesc": "刪除的角色將移至垃圾桶,可選擇還原或永久刪除。刪除的角色將在 3 天後自動永久刪除。", + "shareExport": "分享/匯出", + "risupresetDesc": "Risupreset 是專為叡甦預設設定設計的格式。", + "risuMDesc": "RisuM 是專為叡甦模組設計的格式。", + "jsonDesc": "JSON 是一種人機都易於讀寫的格式。", + "nickname": "暱稱", + "useRegexLorebook": "使用正規表達式", + "customPromptTemplateToggle": "自訂開關", + "defaultVariables": "預設變數", + "hypaAllocatedTokens": "已分配的 Tokens", + "hypaChunkSize": "Chunk 大小", + "hypaV2Desc": "HypaMemory V2 是一種結合摘要資料和向量搜尋的長期記憶系統。", + "supaDesc": "SupaMemory 是一種使用摘要資料的長期記憶系統。", + "hanuraiDesc": "HanuraiMemory 是一個使用向量搜尋的記憶系統。", + "lowLevelAccess": "低層級訪問", + "resultStoredVar": "儲存結果的變數", + "triggerEffRunLLM": "執行主要模型", + "triggerEffectSendAI": "重新發送 AI", + "triggerEffCheckSim": "檢查相似度", + "triggerEffShowAlert": "顯示警告", + "normal": "正常", + "error": "錯誤", + "input": "輸入", + "select": "選擇", + "lowLevelAccessConfirm": "此內容使用低層級訪問,可直接存取 AI 模型和你的儲存資料。你確定要匯入嗎?", + "triggerLowLevelOnly": "此觸發僅適用於低層級訪問,需在角色或模組的進階設定中啟用低層級訪問。", + "truthy": "真值", + "extractRegex": "使用正規表達式提取文字", + "runImgGen": "執行圖像生成", + "cutChat": "分割聊天", + "modifyChat": "修改聊天", + "regex": "正規表達式", + "flags": "修飾詞", + "resultFormat": "結果格式", + "negPrompt": "負面提示詞", + "start": "開始", + "end": "結束", + "index": "索引", + "search": "搜尋", + "goCharacterOnImport": "導入後跳轉至角色頁面", + "format": "格式", + "v2Warning": "警告:已停止支援 V2 角色卡,可能會缺失部分資料。", + "applyModule": "套用模組", + "successApplyModule": "已成功套用模組", + "font": "字型", + "lineHeight": "行距", + "loadAutoServerBackup": "讀取伺服器自動備份", + "notCharxWarn": "角色使用多項資源,建議匯出為 CharX 格式以提升相容性。", + "noPlugins": "未安裝外掛", + "legacyTranslation": "舊版翻譯", + "clipboardSuccess": "已複製到剪貼簿", + "translateContent": "翻譯內容", + "doNotTranslate": "不進行翻譯", + "includePersonaName": "包含使用者名稱", + "hidePersonaName": "隱藏使用者名稱", + "triggerSwitchWarn": "更改觸發類型後,現有觸發將被清除。你確定要繼續嗎?", + "codeMode": "程式碼", + "blockMode": "區塊", + "helpBlock": "幫助", + "hideChatIcon": "隱藏頭像", + "loadInternalBackup": "讀取內部備份", + "createCopy": "新建副本", + "bindPersona": "綁定使用者", + "chatOptions": "聊天選項", + "doYouWantToBindCurrentPersona": "是否綁定當前使用者至此聊天?", + "doYouWantToUnbindCurrentPersona": "是否解除此對話中的使用者綁定?", + "personaBindedSuccess": "使用者綁定已完成", + "personaUnbindedSuccess": "使用者綁定已解除", + "parameters": "參數", + "sizeAndSpeed": "大小與速度", + "useLegacyGUI": "使用舊版介面", + "claudeCachingExperimental": "Claude 快取", + "openClose": "開啟/關閉", + "hideApiKeys": "隱藏 API 金鑰", + "unformatQuotes": "禁用引號格式", + "enableDevTools": "啟用開發者工具", + "selectFile": "選擇檔案", + "namespace": "命名空間", + "moduleIntergration": "模組整合", + "previewInfo": "此預覽顯示模型處理前的提示詞。", + "miscTools": "其他工具", + "promptConvertion": "提示詞轉換", + "convertionStep1": "選擇與提示詞相關的檔案(支持 Context、Instruct 及 Sampler JSON)", + "customCSS": "自訂 CSS", + "betaMobileGUI": "測試版行動介面", + "menu": "選單", + "connectionOpen": "已開啟連線", + "connectionOpenInfo": "多人聊天室已開啟,你可以將聊天室代碼分享給其他使用者。其他使用者可在 Playground > 加入多人聊天室 > 使用代碼加入。", + "createMultiuserRoom": "新建多人聊天室", + "connectionHost": "你是聊天室的主持人。", + "connectionGuest": "你是聊天室的訪客。", + "otherUserRequesting": "其他使用者正在請求中,請稍後重試。", + "jsonSchema": "JSON Schema", + "enableJsonSchema": "Enable Schema", + "strictJsonSchema": "Strict Schema", + "extractJson": "提取 JSON", + "reloadSession": "發現更新版本的儲存資料,正在重新載入對話⋯", + "fixMarkdownNewline": "修正 Markdown 換行", + "customQuotes": "自訂引號", + "leadingSingleQuote": "上單引號", + "leadingDoubleQuote": "上雙引號", + "trailingSingleQuote": "下單引號", + "trailingDoubleQuote": "下雙引號", + "translatorNote": "翻譯器備註", + "formatGroupInSingle": "單一群組格式", + "groupInnerFormat": "非發言者內部格式", + "groupOtherBotRole": "群組內非發言者的身份", + "defineCustomGUI": "自定義界面", + "chatHTML": "聊天介面 HTML", + "logShare": "顯示分享記錄按鈕", + "preview": "預覽", + "recommended": "推薦", + "newChat": "新對話", + "predictedOutput": "預測輸出", + "systemContentReplacement": "替換系統內容", + "systemRoleReplacement": "替換系統角色", + "seperateParameters": "分離參數", + "seperateParametersEnabled": "啟用分離參數", + "summarizationPrompt": "摘要提示詞", + "translatorPrompt": "翻譯提示詞", + "translateBeforeHTMLFormatting": "於 HTML 格式化前翻譯", + "retranslate": "重新翻譯", + "loading": "載入中" +} From d12ab8ead108a46c99ad51d2ff38d60d23265129 Mon Sep 17 00:00:00 2001 From: kwaroran Date: Sat, 30 Nov 2024 02:17:03 +0900 Subject: [PATCH 174/175] Add lightningRealmImport --- src/lib/Setting/Pages/AdvancedSettings.svelte | 7 +++ src/ts/characterCards.ts | 55 ++++++++++++++++++- src/ts/storage/database.svelte.ts | 1 + 3 files changed, 61 insertions(+), 2 deletions(-) diff --git a/src/lib/Setting/Pages/AdvancedSettings.svelte b/src/lib/Setting/Pages/AdvancedSettings.svelte index 22c07940..0bdea6f6 100644 --- a/src/lib/Setting/Pages/AdvancedSettings.svelte +++ b/src/lib/Setting/Pages/AdvancedSettings.svelte @@ -134,6 +134,13 @@
+{#if DBState.db?.account?.useSync} +
+ + + +
+{/if} {#if DBState.db.dynamicAssets}
diff --git a/src/ts/characterCards.ts b/src/ts/characterCards.ts index 0cb309fe..c895d90a 100644 --- a/src/ts/characterCards.ts +++ b/src/ts/characterCards.ts @@ -44,6 +44,7 @@ export async function importCharacter() { export async function importCharacterProcess(f:{ name: string; data: Uint8Array|File|ReadableStream + lightningRealmImport?:boolean }) { if(f.name.endsWith('json')){ if(f.data instanceof ReadableStream){ @@ -123,6 +124,9 @@ export async function importCharacterProcess(f:{ returnTrimed: true }) const assets:{[key:string]:string} = {} + let queueFetch:Promise[] = [] + let queueFetchKey:string[] = [] + let queueFetchData:Buffer[] = [] for await (const chunk of readGenerator){ console.log(chunk) if(!chunk){ @@ -149,10 +153,53 @@ export async function importCharacterProcess(f:{ const assetIndex = chunk.key.replace('chara-ext-asset_:', '').replace('chara-ext-asset_', '') alertWait('Loading... (Reading Asset ' + assetIndex + ')' ) const assetData = Buffer.from(chunk.value, 'base64') + if(db.account?.useSync && f.lightningRealmImport){ + const id = await hasher(assetData) + const xid = 'assets/' + id + '.png' + queueFetchKey.push(assetIndex) + queueFetchData.push(assetData) + queueFetch.push(fetch('https://sv.risuai.xyz/rs/' + xid)) + assets[assetIndex] = 'xid:' + xid + if(queueFetch.length > 10){ + const res = await Promise.all(queueFetch) + for(let i=0;i 0){ + const res = await Promise.all(queueFetch) + for(let i=0;i Date: Sat, 30 Nov 2024 02:20:01 +0900 Subject: [PATCH 175/175] Bump version to 140.1.0 --- src-tauri/tauri.conf.json | 2 +- src/ts/storage/database.svelte.ts | 2 +- version.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 11fcebb9..8b4e30ae 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -29,7 +29,7 @@ }, "productName": "RisuAI", "mainBinaryName": "RisuAI", - "version": "140.0.3", + "version": "140.1.0", "identifier": "co.aiclient.risu", "plugins": { "updater": { diff --git a/src/ts/storage/database.svelte.ts b/src/ts/storage/database.svelte.ts index 17dde10e..cb3187a1 100644 --- a/src/ts/storage/database.svelte.ts +++ b/src/ts/storage/database.svelte.ts @@ -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 = "140.0.3" +export let appVer = "140.1.0" export let webAppSubVer = '' diff --git a/version.json b/version.json index cb3dac75..410941cf 100644 --- a/version.json +++ b/version.json @@ -1 +1 @@ -{"version":"140.0.3"} \ No newline at end of file +{"version":"140.1.0"} \ No newline at end of file