{
+ e.preventDefault()
+ e.dataTransfer.dropEffect = 'move'
+ e.currentTarget.classList.add('bg-green-500')
+ }} on:dragleave={(e) => {
+ e.currentTarget.classList.remove('bg-green-500')
+ }} on:drop={(e) => {
+ e.preventDefault()
+ e.currentTarget.classList.remove('bg-green-500')
+ const da = currentDrag
+ if(da){
+ inserter(da,{index:0})
+ }
+ }} on:dragenter={preventAll} />
+ {#if menuMode === 0}
+ {#each charImages as char, ind}
+
-
-
{
- e.preventDefault()
- e.dataTransfer.dropEffect = 'move'
- e.currentTarget.classList.add('bg-green-500')
- }} on:dragleave={(e) => {
- e.currentTarget.classList.remove('bg-green-500')
- }} on:drop={(e) => {
- e.preventDefault()
- e.currentTarget.classList.remove('bg-green-500')
- const da = currentDrag
- if(da && char.type === 'folder'){
- inserter(da,{index:0,folder:char.id})
- }
- }} on:dragenter={preventAll}/>
- {#each char.folder as char2, ind}
-
{if(char.type === 'folder'){avatarDragStart({index: ind, folder:char.id}, e)}}}
- on:dragover={avatarDragOver}
- on:drop={(e) => {if(char.type === 'folder'){avatarDrop({index: ind, folder:char.id}, e)}}}
- on:dragenter={preventAll}
- on:contextmenu={preventAll}
+ }}
+ on:keydown={(e) => {
+ if (e.key === "Enter") {
+ if(char.type === "normal"){
+ changeChar(char.index);
+ }
+ }
+ }}
+ tabindex="0"
>
-
-
-
{
- if(char2.type === "normal"){
- changeChar(char2.index);
- }
- }}
- on:keydown={(e) => {
- if (e.key === "Enter") {
- if(char2.type === "normal"){
- changeChar(char2.index);
- }
- }
- }}
- tabindex="0"
- >
-
-
-
-
{
+ {#if char.type === 'normal'}
+
+ {:else if char.type === "folder"}
+ {
+ if(char.type !== 'folder'){
+ return
+ }
+ if(openFolders.includes(char.id)){
+ openFolders.splice(openFolders.indexOf(char.id), 1)
+ }
+ else{
+ openFolders.push(char.id)
+ }
+ openFolders = openFolders
+ }}>
+ {#if openFolders.includes(char.id)}
+
+ {:else}
+
+ {/if}
+
+ {/if}
+
+
+ {#if char.type === 'folder' && openFolders.includes(char.id)}
+
+
+
{
e.preventDefault()
e.dataTransfer.dropEffect = 'move'
e.currentTarget.classList.add('bg-green-500')
@@ -425,68 +380,115 @@
e.currentTarget.classList.remove('bg-green-500')
const da = currentDrag
if(da && char.type === 'folder'){
- inserter(da,{index:ind+1,folder:char.id})
+ inserter(da,{index:0,folder:char.id})
}
}} on:dragenter={preventAll}/>
- {/each}
-
- {/if}
-
{
- e.dataTransfer.dropEffect = 'move'
- e.currentTarget.classList.add('bg-green-500')
- }} on:dragleave={(e) => {
- e.currentTarget.classList.remove('bg-green-500')
- }} on:drop={(e) => {
- e.preventDefault()
- e.currentTarget.classList.remove('bg-green-500')
- const da = currentDrag
- if(da){
- inserter(da,{index:ind+1})
- }
- }} on:dragenter={preventAll} />
- {/each}
-
-
{if(char.type === 'folder'){avatarDragStart({index: ind, folder:char.id}, e)}}}
+ on:dragover={avatarDragOver}
+ on:drop={(e) => {if(char.type === 'folder'){avatarDrop({index: ind, folder:char.id}, e)}}}
+ on:dragenter={preventAll}
+ on:contextmenu={preventAll}
+ >
+
+
+ {
+ if(char2.type === "normal"){
+ changeChar(char2.index);
+ }
+ }}
+ on:keydown={(e) => {
+ if (e.key === "Enter") {
+ if(char2.type === "normal"){
+ changeChar(char2.index);
+ }
+ }
+ }}
+ tabindex="0"
+ >
+
+
+
+
{
+ e.preventDefault()
+ e.dataTransfer.dropEffect = 'move'
+ e.currentTarget.classList.add('bg-green-500')
+ }} on:dragleave={(e) => {
+ e.currentTarget.classList.remove('bg-green-500')
+ }} on:drop={(e) => {
+ e.preventDefault()
+ e.currentTarget.classList.remove('bg-green-500')
+ const da = currentDrag
+ if(da && char.type === 'folder'){
+ inserter(da,{index:ind+1,folder:char.id})
+ }
+ }} on:dragenter={preventAll}/>
+ {/each}
+
+ {/if}
+
{
+ e.dataTransfer.dropEffect = 'move'
+ e.currentTarget.classList.add('bg-green-500')
+ }} on:dragleave={(e) => {
+ e.currentTarget.classList.remove('bg-green-500')
+ }} on:drop={(e) => {
+ e.preventDefault()
+ e.currentTarget.classList.remove('bg-green-500')
+ const da = currentDrag
+ if(da){
+ inserter(da,{index:ind+1})
+ }
+ }} on:dragenter={preventAll} />
+ {/each}
+
+
{
+ if (sideBarMode === 1) {
+ reseter();
+ sideBarMode = 0;
+ } else {
+ reseter();
+ sideBarMode = 1;
+ }
+ }}
+ >
+
+ {:else}
+
{
- if (sideBarMode === 1) {
+ if ($settingsOpen) {
reseter();
- sideBarMode = 0;
+ settingsOpen.set(false);
} else {
reseter();
- sideBarMode = 1;
+ settingsOpen.set(true);
}
- }}
- >
-
- {:else}
-
{
- if ($settingsOpen) {
+
+ {
reseter();
- settingsOpen.set(false);
- } else {
- reseter();
- settingsOpen.set(true);
- }
- }}>
- {
- reseter();
- openGrid();
- }}>
- {/if}
+ openGrid();
+ }}>
+ {/if}
+
+ import { DataBase } from "src/ts/database";
+ import { isTauri } from "src/ts/globalApi";
+ import { getHordeModels } from "src/ts/horde/getModels";
+
+ export let value = ""
+
+
+{#await getHordeModels()}
+
+ Loading...
+
+{:then models}
+
+
+ OpenAI GPT-3.5
+ OpenAI GPT-4
+
+
+ Google Palm2
+ Text Generation WebUI
+ {#if $DataBase.plugins.length > 0}
+ Plugin
+ {/if}
+
+
+
+{/await}
\ No newline at end of file
diff --git a/src/ts/characterCards.ts b/src/ts/characterCards.ts
index 30bf445a..c9e25b8a 100644
--- a/src/ts/characterCards.ts
+++ b/src/ts/characterCards.ts
@@ -203,7 +203,8 @@ function convertOldTavernAndJSON(charaData:OldTavernChar, imgp:string|undefined
characterVersion: 0,
personality: charaData.personality ?? '',
scenario:charaData.scenario ?? '',
- firstMsgIndex: -1
+ firstMsgIndex: -1,
+ replaceGlobalNote: ""
}
}
@@ -381,7 +382,7 @@ async function importSpecv2(card:CharacterCardV2, img?:Uint8Array):Promise // 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
-
+ case_sensitive?:boolean
}
\ No newline at end of file
diff --git a/src/ts/characters.ts b/src/ts/characters.ts
index 83774b78..e78c3bc9 100644
--- a/src/ts/characters.ts
+++ b/src/ts/characters.ts
@@ -275,7 +275,6 @@ export function characterFormatUpdate(index:number|character){
cha.exampleMessage = cha.exampleMessage ?? ''
cha.creatorNotes = cha.creatorNotes ?? ''
cha.systemPrompt = cha.systemPrompt ?? ''
- cha.postHistoryInstructions = cha.postHistoryInstructions ?? ''
cha.tags = cha.tags ?? []
cha.creator = cha.creator ?? ''
cha.characterVersion = cha.characterVersion ?? 0
@@ -288,6 +287,12 @@ export function characterFormatUpdate(index:number|character){
character_version: 0
}
+ if(cha.postHistoryInstructions){
+ cha.chats[cha.chatPage].note += "\n" + cha.postHistoryInstructions
+ cha.chats[cha.chatPage].note = cha.chats[cha.chatPage].note.trim()
+ cha.postHistoryInstructions = null
+ }
+
}
if(checkNullish(cha.customscript)){
cha.customscript = []
@@ -332,7 +337,8 @@ export function createBlankChar():character{
characterVersion: 0,
personality:"",
scenario:"",
- firstMsgIndex: -1
+ firstMsgIndex: -1,
+ replaceGlobalNote: ""
}
}
diff --git a/src/ts/database.ts b/src/ts/database.ts
index 9bd3051e..2047fd26 100644
--- a/src/ts/database.ts
+++ b/src/ts/database.ts
@@ -7,7 +7,7 @@ import { cloneDeep } from 'lodash';
export const DataBase = writable({} as any as Database)
export const loadedStore = writable(false)
-export let appVer = '1.15.6'
+export let appVer = '1.16.0'
export function setDatabase(data:Database){
@@ -222,6 +222,26 @@ export function setDatabase(data:Database){
FontColorItalicBold: "#8C8D93"
}
}
+ if(checkNullish(data.hordeConfig)){
+ data.hordeConfig = {
+ apiKey: "",
+ model: "",
+ softPrompt: ""
+ }
+ }
+ if(checkNullish(data.novelai)){
+ data.novelai = {
+ token: "",
+ model: "clio-v1",
+ }
+ }
+ if(checkNullish(data.loreBook)){
+ data.loreBookPage = 0
+ data.loreBook = [{
+ name: "My First LoreBook",
+ data: []
+ }]
+ }
changeLanguage(data.language)
@@ -246,7 +266,9 @@ export interface loreBook{
mode: 'multiple'|'constant'|'normal',
alwaysActive: boolean
selective:boolean
- extentions?:{}
+ extentions?:{
+ risu_case_sensitive:boolean
+ }
}
export interface character{
@@ -290,6 +312,7 @@ export interface character{
supaMemory?:boolean
additionalAssets?:[string, string][]
ttsReadOnlyQuoted?:boolean
+ replaceGlobalNote:string
}
@@ -366,6 +389,11 @@ export interface Database{
jailbreakToggle:boolean
loreBookDepth: number
loreBookToken: number,
+ loreBook: {
+ name:string
+ data:loreBook[]
+ }[]
+ loreBookPage: number
supaMemoryPrompt: string
username: string
userIcon: string
@@ -431,6 +459,17 @@ export interface Database{
textScreenRounded?:boolean
textScreenBorder?:string
characterOrder:(string|folder)[]
+ hordeConfig:hordeConfig,
+ novelai:{
+ token:string,
+ model:string
+ }
+}
+
+interface hordeConfig{
+ apiKey:string
+ model:string
+ softPrompt:string
}
export interface folder{
@@ -542,7 +581,7 @@ export function updateTextTheme(){
}
}
-export function changeToPreset(id =0){
+export function saveCurrentPreset(){
let db = get(DataBase)
let pres = db.botPresets
pres[db.botPresetsId] = {
@@ -568,6 +607,23 @@ export function changeToPreset(id =0){
bias: db.bias
}
db.botPresets = pres
+ DataBase.set(db)
+}
+
+export function copyPreset(id:number){
+ saveCurrentPreset()
+ let db = get(DataBase)
+ let pres = db.botPresets
+ const newPres = cloneDeep(pres[id])
+ newPres.name += " Copy"
+ db.botPresets.push(newPres)
+ DataBase.set(db)
+}
+
+export function changeToPreset(id =0){
+ saveCurrentPreset()
+ let db = get(DataBase)
+ let pres = db.botPresets
const newPres = pres[id]
db.botPresetsId = id
db.apiType = newPres.apiType ?? db.apiType
diff --git a/src/ts/horde/getModels.ts b/src/ts/horde/getModels.ts
new file mode 100644
index 00000000..b2b4e6d8
--- /dev/null
+++ b/src/ts/horde/getModels.ts
@@ -0,0 +1,34 @@
+import { sleep } from "../util"
+
+let modelList:string[]|'loading' = null
+
+//until horde is ready
+modelList = []
+
+export async function getHordeModels():Promise {
+
+ if(modelList === null){
+ try {
+ modelList = 'loading'
+ const models = await fetch("https://stablehorde.net/api/v2/status/models?type=text")
+ modelList = ((await models.json()).map((a) => {
+ return a.name
+ }) as string[])
+ return modelList
+ } catch (error) {
+ modelList = null
+ return []
+ }
+ }
+ else if(modelList === 'loading'){
+ while(true){
+ if(modelList !== 'loading'){
+ return getHordeModels()
+ }
+ await sleep(10)
+ }
+ }
+ else{
+ return modelList
+ }
+}
\ No newline at end of file
diff --git a/src/ts/process/index.ts b/src/ts/process/index.ts
index 564ecdaa..26c7b18b 100644
--- a/src/ts/process/index.ts
+++ b/src/ts/process/index.ts
@@ -18,6 +18,7 @@ export interface OpenAIChat{
role: 'system'|'user'|'assistant'
content: string
memo?:string
+ name?:string
}
export const doingChat = writable(false)
@@ -104,7 +105,7 @@ export async function sendChat(chatProcessIndex = -1):Promise {
}
if(!currentChar.utilityBot){
- const mainp = currentChar.systemPrompt.length > 3 ? currentChar.systemPrompt : db.mainPrompt
+ const mainp = currentChar.systemPrompt || db.mainPrompt
unformated.main.push({
role: 'system',
@@ -120,14 +121,14 @@ export async function sendChat(chatProcessIndex = -1):Promise {
unformated.globalNote.push({
role: 'system',
- content: replacePlaceholders(db.globalNote, currentChar.name)
+ content: replacePlaceholders(currentChar.replaceGlobalNote || db.globalNote, currentChar.name)
})
}
if(currentChat.note !== ''){
unformated.authorNote.push({
role: 'system',
- content: replacePlaceholders(currentChar.postHistoryInstructions, currentChat.note)
+ content: replacePlaceholders(currentChat.note, currentChar.name)
})
}
@@ -200,22 +201,26 @@ export async function sendChat(chatProcessIndex = -1):Promise {
const ms = currentChat.message
for(const msg of ms){
let formedChat = processScript(currentChar,replacePlaceholders(msg.data, currentChar.name), 'editprocess')
- if(nowChatroom.type === 'group'){
- if(msg.saying && msg.role === 'char'){
- formedChat = `${findCharacterbyIdwithCache(msg.saying).name}: ${formedChat}`
-
+ let name = ''
+ if(msg.role === 'char'){
+ if(msg.saying){
+ name = `${findCharacterbyIdwithCache(msg.saying).name}`
}
- else if(msg.role === 'user'){
- formedChat = `${db.username}: ${formedChat}`
+ else{
+ name = `${currentChar.name}`
}
}
+ else if(msg.role === 'user'){
+ name = `${db.username}`
+ }
if(!msg.chatId){
msg.chatId = v4()
}
chats.push({
role: msg.role === 'user' ? 'user' : 'assistant',
content: formedChat,
- memo: msg.chatId
+ memo: msg.chatId,
+ name: name
})
currentTokens += (await tokenize(formedChat) + 1)
}
diff --git a/src/ts/process/lorebook.ts b/src/ts/process/lorebook.ts
index 8a523f1d..137fb056 100644
--- a/src/ts/process/lorebook.ts
+++ b/src/ts/process/lorebook.ts
@@ -10,7 +10,19 @@ import { downloadFile } from "../globalApi";
export function addLorebook(type:number) {
let selectedID = get(selectedCharID)
let db = get(DataBase)
- if(type === 0){
+ if(type === -1){
+ db.loreBook[db.loreBookPage].data.push({
+ key: '',
+ comment: `New Lore ${db.loreBook[db.loreBookPage].data.length + 1}`,
+ content: '',
+ mode: 'normal',
+ insertorder: 100,
+ alwaysActive: false,
+ secondkey: "",
+ selective: false
+ })
+ }
+ else if(type === 0){
db.characters[selectedID].globalLore.push({
key: '',
comment: `New Lore ${db.characters[selectedID].globalLore.length + 1}`,
@@ -53,9 +65,10 @@ export async function loadLoreBookPrompt(){
const db = get(DataBase)
const char = db.characters[selectedID]
const page = char.chatPage
- const globalLore = char.globalLore
- const charLore = char.chats[page].localLore
- const fullLore = globalLore.concat(charLore)
+ const characterLore = char.globalLore
+ const chatLore = char.chats[page].localLore
+ const globalLore = db.loreBook[db.loreBookPage].data
+ const fullLore = characterLore.concat(chatLore.concat(globalLore))
const currentChat = char.chats[page].message
const loreDepth = char.loreSettings?.scanDepth ?? db.loreBookDepth
const loreToken = char.loreSettings?.tokenBudget ?? db.loreBookToken
@@ -144,11 +157,14 @@ export async function loadLoreBookPrompt(){
}
-export async function importLoreBook(mode:'global'|'local'){
+export async function importLoreBook(mode:'global'|'local'|'sglobal'){
const selectedID = get(selectedCharID)
let db = get(DataBase)
const page = db.characters[selectedID].chatPage
- let lore = mode === 'global' ? db.characters[selectedID].globalLore : db.characters[selectedID].chats[page].localLore
+ let lore =
+ mode === 'global' ? db.characters[selectedID].globalLore :
+ mode === 'sglobal' ? db.loreBook[db.loreBookPage].data :
+ db.characters[selectedID].chats[page].localLore
const lorebook = (await selectSingleFile(['json'])).data
if(!lorebook){
return
@@ -189,6 +205,9 @@ export async function importLoreBook(mode:'global'|'local'){
if(mode === 'global'){
db.characters[selectedID].globalLore = lore
}
+ if(mode === 'sglobal'){
+ db.loreBook[db.loreBookPage].data = lore
+ }
else{
db.characters[selectedID].chats[page].localLore = lore
}
@@ -198,13 +217,15 @@ export async function importLoreBook(mode:'global'|'local'){
}
}
-export async function exportLoreBook(mode:'global'|'local'){
+export async function exportLoreBook(mode:'global'|'local'|'sglobal'){
try {
const selectedID = get(selectedCharID)
const db = get(DataBase)
const page = db.characters[selectedID].chatPage
- const lore = mode === 'global' ? db.characters[selectedID].globalLore : db.characters[selectedID].chats[page].localLore
-
+ const lore =
+ mode === 'global' ? db.characters[selectedID].globalLore :
+ mode === 'sglobal' ? db.loreBook[db.loreBookPage].data :
+ db.characters[selectedID].chats[page].localLore
const stringl = Buffer.from(JSON.stringify({
type: 'risu',
ver: 1,
diff --git a/src/ts/process/request.ts b/src/ts/process/request.ts
index 34235e34..83dab435 100644
--- a/src/ts/process/request.ts
+++ b/src/ts/process/request.ts
@@ -3,8 +3,10 @@ import type { OpenAIChat } from ".";
import { DataBase, setDatabase, type character } from "../database";
import { pluginProcess } from "./plugins";
import { language } from "../../lang";
-import { stringlizeChat } from "./stringlize";
-import { globalFetch } from "../globalApi";
+import { stringlizeChat, unstringlizeChat } from "./stringlize";
+import { globalFetch, isTauri } from "../globalApi";
+import { alertError } from "../alert";
+import { sleep } from "../util";
interface requestDataArgument{
formated: OpenAIChat[]
@@ -34,7 +36,7 @@ export async function requestChatData(arg:requestDataArgument, model:'model'|'su
return da
}
trys += 1
- if(trys > db.requestRetrys){
+ if(trys > db.requestRetrys || model.startsWith('horde')){
return da
}
}
@@ -53,6 +55,11 @@ export async function requestChatDataMain(arg:requestDataArgument, model:'model'
switch(aiModel){
case 'gpt35':
case 'gpt4':{
+
+ for(let i=0;i {
+
+ if(a && a.models && a.id){
+ console.log(a)
+ return a.models.includes(realModel)
+ }
+ return false
+ }).map((a) => {
+ return a.id
+ })
+
+ const argument = {
+ "prompt": "string",
+ "params": {
+ "n": 1,
+ "frmtadsnsp": false,
+ "frmtrmblln": false,
+ "frmtrmspch": false,
+ "frmttriminc": false,
+ "max_context_length": 200,
+ "max_length": 20,
+ "rep_pen": 3,
+ "rep_pen_range": 0,
+ "rep_pen_slope": 10,
+ "singleline": false,
+ "temperature": db.temperature / 25,
+ "tfs": 1,
+ "top_a": 1,
+ "top_k": 100,
+ "top_p": 1,
+ "typical": 1,
+ "sampler_order": [
+ 0
+ ]
+ },
+ "trusted_workers": false,
+ "slow_workers": true,
+ "worker_blacklist": false,
+ "dry_run": false
+ }
+
+ const da = await fetch("https://stablehorde.net/api/v2/generate/text/async", {
+ body: JSON.stringify(argument),
+ method: "POST",
+ headers: {
+ "content-type": "application/json",
+ "apikey": db.hordeConfig.apiKey
+ }
+ })
+
+ 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 && json.message.startsWith("Warning:")){
+ warnMessage = "with " + json.message
+ }
+
+ while(true){
+ await sleep(1000)
+ 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
+ }
+ }
+ if(data.done){
+ const generations:{text:string}[] = data.generations
+ if(generations && generations.length > 0){
+ return {
+ type: "success",
+ result: generations[0].text
+ }
+ }
+ return {
+ type: 'fail',
+ result: "No Generations when done"
+ }
+ }
+ }
+
+
+ }
return {
type: 'fail',
result: (language.errors.unknownModel)
diff --git a/src/ts/process/stringlize.ts b/src/ts/process/stringlize.ts
index b8fb6777..62e58d4d 100644
--- a/src/ts/process/stringlize.ts
+++ b/src/ts/process/stringlize.ts
@@ -8,14 +8,48 @@ export function stringlizeChat(formated:OpenAIChat[], char:string = ''){
let resultString:string[] = []
for(const form of formated){
if(form.role === 'system'){
- resultString.push("'System Note: " + form.content)
+ resultString.push("system note: " + form.content)
}
- else if(form.role === 'user'){
- resultString.push("user: " + form.content)
+ else if(form.name){
+ resultString.push(form.name + ": " + form.content)
}
- else if(form.role === 'assistant'){
- resultString.push("assistant: " + form.content)
+ else{
+ resultString.push(form.content)
}
}
return resultString.join('\n\n') + `\n\n${char}:`
+}
+
+export function unstringlizeChat(text:string, formated:OpenAIChat[], char:string = ''){
+ console.log(text)
+ let minIndex = -1
+ let chunks:string[] = ["system note:"]
+ if(char){
+ chunks.push(`${char}:`)
+ }
+
+ for(const form of formated){
+ if(form.name){
+ const chunk = `${form.name}:`
+ if(!chunks.includes(chunk)){
+ chunks.push(chunk)
+ }
+ }
+ }
+
+ for(const chunk of chunks){
+ const ind = text.indexOf(chunk)
+ if(ind === -1){
+ continue
+ }
+ if(minIndex === -1 || minIndex > ind){
+ minIndex = ind
+ }
+ }
+
+ if(minIndex !== -1){
+ text = text.substring(0, minIndex).trim()
+ }
+
+ return text
}
\ No newline at end of file
diff --git a/version.json b/version.json
index 27b8e9f0..dba713ae 100644
--- a/version.json
+++ b/version.json
@@ -1 +1 @@
-{"version":"1.15.6"}
\ No newline at end of file
+{"version":"1.16.0"}
\ No newline at end of file