import { get } from "svelte/store"; import { tokenizeAccurate } from "../tokenizer"; import { DataBase, presetTemplate, setDatabase, type Database } from "../storage/database"; import { alertError, alertNormal } from "../alert"; import type { OobaChatCompletionRequestParams } from "../model/ooba"; export type PromptItem = PromptItemPlain|PromptItemTyped|PromptItemChat|PromptItemAuthorNote; export type PromptType = PromptItem['type']; export type PromptSettings = { assistantPrefill: string postEndInnerFormat: string sendChatAsSystem: boolean sendName: boolean utilOverride: boolean customChainOfThought?: boolean maxThoughtTagDepth?: number } export interface PromptItemPlain { type: 'plain'|'jailbreak'|'cot'; type2: 'normal'|'globalNote'|'main' text: string; role: 'user'|'bot'|'system'; } export interface PromptItemTyped { type: 'persona'|'description'|'lorebook'|'postEverything'|'memory' innerFormat?: string } export interface PromptItemAuthorNote { type : 'authornote' innerFormat?: string defaultText?: string } export interface PromptItemChat { type: 'chat'; rangeStart: number; rangeEnd: number|'end'; chatAsOriginalOnSystem?: boolean; } export async function tokenizePreset(prompts:PromptItem[], consti:boolean = false){ let total = 0 for(const prompt of prompts){ switch(prompt.type){ case 'plain': case 'jailbreak':{ total += await tokenizeAccurate(prompt.text, consti) break } case 'persona': case 'description': case 'lorebook': case 'postEverything': case 'authornote': case 'memory':{ if(prompt.innerFormat){ total += await tokenizeAccurate(prompt.innerFormat, consti) } break } } } return total } export function detectPromptJSONType(text:string){ function notNull(x:T|null):x is T{ return x !== null && x !== undefined } try { const parsed = JSON.parse(text) if(notNull(parsed.chat_completion_source) && Array.isArray(parsed.prompts)&& Array.isArray(parsed.prompt_order)){ return "STCHAT" } else if(notNull(parsed.temp) && notNull(parsed.rep_pen) && notNull(parsed.min_length)){ return "PARAMETERS" } else if(notNull(parsed.story_string) && notNull(parsed.chat_start)){ return "STCONTEXT" } else if(notNull(parsed.input_sequence) && notNull(parsed.output_sequence)){ return "STINST" } } catch (e) {} return 'NOTSUPPORTED' } const typePriority = [ 'STINST', 'PARAMETERS', 'STCONTEXT', 'STCHAT', ] type InstData = { "system_prompt": string, "input_sequence": string, "output_sequence": string, "last_output_sequence": string, "system_sequence": string, "stop_sequence": string, "system_sequence_prefix": string, "system_sequence_suffix": string, "first_output_sequence": string, "output_suffix": string, "input_suffix": string, "system_suffix": string, "user_alignment_message": string, "system_same_as_user": boolean, "last_system_sequence": string, "first_input_sequence": string, "last_input_sequence": string, "name": string } export function stChatConvert(pre:any){ //ST preset let promptTemplate = [] function findPrompt(identifier:number){ return pre.prompts.find((p:any) => p.identifier === identifier) } for(const prompt of pre?.prompt_order?.[0]?.order){ if(!prompt?.enabled){ continue } const p = findPrompt(prompt?.identifier ?? '') if(p){ switch(p.identifier){ case 'main':{ promptTemplate.push({ type: 'plain', type2: 'main', text: p.content ?? "", role: p.role ?? "system" }) break } case 'jailbreak': case 'nsfw':{ promptTemplate.push({ type: 'jailbreak', type2: 'normal', text: p.content ?? "", role: p.role ?? "system" }) break } case 'dialogueExamples': case 'charPersonality': case 'scenario':{ break //ignore } case 'chatHistory':{ promptTemplate.push({ type: 'chat', rangeEnd: 'end', rangeStart: 0 }) break } case 'worldInfoBefore':{ promptTemplate.push({ type: 'lorebook' }) break } case 'worldInfoAfter':{ break } case 'charDescription':{ promptTemplate.push({ type: 'description' }) break } case 'personaDescription':{ promptTemplate.push({ type: 'persona' }) break } default:{ console.log(p) promptTemplate.push({ type: 'plain', type2: 'normal', text: p.content ?? "", role: p.role ?? "system" }) } } } else{ console.log("Prompt not found", prompt) } } if(pre?.assistant_prefill){ promptTemplate.push({ type: 'postEverything' }) promptTemplate.push({ type: 'plain', type2: 'main', text: `{{#if {{prefill_supported}}}}${pre?.assistant_prefill}{{/if}}`, role: 'bot' }) } return promptTemplate } export const OobaParams = [ "tokenizer", "min_p", "top_k", "repetition_penalty", "repetition_penalty_range", "typical_p", "tfs", "top_a", "epsilon_cutoff", "eta_cutoff", "guidance_scale", "negative_prompt", "penalty_alpha", "mirostat_mode", "mirostat_tau", "mirostat_eta", "temperature_last", "do_sample", "seed", "encoder_repetition_penalty", "no_repeat_ngram_size", "min_length", "num_beams", "length_penalty", "early_stopping", "truncation_length", "max_tokens_second", "custom_token_bans", "auto_max_new_tokens", "ban_eos_token", "add_bos_token", "skip_special_tokens", "grammar_string" ] export function promptConvertion(files:{ name: string, content: string, type:string }[]){ let preset = structuredClone(presetTemplate) let instData = { "system_prompt": "", "input_sequence": "", "output_sequence": "", "last_output_sequence": "", "system_sequence": "", "stop_sequence": "", "system_sequence_prefix": "", "system_sequence_suffix": "", "first_output_sequence": "", "output_suffix": "", "input_suffix": "", "system_suffix": "", "user_alignment_message": "", "system_same_as_user": false, "last_system_sequence": "", "first_input_sequence": "", "last_input_sequence": "", "name": "" } let story_string = '' let chat_start = '' preset.name = '' let type = '' files = files.filter(x=>x.type !== 'NOTSUPPORTED').sort((a,b)=>{ return typePriority.indexOf(a.type) - typePriority.indexOf(b.type) }) if(files.findIndex(x=>x.type === 'STINST') !== -1){ type = 'STINST' } if(files.findIndex(x=>x.type === 'STCHAT') !== -1){ if(type !== ''){ alertError(`Both ${type} and STCHAT are not supported together.`) return } type = 'STCHAT' } let samplers:string[] = [] let oobaData:OobaChatCompletionRequestParams = { mode: 'instruct', } for(let i=0;i { if(getname === ''){ getname = setname } let multiplier = arg.multiplier ?? 1 if(samplers.includes(getname)){ //@ts-ignore preset[setname] = data[getname] * multiplier } else{ // @ts-ignore preset[setname] = -1000 } if(OobaParams.includes(getname)){ oobaData[getname] = data[getname] } } preset.name ||= instData.name ?? '' switch(file.type){ case 'STINST':{ instData = data as InstData if(data.system_same_as_user){ instData.system_sequence = '' instData.system_sequence_prefix = instData.input_sequence instData.system_sequence_suffix = instData.output_sequence } break } case 'PARAMETERS':{ samplers = data.samplers getParam('temperature', 'temp', {multiplier: 100}) getParam('top_p') getParam('top_k') getParam('top_a') getParam('min_p') getParam('repetition_penalty', 'rep_pen') getParam('frequencyPenalty', 'freq_pen', {multiplier: 100}) getParam('PresensePenalty', 'presence_penalty', {multiplier: 100}) for(const key of OobaParams){ if(samplers.includes(key) && (data[key] !== undefined) && (data[key] !== null)){ oobaData[key] = data[key] } } break } case 'STCONTEXT':{ story_string = data.story_string chat_start = data.chat_start break } case 'STCHAT':{ samplers = [] getParam('temperature', 'temperature', {multiplier: 100}) getParam('top_p') getParam('top_k') getParam('top_a') getParam('min_p') getParam('repetition_penalty', 'repetition_penalty') getParam('frequencyPenalty', 'frequency_penalty', {multiplier: 100}) getParam('PresensePenalty', 'presence_penalty', {multiplier: 100}) const prompts = stChatConvert(data) preset.promptTemplate = prompts } } } if(type === 'STCHAT'){ preset.aiModel = 'openrouter' preset.subModel = 'openrouter' const db = get(DataBase) db.botPresets.push(preset) setDatabase(db) alertNormal('Preset converted successfully. You can find it in bot setting presets') return } preset.reverseProxyOobaArgs = oobaData preset.promptTemplate = [{ type: 'plain', type2: 'main', text: '', role: 'system' },{ type: 'description', },{ type: 'persona', },{ type: 'lorebook', },{ type: 'chat', rangeStart: 0, rangeEnd: 'end', }, { type: 'authornote', }, { type: 'plain', type2: 'globalNote', text: '', role: 'system' }] //build a jinja template from the instData let jinja = '' jinja += story_string .replace(/{{user}}/gi, '{{risu_user}}') .replace(/{{user}}/gi, '{{risu_user}}') .replace(/{{system_prompt}}/gi, instData.system_prompt) .replace(/{{system}}/gi, instData.system_prompt) .replace(/{{#if (.+?){{\/if}}/gis, '') .replace(/{{(.+?)}}/gi, '') .replace(/\n\n+/g, '\n\n') jinja += chat_start jinja += `{% for message in messages %}` jinja += `{% if message.role == 'user' %}` jinja += instData.input_sequence jinja += `{{ message.content }}` jinja += instData.input_suffix jinja += `{% endif %}` jinja += `{% if message.role == 'assistant' %}` jinja += instData.output_sequence jinja += `{{ message.content }}` jinja += instData.output_suffix jinja += `{% endif %}` jinja += `{% if message.role == 'system' %}` jinja += instData.system_sequence jinja += instData.system_sequence_prefix jinja += `{{ message.content }}` jinja += instData.system_sequence_suffix jinja += instData.system_suffix jinja += `{% endif %}` jinja += `{% endfor %}` jinja += instData.output_sequence preset.instructChatTemplate = "jinja" preset.JinjaTemplate = jinja preset.aiModel = 'openrouter' preset.subModel = 'openrouter' preset.useInstructPrompt = true preset.name ||= 'Converted from JSON' const db = get(DataBase) db.botPresets.push(preset) setDatabase(db) alertNormal('Preset converted successfully. You can find it in bot setting presets') }