|
3046 | 3046 |
|
3047 | 3047 | const promptText = useMemo(() => joinPrompt(promptChunks), [promptChunks]);
|
3048 | 3048 |
|
3049 |
| - // compute separately as I imagine this can get expensive |
3050 |
| - const assembledWorldInfo = useMemo(() => { |
| 3049 | + const assembleWorldInfo = (promptText) => { |
3051 | 3050 | // assemble non-empty wi
|
3052 | 3051 | const validWorldInfo = !Array.isArray(worldInfo.entries) ? [] : worldInfo.entries.filter(entry =>
|
3053 | 3052 | entry.keys.length > 0 && !(entry.keys.length == 1 && entry.keys[0] == "") && entry.text !== "");
|
|
3082 | 3081 | });
|
3083 | 3082 | });
|
3084 | 3083 |
|
3085 |
| - const assembledWorldInfo = activeWorldInfo.length > 0 |
| 3084 | + return activeWorldInfo.length > 0 |
3086 | 3085 | ? activeWorldInfo.map(entry => entry.text).join("\n")
|
3087 | 3086 | : "";
|
| 3087 | + }; |
3088 | 3088 |
|
3089 |
| - return assembledWorldInfo |
| 3089 | + // compute separately as I imagine this can get expensive |
| 3090 | + const assembledWorldInfo = useMemo(() => { |
| 3091 | + return assembleWorldInfo(promptText); |
3090 | 3092 | }, [worldInfo]);
|
3091 | 3093 |
|
3092 |
| - const modifiedPrompt = useMemo(() => { |
3093 |
| - // add world info to memory for easier assembly |
3094 |
| - memoryTokens["worldInfo"] = assembledWorldInfo; |
| 3094 | + const assembleFinalPrompt = (assembledWorldInfo, promptText) => { |
| 3095 | + if ("worldInfo" in memoryTokens) |
| 3096 | + delete memoryTokens["worldInfo"]; |
3095 | 3097 |
|
3096 | 3098 | const order = ["prefix","text","suffix"]
|
3097 | 3099 | const assembledAuthorNote = authorNoteTokens.text && authorNoteTokens.text !== ""
|
|
3100 | 3102 |
|
3101 | 3103 | // replacements for the contextOrder string
|
3102 | 3104 | const replacements = {
|
3103 |
| - "{wiPrefix}": memoryTokens.worldInfo && memoryTokens.worldInfo !== "" |
| 3105 | + "{wiPrefix}": assembledWorldInfo && assembledWorldInfo !== "" |
3104 | 3106 | ? worldInfo.prefix
|
3105 | 3107 | : "", // wi prefix and suffix will be added whenever wi isn't empty
|
3106 |
| - "{wiText}": memoryTokens.worldInfo, |
3107 |
| - "{wiSuffix}": memoryTokens.worldInfo && memoryTokens.worldInfo !== "" |
| 3108 | + "{wiText}": assembledWorldInfo, |
| 3109 | + "{wiSuffix}": assembledWorldInfo && assembledWorldInfo !== "" |
3108 | 3110 | ? worldInfo.suffix
|
3109 | 3111 | : "",
|
3110 | 3112 |
|
3111 |
| - "{memPrefix}": memoryTokens.text && memoryTokens.text !== "" || memoryTokens.worldInfo !== "" |
| 3113 | + "{memPrefix}": memoryTokens.text && memoryTokens.text !== "" || assembledWorldInfo !== "" |
3112 | 3114 | ? memoryTokens.prefix
|
3113 | 3115 | : "", // memory prefix and suffix will be added whenever memory or wi aren't empty
|
3114 | 3116 | "{memText}": memoryTokens.text,
|
3115 |
| - "{memSuffix}": memoryTokens.text && memoryTokens.text !== "" || memoryTokens.worldInfo !== "" |
| 3117 | + "{memSuffix}": memoryTokens.text && memoryTokens.text !== "" || assembledWorldInfo !== "" |
3116 | 3118 | ? memoryTokens.suffix
|
3117 | 3119 | : "",
|
3118 | 3120 | }
|
|
3160 | 3162 | }).join("\n").replace(/\\n/g, '\n');
|
3161 | 3163 |
|
3162 | 3164 | return permContextPrompt;
|
| 3165 | + }; |
| 3166 | + |
| 3167 | + const modifiedPrompt = useMemo(() => { |
| 3168 | + return assembleFinalPrompt(assembledWorldInfo, promptText); |
3163 | 3169 | }, [contextLength, promptText, memoryTokens, authorNoteTokens, authorNoteDepth, assembledWorldInfo, worldInfo.prefix, worldInfo.suffix]);
|
3164 | 3170 |
|
3165 |
| - async function predict(prompt = modifiedPrompt, chunkCount = promptChunks.length) { |
| 3171 | + // predict all {fill} placeholders |
| 3172 | + async function fillsPredict() { |
| 3173 | + const fillPlaceholder = "{fill}"; |
| 3174 | + |
| 3175 | + let leftPromptChunks = []; |
| 3176 | + let rightPromptChunks = []; |
| 3177 | + let fillIdx = undefined; |
| 3178 | + |
| 3179 | + for (let i = 0; i < promptChunks.length; i++) { |
| 3180 | + const chunk = promptChunks[i]; |
| 3181 | + if (chunk.content.includes(fillPlaceholder)) { |
| 3182 | + // split the chunk in 2 |
| 3183 | + const left = { content: chunk.content.substring(0, chunk.content.indexOf(fillPlaceholder)), type: "user" }; |
| 3184 | + const right = { content: chunk.content.substring(chunk.content.indexOf(fillPlaceholder) + fillPlaceholder.length), type: "user" }; |
| 3185 | + fillIdx = i + 1; |
| 3186 | + leftPromptChunks = [ |
| 3187 | + ...promptChunks.slice(0, Math.max(0, i - 1)), |
| 3188 | + ...[left] |
| 3189 | + ]; |
| 3190 | + rightPromptChunks = [ |
| 3191 | + ...[right], |
| 3192 | + ...promptChunks.slice(i + 1, promptChunks.length - 1), |
| 3193 | + ]; |
| 3194 | + break; |
| 3195 | + } |
| 3196 | + } |
| 3197 | + |
| 3198 | + if (!fillIdx) |
| 3199 | + return; |
| 3200 | + |
| 3201 | + let promptText = joinPrompt(leftPromptChunks); |
| 3202 | + let assembledWorldInfo = assembleWorldInfo(promptText); |
| 3203 | + let finalPrompt = assembleFinalPrompt(assembledWorldInfo, promptText); |
| 3204 | + |
| 3205 | + predict(finalPrompt, leftPromptChunks.length, (chunk) => { |
| 3206 | + console.log(chunk); |
| 3207 | + if (rightPromptChunks[0]) { |
| 3208 | + if (chunk.content.trim().startsWith(rightPromptChunks[0].content[0])) { |
| 3209 | + if (chunk.content[0] == ' ' && rightPromptChunks[0].content[0] != ' ') { |
| 3210 | + rightPromptChunks[0].content = ' ' + rightPromptChunks[0].content; |
| 3211 | + setPromptChunks(p => [ |
| 3212 | + ...leftPromptChunks, |
| 3213 | + ...rightPromptChunks |
| 3214 | + ]); |
| 3215 | + } |
| 3216 | + return false; |
| 3217 | + } |
| 3218 | + } |
| 3219 | + leftPromptChunks = [ |
| 3220 | + ...leftPromptChunks, |
| 3221 | + chunk |
| 3222 | + ]; |
| 3223 | + setPromptChunks(p => [ |
| 3224 | + ...leftPromptChunks, |
| 3225 | + ...rightPromptChunks |
| 3226 | + ]); |
| 3227 | + setTokens(t => t + (chunk?.completion_probabilities?.length ?? 1)); |
| 3228 | + return true; |
| 3229 | + }); |
| 3230 | + } |
| 3231 | + |
| 3232 | + async function predict(prompt = modifiedPrompt, chunkCount = promptChunks.length, callback = undefined) { |
3166 | 3233 | if (cancel) {
|
3167 | 3234 | cancel?.();
|
3168 | 3235 |
|
|
3172 | 3239 | setCancel(() => () => cancelled = true);
|
3173 | 3240 | await new Promise(resolve => setTimeout(resolve, 500));
|
3174 | 3241 | if (cancelled)
|
3175 |
| - return; |
| 3242 | + return false; |
3176 | 3243 | }
|
3177 | 3244 |
|
3178 | 3245 | const ac = new AbortController();
|
|
3192 | 3259 | // so let's set the predictStartTokens beforehand.
|
3193 | 3260 | setPredictStartTokens(tokens);
|
3194 | 3261 |
|
3195 |
| - const tokenCount = await getTokenCount({ |
3196 |
| - endpoint, |
3197 |
| - endpointAPI, |
3198 |
| - ...(endpointAPI == 3 || endpointAPI == 0 ? { endpointAPIKey } : {}), |
3199 |
| - content: prompt, |
3200 |
| - signal: ac.signal, |
3201 |
| - ...(isMikupadEndpoint ? { proxyEndpoint: sessionStorage.proxyEndpoint } : {}) |
3202 |
| - }); |
3203 |
| - setTokens(tokenCount); |
3204 |
| - setPredictStartTokens(tokenCount); |
| 3262 | + if (!callback) { |
| 3263 | + const tokenCount = await getTokenCount({ |
| 3264 | + endpoint, |
| 3265 | + endpointAPI, |
| 3266 | + ...(endpointAPI == 3 || endpointAPI == 0 ? { endpointAPIKey } : {}), |
| 3267 | + content: prompt, |
| 3268 | + signal: ac.signal, |
| 3269 | + ...(isMikupadEndpoint ? { proxyEndpoint: sessionStorage.proxyEndpoint } : {}) |
| 3270 | + }); |
| 3271 | + setTokens(tokenCount); |
| 3272 | + setPredictStartTokens(tokenCount); |
3205 | 3273 |
|
3206 |
| - while (undoStack.current.at(-1) >= chunkCount) |
3207 |
| - undoStack.current.pop(); |
3208 |
| - undoStack.current.push(chunkCount); |
3209 |
| - redoStack.current = []; |
| 3274 | + while (undoStack.current.at(-1) >= chunkCount) |
| 3275 | + undoStack.current.pop(); |
| 3276 | + undoStack.current.push(chunkCount); |
| 3277 | + redoStack.current = []; |
| 3278 | + } |
3210 | 3279 | setUndoHovered(false);
|
3211 | 3280 | setRejectedAPIKey(false);
|
3212 | 3281 | promptArea.current.scrollTarget = undefined;
|
|
3259 | 3328 | chunk.content = chunk.stopping_word;
|
3260 | 3329 | if (!chunk.content)
|
3261 | 3330 | continue;
|
3262 |
| - setPromptChunks(p => [...p, chunk]); |
3263 |
| - setTokens(t => t + (chunk?.completion_probabilities?.length ?? 1)); |
| 3331 | + if (callback) { |
| 3332 | + if (!callback(chunk)) |
| 3333 | + break; |
| 3334 | + } else { |
| 3335 | + setPromptChunks(p => [...p, chunk]); |
| 3336 | + setTokens(t => t + (chunk?.completion_probabilities?.length ?? 1)); |
| 3337 | + } |
3264 | 3338 | chunkCount += 1;
|
3265 | 3339 | }
|
3266 | 3340 | } catch (e) {
|
|
3279 | 3353 | return false;
|
3280 | 3354 | } finally {
|
3281 | 3355 | setCancel(c => c === cancelThis ? null : c);
|
3282 |
| - if (undoStack.current.at(-1) === chunkCount) |
3283 |
| - undoStack.current.pop(); |
| 3356 | + if (!callback) { |
| 3357 | + if (undoStack.current.at(-1) === chunkCount) |
| 3358 | + undoStack.current.pop(); |
| 3359 | + } |
3284 | 3360 | }
|
| 3361 | + return true; |
3285 | 3362 | }
|
3286 | 3363 |
|
3287 | 3364 | function undo() {
|
|
3507 | 3584 | switch (`${altKey}:${ctrlKey}:${shiftKey}:${key}`) {
|
3508 | 3585 | case 'false:false:true:Enter':
|
3509 | 3586 | case 'false:true:false:Enter':
|
3510 |
| - predict(); |
| 3587 | + fillsPredict();//predict(); |
3511 | 3588 | break;
|
3512 | 3589 | case 'false:false:false:Escape':
|
3513 | 3590 | cancel();
|
|
3654 | 3731 | newValue = newValue.slice(0, -chunk.content.length);
|
3655 | 3732 | }
|
3656 | 3733 |
|
| 3734 | + // Merge chunks if they're from the user |
| 3735 | + let mergeUserChunks = (chunks, newContent) => { |
| 3736 | + let lastChunk = chunks[chunks.length - 1]; |
| 3737 | + while (lastChunk && lastChunk.type === 'user') { |
| 3738 | + lastChunk.content += newContent; |
| 3739 | + if (chunks[chunks.length - 2] && chunks[chunks.length - 2].type === 'user') { |
| 3740 | + newContent = lastChunk.content; |
| 3741 | + lastChunk = chunks[chunks.length - 2]; |
| 3742 | + chunks.splice(chunks.length - 1, 1); |
| 3743 | + } else { |
| 3744 | + return chunks; |
| 3745 | + } |
| 3746 | + } |
| 3747 | + return [...chunks, { type: 'user', content: newContent }]; |
| 3748 | + }; |
| 3749 | + |
| 3750 | + let newPrompt = [...start]; |
| 3751 | + if (newValue) { |
| 3752 | + newPrompt = mergeUserChunks(newPrompt, newValue); |
| 3753 | + } |
| 3754 | + if (end.length && end[0].type === 'user') { |
| 3755 | + newPrompt = mergeUserChunks(newPrompt, end.shift().content); |
| 3756 | + } |
| 3757 | + newPrompt.push(...end); |
| 3758 | + |
3657 | 3759 | // Remove all undo positions within the modified range.
|
3658 |
| - undoStack.current = undoStack.current.filter(pos => start.length < pos); |
| 3760 | + undoStack.current = undoStack.current.filter(pos => pos > start.length && pos < newPrompt.length); |
3659 | 3761 | if (!undoStack.current.length)
|
3660 | 3762 | setUndoHovered(false);
|
3661 | 3763 |
|
3662 |
| - // Update all undo positions. |
3663 |
| - if (start.length + end.length + (+!!newValue) !== oldPromptLength) { |
3664 |
| - // Reset redo stack if a new chunk is added/removed at the end. |
3665 |
| - if (!end.length) |
3666 |
| - redoStack.current = []; |
| 3764 | + // Adjust undo/redo stacks. |
| 3765 | + const chunkDifference = oldPromptLength - newPrompt.length; |
| 3766 | + undoStack.current = undoStack.current.map(pos => { |
| 3767 | + if (pos >= start.length) { |
| 3768 | + return pos - chunkDifference; |
| 3769 | + } |
| 3770 | + return pos; |
| 3771 | + }); |
3667 | 3772 |
|
3668 |
| - if (!oldPrompt.length) |
3669 |
| - undoStack.current = undoStack.current.map(pos => pos + 1); |
3670 |
| - else |
3671 |
| - undoStack.current = undoStack.current.map(pos => pos - oldPrompt.length); |
| 3773 | + // Reset redo stack if a new chunk is added/removed at the end. |
| 3774 | + if (chunkDifference < 0 && !end.length) { |
| 3775 | + redoStack.current = []; |
3672 | 3776 | }
|
3673 | 3777 |
|
3674 |
| - const newPrompt = [ |
3675 |
| - ...start, |
3676 |
| - ...(newValue ? [{ type: 'user', content: newValue }] : []), |
3677 |
| - ...end, |
3678 |
| - ]; |
3679 | 3778 | return newPrompt;
|
3680 | 3779 | });
|
3681 | 3780 | }
|
|
0 commit comments