[feat] added horde support, added spec2 requirements that didn't implemented

This commit is contained in:
kwaroran
2023-05-24 08:49:35 +09:00
parent 022f70f214
commit a999d6d780
9 changed files with 214 additions and 62 deletions

View File

@@ -6,6 +6,7 @@
import { customProviderStore, getCurrentPluginMax } from "src/ts/process/plugins"; import { customProviderStore, getCurrentPluginMax } from "src/ts/process/plugins";
import { isTauri } from "src/ts/globalApi"; import { isTauri } from "src/ts/globalApi";
import { tokenize } from "src/ts/tokenizer"; import { tokenize } from "src/ts/tokenizer";
import ModelList from "src/lib/UI/ModelList.svelte";
import DropList from "src/lib/SideBars/DropList.svelte"; import DropList from "src/lib/SideBars/DropList.svelte";
import { PlusIcon, TrashIcon } from "lucide-svelte"; import { PlusIcon, TrashIcon } from "lucide-svelte";
let tokens = { let tokens = {
@@ -36,40 +37,10 @@
<h2 class="mb-2 text-2xl font-bold mt-2">{language.chatBot}</h2> <h2 class="mb-2 text-2xl font-bold mt-2">{language.chatBot}</h2>
<span class="text-neutral-200 mt-4">{language.model} <Help key="model"/></span> <span class="text-neutral-200 mt-4">{language.model} <Help key="model"/></span>
<select class="bg-transparent input-text mt-2 mb-2 text-gray-200 appearance-none text-sm" bind:value={$DataBase.aiModel}> <ModelList bind:value={$DataBase.aiModel}/>
<optgroup class="bg-darkbg appearance-none" label="OpenAI">
<option value="gpt35" class="bg-darkbg appearance-none">OpenAI GPT-3.5</option>
<option value="gpt4" class="bg-darkbg appearance-none">OpenAI GPT-4</option>
</optgroup>
<optgroup class="bg-darkbg appearance-none" label="Other Providers">
<option value="palm2" class="bg-darkbg appearance-none">Google Palm2</option>
{#if $DataBase.aiModel === 'novelai' || isTauri}
<option value="novelai" class="bg-darkbg appearance-none">NovelAI Clio</option>
{/if}
<option value="textgen_webui" class="bg-darkbg appearance-none">Text Generation WebUI</option>
{#if $DataBase.plugins.length > 0}
<option value="custom" class="bg-darkbg appearance-none">Plugin</option>
{/if}
</optgroup>
</select>
<span class="text-neutral-200 mt-2">{language.submodel} <Help key="submodel"/></span> <span class="text-neutral-200 mt-2">{language.submodel} <Help key="submodel"/></span>
<select class="bg-transparent input-text mt-2 mb-2 text-gray-200 appearance-none text-sm" bind:value={$DataBase.subModel}> <ModelList bind:value={$DataBase.subModel}/>
<optgroup class="bg-darkbg appearance-none" label="OpenAI">
<option value="gpt35" class="bg-darkbg appearance-none">OpenAI GPT-3.5</option>
<option value="gpt4" class="bg-darkbg appearance-none">OpenAI GPT-4</option>
</optgroup>
<optgroup class="bg-darkbg appearance-none" label="Other Providers">
<option value="palm2" class="bg-darkbg appearance-none">Google Palm2</option>
{#if $DataBase.aiModel === 'novelai' || isTauri}
<option value="novelai" class="bg-darkbg appearance-none">NovelAI Clio</option>
{/if}
<option value="textgen_webui" class="bg-darkbg appearance-none">Text Generation WebUI</option>
{#if $DataBase.plugins.length > 0}
<option value="custom" class="bg-darkbg appearance-none">Plugin</option>
{/if}
</optgroup>
</select>
{#if $DataBase.aiModel === 'palm2' || $DataBase.subModel === 'palm2'} {#if $DataBase.aiModel === 'palm2' || $DataBase.subModel === 'palm2'}
<span class="text-neutral-200">Palm2 {language.apiKey}</span> <span class="text-neutral-200">Palm2 {language.apiKey}</span>
@@ -96,6 +67,12 @@
<span class="text-neutral-200">NovelAI Bearer Token</span> <span class="text-neutral-200">NovelAI Bearer Token</span>
<input class="text-neutral-200 p-2 bg-transparent input-text focus:bg-selected text-sm mb-2" bind:value={$DataBase.novelai.token}> <input class="text-neutral-200 p-2 bg-transparent input-text focus:bg-selected text-sm mb-2" bind:value={$DataBase.novelai.token}>
{/if}
{#if $DataBase.aiModel.startsWith("horde") || $DataBase.subModel.startsWith("horde") }
<span class="text-neutral-200">Horde {language.apiKey}</span>
<input class="text-neutral-200 p-2 bg-transparent input-text focus:bg-selected text-sm mb-2" bind:value={$DataBase.hordeConfig.apiKey}>
{/if} {/if}
{#if $DataBase.aiModel === 'textgen_webui' || $DataBase.subModel === 'textgen_webui'} {#if $DataBase.aiModel === 'textgen_webui' || $DataBase.subModel === 'textgen_webui'}
<span class="text-neutral-200">TextGen {language.providerURL} <Help key="oogaboogaURL"/></span> <span class="text-neutral-200">TextGen {language.providerURL} <Help key="oogaboogaURL"/></span>

View File

@@ -16,7 +16,7 @@
import RegexData from "./RegexData.svelte"; import RegexData from "./RegexData.svelte";
import { exportChar } from "src/ts/characterCards"; import { exportChar } from "src/ts/characterCards";
import { getElevenTTSVoices, getWebSpeechTTSVoices } from "src/ts/process/tts"; import { getElevenTTSVoices, getWebSpeechTTSVoices } from "src/ts/process/tts";
import { checkCharOrder } from "src/ts/globalApi"; import { checkCharOrder } from "src/ts/globalApi";
let subMenu = 0 let subMenu = 0
let subberMenu = 0 let subberMenu = 0
@@ -181,10 +181,10 @@
<span class="text-neutral-200">{language.firstMessage} <Help key="charFirstMessage"/></span> <span class="text-neutral-200">{language.firstMessage} <Help key="charFirstMessage"/></span>
<textarea class="bg-transparent input-text mt-2 mb-2 text-gray-200 text-xs resize-none h-20 focus:bg-selected" autocomplete="off" bind:value={currentChar.data.firstMessage}></textarea> <textarea class="bg-transparent input-text mt-2 mb-2 text-gray-200 text-xs resize-none h-20 focus:bg-selected" autocomplete="off" bind:value={currentChar.data.firstMessage}></textarea>
<span class="text-gray-400 mb-6 text-sm">{tokens.firstMsg} {language.tokens}</span> <span class="text-gray-400 mb-6 text-sm">{tokens.firstMsg} {language.tokens}</span>
<span class="text-neutral-200">{language.authorNote} <Help key="charNote"/></span> <span class="text-neutral-200">{language.authorNote} <Help key="chatNote"/></span>
<textarea class="bg-transparent input-text mt-2 mb-2 text-gray-200 resize-none h-20 focus:bg-selected text-xs" autocomplete="off" bind:value={currentChar.data.postHistoryInstructions}></textarea> <textarea class="bg-transparent input-text mt-2 mb-2 text-gray-200 resize-none h-20 focus:bg-selected text-xs" autocomplete="off" bind:value={currentChar.data.chats[currentChar.data.chatPage].note}></textarea>
<span class="text-gray-400 mb-6 text-sm">{tokens.charaNote} {language.tokens}</span> <span class="text-gray-400 mb-6 text-sm">{tokens.localNote} {language.tokens}</span>
{:else} {:else}
<input class="text-neutral-200 mt-2 mb-4 p-2 bg-transparent input-text text-xl focus:bg-selected" placeholder="Group Name" bind:value={currentChar.data.name}> <input class="text-neutral-200 mt-2 mb-4 p-2 bg-transparent input-text text-xl focus:bg-selected" placeholder="Group Name" bind:value={currentChar.data.name}>
<span class="text-neutral-200">{language.character}</span> <span class="text-neutral-200">{language.character}</span>
@@ -213,11 +213,6 @@
</button> </button>
</div> </div>
<span class="text-neutral-200">{language.chatNotes} <Help key="charNote"/></span>
<textarea class="bg-transparent input-text mt-2 mb-2 text-gray-200 resize-none h-20 focus:bg-selected text-xs" autocomplete="off" bind:value={currentChar.data.chats[currentChar.data.chatPage].note}></textarea>
<span class="text-gray-400 mb-6 text-sm">{tokens.localNote} {language.tokens}</span>
{/if} {/if}
<div class="flex mt-6 items-center"> <div class="flex mt-6 items-center">
@@ -520,10 +515,7 @@
<span class="text-neutral-200">{language.systemPrompt} <Help key="systemPrompt"/></span> <span class="text-neutral-200">{language.systemPrompt} <Help key="systemPrompt"/></span>
<textarea class="bg-transparent input-text mt-2 mb-2 text-gray-200 text-xs resize-none h-20 focus:bg-selected" autocomplete="off" bind:value={currentChar.data.systemPrompt}></textarea> <textarea class="bg-transparent input-text mt-2 mb-2 text-gray-200 text-xs resize-none h-20 focus:bg-selected" autocomplete="off" bind:value={currentChar.data.systemPrompt}></textarea>
<span class="text-neutral-200">{language.chatNotes} <Help key="chatNote"/></span>
<textarea class="bg-transparent input-text mt-2 mb-2 text-gray-200 resize-none h-20 focus:bg-selected text-xs" autocomplete="off" bind:value={currentChar.data.chats[currentChar.data.chatPage].note}></textarea>
<span class="text-gray-400 mb-6 text-sm">{tokens.localNote} {language.tokens}</span>
{#if currentChar.data.chats[currentChar.data.chatPage].supaMemoryData && currentChar.data.chats[currentChar.data.chatPage].supaMemoryData.length > 4} {#if currentChar.data.chats[currentChar.data.chatPage].supaMemoryData && currentChar.data.chats[currentChar.data.chatPage].supaMemoryData.length > 4}
<span class="text-neutral-200">{language.SuperMemory}</span> <span class="text-neutral-200">{language.SuperMemory}</span>
<textarea class="bg-transparent input-text mt-2 mb-2 text-gray-200 text-xs resize-none h-20 focus:bg-selected" autocomplete="off" bind:value={currentChar.data.chats[currentChar.data.chatPage].supaMemoryData}></textarea> <textarea class="bg-transparent input-text mt-2 mb-2 text-gray-200 text-xs resize-none h-20 focus:bg-selected" autocomplete="off" bind:value={currentChar.data.chats[currentChar.data.chatPage].supaMemoryData}></textarea>

View File

@@ -0,0 +1,35 @@
<script lang="ts">
import { DataBase } from "src/ts/database";
import { isTauri } from "src/ts/globalApi";
import { getHordeModels } from "src/ts/horde/getModels";
export let value = ""
</script>
{#await getHordeModels()}
<select class="bg-transparent input-text mt-2 mb-2 text-gray-200 appearance-none text-sm" value="">
<option value="" class="bg-darkbg appearance-none">Loading...</option>
</select>
{:then models}
<select class="bg-transparent input-text mt-2 mb-2 text-gray-200 appearance-none text-sm" bind:value>
<optgroup class="bg-darkbg appearance-none" label="OpenAI">
<option value="gpt35" class="bg-darkbg appearance-none">OpenAI GPT-3.5</option>
<option value="gpt4" class="bg-darkbg appearance-none">OpenAI GPT-4</option>
</optgroup>
<optgroup class="bg-darkbg appearance-none" label="Other Providers">
<option value="palm2" class="bg-darkbg appearance-none">Google Palm2</option>
{#if value === 'novelai' || isTauri}
<option value="novelai" class="bg-darkbg appearance-none">NovelAI Clio</option>
{/if}
<option value="textgen_webui" class="bg-darkbg appearance-none">Text Generation WebUI</option>
{#if $DataBase.plugins.length > 0}
<option value="custom" class="bg-darkbg appearance-none">Plugin</option>
{/if}
</optgroup>
<optgroup class="bg-darkbg appearance-none" label="Horde">
{#each models as model}
<option value={"horde:::" + model} class="bg-darkbg appearance-none">{model}</option>
{/each}
</optgroup>
</select>
{/await}

View File

@@ -203,7 +203,8 @@ function convertOldTavernAndJSON(charaData:OldTavernChar, imgp:string|undefined
characterVersion: 0, characterVersion: 0,
personality: charaData.personality ?? '', personality: charaData.personality ?? '',
scenario:charaData.scenario ?? '', scenario:charaData.scenario ?? '',
firstMsgIndex: -1 firstMsgIndex: -1,
replaceGlobalNote: ""
} }
} }
@@ -381,7 +382,7 @@ async function importSpecv2(card:CharacterCardV2, img?:Uint8Array):Promise<boole
mode: "normal", mode: "normal",
alwaysActive: book.constant ?? false, alwaysActive: book.constant ?? false,
selective: book.selective ?? false, selective: book.selective ?? false,
extentions: book.extensions extentions: {...book.extensions, risu_case_sensitive: book.case_sensitive}
}) })
} }
@@ -412,7 +413,7 @@ async function importSpecv2(card:CharacterCardV2, img?:Uint8Array):Promise<boole
exampleMessage: data.mes_example ?? '', exampleMessage: data.mes_example ?? '',
creatorNotes:data.creator_notes ?? '', creatorNotes:data.creator_notes ?? '',
systemPrompt:data.system_prompt ?? '', systemPrompt:data.system_prompt ?? '',
postHistoryInstructions:data.post_history_instructions ?? '', postHistoryInstructions:'',
alternateGreetings:data.alternate_greetings ?? [], alternateGreetings:data.alternate_greetings ?? [],
tags:data.tags ?? [], tags:data.tags ?? [],
creator:data.creator ?? '', creator:data.creator ?? '',
@@ -428,7 +429,8 @@ async function importSpecv2(card:CharacterCardV2, img?:Uint8Array):Promise<boole
creator: data.creator, creator: data.creator,
character_version: data.character_version character_version: data.character_version
}, },
additionalAssets: extAssets additionalAssets: extAssets,
replaceGlobalNote: data.post_history_instructions ?? ''
} }
db.characters.push(char) db.characters.push(char)
@@ -458,7 +460,8 @@ export async function exportSpecV2(char:character) {
constant: lore.alwaysActive, constant: lore.alwaysActive,
selective:lore.selective, selective:lore.selective,
name: lore.comment, name: lore.comment,
comment: lore.comment comment: lore.comment,
case_sensitive: lore.extentions?.risu_case_sensitive
}) })
} }
@@ -474,7 +477,7 @@ export async function exportSpecV2(char:character) {
mes_example: char.exampleMessage, mes_example: char.exampleMessage,
creator_notes: char.creatorNotes, creator_notes: char.creatorNotes,
system_prompt: char.systemPrompt, system_prompt: char.systemPrompt,
post_history_instructions: char.postHistoryInstructions, post_history_instructions: char.replaceGlobalNote,
alternate_greetings: char.alternateGreetings, alternate_greetings: char.alternateGreetings,
character_book: { character_book: {
scan_depth: char.loreSettings?.scanDepth, scan_depth: char.loreSettings?.scanDepth,
@@ -625,5 +628,5 @@ interface charBookEntry{
secondary_keys?: Array<string> // see field `selective`. ignored if selective == false secondary_keys?: Array<string> // see field `selective`. ignored if selective == false
constant?: boolean // if true, always inserted in the prompt (within budget limit) 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 position?: 'before_char' | 'after_char' // whether the entry is placed before or after the character defs
case_sensitive?:boolean
} }

View File

@@ -275,7 +275,6 @@ export function characterFormatUpdate(index:number|character){
cha.exampleMessage = cha.exampleMessage ?? '' cha.exampleMessage = cha.exampleMessage ?? ''
cha.creatorNotes = cha.creatorNotes ?? '' cha.creatorNotes = cha.creatorNotes ?? ''
cha.systemPrompt = cha.systemPrompt ?? '' cha.systemPrompt = cha.systemPrompt ?? ''
cha.postHistoryInstructions = cha.postHistoryInstructions ?? ''
cha.tags = cha.tags ?? [] cha.tags = cha.tags ?? []
cha.creator = cha.creator ?? '' cha.creator = cha.creator ?? ''
cha.characterVersion = cha.characterVersion ?? 0 cha.characterVersion = cha.characterVersion ?? 0
@@ -288,6 +287,12 @@ export function characterFormatUpdate(index:number|character){
character_version: 0 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)){ if(checkNullish(cha.customscript)){
cha.customscript = [] cha.customscript = []
@@ -332,7 +337,8 @@ export function createBlankChar():character{
characterVersion: 0, characterVersion: 0,
personality:"", personality:"",
scenario:"", scenario:"",
firstMsgIndex: -1 firstMsgIndex: -1,
replaceGlobalNote: ""
} }
} }

View File

@@ -259,7 +259,9 @@ export interface loreBook{
mode: 'multiple'|'constant'|'normal', mode: 'multiple'|'constant'|'normal',
alwaysActive: boolean alwaysActive: boolean
selective:boolean selective:boolean
extentions?:{} extentions?:{
risu_case_sensitive:boolean
}
} }
export interface character{ export interface character{
@@ -303,6 +305,7 @@ export interface character{
supaMemory?:boolean supaMemory?:boolean
additionalAssets?:[string, string][] additionalAssets?:[string, string][]
ttsReadOnlyQuoted?:boolean ttsReadOnlyQuoted?:boolean
replaceGlobalNote:string
} }

34
src/ts/horde/getModels.ts Normal file
View File

@@ -0,0 +1,34 @@
import { sleep } from "../util"
let modelList:string[]|'loading' = null
//until horde is ready
modelList = []
export async function getHordeModels():Promise<string[]> {
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
}
}

View File

@@ -105,7 +105,7 @@ export async function sendChat(chatProcessIndex = -1):Promise<boolean> {
} }
if(!currentChar.utilityBot){ if(!currentChar.utilityBot){
const mainp = currentChar.systemPrompt.length > 3 ? currentChar.systemPrompt : db.mainPrompt const mainp = currentChar.systemPrompt || db.mainPrompt
unformated.main.push({ unformated.main.push({
role: 'system', role: 'system',
@@ -121,14 +121,14 @@ export async function sendChat(chatProcessIndex = -1):Promise<boolean> {
unformated.globalNote.push({ unformated.globalNote.push({
role: 'system', role: 'system',
content: replacePlaceholders(db.globalNote, currentChar.name) content: replacePlaceholders(currentChar.replaceGlobalNote || db.globalNote, currentChar.name)
}) })
} }
if(currentChat.note !== ''){ if(currentChat.note !== ''){
unformated.authorNote.push({ unformated.authorNote.push({
role: 'system', role: 'system',
content: replacePlaceholders(currentChar.postHistoryInstructions, currentChat.note) content: replacePlaceholders(currentChat.note, currentChar.name)
}) })
} }

View File

@@ -5,6 +5,8 @@ import { pluginProcess } from "./plugins";
import { language } from "../../lang"; import { language } from "../../lang";
import { stringlizeChat, unstringlizeChat } from "./stringlize"; import { stringlizeChat, unstringlizeChat } from "./stringlize";
import { globalFetch, isTauri } from "../globalApi"; import { globalFetch, isTauri } from "../globalApi";
import { alertError } from "../alert";
import { sleep } from "../util";
interface requestDataArgument{ interface requestDataArgument{
formated: OpenAIChat[] formated: OpenAIChat[]
@@ -34,7 +36,7 @@ export async function requestChatData(arg:requestDataArgument, model:'model'|'su
return da return da
} }
trys += 1 trys += 1
if(trys > db.requestRetrys){ if(trys > db.requestRetrys || model.startsWith('horde')){
return da return da
} }
} }
@@ -411,7 +413,107 @@ export async function requestChatDataMain(arg:requestDataArgument, model:'model'
} }
} }
} }
default:{ default:{
if(aiModel.startsWith("horde:::")){
const realModel = aiModel.split(":::")[1].trim()
const workers = ((await (await fetch("https://stablehorde.net/api/v2/workers")).json()) as {id:string,models:string[]}[]).filter((a) => {
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 { return {
type: 'fail', type: 'fail',
result: (language.errors.unknownModel) result: (language.errors.unknownModel)