[feat] prompt template

This commit is contained in:
kwaroran
2023-08-03 12:34:07 +09:00
parent 8894038e17
commit dc1799f7fc
8 changed files with 328 additions and 38 deletions

View File

@@ -10,7 +10,8 @@ export const languageEnglish = {
'authorNote': "Author's Note",
'lastChat': "Last Chat",
"description": "Character Description",
'personaPrompt':'Persona Prompt'
'personaPrompt':'Persona Prompt',
'plain': "Plain Prompt",
},
errors:{
toomuchtoken: 'Error: The minimum required token is greater than the Max Context Size.',
@@ -400,4 +401,8 @@ export const languageEnglish = {
hideRealm: "Hide RisuRealm",
popularityLevel: "{} Popularity",
colorScheme: "Color Scheme",
rangeStart: "Range Start",
rangeEnd: "Range End",
untilChatEnd: "Until Chat End",
usePromptTemplate: "Use Prompt Template",
}

View File

@@ -417,7 +417,17 @@
<div class="flex items-center mt-4">
<Check bind:check={$DataBase.promptPreprocess} name={language.promptPreprocess}/>
</div>
<div class="flex items-center mt-4">
{#if $DataBase.promptTemplate}
<Check check={true} name={language.usePromptTemplate} onChange={()=>{
$DataBase.promptTemplate = undefined
}}/>
{:else}
<Check check={false} name={language.usePromptTemplate} onChange={() => {
$DataBase.promptTemplate = []
}}/>
{/if}
</div>
<Button on:click={() => {openPresetList = true}} className="mt-4">{language.presets}</Button>
{/if}

View File

@@ -0,0 +1,45 @@
<script lang="ts">
import { PlusIcon } from "lucide-svelte";
import { language } from "src/lang";
import ProomptItem from "src/lib/UI/ProomptItem.svelte";
import type { Proompt } from "src/ts/process/proompt";
import { DataBase } from "src/ts/storage/database";
let sorted = 0
let opened = 0
const onOpen = () => {
opened += 1
}
const onClose = () => {
opened -= 1
}
</script>
<h2 class="mb-2 text-2xl font-bold mt-2">{language.prompt}</h2>
<div class="contain w-full max-w-full mt-4 flex flex-col p-3 border-selected border-1 bg-darkbg rounded-md">
{#if $DataBase.promptTemplate.length === 0}
<div class="text-textcolor2">No Format</div>
{/if}
{#key sorted}
{#each $DataBase.promptTemplate as proompt, i}
<ProomptItem bind:proompt={proompt} onRemove={() => {
let templates = $DataBase.promptTemplate
templates.splice(i, 1)
$DataBase.promptTemplate = templates
}} />
{/each}
{/key}
</div>
<button class="font-medium cursor-pointer hover:text-green-500" on:click={() => {
let value = $DataBase.promptTemplate ?? []
value.push({
type: "plain",
text: "",
role: "bot",
type2: 'normal'
})
$DataBase.promptTemplate = value
}}><PlusIcon /></button>

View File

@@ -17,6 +17,8 @@
import LanguageSettings from "./Pages/LanguageSettings.svelte";
import AccessibilitySettings from "./Pages/AccessibilitySettings.svelte";
import PersonaSettings from "./Pages/PersonaSettings.svelte";
import PromptSettings from "./Pages/PromptSettings.svelte";
import { DataBase } from "src/ts/storage/database";
let selected = -1
let openPresetList = false
let openLoreList = false
@@ -30,12 +32,23 @@
{#if window.innerWidth >= 700 || selected === -1}
<div class="flex h-full flex-col p-4 pt-8 bg-darkbg gap-2 overflow-y-auto relative"
class:w-full={window.innerWidth < 700}>
<button class="text-textcolor2 flex gap-2 items-center hover:text-textcolor" class:text-textcolor={selected === 1} on:click={() => {
<button class="flex gap-2 items-center hover:text-textcolor"
class:text-textcolor={selected === 1}
class:text-textcolor2={selected !== 1}
on:click={() => {
selected = 1
}}>
<BotIcon />
<span>{language.chatBot}</span>
</button>
{#if $DataBase.promptTemplate}
<button class="text-textcolor2 flex gap-2 items-center hover:text-textcolor" class:text-textcolor={selected === 13} on:click={() => {
selected = 13
}}>
<BotIcon />
<span>{language.prompt}</span>
</button>
{/if}
<button class="text-textcolor2 flex gap-2 items-center hover:text-textcolor" class:text-textcolor={selected === 12} on:click={() => {
selected = 12
}}>
@@ -137,6 +150,8 @@
<AccessibilitySettings/>
{:else if selected === 12}
<PersonaSettings/>
{:else if selected === 13}
<PromptSettings/>
{/if}
</div>
<button class="absolute top-2 right-2 hover:text-green-500 text-textcolor" on:click={() => {

View File

@@ -0,0 +1,65 @@
<script lang="ts">
import type { Proompt } from "src/ts/process/proompt";
import OptionInput from "./GUI/OptionInput.svelte";
import TextAreaInput from "./GUI/TextAreaInput.svelte";
import SelectInput from "./GUI/SelectInput.svelte";
import { language } from "src/lang";
import NumberInput from "./GUI/NumberInput.svelte";
import CheckInput from "./GUI/CheckInput.svelte";
import { XIcon } from "lucide-svelte";
export let proompt:Proompt
export let onRemove:() => void = () => {}
</script>
<div class="flex flex-col first:pt-0 first:mt-0 first:border-0 pt-2 mt-2 border-t border-t-selected">
<span>{language.type} <button class="float-right" on:click={onRemove}><XIcon /></button></span>
<SelectInput bind:value={proompt.type} on:change={() => {
if(proompt.type === 'plain' || proompt.type === 'jailbreak'){
proompt.text = ""
proompt.role = "bot"
}
if(proompt.type === 'chat'){
proompt.rangeStart = 0
proompt.rangeEnd = 'end'
}
}} >
<OptionInput value="plain">{language.formating.plain}</OptionInput>
<OptionInput value="jailbreak">{language.formating.jailbreak}</OptionInput>
<OptionInput value="chat">{language.Chat}</OptionInput>
<OptionInput value="persona">{language.formating.personaPrompt}</OptionInput>
<OptionInput value="description">{language.formating.description}</OptionInput>
<OptionInput value="authornote">{language.formating.authorNote}</OptionInput>
<OptionInput value="lorebook">{language.formating.lorebook}</OptionInput>
</SelectInput>
{#if proompt.type === 'plain' || proompt.type === 'jailbreak'}
<span>{language.prompt}</span>
<TextAreaInput bind:value={proompt.text} />
<span>{language.role}</span>
<SelectInput bind:value={proompt.role}>
<OptionInput value="plain">{language.user}</OptionInput>
<OptionInput value="jailbreak">{language.character}</OptionInput>
<OptionInput value="system">{language.systemPrompt}</OptionInput>
</SelectInput>
{/if}
{#if proompt.type === 'chat'}
<span>{language.rangeStart}</span>
<NumberInput bind:value={proompt.rangeStart} />
<span>{language.rangeEnd}</span>
{#if proompt.rangeEnd === 'end'}
<CheckInput name={language.untilChatEnd} check={true} onChange={() => {
if(proompt.type === 'chat'){
proompt.rangeEnd = 0
}
}} />
{:else}
<NumberInput bind:value={proompt.rangeEnd} marginBottom />
<CheckInput name={language.untilChatEnd} check={false} onChange={() => {
if(proompt.type === 'chat'){
proompt.rangeEnd = 'end'
}
}} />
{/if}
{/if}
</div>

View File

@@ -1,7 +1,7 @@
import { get, writable } from "svelte/store";
import { DataBase, setDatabase, type character } from "../storage/database";
import { CharEmotion, selectedCharID } from "../stores";
import { ChatTokenizer, tokenizeNum } from "../tokenizer";
import { ChatTokenizer, tokenize, tokenizeNum } from "../tokenizer";
import { language } from "../../lang";
import { alertError } from "../alert";
import { loadLoreBookPrompt } from "./lorebook";
@@ -169,7 +169,7 @@ export async function sendChat(chatProcessIndex = -1,arg:{chatAdditonalTokens?:n
'personaPrompt':([] as OpenAIChat[])
}
if(!currentChar.utilityBot){
if((!currentChar.utilityBot) && (!db.promptTemplate)){
const mainp = currentChar.systemPrompt?.replaceAll('{{original}}', db.mainPrompt) || db.mainPrompt
@@ -255,12 +255,86 @@ export async function sendChat(chatProcessIndex = -1,arg:{chatAdditonalTokens?:n
//await tokenize currernt
let currentTokens = db.maxResponse
if(db.promptTemplate){
const template = db.promptTemplate
async function tokenizeChatArray(chats:OpenAIChat[]){
for(const chat of chats){
const tokens = await tokenizer.tokenizeChat(chat)
currentTokens += tokens
}
}
for(const card of template){
switch(card.type){
case 'persona':{
await tokenizeChatArray(unformated.personaPrompt)
break
}
case 'description':{
await tokenizeChatArray(unformated.description)
break
}
case 'authornote':{
await tokenizeChatArray(unformated.authorNote)
break
}
case 'lorebook':{
await tokenizeChatArray(unformated.lorebook)
break
}
case 'plain':
case 'jailbreak':{
if((!db.jailbreakToggle) && (card.type === 'jailbreak')){
continue
}
const convertRole = {
"system": "system",
"user": "user",
"bot": "assistant"
} as const
let content = card.text
if(card.type2 === 'globalNote'){
content = (risuChatParser(currentChar.replaceGlobalNote?.replaceAll('{{original}}', content) || content, {chara:currentChar}))
}
else if(card.type2 === 'main'){
content = (risuChatParser(content, {chara: currentChar}))
}
else{
content = risuChatParser(content, {chara: currentChar})
}
const prompt:OpenAIChat ={
role: convertRole[card.role],
content: content
}
await tokenizeChatArray([prompt])
break
}
case 'chat':{
const start = card.rangeStart
const end = (card.rangeEnd === 'end') ? unformated.chats.length : card.rangeEnd
const chats = unformated.chats.slice(start, end)
await tokenizeChatArray(chats)
break
}
}
}
}
else{
for(const key in unformated){
const chats = unformated[key] as OpenAIChat[]
for(const chat of chats){
currentTokens += await tokenizer.tokenizeChat(chat)
}
}
}
const examples = exampleMessage(currentChar, db.username)
@@ -407,42 +481,97 @@ export async function sendChat(chatProcessIndex = -1,arg:{chatAdditonalTokens?:n
let formated:OpenAIChat[] = []
const formatOrder = cloneDeep(db.formatingOrder)
formatOrder.push('postEverything')
let sysPrompts:string[] = []
for(let i=0;i<formatOrder.length;i++){
const cha = unformated[formatOrder[i]]
if(cha.length === 1 && cha[0].content.length === 0){
function pushPrompts(cha:OpenAIChat[]){
for(const chat of cha){
if(!chat.content){
continue
}
else if(cha.length === 1 && cha[0].role === 'system'){
sysPrompts.push(cha[0].content)
}
else if(sysPrompts.length > 0){
const prompt = sysPrompts.join('\n')
if(prompt.replace(/\n/g,'').length > 3){
formated.push({
role: 'system',
content: prompt
})
}
sysPrompts = []
formated = formated.concat(cha)
if(chat.role === 'system'){
const endf = formated.at(-1)
if(endf && endf.role === 'system' && endf.memo === chat.memo && endf.name === chat.name){
formated[formated.length - 1].content += '\n\n' + chat.content
}
else{
formated = formated.concat(cha)
formated.push(chat)
}
formated.at(-1).content += ''
}
else{
formated.push(chat)
}
}
}
if(sysPrompts.length > 0){
const prompt = sysPrompts.join('\n')
if(db.promptTemplate){
const template = db.promptTemplate
if(prompt.replace(/\n/g,'').length > 3){
formated.push({
role: 'system',
content: prompt
})
for(const card of template){
switch(card.type){
case 'persona':{
pushPrompts(unformated.personaPrompt)
break
}
case 'description':{
pushPrompts(unformated.description)
break
}
case 'authornote':{
pushPrompts(unformated.authorNote)
break
}
case 'lorebook':{
pushPrompts(unformated.lorebook)
break
}
case 'plain':
case 'jailbreak':{
if((!db.jailbreakToggle) && (card.type === 'jailbreak')){
continue
}
const convertRole = {
"system": "system",
"user": "user",
"bot": "assistant"
} as const
let content = card.text
if(card.type2 === 'globalNote'){
content = (risuChatParser(currentChar.replaceGlobalNote?.replaceAll('{{original}}', content) || content, {chara:currentChar}))
}
else if(card.type2 === 'main'){
content = (risuChatParser(content, {chara: currentChar}))
}
else{
content = risuChatParser(content, {chara: currentChar})
}
const prompt:OpenAIChat ={
role: convertRole[card.role],
content: content
}
pushPrompts([prompt])
break
}
case 'chat':{
const start = card.rangeStart
const end = (card.rangeEnd === 'end') ? unformated.chats.length : card.rangeEnd
const chats = unformated.chats.slice(start, end)
pushPrompts(chats)
break
}
}
}
}
else{
for(let i=0;i<formatOrder.length;i++){
const cha = unformated[formatOrder[i]]
pushPrompts(cha)
}
sysPrompts = []
}

19
src/ts/process/proompt.ts Normal file
View File

@@ -0,0 +1,19 @@
export type Proompt = ProomptPlain|ProomptTyped|ProomptChat;
export interface ProomptPlain {
type: 'plain'|'jailbreak';
type2: 'normal'|'globalNote'|'main'
text: string;
role: 'user'|'bot'|'system';
}
export interface ProomptTyped {
type: 'persona'|'description'|'authornote'|'lorebook'
}
export interface ProomptChat {
type: 'chat';
rangeStart: number;
rangeEnd: number|'end';
}

View File

@@ -10,6 +10,7 @@ import { alertNormal } from '../alert';
import type { NAISettings } from '../process/models/nai';
import { prebuiltNAIpresets } from '../process/templates/templates';
import { defaultColorScheme, type ColorScheme } from '../gui/colorscheme';
import type { Proompt } from '../process/proompt';
export const DataBase = writable({} as any as Database)
export const loadedStore = writable(false)
@@ -609,6 +610,7 @@ export interface Database{
hideRealm:boolean
colorScheme:ColorScheme
colorSchemeName:string
promptTemplate?:Proompt[]
}
interface hordeConfig{