feat: add prompt comparison feature
This commit is contained in:
@@ -47,6 +47,7 @@
|
|||||||
"core-js": "^3.35.0",
|
"core-js": "^3.35.0",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"crc": "^4.3.2",
|
"crc": "^4.3.2",
|
||||||
|
"diff": "^7.0.0",
|
||||||
"dompurify": "^3.0.8",
|
"dompurify": "^3.0.8",
|
||||||
"eventsource-parser": "^1.1.2",
|
"eventsource-parser": "^1.1.2",
|
||||||
"exifr": "^7.1.3",
|
"exifr": "^7.1.3",
|
||||||
|
|||||||
9
pnpm-lock.yaml
generated
9
pnpm-lock.yaml
generated
@@ -101,6 +101,9 @@ importers:
|
|||||||
crc:
|
crc:
|
||||||
specifier: ^4.3.2
|
specifier: ^4.3.2
|
||||||
version: 4.3.2(buffer@6.0.3)
|
version: 4.3.2(buffer@6.0.3)
|
||||||
|
diff:
|
||||||
|
specifier: ^7.0.0
|
||||||
|
version: 7.0.0
|
||||||
dompurify:
|
dompurify:
|
||||||
specifier: ^3.0.8
|
specifier: ^3.0.8
|
||||||
version: 3.0.8
|
version: 3.0.8
|
||||||
@@ -1888,6 +1891,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==}
|
resolution: {integrity: sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==}
|
||||||
engines: {node: '>=0.3.1'}
|
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:
|
dir-glob@3.0.1:
|
||||||
resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==}
|
resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
@@ -5561,6 +5568,8 @@ snapshots:
|
|||||||
|
|
||||||
diff@5.1.0: {}
|
diff@5.1.0: {}
|
||||||
|
|
||||||
|
diff@7.0.0: {}
|
||||||
|
|
||||||
dir-glob@3.0.1:
|
dir-glob@3.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
path-type: 4.0.0
|
path-type: 4.0.0
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { alertCardExport, alertConfirm, alertError } from "../../ts/alert";
|
import { alertCardExport, alertConfirm, alertError, alertMd, alertWait } from "../../ts/alert";
|
||||||
import { language } from "../../lang";
|
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 { 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 TextInput from "../UI/GUI/TextInput.svelte";
|
||||||
import { prebuiltPresets } from "src/ts/process/templates/templates";
|
import { prebuiltPresets } from "src/ts/process/templates/templates";
|
||||||
import { ShowRealmFrameStore } from "src/ts/stores.svelte";
|
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)
|
let editMode = $state(false)
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -15,6 +17,191 @@
|
|||||||
|
|
||||||
let { close = () => {} }: Props = $props();
|
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>
|
</script>
|
||||||
|
|
||||||
<div class="absolute w-full h-full z-40 bg-black bg-opacity-50 flex justify-center items-center">
|
<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>
|
<span>{preset.name}</span>
|
||||||
{/if}
|
{/if}
|
||||||
<div class="flex-grow flex justify-end">
|
<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) => {
|
<div class="text-textcolor2 hover:text-green-500 cursor-pointer mr-2" role="button" tabindex="0" onclick={(e) => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
copyPreset(i)
|
copyPreset(i)
|
||||||
|
|||||||
Reference in New Issue
Block a user