|
3634 | 3634 |
|
3635 | 3635 | const promptText = useMemo(() => joinPrompt(promptChunks), [promptChunks]);
|
3636 | 3636 |
|
3637 |
| - // compute separately as I imagine this can get expensive |
3638 |
| - const assembledWorldInfo = useMemo(() => { |
| 3637 | + const assembleWorldInfo = (promptText) => { |
3639 | 3638 | // assemble non-empty wi
|
3640 | 3639 | const validWorldInfo = !Array.isArray(worldInfo.entries) ? [] : worldInfo.entries.filter(entry =>
|
3641 | 3640 | entry.keys.length > 0 && !(entry.keys.length == 1 && entry.keys[0] == "") && entry.text !== "");
|
|
3670 | 3669 | });
|
3671 | 3670 | });
|
3672 | 3671 |
|
3673 |
| - const assembledWorldInfo = activeWorldInfo.length > 0 |
| 3672 | + return activeWorldInfo.length > 0 |
3674 | 3673 | ? activeWorldInfo.map(entry => entry.text).join("\n")
|
3675 | 3674 | : "";
|
| 3675 | + }; |
3676 | 3676 |
|
3677 |
| - return assembledWorldInfo |
| 3677 | + // compute separately as I imagine this can get expensive |
| 3678 | + const assembledWorldInfo = useMemo(() => { |
| 3679 | + return assembleWorldInfo(promptText); |
3678 | 3680 | }, [worldInfo]);
|
3679 | 3681 |
|
3680 |
| - const additionalContextPrompt = useMemo(() => { |
3681 |
| - // add world info to memory for easier assembly |
3682 |
| - memoryTokens["worldInfo"] = assembledWorldInfo; |
| 3682 | + const assembleAdditionalContext = (assembledWorldInfo, promptText) => { |
| 3683 | + if ("worldInfo" in memoryTokens) |
| 3684 | + delete memoryTokens["worldInfo"]; |
3683 | 3685 |
|
3684 | 3686 | const order = ["prefix","text","suffix"]
|
3685 | 3687 | const assembledAuthorNote = authorNoteTokens.text && authorNoteTokens.text !== ""
|
|
3688 | 3690 |
|
3689 | 3691 | // replacements for the contextOrder string
|
3690 | 3692 | const contextReplacements = {
|
3691 |
| - "{wiPrefix}": memoryTokens.worldInfo && memoryTokens.worldInfo !== "" |
| 3693 | + "{wiPrefix}": assembledWorldInfo && assembledWorldInfo !== "" |
3692 | 3694 | ? worldInfo.prefix
|
3693 | 3695 | : "", // wi prefix and suffix will be added whenever wi isn't empty
|
3694 |
| - "{wiText}": memoryTokens.worldInfo, |
3695 |
| - "{wiSuffix}": memoryTokens.worldInfo && memoryTokens.worldInfo !== "" |
| 3696 | + "{wiText}": assembledWorldInfo, |
| 3697 | + "{wiSuffix}": assembledWorldInfo && assembledWorldInfo !== "" |
3696 | 3698 | ? worldInfo.suffix
|
3697 | 3699 | : "",
|
3698 | 3700 |
|
3699 |
| - "{memPrefix}": memoryTokens.text && memoryTokens.text !== "" || memoryTokens.worldInfo !== "" |
| 3701 | + "{memPrefix}": memoryTokens.text && memoryTokens.text !== "" || assembledWorldInfo !== "" |
3700 | 3702 | ? memoryTokens.prefix
|
3701 | 3703 | : "", // memory prefix and suffix will be added whenever memory or wi aren't empty
|
3702 | 3704 | "{memText}": memoryTokens.text,
|
3703 |
| - "{memSuffix}": memoryTokens.text && memoryTokens.text !== "" || memoryTokens.worldInfo !== "" |
| 3705 | + "{memSuffix}": memoryTokens.text && memoryTokens.text !== "" || assembledWorldInfo !== "" |
3704 | 3706 | ? memoryTokens.suffix
|
3705 | 3707 | : "",
|
3706 | 3708 | }
|
|
3749 | 3751 | }).join("\n").replace(/\\n/g, '\n');
|
3750 | 3752 |
|
3751 | 3753 | return permContextPrompt;
|
| 3754 | + }; |
| 3755 | + |
| 3756 | + const additionalContextPrompt = useMemo(() => { |
| 3757 | + return assembleAdditionalContext(assembledWorldInfo, promptText); |
3752 | 3758 | }, [contextLength, promptText, memoryTokens, authorNoteTokens, authorNoteDepth, assembledWorldInfo, worldInfo.prefix, worldInfo.suffix]);
|
3753 | 3759 |
|
3754 |
| - const modifiedPrompt = useMemo(() => { |
| 3760 | + const assembleFinalPrompt = (additionalContextPrompt) => { |
3755 | 3761 | const templateReplacements = {
|
3756 | 3762 | "{inst}": templates[selectedTemplate]?.instPre && templates[selectedTemplate]?.instPre !== ""
|
3757 | 3763 | ? templates[selectedTemplate]?.instPre
|
|
3774 | 3780 | }).replace(/\\n/g, '\n');
|
3775 | 3781 |
|
3776 | 3782 | return finalPrompt;
|
| 3783 | + } |
| 3784 | + |
| 3785 | + const modifiedPrompt = useMemo(() => { |
| 3786 | + return assembleFinalPrompt(additionalContextPrompt); |
3777 | 3787 | }, [additionalContextPrompt, templates, selectedTemplate]);
|
3778 | 3788 |
|
3779 |
| - async function predict(prompt = modifiedPrompt, chunkCount = promptChunks.length) { |
| 3789 | + // predict all {fill} placeholders |
| 3790 | + async function fillsPredict() { |
| 3791 | + const fillPlaceholder = "{fill}"; |
| 3792 | + |
| 3793 | + let leftPromptChunks = []; |
| 3794 | + let rightPromptChunks = []; |
| 3795 | + let fillIdx = undefined; |
| 3796 | + |
| 3797 | + for (let i = 0; i < promptChunks.length; i++) { |
| 3798 | + const chunk = promptChunks[i]; |
| 3799 | + if (chunk.content.includes(fillPlaceholder)) { |
| 3800 | + // split the chunk in 2 |
| 3801 | + const left = { content: chunk.content.substring(0, chunk.content.indexOf(fillPlaceholder)), type: "user" }; |
| 3802 | + const right = { content: chunk.content.substring(chunk.content.indexOf(fillPlaceholder) + fillPlaceholder.length), type: "user" }; |
| 3803 | + fillIdx = i + 1; |
| 3804 | + leftPromptChunks = [ |
| 3805 | + ...promptChunks.slice(0, Math.max(0, i - 1)), |
| 3806 | + ...[left] |
| 3807 | + ]; |
| 3808 | + rightPromptChunks = [ |
| 3809 | + ...[right], |
| 3810 | + ...promptChunks.slice(i + 1, promptChunks.length - 1), |
| 3811 | + ]; |
| 3812 | + break; |
| 3813 | + } |
| 3814 | + } |
| 3815 | + |
| 3816 | + if (!fillIdx) |
| 3817 | + return; |
| 3818 | + |
| 3819 | + const promptText = joinPrompt(leftPromptChunks); |
| 3820 | + const assembledWorldInfo = assembleWorldInfo(promptText); |
| 3821 | + const additionalContextPrompt = assembleAdditionalContext(assembledWorldInfo, promptText); |
| 3822 | + const finalPrompt = assembleFinalPrompt(additionalContextPrompt); |
| 3823 | + |
| 3824 | + predict(finalPrompt, leftPromptChunks.length, (chunk) => { |
| 3825 | + console.log(chunk); |
| 3826 | + if (rightPromptChunks[0]) { |
| 3827 | + if (chunk.content.trim().startsWith(rightPromptChunks[0].content[0])) { |
| 3828 | + if (chunk.content[0] == ' ' && rightPromptChunks[0].content[0] != ' ') { |
| 3829 | + rightPromptChunks[0].content = ' ' + rightPromptChunks[0].content; |
| 3830 | + setPromptChunks(p => [ |
| 3831 | + ...leftPromptChunks, |
| 3832 | + ...rightPromptChunks |
| 3833 | + ]); |
| 3834 | + } |
| 3835 | + return false; |
| 3836 | + } |
| 3837 | + } |
| 3838 | + leftPromptChunks = [ |
| 3839 | + ...leftPromptChunks, |
| 3840 | + chunk |
| 3841 | + ]; |
| 3842 | + setPromptChunks(p => [ |
| 3843 | + ...leftPromptChunks, |
| 3844 | + ...rightPromptChunks |
| 3845 | + ]); |
| 3846 | + setTokens(t => t + (chunk?.completion_probabilities?.length ?? 1)); |
| 3847 | + return true; |
| 3848 | + }); |
| 3849 | + } |
| 3850 | + |
| 3851 | + async function predict(prompt = modifiedPrompt, chunkCount = promptChunks.length, callback = undefined) { |
3780 | 3852 | if (cancel) {
|
3781 | 3853 | cancel?.();
|
3782 | 3854 |
|
|
3786 | 3858 | setCancel(() => () => cancelled = true);
|
3787 | 3859 | await new Promise(resolve => setTimeout(resolve, 500));
|
3788 | 3860 | if (cancelled)
|
3789 |
| - return; |
| 3861 | + return false; |
3790 | 3862 | }
|
3791 | 3863 |
|
3792 | 3864 | const ac = new AbortController();
|
|
3806 | 3878 | // so let's set the predictStartTokens beforehand.
|
3807 | 3879 | setPredictStartTokens(tokens);
|
3808 | 3880 |
|
3809 |
| - const tokenCount = await getTokenCount({ |
3810 |
| - endpoint, |
3811 |
| - endpointAPI, |
3812 |
| - ...(endpointAPI == 3 || endpointAPI == 0 ? { endpointAPIKey } : {}), |
3813 |
| - content: prompt, |
3814 |
| - signal: ac.signal, |
3815 |
| - ...(isMikupadEndpoint ? { proxyEndpoint: sessionStorage.proxyEndpoint } : {}) |
3816 |
| - }); |
3817 |
| - setTokens(tokenCount); |
3818 |
| - setPredictStartTokens(tokenCount); |
3819 |
| - |
3820 |
| - // Chat Mode |
3821 |
| - if (chatMode && !restartedPredict) { |
3822 |
| - // add user EOT template (instruct suffix) if not switch completion |
3823 |
| - const eotUser = templates[selectedTemplate]?.instSuf.replace(/\\n/g, '\n') |
3824 |
| - setPromptChunks(p => [...p, { type: 'user', content: eotUser }]) |
3825 |
| - prompt += `${eotUser}` |
3826 |
| - } |
3827 |
| - setRestartedPredict(false) |
| 3881 | + if (!callback) { |
| 3882 | + const tokenCount = await getTokenCount({ |
| 3883 | + endpoint, |
| 3884 | + endpointAPI, |
| 3885 | + ...(endpointAPI == 3 || endpointAPI == 0 ? { endpointAPIKey } : {}), |
| 3886 | + content: prompt, |
| 3887 | + signal: ac.signal, |
| 3888 | + ...(isMikupadEndpoint ? { proxyEndpoint: sessionStorage.proxyEndpoint } : {}) |
| 3889 | + }); |
| 3890 | + setTokens(tokenCount); |
| 3891 | + setPredictStartTokens(tokenCount); |
| 3892 | + |
| 3893 | + // Chat Mode |
| 3894 | + if (chatMode && !restartedPredict) { |
| 3895 | + // add user EOT template (instruct suffix) if not switch completion |
| 3896 | + const eotUser = templates[selectedTemplate]?.instSuf.replace(/\\n/g, '\n') |
| 3897 | + setPromptChunks(p => [...p, { type: 'user', content: eotUser }]) |
| 3898 | + prompt += `${eotUser}` |
| 3899 | + } |
| 3900 | + setRestartedPredict(false) |
3828 | 3901 |
|
3829 |
| - while (undoStack.current.at(-1) >= chunkCount) |
3830 |
| - undoStack.current.pop(); |
3831 |
| - undoStack.current.push(chunkCount); |
3832 |
| - redoStack.current = []; |
| 3902 | + while (undoStack.current.at(-1) >= chunkCount) |
| 3903 | + undoStack.current.pop(); |
| 3904 | + undoStack.current.push(chunkCount); |
| 3905 | + redoStack.current = []; |
| 3906 | + } |
3833 | 3907 | setUndoHovered(false);
|
3834 | 3908 | setRejectedAPIKey(false);
|
3835 | 3909 | promptArea.current.scrollTarget = undefined;
|
|
3882 | 3956 | chunk.content = chunk.stopping_word;
|
3883 | 3957 | if (!chunk.content)
|
3884 | 3958 | continue;
|
3885 |
| - setPromptChunks(p => [...p, chunk]); |
3886 |
| - setTokens(t => t + (chunk?.completion_probabilities?.length ?? 1)); |
| 3959 | + if (callback) { |
| 3960 | + if (!callback(chunk)) |
| 3961 | + break; |
| 3962 | + } else { |
| 3963 | + setPromptChunks(p => [...p, chunk]); |
| 3964 | + setTokens(t => t + (chunk?.completion_probabilities?.length ?? 1)); |
| 3965 | + } |
3887 | 3966 | chunkCount += 1;
|
3888 | 3967 | }
|
3889 | 3968 | } catch (e) {
|
|
3902 | 3981 | return false;
|
3903 | 3982 | } finally {
|
3904 | 3983 | setCancel(c => c === cancelThis ? null : c);
|
3905 |
| - if (undoStack.current.at(-1) === chunkCount) |
3906 |
| - undoStack.current.pop(); |
| 3984 | + if (!callback) { |
| 3985 | + if (undoStack.current.at(-1) === chunkCount) |
| 3986 | + undoStack.current.pop(); |
| 3987 | + } |
3907 | 3988 | }
|
| 3989 | + |
3908 | 3990 | // Chat Mode
|
3909 |
| - if (chatMode) { |
| 3991 | + if (!callback && chatMode) { |
3910 | 3992 | // add bot EOT template (instruct prefix)
|
3911 | 3993 | const eotBot = templates[selectedTemplate]?.instPre.replace(/\\n/g, '\n')
|
3912 | 3994 | setPromptChunks(p => [...p, { type: 'user', content: eotBot }])
|
3913 | 3995 | prompt += `${eotBot}`
|
3914 | 3996 | }
|
| 3997 | + |
| 3998 | + return true; |
3915 | 3999 | }
|
3916 | 4000 |
|
3917 | 4001 | function undo() {
|
|
4139 | 4223 | switch (`${altKey}:${ctrlKey}:${shiftKey}:${key}`) {
|
4140 | 4224 | case 'false:false:true:Enter':
|
4141 | 4225 | case 'false:true:false:Enter':
|
4142 |
| - predict(); |
| 4226 | + fillsPredict();//predict(); |
4143 | 4227 | break;
|
4144 | 4228 | case 'false:false:false:Escape':
|
4145 | 4229 | cancel();
|
|
4286 | 4370 | newValue = newValue.slice(0, -chunk.content.length);
|
4287 | 4371 | }
|
4288 | 4372 |
|
| 4373 | + // Merge chunks if they're from the user |
| 4374 | + let mergeUserChunks = (chunks, newContent) => { |
| 4375 | + let lastChunk = chunks[chunks.length - 1]; |
| 4376 | + while (lastChunk && lastChunk.type === 'user') { |
| 4377 | + lastChunk.content += newContent; |
| 4378 | + if (chunks[chunks.length - 2] && chunks[chunks.length - 2].type === 'user') { |
| 4379 | + newContent = lastChunk.content; |
| 4380 | + lastChunk = chunks[chunks.length - 2]; |
| 4381 | + chunks.splice(chunks.length - 1, 1); |
| 4382 | + } else { |
| 4383 | + return chunks; |
| 4384 | + } |
| 4385 | + } |
| 4386 | + return [...chunks, { type: 'user', content: newContent }]; |
| 4387 | + }; |
| 4388 | + |
| 4389 | + let newPrompt = [...start]; |
| 4390 | + if (newValue) { |
| 4391 | + newPrompt = mergeUserChunks(newPrompt, newValue); |
| 4392 | + } |
| 4393 | + if (end.length && end[0].type === 'user') { |
| 4394 | + newPrompt = mergeUserChunks(newPrompt, end.shift().content); |
| 4395 | + } |
| 4396 | + newPrompt.push(...end); |
| 4397 | + |
4289 | 4398 | // Remove all undo positions within the modified range.
|
4290 |
| - undoStack.current = undoStack.current.filter(pos => start.length < pos); |
| 4399 | + undoStack.current = undoStack.current.filter(pos => pos > start.length && pos < newPrompt.length); |
4291 | 4400 | if (!undoStack.current.length)
|
4292 | 4401 | setUndoHovered(false);
|
4293 | 4402 |
|
4294 |
| - // Update all undo positions. |
4295 |
| - if (start.length + end.length + (+!!newValue) !== oldPromptLength) { |
4296 |
| - // Reset redo stack if a new chunk is added/removed at the end. |
4297 |
| - if (!end.length) |
4298 |
| - redoStack.current = []; |
| 4403 | + // Adjust undo/redo stacks. |
| 4404 | + const chunkDifference = oldPromptLength - newPrompt.length; |
| 4405 | + undoStack.current = undoStack.current.map(pos => { |
| 4406 | + if (pos >= start.length) { |
| 4407 | + return pos - chunkDifference; |
| 4408 | + } |
| 4409 | + return pos; |
| 4410 | + }); |
4299 | 4411 |
|
4300 |
| - if (!oldPrompt.length) |
4301 |
| - undoStack.current = undoStack.current.map(pos => pos + 1); |
4302 |
| - else |
4303 |
| - undoStack.current = undoStack.current.map(pos => pos - oldPrompt.length); |
| 4412 | + // Reset redo stack if a new chunk is added/removed at the end. |
| 4413 | + if (chunkDifference < 0 && !end.length) { |
| 4414 | + redoStack.current = []; |
4304 | 4415 | }
|
4305 | 4416 |
|
4306 |
| - const newPrompt = [ |
4307 |
| - ...start, |
4308 |
| - ...(newValue ? [{ type: 'user', content: newValue }] : []), |
4309 |
| - ...end, |
4310 |
| - ]; |
4311 | 4417 | return newPrompt;
|
4312 | 4418 | });
|
4313 | 4419 | }
|
|
0 commit comments