diff --git a/public/sample/yuzu.png b/public/sample/yuzu.png
index b6ef076c..5b824375 100644
Binary files a/public/sample/yuzu.png and b/public/sample/yuzu.png differ
diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json
index 74b0fd39..d2ff6511 100644
--- a/src-tauri/tauri.conf.json
+++ b/src-tauri/tauri.conf.json
@@ -8,7 +8,7 @@
},
"package": {
"productName": "RisuAI",
- "version": "0.7.2"
+ "version": "0.7.3"
},
"tauri": {
"allowlist": {
diff --git a/src/lib/SideBars/CharConfig.svelte b/src/lib/SideBars/CharConfig.svelte
index 50d7b9ed..03b5e2ca 100644
--- a/src/lib/SideBars/CharConfig.svelte
+++ b/src/lib/SideBars/CharConfig.svelte
@@ -5,7 +5,7 @@
import { selectedCharID } from "../../ts/stores";
import { PlusIcon, SmileIcon, TrashIcon, UserIcon, ActivityIcon, BookIcon, LoaderIcon, User } from 'lucide-svelte'
import Check from "../Others/Check.svelte";
- import { addCharEmotion, addingEmotion, exportChar, getCharImage, rmCharEmotion, selectCharImg, makeGroupImage } from "../../ts/characters";
+ import { addCharEmotion, addingEmotion, getCharImage, rmCharEmotion, selectCharImg, makeGroupImage } from "../../ts/characters";
import LoreBook from "./LoreBookSetting.svelte";
import { alertConfirm, alertError, alertSelectChar } from "../../ts/alert";
import BarIcon from "./BarIcon.svelte";
@@ -14,6 +14,7 @@
import {isEqual, cloneDeep} from 'lodash'
import Help from "../Others/Help.svelte";
import RegexData from "./RegexData.svelte";
+ import { exportChar } from "src/ts/characterCards";
let subMenu = 0
let subberMenu = 0
diff --git a/src/lib/SideBars/Settings.svelte b/src/lib/SideBars/Settings.svelte
index 79cecddf..e790c266 100644
--- a/src/lib/SideBars/Settings.svelte
+++ b/src/lib/SideBars/Settings.svelte
@@ -262,11 +262,6 @@
Sampler
- Upscaler
-
Enable Hires
diff --git a/src/lib/SideBars/Sidebar.svelte b/src/lib/SideBars/Sidebar.svelte
index d784e27c..515c3106 100644
--- a/src/lib/SideBars/Sidebar.svelte
+++ b/src/lib/SideBars/Sidebar.svelte
@@ -3,7 +3,8 @@
import { DataBase } from "../../ts/database";
import BarIcon from "./BarIcon.svelte";
import { Plus, User, X, Settings, Users, Edit3Icon, ArrowUp, ArrowDown, ListIcon, LayoutGridIcon, PlusIcon} from 'lucide-svelte'
- import { characterFormatUpdate, createNewCharacter, createNewGroup, getCharImage, importCharacter } from "../../ts/characters";
+ import { characterFormatUpdate, createNewCharacter, createNewGroup, getCharImage } from "../../ts/characters";
+ import {importCharacter} from 'src/ts/characterCards'
import SettingsDom from './Settings.svelte'
import CharConfig from "./CharConfig.svelte";
import { language } from "../../lang";
diff --git a/src/ts/characterCards.ts b/src/ts/characterCards.ts
new file mode 100644
index 00000000..bc7fdacd
--- /dev/null
+++ b/src/ts/characterCards.ts
@@ -0,0 +1,336 @@
+import { get } from "svelte/store"
+import { alertConfirm, alertError, alertNormal, alertStore } from "./alert"
+import { DataBase, defaultSdDataFunc, type character, saveImage, setDatabase } from "./database"
+import { checkNullish, selectSingleFile, sleep } from "./util"
+import { language } from "src/lang"
+import { encode as encodeMsgpack, decode as decodeMsgpack } from "@msgpack/msgpack";
+import { v4 as uuidv4 } from 'uuid';
+import exifr from 'exifr'
+import { PngMetadata } from "./exif"
+import { characterFormatUpdate } from "./characters"
+import { downloadFile, readImage } from "./globalApi"
+
+type OfficialCardSpec = {
+ spec: 'chara_card_v2'
+ spec_version: '2.0' // May 8th addition
+ data: {
+ name: string
+ description: string
+ personality: string
+ scenario: string
+ first_mes: string
+ mes_example: string
+ creator_notes: string
+ system_prompt: string
+ post_history_instructions: string
+ alternate_greetings: string[]
+ character_book?: CharacterBook
+ tags: string[]
+ creator: string
+ character_version: number
+ extensions: Record
+ }
+}
+
+type CharacterBook = null
+
+export async function importCharacter() {
+ try {
+ const f = await selectSingleFile(['png', 'json'])
+ if(!f){
+ return
+ }
+ if(f.name.endsWith('json')){
+ const da = JSON.parse(Buffer.from(f.data).toString('utf-8'))
+ if(await importSpecv2(da)){
+ return
+ }
+ if((da.char_name || da.name) && (da.char_persona || da.description) && (da.char_greeting || da.first_mes)){
+ let db = get(DataBase)
+ db.characters.push({
+ name: da.char_name ?? da.name,
+ firstMessage: da.char_greeting ?? da.first_mes,
+ desc: da.char_persona ?? da.description,
+ notes: '',
+ chats: [{
+ message: [],
+ note: '',
+ name: 'Chat 1',
+ localLore: []
+ }],
+ chatPage: 0,
+ image: '',
+ emotionImages: [],
+ bias: [],
+ globalLore: [],
+ viewScreen: 'none',
+ chaId: uuidv4(),
+ sdData: defaultSdDataFunc(),
+ utilityBot: false,
+ customscript: [],
+ exampleMessage: ''
+ })
+ DataBase.set(db)
+ alertNormal(language.importedCharacter)
+ return
+ }
+ else{
+ alertError(language.errors.noData)
+ return
+ }
+ }
+ alertStore.set({
+ type: 'wait',
+ msg: 'Loading... (Reading)'
+ })
+ await sleep(10)
+ const img = f.data
+ const readed = (await exifr.parse(img, true))
+
+ console.log(readed)
+ if(readed.chara){
+ // standard spec v2 imports
+ const charaData:CharacterCardV2 = JSON.parse(Buffer.from(readed.chara, 'base64').toString('utf-8'))
+ if(await importSpecv2(charaData)){
+ return
+ }
+ }
+ if(readed.risuai){
+ // old risu imports
+ await sleep(10)
+ const va = decodeMsgpack(Buffer.from(readed.risuai, 'base64')) as any
+ if(va.type !== 101){
+ alertError(language.errors.noData)
+ return
+ }
+
+
+ let char:character = va.data
+ let db = get(DataBase)
+ if(char.emotionImages && char.emotionImages.length > 0){
+ for(let i=0;i 0){
+ for(let i=0;i",
+ name: char.name,
+ personality: "",
+ scenario: "",
+ talkativeness: "0.5"
+ }
+
+ await sleep(10)
+ img = PngMetadata.write(img, {
+ 'chara': Buffer.from(JSON.stringify(tavernData)).toString('base64'),
+ 'risuai': data
+ })
+
+ alertStore.set({
+ type: 'wait',
+ msg: 'Loading... (Writing)'
+ })
+
+ char.image = ''
+ await sleep(10)
+ await downloadFile(`${char.name.replace(/[<>:"/\\|?*\.\,]/g, "")}_export.png`, img)
+
+ alertNormal(language.successExport)
+
+ }
+ catch(e){
+ alertError(`${e}`)
+ }
+
+}
+
+
+async function importSpecv2(card:CharacterCardV2):Promise{
+ if(!card ||card.spec !== 'chara_card_v2'){
+ return false
+ }
+ let data = card.data
+
+ let db = get(DataBase)
+
+ const risuext = data.extensions.risuai
+ if(risuext && risuext.emotions){
+ for(let i=0;i"
+ name: string
+ personality: ""
+ scenario: ""
+ talkativeness: "0.5"
+}
diff --git a/src/ts/characters.ts b/src/ts/characters.ts
index 92bc5dcd..705b58e4 100644
--- a/src/ts/characters.ts
+++ b/src/ts/characters.ts
@@ -42,143 +42,6 @@ export function createNewGroup(){
return db.characters.length - 1
}
-export async function importCharacter() {
- try {
- const f = await selectSingleFile(['png', 'json'])
- if(!f){
- return
- }
- if(f.name.endsWith('json')){
- const da = JSON.parse(Buffer.from(f.data).toString('utf-8'))
- if((da.char_name || da.name) && (da.char_persona || da.description) && (da.char_greeting || da.first_mes)){
- let db = get(DataBase)
- db.characters.push({
- name: da.char_name ?? da.name,
- firstMessage: da.char_greeting ?? da.first_mes,
- desc: da.char_persona ?? da.description,
- notes: '',
- chats: [{
- message: [],
- note: '',
- name: 'Chat 1',
- localLore: []
- }],
- chatPage: 0,
- image: '',
- emotionImages: [],
- bias: [],
- globalLore: [],
- viewScreen: 'none',
- chaId: uuidv4(),
- sdData: defaultSdDataFunc(),
- utilityBot: false,
- customscript: [],
- exampleMessage: ''
- })
- DataBase.set(db)
- alertNormal(language.importedCharacter)
- return
- }
- else{
- alertError(language.errors.noData)
- return
- }
- }
- alertStore.set({
- type: 'wait',
- msg: 'Loading... (Reading)'
- })
- await sleep(10)
- const img = f.data
- const readed = (await exifr.parse(img, true))
-
- console.log(readed)
- if(readed.risuai){
- await sleep(10)
- const va = decodeMsgpack(Buffer.from(readed.risuai, 'base64')) as any
- if(va.type !== 101){
- alertError(language.errors.noData)
- return
- }
-
-
- let char:character = va.data
- let db = get(DataBase)
- if(char.emotionImages && char.emotionImages.length > 0){
- for(let i=0;i"
- name: string
- personality: ""
- scenario: ""
- talkativeness: "0.5"
-}
-
export async function selectCharImg(charId:number) {
const selected = await selectSingleFile(['png'])
if(!selected){
@@ -270,94 +120,6 @@ export async function rmCharEmotion(charId:number, emotionId:number) {
setDatabase(db)
}
-export async function exportChar(charaID:number) {
- const db = get(DataBase)
- let char:character = JSON.parse(JSON.stringify(db.characters[charaID]))
-
- if(!char.image){
- alertError('Image Required')
- return
- }
- const conf = await alertConfirm(language.exportConfirm)
- if(!conf){
- return
- }
-
- alertStore.set({
- type: 'wait',
- msg: 'Loading...'
- })
-
- let img = await readImage(char.image)
-
- try{
- if(char.emotionImages && char.emotionImages.length > 0){
- for(let i=0;i",
- name: char.name,
- personality: "",
- scenario: "",
- talkativeness: "0.5"
- }
-
- await sleep(10)
- img = PngMetadata.write(img, {
- 'chara': Buffer.from(JSON.stringify(tavernData)).toString('base64'),
- 'risuai': data
- })
-
- alertStore.set({
- type: 'wait',
- msg: 'Loading... (Writing)'
- })
-
- char.image = ''
- await sleep(10)
- await downloadFile(`${char.name.replace(/[<>:"/\\|?*\.\,]/g, "")}_export.png`, img)
-
- alertNormal(language.successExport)
-
- }
- catch(e){
- alertError(`${e}`)
- }
-
-}
-
export async function exportChat(page:number){
try {
diff --git a/src/ts/database.ts b/src/ts/database.ts
index 11f14581..a7ae81d4 100644
--- a/src/ts/database.ts
+++ b/src/ts/database.ts
@@ -6,7 +6,7 @@ import { saveImage as saveImageGlobal } from './globalApi';
export const DataBase = writable({} as any as Database)
export const loadedStore = writable(false)
-export let appVer = '0.7.2'
+export let appVer = '0.7.3'
export function setDatabase(data:Database){
diff --git a/src/ts/process/request.ts b/src/ts/process/request.ts
index e8c5eba2..4b144da2 100644
--- a/src/ts/process/request.ts
+++ b/src/ts/process/request.ts
@@ -111,7 +111,8 @@ export async function requestChatDataMain(arg:requestDataArgument, model:'model'
let DURL = db.textgenWebUIURL
let bodyTemplate:any
const proompt = stringlizeChat(formated, currentChar.name)
- if(DURL.includes('api')){
+ const isNewAPI = DURL.includes('api')
+ if(isNewAPI){
bodyTemplate = {
'max_new_tokens': 80,
'do_sample': true,
@@ -174,6 +175,12 @@ export async function requestChatDataMain(arg:requestDataArgument, model:'model'
console.log(res.data)
if(res.ok){
try {
+ if(isNewAPI){
+ return {
+ type: 'success',
+ result: dat.results[0].text.substring(proompt.length)
+ }
+ }
return {
type: 'success',
result: dat.data[0].substring(proompt.length)
diff --git a/version.json b/version.json
index 638300ad..cb4cf34b 100644
--- a/version.json
+++ b/version.json
@@ -1 +1 @@
-{"version":"0.7.2"}
+{"version":"0.7.3"}