feat: add prompt comparison feature
This commit is contained in:
@@ -47,6 +47,7 @@
|
||||
"core-js": "^3.35.0",
|
||||
"cors": "^2.8.5",
|
||||
"crc": "^4.3.2",
|
||||
"diff": "^7.0.0",
|
||||
"dompurify": "^3.0.8",
|
||||
"eventsource-parser": "^1.1.2",
|
||||
"exifr": "^7.1.3",
|
||||
|
||||
9
pnpm-lock.yaml
generated
9
pnpm-lock.yaml
generated
@@ -101,6 +101,9 @@ importers:
|
||||
crc:
|
||||
specifier: ^4.3.2
|
||||
version: 4.3.2(buffer@6.0.3)
|
||||
diff:
|
||||
specifier: ^7.0.0
|
||||
version: 7.0.0
|
||||
dompurify:
|
||||
specifier: ^3.0.8
|
||||
version: 3.0.8
|
||||
@@ -1888,6 +1891,10 @@ packages:
|
||||
resolution: {integrity: sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==}
|
||||
engines: {node: '>=0.3.1'}
|
||||
|
||||
diff@7.0.0:
|
||||
resolution: {integrity: sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==}
|
||||
engines: {node: '>=0.3.1'}
|
||||
|
||||
dir-glob@3.0.1:
|
||||
resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -5561,6 +5568,8 @@ snapshots:
|
||||
|
||||
diff@5.1.0: {}
|
||||
|
||||
diff@7.0.0: {}
|
||||
|
||||
dir-glob@3.0.1:
|
||||
dependencies:
|
||||
path-type: 4.0.0
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
<script lang="ts">
|
||||
import { alertCardExport, alertConfirm, alertError } from "../../ts/alert";
|
||||
import { alertCardExport, alertConfirm, alertError, alertMd, alertWait } from "../../ts/alert";
|
||||
import { language } from "../../lang";
|
||||
import { changeToPreset, copyPreset, downloadPreset, importPreset } from "../../ts/storage/database.svelte";
|
||||
import { changeToPreset, copyPreset, downloadPreset, importPreset, getDatabase } from "../../ts/storage/database.svelte";
|
||||
import { DBState } from 'src/ts/stores.svelte';
|
||||
import { CopyIcon, Share2Icon, PencilIcon, FolderUpIcon, PlusIcon, TrashIcon, XIcon } from "lucide-svelte";
|
||||
import { CopyIcon, Share2Icon, PencilIcon, FolderUpIcon, PlusIcon, TrashIcon, XIcon, GitCompare } from "lucide-svelte";
|
||||
import TextInput from "../UI/GUI/TextInput.svelte";
|
||||
import { prebuiltPresets } from "src/ts/process/templates/templates";
|
||||
import { ShowRealmFrameStore } from "src/ts/stores.svelte";
|
||||
import type { PromptItem, PromptItemPlain, PromptItemChatML, PromptItemTyped, PromptItemAuthorNote, PromptItemChat } from "src/ts/process/prompt.ts";
|
||||
import { diffWordsWithSpace, diffLines } from 'diff';
|
||||
|
||||
let editMode = $state(false)
|
||||
interface Props {
|
||||
@@ -15,6 +17,191 @@
|
||||
|
||||
let { close = () => {} }: Props = $props();
|
||||
|
||||
let diffMode = false
|
||||
let selectedPrompts: string[] = []
|
||||
let selectedDiffPreset = $state(-1)
|
||||
|
||||
function isPromptItemPlain(item: PromptItem): item is PromptItemPlain {
|
||||
return (
|
||||
item.type === 'plain' || item.type === 'jailbreak' || item.type === 'cot'
|
||||
);
|
||||
}
|
||||
|
||||
function isPromptItemChatML(item: PromptItem): item is PromptItemChatML {
|
||||
return item.type === 'chatML'
|
||||
}
|
||||
|
||||
function isPromptItemTyped(item: PromptItem): item is PromptItemTyped {
|
||||
return (
|
||||
item.type === 'persona' ||
|
||||
item.type === 'description' ||
|
||||
item.type === 'lorebook' ||
|
||||
item.type === 'postEverything' ||
|
||||
item.type === 'memory'
|
||||
)
|
||||
}
|
||||
|
||||
function isPromptItemAuthorNote(item: PromptItem): item is PromptItemAuthorNote {
|
||||
return item.type === 'authornote'
|
||||
}
|
||||
|
||||
function isPromptItemChat(item: PromptItem): item is PromptItemChat {
|
||||
return item.type === 'chat'
|
||||
}
|
||||
|
||||
function escapeHtml(text: string): string {
|
||||
return text
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''')
|
||||
.replace(/\//g, '/')
|
||||
.replace(/\\/g, '\')
|
||||
.replace(/`/g, '`')
|
||||
.replace(/ /g, ' \u200B')
|
||||
.replace(/\n/g, '<br>')
|
||||
}
|
||||
|
||||
function getPrompt(id: number): string {
|
||||
const db = getDatabase()
|
||||
const formated = safeStructuredClone(db.botPresets[id].promptTemplate)
|
||||
let prompt = ''
|
||||
|
||||
for(let i=0;i<formated.length;i++){
|
||||
const item = formated[i]
|
||||
|
||||
switch (true) {
|
||||
case isPromptItemPlain(item):{
|
||||
prompt += '## ' + (item.role ?? 'Unknown') + '; ' + item.type + '; ' + item.type2 + '\n'
|
||||
prompt += '\n' + item.text.replaceAll('```', '\\`\\`\\`') + '\n\n'
|
||||
break
|
||||
}
|
||||
|
||||
case isPromptItemChatML(item):{
|
||||
prompt += '## ' + item.type + '\n'
|
||||
prompt += '\n' + item.text.replaceAll('```', '\\`\\`\\`') + '\n\n'
|
||||
break
|
||||
}
|
||||
|
||||
case isPromptItemTyped(item):{
|
||||
prompt += '## ' + 'system' + '; ' + item.type + '\n'
|
||||
if(item.innerFormat){
|
||||
prompt += '\n' + item.innerFormat.replaceAll('```', '\\`\\`\\`') + '\n\n'
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
case isPromptItemAuthorNote(item):{
|
||||
prompt += '## ' + 'system' + '; ' + item.type + '\n'
|
||||
if(item.innerFormat){
|
||||
prompt += '\n' + item.innerFormat.replaceAll('```', '\\`\\`\\`') + '\n\n'
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
case isPromptItemChat(item):{
|
||||
prompt += '## ' + 'chat' + '; ' + item.type + '\n'
|
||||
prompt += '\n' + item.rangeStart + ' - ' + item.rangeEnd + '\n\n'
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return prompt
|
||||
}
|
||||
|
||||
function checkDiff(prompt1: string, prompt2: string): string {
|
||||
const lineDiffs = diffLines(prompt1, prompt2)
|
||||
|
||||
let resultHtml = '';
|
||||
|
||||
for (let i = 0; i < lineDiffs.length; i++) {
|
||||
const linePart = lineDiffs[i]
|
||||
|
||||
if(linePart.removed){
|
||||
const nextPart = lineDiffs[i + 1]
|
||||
if(nextPart?.added){
|
||||
resultHtml += `<div style="border-left: 4px solid blue; padding-left: 8px;">${highlightChanges(linePart.value, nextPart.value)}</div>`
|
||||
i++;
|
||||
}
|
||||
else{
|
||||
resultHtml += `<div style="color: red; background-color: #ffe6e6;">${escapeHtml(linePart.value)}</div>`
|
||||
}
|
||||
}
|
||||
else if(linePart.added){
|
||||
resultHtml += `<div style="color: green; background-color: #e6ffe6;">${escapeHtml(linePart.value)}</div>`
|
||||
}
|
||||
else{
|
||||
resultHtml += `<div>${escapeHtml(linePart.value)}</div>`
|
||||
}
|
||||
}
|
||||
|
||||
if(lineDiffs.length === 1 && !lineDiffs[0].added && !lineDiffs[0].removed) {
|
||||
resultHtml = `<div style="background-color: #4caf50; color: white; padding: 10px 20px; border-radius: 5px; text-align: center;">No differences detected.</div>` + resultHtml
|
||||
}
|
||||
else{
|
||||
resultHtml = `<div style="background-color: #ff9800; color: white; padding: 10px 20px; border-radius: 5px; text-align: center;">Differences detected. Please review the changes.</div>` + resultHtml
|
||||
}
|
||||
|
||||
return resultHtml
|
||||
}
|
||||
|
||||
function highlightChanges(string1: string, string2: string) {
|
||||
const charDiffs = diffWordsWithSpace(string1, string2)
|
||||
|
||||
return charDiffs
|
||||
.map(charPart => {
|
||||
const escapedText = escapeHtml(charPart.value)
|
||||
|
||||
if (charPart.added) {
|
||||
return `<span style="color: green; background-color: #e6ffe6;">${escapedText}</span>`
|
||||
}
|
||||
else if(charPart.removed) {
|
||||
return `<span style="color: red; background-color: #ffe6e6;">${escapedText}</span>`
|
||||
}
|
||||
else{
|
||||
return escapedText
|
||||
}
|
||||
})
|
||||
.join('')
|
||||
}
|
||||
|
||||
|
||||
function handleDiffMode(id: number) {
|
||||
if (selectedDiffPreset === id) {
|
||||
selectedDiffPreset = -1
|
||||
selectedPrompts = []
|
||||
diffMode = !diffMode
|
||||
return
|
||||
} else {
|
||||
selectedDiffPreset = id
|
||||
}
|
||||
|
||||
const prompt = getPrompt(id)
|
||||
|
||||
if(!prompt){
|
||||
return
|
||||
}
|
||||
|
||||
if(!diffMode){
|
||||
selectedPrompts = [prompt]
|
||||
}
|
||||
else if(selectedPrompts.length === 0){
|
||||
return
|
||||
}
|
||||
else{
|
||||
alertWait("Loading...")
|
||||
const result = checkDiff(selectedPrompts[0], prompt)
|
||||
alertMd(result)
|
||||
selectedDiffPreset = -1
|
||||
selectedPrompts = []
|
||||
}
|
||||
|
||||
diffMode = !diffMode
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<div class="absolute w-full h-full z-40 bg-black bg-opacity-50 flex justify-center items-center">
|
||||
@@ -47,6 +234,16 @@
|
||||
<span>{preset.name}</span>
|
||||
{/if}
|
||||
<div class="flex-grow flex justify-end">
|
||||
<div class="{selectedDiffPreset === i ? 'text-green-500' : 'text-textcolor2 hover:text-green-500'} cursor-pointer mr-2" role="button" tabindex="0" onclick={(e) => {
|
||||
e.stopPropagation()
|
||||
handleDiffMode(i)
|
||||
}} onkeydown={(e) => {
|
||||
if(e.key === 'Enter'){
|
||||
e.currentTarget.click()
|
||||
}
|
||||
}}>
|
||||
<GitCompare size={18}/>
|
||||
</div>
|
||||
<div class="text-textcolor2 hover:text-green-500 cursor-pointer mr-2" role="button" tabindex="0" onclick={(e) => {
|
||||
e.stopPropagation()
|
||||
copyPreset(i)
|
||||
|
||||
Reference in New Issue
Block a user