Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/Persistent Context Token Counts #68

Merged
merged 3 commits into from
Jun 1, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
added token counts for persistent contexts
  • Loading branch information
neCo2 committed May 25, 2024
commit c58a020685ef9f18bbf6ccb151f1c36cd911b73c
269 changes: 220 additions & 49 deletions mikupad.html
Original file line number Diff line number Diff line change
@@ -382,6 +382,7 @@
color:var(--color-text);
}
html.nockoffAI #context-order-desc,
html.nockoffAI #contextTokensTable,
html.nockoffAI .modal-desc {
color:var(--color-base-80);
}
@@ -699,6 +700,9 @@
html.nockoffAI .SelectBox > select:disabled {
background: var(--color-disabled);
}
.relative {
position: relative;
}

.Checkbox {
user-select: none;
@@ -796,6 +800,17 @@
top: 1.45em;
right: 5px;
}
.token-counter {
pointer-events: none;
position:absolute;
bottom:0;
right:0;
transform: translate(0%,-50%);
color: var(--color-base-50);
background: color-mix(in srgb, var(--color-base-30), transparent 50%);
}.token-counter::after {
content:" Tokens";
}
button:hover {
background: var(--color-base-40);
}
@@ -2008,14 +2023,19 @@
<${InputBox} label="Suffix" type="text" placeholder="[/INST]"
readOnly=${!!cancel} value=${memoryTokens.suffix} onValueChange=${(value) => handleMemoryTokensChange("suffix", value)}/>
</div>
<textarea
readOnly=${!!cancel}
placeholder="Anything written here will be injected at the head of the prompt. Tokens here DO count towards the Context Limit."
defaultValue=${memoryTokens.text}
value=${memoryTokens.text}
onInput=${(e) => handleMemoryTokensChange("text", e.target.value) }
class="expanded-text-area-settings"
id="memory-area-settings"/>
<div class="relative">
<textarea
readOnly=${!!cancel}
placeholder="Anything written here will be injected at the head of the prompt. Tokens here DO count towards the Context Limit."
defaultValue=${memoryTokens.text}
value=${memoryTokens.text}
onInput=${(e) => handleMemoryTokensChange("text", e.target.value) }
class="expanded-text-area-settings"
id="memory-area-settings"/>
<div class="token-counter">
${memoryTokens.tokens}
</div>
</div>
</${Modal}>`;
}

@@ -2037,22 +2057,53 @@
<${InputBox} label="AN Injection Depth (0-N)" type="number" step="1"
readOnly=${!!cancel} value=${authorNoteDepth} onValueChange=${handleAuthorNoteDepthChange}/>
</div>
<textarea
readOnly=${!!cancel}
placeholder="Anything written here will be injected ${authorNoteDepth} newlines from bottom into context."
defaultValue=${authorNoteTokens.text}
value=${authorNoteTokens.text}
onInput=${(e) => handleauthorNoteTokensChange("text", e.target.value) }
class="expanded-text-area-settings"
id="expanded-an-settings"/>
<div class="relative">
<textarea
readOnly=${!!cancel}
placeholder="Anything written here will be injected ${authorNoteDepth} newlines from bottom into context."
defaultValue=${authorNoteTokens.text}
value=${authorNoteTokens.text}
onInput=${(e) => handleauthorNoteTokensChange("text", e.target.value) }
class="expanded-text-area-settings"
id="expanded-an-settings"/>
<div class="token-counter">
${authorNoteTokens.tokens}
</div>
</div>
</${Modal}>`;
}

function ContextModal({ isOpen, closeModal, memoryTokens, handleMemoryTokensChange, modifiedPrompt, defaultPresets, cancel }) {
function ContextModal({ isOpen, closeModal, tokens, memoryTokens, authorNoteTokens, handleMemoryTokensChange, modifiedPrompt, defaultPresets, cancel }) {
return html`
<${Modal} isOpen=${isOpen} onClose=${closeModal}
title="Context"
description="This is the prompt being sent to your large language model.">
<div id="advancedContextPlaceholders">
<table id="contextTokensTable" border="1" frame="void" rules="all">
<thead>
<tr>
<th></th>
<th>Memory</th>
<th>World Info</th>
<th>Author's Note</th>
<th>Prompt</th>
<th></th>
<th>Total</th>
</tr>
</thead>
<tbody>
<tr>
<th>Tokens</th>
<td>${memoryTokens.tokens}</td>
<td>${memoryTokens.tokensWI}</td>
<td>${authorNoteTokens.tokens}</td>
<td>${tokens - authorNoteTokens.tokens - memoryTokens.tokensWI - memoryTokens.tokens}</td>
<td></td>
<td>${tokens}</td>
</tr>
</tbody>
</table>
</div>
<${CollapsibleGroup} label="Advanced Context Ordering">
<div id="context-order-desc">
You can use the following placeholders to order the context according to your needs:<br />
@@ -3556,6 +3607,7 @@
const sessionReconnectTimer = useRef();
const useScrollSmoothing = useRef(true);
const [templates, setTemplates] = useDBTemplates(defaultPresets.instructTemplates);
const [templateReplacements, setTemplateReplacements] = useState(false);
const [templatesImport, setTemplatesImport] = useState(false);
const [selectedTemplate, setSelectedTemplate] = useSessionState('template', "Llama 3");
const [chatMode, setChatMode] = useSessionState('chatMode', false);
@@ -3616,13 +3668,157 @@
const [authorNoteDepth, setAuthorNoteDepth] = useSessionState('authorNoteDepth', defaultPresets.authorNoteDepth);
const [worldInfo, setWorldInfo] = useSessionState('worldInfo', defaultPresets.worldInfo);


function replacePlaceholders(string,placeholders) {
// give placeholders as json object
// { "placeholder":"replacement" }
return string.replace(/\{[^}]+\}/g, function (placeholder) {
return placeholders.hasOwnProperty(placeholder)
? placeholders[placeholder]
: placeholder;
}).replace(/\\n/g, '\n')
};
useMemo(() => {
setTemplateReplacements({
"{inst}": templates[selectedTemplate]?.instPre && templates[selectedTemplate]?.instPre !== ""
? templates[selectedTemplate]?.instPre
: "",
"{/inst}": templates[selectedTemplate]?.instSuf && templates[selectedTemplate]?.instSuf !== ""
? templates[selectedTemplate]?.instSuf
: "",
"{sys}": templates[selectedTemplate]?.sysPre && templates[selectedTemplate]?.sysPre !== ""
? templates[selectedTemplate]?.sysPre
: "",
"{/sys}": templates[selectedTemplate]?.sysSuf && templates[selectedTemplate]?.sysSuf !== ""
? templates[selectedTemplate]?.sysSuf
: "",
})
}, [selectedTemplate,templates])

// AN and Memory
function handleauthorNoteTokensChange(key,value) {
setAuthorNoteTokens((prevauthorNoteTokens) => ({ ...prevauthorNoteTokens, [key]: value }));
}
// token counts for an
useEffect(() => {
setNKeepTimeout(false)
const order = ["prefix","text","suffix"]
const assembled = authorNoteTokens.text && authorNoteTokens.text !== ""
? order.map(key => authorNoteTokens[key]).join("")
: "";
if (assembled == "" || endpointAPI == 3) {
setAuthorNoteTokens((prevauthorNoteTokens) => ({ ...prevauthorNoteTokens, "tokens": 0 }))
return
}
const ac = new AbortController();
const to = setTimeout(async () => {
try {
const tokenCount = await getTokenCount({
endpoint,
endpointAPI,
...(endpointAPI == 3 || endpointAPI == 0 ? { endpointAPIKey } : {}),
content: `${replacePlaceholders(assembled,templateReplacements)}`,
signal: ac.signal,
...(isMikupadEndpoint ? { proxyEndpoint: sessionStorage.proxyEndpoint } : {})
});
setAuthorNoteTokens((prevauthorNoteTokens) => ({
...prevauthorNoteTokens,
"tokens": tokenCount - 1
}));
} catch (e) {
if (e.name !== 'AbortError'){
reportError(e);
setNKeepTimeout(true)
setAuthorNoteTokens((prevauthorNoteTokens) => ({ ...prevauthorNoteTokens, "tokens": 0 }))
}
}
}, 500);

ac.signal.addEventListener('abort', () => clearTimeout(to));
return () => ac.abort();
},[modalState["context"],authorNoteTokens.text,authorNoteTokens.prefix,authorNoteTokens.suffix,cancel,endpoint,endpointAPI])

function handleMemoryTokensChange(key,value) {
setMemoryTokens((prevMemoryTokens) => ({ ...prevMemoryTokens, [key]: value }));
}
// token counts for memory
useEffect(() => {
setNKeepTimeout(false)
const order = ["prefix","text","suffix"]
const assembled = memoryTokens.text && memoryTokens.text !== ""
? order.map(key => memoryTokens[key]).join("")
: "";
if (assembled == "" || endpointAPI == 3){
setMemoryTokens((prevMemoryTokens) => ({ ...prevMemoryTokens, "tokens": 0 }));
return
}

const ac = new AbortController();
const to = setTimeout(async () => {
try {
const tokenCount = await getTokenCount({
endpoint,
endpointAPI,
...(endpointAPI == 3 || endpointAPI == 0 ? { endpointAPIKey } : {}),
content: `${replacePlaceholders(assembled,templateReplacements)}`,
signal: ac.signal,
...(isMikupadEndpoint ? { proxyEndpoint: sessionStorage.proxyEndpoint } : {})
});
setMemoryTokens((prevMemoryTokens) => ({
...prevMemoryTokens,
"tokens": tokenCount - 1
}));
} catch (e) {
if (e.name !== 'AbortError'){
reportError(e);
setNKeepTimeout(true)
setMemoryTokens((prevMemoryTokens) => ({ ...prevMemoryTokens, "tokens": 0 }));
}
}
}, 500);

ac.signal.addEventListener('abort', () => clearTimeout(to));
return () => ac.abort();
},[modalState["context"],memoryTokens.text,memoryTokens.prefix,memoryTokens.suffix,cancel,endpoint,endpointAPI])
// token counts for wi
useEffect(() => {
setNKeepTimeout(false)
const assembled = memoryTokens.worldInfo && memoryTokens.worldInfo !== ""
? [worldInfo.prefix,memoryTokens.worldInfo,worldInfo.suffix].join("")
: "";
if (assembled == "" || endpointAPI == 3){
setMemoryTokens((prevMemoryTokens) => ({ ...prevMemoryTokens, "tokensWI": 0 }));
return
}

const ac = new AbortController();
const to = setTimeout(async () => {
try {
const tokenCount = await getTokenCount({
endpoint,
endpointAPI,
...(endpointAPI == 3 || endpointAPI == 0 ? { endpointAPIKey } : {}),
content: `${replacePlaceholders(assembled,templateReplacements)}`,
signal: ac.signal,
...(isMikupadEndpoint ? { proxyEndpoint: sessionStorage.proxyEndpoint } : {})
});
setMemoryTokens((prevMemoryTokens) => ({
...prevMemoryTokens,
"tokensWI": tokenCount - 1
}));
} catch (e) {
if (e.name !== 'AbortError'){
reportError(e);
setNKeepTimeout(true)
setMemoryTokens((prevMemoryTokens) => ({ ...prevMemoryTokens, "tokensWI": 0 }));
}
}
}, 500);

ac.signal.addEventListener('abort', () => clearTimeout(to));
return () => ac.abort();
},[modalState["context"],worldInfo.prefix,memoryTokens.worldInfo,worldInfo.suffix,cancel,endpoint,endpointAPI])



const insertTemplate = (sysInst) => {
@@ -3667,7 +3863,6 @@
onInput({ target: elem });
}


const toggleModal = (modalKey) => {
setModalState((prevState) => ({
...prevState,
@@ -3789,11 +3984,7 @@
// replaced, (3) instruct template placeholders are replaced (4) non-empty
// lines are joined back together.
const permContextPrompt = workingContextOrder.split("\n").map(function (line) {
return line.replace(/\{[^}]+\}/g, function (placeholder) {
return contextReplacements.hasOwnProperty(placeholder)
? contextReplacements[placeholder]
: placeholder;
});
return replacePlaceholders(line,contextReplacements)
}).filter(function (line) {
return line.trim() !== "";
}).join("\n").replace(/\\n/g, '\n');
@@ -3802,28 +3993,7 @@
}, [contextLength, promptText, memoryTokens, authorNoteTokens, authorNoteDepth, assembledWorldInfo, worldInfo.prefix, worldInfo.suffix]);

const modifiedPrompt = useMemo(() => {
const templateReplacements = {
"{inst}": templates[selectedTemplate]?.instPre && templates[selectedTemplate]?.instPre !== ""
? templates[selectedTemplate]?.instPre
: "",
"{/inst}": templates[selectedTemplate]?.instSuf && templates[selectedTemplate]?.instSuf !== ""
? templates[selectedTemplate]?.instSuf
: "",
"{sys}": templates[selectedTemplate]?.sysPre && templates[selectedTemplate]?.sysPre !== ""
? templates[selectedTemplate]?.sysPre
: "",
"{/sys}": templates[selectedTemplate]?.sysSuf && templates[selectedTemplate]?.sysSuf !== ""
? templates[selectedTemplate]?.sysSuf
: "",
}
const finalPrompt = additionalContextPrompt
.replace(/\{[^}]+\}/g, function (placeholder) {
return templateReplacements.hasOwnProperty(placeholder)
? templateReplacements[placeholder]
: placeholder;
}).replace(/\\n/g, '\n');

return finalPrompt;
return replacePlaceholders(additionalContextPrompt,templateReplacements);
}, [additionalContextPrompt, templates, selectedTemplate]);

async function predict(prompt = modifiedPrompt, chunkCount = promptChunks.length) {
@@ -4154,7 +4324,7 @@
}, 500);
ac.signal.addEventListener('abort', () => clearTimeout(to));
return () => ac.abort();
}, [promptText, cancel, endpoint, endpointAPI]);
}, [modalState["context"], promptText, cancel, endpoint, endpointAPI]);

useEffect(() => {
if (endpointAPI != 3)
@@ -4762,7 +4932,7 @@
</${CollapsibleGroup}>
<${CollapsibleGroup} label="Persistent Context">
<label className="TextArea">
Memory
<div>Memory ${memoryTokens.tokens > 0 ? html`<small>(${memoryTokens.tokens} Tokens)</small>`:""}</div>
<textarea
readOnly=${!!cancel}
placeholder="Anything written here will be injected at the head of the prompt. Tokens here DO count towards the Context Limit."
@@ -4774,12 +4944,11 @@
className="textAreaSettings"
disabled=${!!cancel}
onClick=${() => toggleModal("memory")}>

<svg stroke="currentColor" fill="currentColor" stroke-width="0" viewBox="-1 -5 8 7" xmlns="http://www.w3.org/2000/svg"><path d="M0 0 3-3C3-4 3-5 5-5L4-4 5-3 6-4C6-2 5-2 4-2L1 1C0 2-1 1 0 0"></path></svg>
</button>
</label>
<label className="TextArea">
Author's Note
<div>Author's Note ${authorNoteTokens.tokens > 0 ? html`<small>(${authorNoteTokens.tokens} Tokens)</small>`:""}</div>
<textarea
readOnly=${!!cancel}
placeholder="Anything written here will be injected ${authorNoteDepth} newlines from bottom into context."
@@ -4913,7 +5082,9 @@
<${ContextModal}
isOpen=${modalState.context}
closeModal=${() => closeModal("context")}
tokens=${tokens}
memoryTokens=${memoryTokens}
authorNoteTokens=${authorNoteTokens}
handleMemoryTokensChange=${handleMemoryTokensChange}
modifiedPrompt=${modifiedPrompt}
defaultPresets=${defaultPresets}