From fc75e61974285ef81f24f74613c00d7021019298 Mon Sep 17 00:00:00 2001 From: kwaroran Date: Fri, 12 May 2023 23:40:34 +0900 Subject: [PATCH] [feat] added spec v2 import/export --- src/ts/characterCards.ts | 191 +++++++++++++++++++++++++++++++++++++-- src/ts/database.ts | 8 ++ 2 files changed, 193 insertions(+), 6 deletions(-) diff --git a/src/ts/characterCards.ts b/src/ts/characterCards.ts index 3d9c9a31..0e0bd428 100644 --- a/src/ts/characterCards.ts +++ b/src/ts/characterCards.ts @@ -1,6 +1,6 @@ import { get } from "svelte/store" -import { alertConfirm, alertError, alertNormal, alertStore } from "./alert" -import { DataBase, defaultSdDataFunc, type character, saveImage, setDatabase, type customscript } from "./database" +import { alertConfirm, alertError, alertNormal, alertSelect, alertStore } from "./alert" +import { DataBase, defaultSdDataFunc, type character, saveImage, setDatabase, type customscript, type loreSettings, type loreBook } from "./database" import { checkNullish, selectSingleFile, sleep } from "./util" import { language } from "src/lang" import { encode as encodeMsgpack, decode as decodeMsgpack } from "@msgpack/msgpack"; @@ -11,7 +11,6 @@ import { characterFormatUpdate } from "./characters" import { downloadFile, readImage } from "./globalApi" import { cloneDeep } from "lodash" -type CharacterBook = null export async function importCharacter() { try { @@ -198,7 +197,11 @@ function convertOldTavernAndJSON(charaData:OldTavernChar, imgp:string|undefined export async function exportChar(charaID:number) { const db = get(DataBase) - let char:character = JSON.parse(JSON.stringify(db.characters[charaID])) + let char = cloneDeep(db.characters[charaID]) + + if(char.type === 'group'){ + return + } if(!char.image){ alertError('Image Required') @@ -209,6 +212,12 @@ export async function exportChar(charaID:number) { return } + const sel = await alertSelect(['Export as Spec V2','Export as Old RisuCard']) + if(sel === '0'){ + exportSpecV2(char) + return + } + alertStore.set({ type: 'wait', msg: 'Loading...' @@ -289,6 +298,7 @@ async function importSpecv2(card:CharacterCardV2, img?:Uint8Array):Promise r.trim()), + secondary_keys: lore.selective ? lore.secondkey.split(',').map(r => r.trim()) : undefined, + content: lore.content, + extensions: lore.extentions ?? {}, + enabled: true, + insertion_order: lore.insertorder, + constant: lore.alwaysActive, + selective:lore.selective, + name: lore.comment, + comment: lore.comment + }) + } + + const card:CharacterCardV2 = { + spec: "chara_card_v2", + spec_version: "2.0", + data: { + name: char.name, + description: char.desc, + personality: char.personality, + scenario: char.scenario, + first_mes: char.firstMessage, + mes_example: char.exampleMessage, + creator_notes: char.creatorNotes, + system_prompt: char.systemPrompt, + post_history_instructions: char.postHistoryInstructions, + alternate_greetings: char.alternateGreetings, + character_book: { + scan_depth: char.loreSettings?.scanDepth, + token_budget: char.loreSettings?.tokenBudget, + recursive_scanning: char.loreSettings?.recursiveScanning, + extensions: char.loreExt ?? {}, + entries: [] + }, + tags: char.additionalData?.tag ?? [], + creator: char.additionalData?.creator ?? '', + character_version: char.additionalData?.character_version ?? 0, + extensions: { + risuai: { + emotions: char.emotionImages, + bias: char.bias, + viewScreen: char.viewScreen, + customScripts: char.customscript, + utilityBot: char.utilityBot, + sdData: char.sdData + } + } + } + } + + + if(card.data.extensions.risuai.emotions && card.data.extensions.risuai.emotions.length > 0){ + for(let i=0;i:"/\\|?*\.\,]/g, "")}_export.png`, img) + + alertNormal(language.successExport) + + } + catch(e){ + alertError(`${e}`) + } +} + type CharacterCardV2 = { spec: 'chara_card_v2' @@ -408,3 +557,33 @@ interface OldTavernChar{ scenario: "" talkativeness: "0.5" } +type CharacterBook = { + name?: string + description?: string + scan_depth?: number // agnai: "Memory: Chat History Depth" + token_budget?: number // agnai: "Memory: Context Limit" + recursive_scanning?: boolean // no agnai equivalent. whether entry content can trigger other entries + extensions: Record + entries: Array + } + +interface charBookEntry{ + keys: Array + content: string + extensions: Record + enabled: boolean + insertion_order: number // if two entries inserted, lower "insertion order" = inserted higher + + // FIELDS WITH NO CURRENT EQUIVALENT IN SILLY + name?: string // not used in prompt engineering + priority?: number // if token budget reached, lower priority value = discarded first + + // FIELDS WITH NO CURRENT EQUIVALENT IN AGNAI + id?: number // not used in prompt engineering + comment?: string // not used in prompt engineering + selective?: boolean // if `true`, require a key from both `keys` and `secondary_keys` to trigger the entry + secondary_keys?: Array // see field `selective`. ignored if selective == false + constant?: boolean // if true, always inserted in the prompt (within budget limit) + position?: 'before_char' | 'after_char' // whether the entry is placed before or after the character defs + +} \ No newline at end of file diff --git a/src/ts/database.ts b/src/ts/database.ts index e0ac8165..b9405013 100644 --- a/src/ts/database.ts +++ b/src/ts/database.ts @@ -223,6 +223,7 @@ export interface loreBook{ mode: 'multiple'|'constant'|'normal', alwaysActive: boolean selective:boolean + extentions?:{} } export interface character{ @@ -255,6 +256,12 @@ export interface character{ scenario:string firstMsgIndex:number loreSettings?:loreSettings + loreExt?:any + additionalData?: { + tag?:string[] + creator?:string + character_version?:number + } } @@ -264,6 +271,7 @@ export interface loreSettings{ recursiveScanning: boolean } + export interface groupChat{ type: 'group' image?:string