Chat Completion: switch to async token handling
| @@ -315,7 +315,7 @@ class PromptManager { | |||
| 315 | */ | 315 | */ |
| 316 | init(moduleConfiguration, serviceSettings) { | 316 | init(moduleConfiguration, serviceSettings) { |
| 317 | this.configuration = Object.assign(this.configuration, moduleConfiguration); | 317 | this.configuration = Object.assign(this.configuration, moduleConfiguration); |
| 318 | this.tokenHandler = this.tokenHandler || new TokenHandler(); | 318 | this.tokenHandler = this.tokenHandler || new TokenHandler(() => { throw new Error('Token handler not set'); }); |
| 319 | this.serviceSettings = serviceSettings; | 319 | this.serviceSettings = serviceSettings; |
| 320 | this.containerElement = document.getElementById(this.configuration.containerIdentifier); | 320 | this.containerElement = document.getElementById(this.configuration.containerIdentifier); |
| 321 | 321 | ||
| @@ -60,7 +60,7 @@ import { | |||
| 60 | resetScrollHeight, | 60 | resetScrollHeight, |
| 61 | stringFormat, | 61 | stringFormat, |
| 62 | } from './utils.js'; | 62 | } from './utils.js'; |
| 63 | import { countTokensOpenAI, getTokenizerModel } from './tokenizers.js'; | 63 | import { countTokensOpenAIAsync, getTokenizerModel } from './tokenizers.js'; |
| 64 | import { isMobile } from './RossAscends-mods.js'; | 64 | import { isMobile } from './RossAscends-mods.js'; |
| 65 | import { saveLogprobsForActiveMessage } from './logprobs.js'; | 65 | import { saveLogprobsForActiveMessage } from './logprobs.js'; |
| 66 | import { SlashCommandParser } from './slash-commands/SlashCommandParser.js'; | 66 | import { SlashCommandParser } from './slash-commands/SlashCommandParser.js'; |
| @@ -671,14 +671,14 @@ async function populateChatHistory(messages, prompts, chatCompletion, type = nul | |||
| 671 | 671 | ||
| 672 | // Reserve budget for new chat message | 672 | // Reserve budget for new chat message |
| 673 | const newChat = selected_group ? oai_settings.new_group_chat_prompt : oai_settings.new_chat_prompt; | 673 | const newChat = selected_group ? oai_settings.new_group_chat_prompt : oai_settings.new_chat_prompt; |
| 674 | const newChatMessage = new Message('system', substituteParams(newChat), 'newMainChat'); | 674 | const newChatMessage = await Message.createAsync('system', substituteParams(newChat), 'newMainChat'); |
| 675 | chatCompletion.reserveBudget(newChatMessage); | 675 | chatCompletion.reserveBudget(newChatMessage); |
| 676 | 676 | ||
| 677 | // Reserve budget for group nudge | 677 | // Reserve budget for group nudge |
| 678 | let groupNudgeMessage = null; | 678 | let groupNudgeMessage = null; |
| 679 | const noGroupNudgeTypes = ['impersonate']; | 679 | const noGroupNudgeTypes = ['impersonate']; |
| 680 | if (selected_group && prompts.has('groupNudge') && !noGroupNudgeTypes.includes(type)) { | 680 | if (selected_group && prompts.has('groupNudge') && !noGroupNudgeTypes.includes(type)) { |
| 681 | groupNudgeMessage = Message.fromPrompt(prompts.get('groupNudge')); | 681 | groupNudgeMessage = await Message.fromPromptAsync(prompts.get('groupNudge')); |
| 682 | chatCompletion.reserveBudget(groupNudgeMessage); | 682 | chatCompletion.reserveBudget(groupNudgeMessage); |
| 683 | } | 683 | } |
| 684 | 684 | ||
| @@ -693,12 +693,12 @@ async function populateChatHistory(messages, prompts, chatCompletion, type = nul | |||
| 693 | }; | 693 | }; |
| 694 | const continuePrompt = new Prompt(promptObject); | 694 | const continuePrompt = new Prompt(promptObject); |
| 695 | const preparedPrompt = promptManager.preparePrompt(continuePrompt); | 695 | const preparedPrompt = promptManager.preparePrompt(continuePrompt); |
| 696 | continueMessage = Message.fromPrompt(preparedPrompt); | 696 | continueMessage = await Message.fromPromptAsync(preparedPrompt); |
| 697 | chatCompletion.reserveBudget(continueMessage); | 697 | chatCompletion.reserveBudget(continueMessage); |
| 698 | } | 698 | } |
| 699 | 699 | ||
| 700 | const lastChatPrompt = messages[messages.length - 1]; | 700 | const lastChatPrompt = messages[messages.length - 1]; |
| 701 | const message = new Message('user', oai_settings.send_if_empty, 'emptyUserMessageReplacement'); | 701 | const message = await Message.createAsync('user', oai_settings.send_if_empty, 'emptyUserMessageReplacement'); |
| 702 | if (lastChatPrompt && lastChatPrompt.role === 'assistant' && oai_settings.send_if_empty && chatCompletion.canAfford(message)) { | 702 | if (lastChatPrompt && lastChatPrompt.role === 'assistant' && oai_settings.send_if_empty && chatCompletion.canAfford(message)) { |
| 703 | chatCompletion.insert(message, 'chatHistory'); | 703 | chatCompletion.insert(message, 'chatHistory'); |
| 704 | } | 704 | } |
| @@ -715,11 +715,11 @@ async function populateChatHistory(messages, prompts, chatCompletion, type = nul | |||
| 715 | // We do not want to mutate the prompt | 715 | // We do not want to mutate the prompt |
| 716 | const prompt = new Prompt(chatPrompt); | 716 | const prompt = new Prompt(chatPrompt); |
| 717 | prompt.identifier = `chatHistory-${messages.length - index}`; | 717 | prompt.identifier = `chatHistory-${messages.length - index}`; |
| 718 | const chatMessage = Message.fromPrompt(promptManager.preparePrompt(prompt)); | 718 | const chatMessage = await Message.fromPromptAsync(promptManager.preparePrompt(prompt)); |
| 719 | 719 | ||
| 720 | if (promptManager.serviceSettings.names_behavior === character_names_behavior.COMPLETION && prompt.name) { | 720 | if (promptManager.serviceSettings.names_behavior === character_names_behavior.COMPLETION && prompt.name) { |
| 721 | const messageName = promptManager.isValidName(prompt.name) ? prompt.name : promptManager.sanitizeName(prompt.name); | 721 | const messageName = promptManager.isValidName(prompt.name) ? prompt.name : promptManager.sanitizeName(prompt.name); |
| 722 | chatMessage.setName(messageName); | 722 | await chatMessage.setName(messageName); |
| 723 | } | 723 | } |
| 724 | 724 | ||
| 725 | if (imageInlining && chatPrompt.image) { | 725 | if (imageInlining && chatPrompt.image) { |
| @@ -729,9 +729,9 @@ async function populateChatHistory(messages, prompts, chatCompletion, type = nul | |||
| 729 | if (canUseTools && Array.isArray(chatPrompt.invocations)) { | 729 | if (canUseTools && Array.isArray(chatPrompt.invocations)) { |
| 730 | /** @type {import('./tool-calling.js').ToolInvocation[]} */ | 730 | /** @type {import('./tool-calling.js').ToolInvocation[]} */ |
| 731 | const invocations = chatPrompt.invocations; | 731 | const invocations = chatPrompt.invocations; |
| 732 | const toolCallMessage = new Message(chatMessage.role, undefined, 'toolCall-' + chatMessage.identifier); | 732 | const toolCallMessage = await Message.createAsync(chatMessage.role, undefined, 'toolCall-' + chatMessage.identifier); |
| 733 | const toolResultMessages = invocations.slice().reverse().map((invocation) => new Message('tool', invocation.result || '[No content]', invocation.id)); | 733 | const toolResultMessages = await Promise.all(invocations.slice().reverse().map((invocation) => Message.createAsync('tool', invocation.result || '[No content]', invocation.id))); |
| 734 | toolCallMessage.setToolCalls(invocations); | 734 | await toolCallMessage.setToolCalls(invocations); |
| 735 | if (chatCompletion.canAffordAll([toolCallMessage, ...toolResultMessages])) { | 735 | if (chatCompletion.canAffordAll([toolCallMessage, ...toolResultMessages])) { |
| 736 | for (const resultMessage of toolResultMessages) { | 736 | for (const resultMessage of toolResultMessages) { |
| 737 | chatCompletion.insertAtStart(resultMessage, 'chatHistory'); | 737 | chatCompletion.insertAtStart(resultMessage, 'chatHistory'); |
| @@ -748,7 +748,8 @@ async function populateChatHistory(messages, prompts, chatCompletion, type = nul | |||
| 748 | if (type === 'continue' && oai_settings.continue_prefill && chatPrompt === firstNonInjected) { | 748 | if (type === 'continue' && oai_settings.continue_prefill && chatPrompt === firstNonInjected) { |
| 749 | // in case we are using continue_prefill and the latest message is an assistant message, we want to prepend the users assistant prefill on the message | 749 | // in case we are using continue_prefill and the latest message is an assistant message, we want to prepend the users assistant prefill on the message |
| 750 | if (chatPrompt.role === 'assistant') { | 750 | if (chatPrompt.role === 'assistant') { |
| 751 | const collection = new MessageCollection('continuePrefill', new Message(chatMessage.role, substituteParams(oai_settings.assistant_prefill + '\n\n') + chatMessage.content, chatMessage.identifier)); | 751 | const continueMessage = await Message.createAsync(chatMessage.role, substituteParams(oai_settings.assistant_prefill + '\n\n') + chatMessage.content, chatMessage.identifier); |
| 752 | const collection = new MessageCollection('continuePrefill', ); | ||
| 752 | chatCompletion.add(collection, -1); | 753 | chatCompletion.add(collection, -1); |
| 753 | continue; | 754 | continue; |
| 754 | } | 755 | } |
| @@ -787,15 +788,16 @@ async function populateChatHistory(messages, prompts, chatCompletion, type = nul | |||
| 787 | * @param {ChatCompletion} chatCompletion - An instance of ChatCompletion class that will be populated with the prompts. | 788 | * @param {ChatCompletion} chatCompletion - An instance of ChatCompletion class that will be populated with the prompts. |
| 788 | * @param {Object[]} messageExamples - Array containing all message examples. | 789 | * @param {Object[]} messageExamples - Array containing all message examples. |
| 789 | */ | 790 | */ |
| 790 | function populateDialogueExamples(prompts, chatCompletion, messageExamples) { | 791 | async function populateDialogueExamples(prompts, chatCompletion, messageExamples) { |
| 791 | if (!prompts.has('dialogueExamples')) { | 792 | if (!prompts.has('dialogueExamples')) { |
| 792 | return; | 793 | return; |
| 793 | } | 794 | } |
| 794 | 795 | ||
| 795 | chatCompletion.add(new MessageCollection('dialogueExamples'), prompts.index('dialogueExamples')); | 796 | chatCompletion.add(new MessageCollection('dialogueExamples'), prompts.index('dialogueExamples')); |
| 796 | if (Array.isArray(messageExamples) && messageExamples.length) { | 797 | if (Array.isArray(messageExamples) && messageExamples.length) { |
| 797 | const newExampleChat = new Message('system', substituteParams(oai_settings.new_example_chat_prompt), 'newChat'); | 798 | const newExampleChat = await Message.createAsync('system', substituteParams(oai_settings.new_example_chat_prompt), 'newChat'); |
| 798 | [...messageExamples].forEach((dialogue, dialogueIndex) => { | 799 | for (const dialogue of [...messageExamples]) { |
| 800 | const dialogueIndex = messageExamples.indexOf(dialogue); | ||
| 799 | let examplesAdded = 0; | 801 | let examplesAdded = 0; |
| 800 | 802 | ||
| 801 | if (chatCompletion.canAfford(newExampleChat)) chatCompletion.insert(newExampleChat, 'dialogueExamples'); | 803 | if (chatCompletion.canAfford(newExampleChat)) chatCompletion.insert(newExampleChat, 'dialogueExamples'); |
| @@ -806,8 +808,8 @@ function populateDialogueExamples(prompts, chatCompletion, messageExamples) { | |||
| 806 | const content = prompt.content || ''; | 808 | const content = prompt.content || ''; |
| 807 | const identifier = `dialogueExamples ${dialogueIndex}-${promptIndex}`; | 809 | const identifier = `dialogueExamples ${dialogueIndex}-${promptIndex}`; |
| 808 | 810 | ||
| 809 | const chatMessage = new Message(role, content, identifier); | 811 | const chatMessage = await Message.createAsync(role, content, identifier); |
| 810 | chatMessage.setName(prompt.name); | 812 | await chatMessage.setName(prompt.name); |
| 811 | if (!chatCompletion.canAfford(chatMessage)) { | 813 | if (!chatCompletion.canAfford(chatMessage)) { |
| 812 | break; | 814 | break; |
| 813 | } | 815 | } |
| @@ -818,7 +820,7 @@ function populateDialogueExamples(prompts, chatCompletion, messageExamples) { | |||
| 818 | if (0 === examplesAdded) { | 820 | if (0 === examplesAdded) { |
| 819 | chatCompletion.removeLastFrom('dialogueExamples'); | 821 | chatCompletion.removeLastFrom('dialogueExamples'); |
| 820 | } | 822 | } |
| 821 | }); | 823 | } |
| 822 | } | 824 | } |
| 823 | } | 825 | } |
| 824 | 826 | ||
| @@ -873,7 +875,7 @@ function getPromptRole(role) { | |||
| 873 | */ | 875 | */ |
| 874 | async function populateChatCompletion(prompts, chatCompletion, { bias, quietPrompt, quietImage, type, cyclePrompt, messages, messageExamples }) { | 876 | async function populateChatCompletion(prompts, chatCompletion, { bias, quietPrompt, quietImage, type, cyclePrompt, messages, messageExamples }) { |
| 875 | // Helper function for preparing a prompt, that already exists within the prompt collection, for completion | 877 | // Helper function for preparing a prompt, that already exists within the prompt collection, for completion |
| 876 | const addToChatCompletion = (source, target = null) => { | 878 | const addToChatCompletion = async (source, target = null) => { |
| 877 | // We need the prompts array to determine a position for the source. | 879 | // We need the prompts array to determine a position for the source. |
| 878 | if (false === prompts.has(source)) return; | 880 | if (false === prompts.has(source)) return; |
| 879 | 881 | ||
| @@ -891,30 +893,31 @@ async function populateChatCompletion(prompts, chatCompletion, { bias, quietProm | |||
| 891 | 893 | ||
| 892 | const index = target ? prompts.index(target) : prompts.index(source); | 894 | const index = target ? prompts.index(target) : prompts.index(source); |
| 893 | const collection = new MessageCollection(source); | 895 | const collection = new MessageCollection(source); |
| 894 | collection.add(Message.fromPrompt(prompt)); | 896 | const message = await Message.fromPromptAsync(prompt); |
| 897 | collection.add(message); | ||
| 895 | chatCompletion.add(collection, index); | 898 | chatCompletion.add(collection, index); |
| 896 | }; | 899 | }; |
| 897 | 900 | ||
| 898 | chatCompletion.reserveBudget(3); // every reply is primed with <|start|>assistant<|message|> | 901 | chatCompletion.reserveBudget(3); // every reply is primed with <|start|>assistant<|message|> |
| 899 | // Character and world information | 902 | // Character and world information |
| 900 | addToChatCompletion('worldInfoBefore'); | 903 | await addToChatCompletion('worldInfoBefore'); |
| 901 | addToChatCompletion('main'); | 904 | await addToChatCompletion('main'); |
| 902 | addToChatCompletion('worldInfoAfter'); | 905 | await addToChatCompletion('worldInfoAfter'); |
| 903 | addToChatCompletion('charDescription'); | 906 | await addToChatCompletion('charDescription'); |
| 904 | addToChatCompletion('charPersonality'); | 907 | await addToChatCompletion('charPersonality'); |
| 905 | addToChatCompletion('scenario'); | 908 | await addToChatCompletion('scenario'); |
| 906 | addToChatCompletion('personaDescription'); | 909 | await addToChatCompletion('personaDescription'); |
| 907 | 910 | ||
| 908 | // Collection of control prompts that will always be positioned last | 911 | // Collection of control prompts that will always be positioned last |
| 909 | chatCompletion.setOverriddenPrompts(prompts.overriddenPrompts); | 912 | chatCompletion.setOverriddenPrompts(prompts.overriddenPrompts); |
| 910 | const controlPrompts = new MessageCollection('controlPrompts'); | 913 | const controlPrompts = new MessageCollection('controlPrompts'); |
| 911 | 914 | ||
| 912 | const impersonateMessage = Message.fromPrompt(prompts.get('impersonate')) ?? null; | 915 | const impersonateMessage = await Message.fromPromptAsync(prompts.get('impersonate')) ?? null; |
| 913 | if (type === 'impersonate') controlPrompts.add(impersonateMessage); | 916 | if (type === 'impersonate') controlPrompts.add(impersonateMessage); |
| 914 | 917 | ||
| 915 | // Add quiet prompt to control prompts | 918 | // Add quiet prompt to control prompts |
| 916 | // This should always be last, even in control prompts. Add all further control prompts BEFORE this prompt | 919 | // This should always be last, even in control prompts. Add all further control prompts BEFORE this prompt |
| 917 | const quietPromptMessage = Message.fromPrompt(prompts.get('quietPrompt')) ?? null; | 920 | const quietPromptMessage = await Message.fromPromptAsync(prompts.get('quietPrompt')) ?? null; |
| 918 | if (quietPromptMessage && quietPromptMessage.content) { | 921 | if (quietPromptMessage && quietPromptMessage.content) { |
| 919 | if (isImageInliningSupported() && quietImage) { | 922 | if (isImageInliningSupported() && quietImage) { |
| 920 | await quietPromptMessage.addImage(quietImage); | 923 | await quietPromptMessage.addImage(quietImage); |
| @@ -940,20 +943,23 @@ async function populateChatCompletion(prompts, chatCompletion, { bias, quietProm | |||
| 940 | return acc; | 943 | return acc; |
| 941 | }, []); | 944 | }, []); |
| 942 | 945 | ||
| 943 | [...systemPrompts, ...userRelativePrompts].forEach(identifier => addToChatCompletion(identifier)); | 946 | for (const identifier of [...systemPrompts, ...userRelativePrompts]) { |
| 947 | await addToChatCompletion(identifier); | ||
| 948 | } | ||
| 944 | 949 | ||
| 945 | // Add enhance definition instruction | 950 | // Add enhance definition instruction |
| 946 | if (prompts.has('enhanceDefinitions')) addToChatCompletion('enhanceDefinitions'); | 951 | if (prompts.has('enhanceDefinitions')) await addToChatCompletion('enhanceDefinitions'); |
| 947 | 952 | ||
| 948 | // Bias | 953 | // Bias |
| 949 | if (bias && bias.trim().length) addToChatCompletion('bias'); | 954 | if (bias && bias.trim().length) await addToChatCompletion('bias'); |
| 950 | 955 | ||
| 951 | // Tavern Extras - Summary | 956 | // Tavern Extras - Summary |
| 952 | if (prompts.has('summary')) { | 957 | if (prompts.has('summary')) { |
| 953 | const summary = prompts.get('summary'); | 958 | const summary = prompts.get('summary'); |
| 954 | 959 | ||
| 955 | if (summary.position) { | 960 | if (summary.position) { |
| 956 | chatCompletion.insert(Message.fromPrompt(summary), 'main', summary.position); | 961 | const message = await Message.fromPromptAsync(summary); |
| 962 | chatCompletion.insert(message, 'main', summary.position); | ||
| 957 | } | 963 | } |
| 958 | } | 964 | } |
| 959 | 965 | ||
| @@ -962,7 +968,8 @@ async function populateChatCompletion(prompts, chatCompletion, { bias, quietProm | |||
| 962 | const authorsNote = prompts.get('authorsNote'); | 968 | const authorsNote = prompts.get('authorsNote'); |
| 963 | 969 | ||
| 964 | if (authorsNote.position) { | 970 | if (authorsNote.position) { |
| 965 | chatCompletion.insert(Message.fromPrompt(authorsNote), 'main', authorsNote.position); | 971 | const message = await Message.fromPromptAsync(authorsNote); |
| 972 | chatCompletion.insert(message, 'main', authorsNote.position); | ||
| 966 | } | 973 | } |
| 967 | } | 974 | } |
| 968 | 975 | ||
| @@ -971,7 +978,8 @@ async function populateChatCompletion(prompts, chatCompletion, { bias, quietProm | |||
| 971 | const vectorsMemory = prompts.get('vectorsMemory'); | 978 | const vectorsMemory = prompts.get('vectorsMemory'); |
| 972 | 979 | ||
| 973 | if (vectorsMemory.position) { | 980 | if (vectorsMemory.position) { |
| 974 | chatCompletion.insert(Message.fromPrompt(vectorsMemory), 'main', vectorsMemory.position); | 981 | const message = await Message.fromPromptAsync(vectorsMemory); |
| 982 | chatCompletion.insert(message, 'main', vectorsMemory.position); | ||
| 975 | } | 983 | } |
| 976 | } | 984 | } |
| 977 | 985 | ||
| @@ -980,7 +988,8 @@ async function populateChatCompletion(prompts, chatCompletion, { bias, quietProm | |||
| 980 | const vectorsDataBank = prompts.get('vectorsDataBank'); | 988 | const vectorsDataBank = prompts.get('vectorsDataBank'); |
| 981 | 989 | ||
| 982 | if (vectorsDataBank.position) { | 990 | if (vectorsDataBank.position) { |
| 983 | chatCompletion.insert(Message.fromPrompt(vectorsDataBank), 'main', vectorsDataBank.position); | 991 | const message = await Message.fromPromptAsync(vectorsDataBank); |
| 992 | chatCompletion.insert(message, 'main', vectorsDataBank.position); | ||
| 984 | } | 993 | } |
| 985 | } | 994 | } |
| 986 | 995 | ||
| @@ -989,13 +998,15 @@ async function populateChatCompletion(prompts, chatCompletion, { bias, quietProm | |||
| 989 | const smartContext = prompts.get('smartContext'); | 998 | const smartContext = prompts.get('smartContext'); |
| 990 | 999 | ||
| 991 | if (smartContext.position) { | 1000 | if (smartContext.position) { |
| 992 | chatCompletion.insert(Message.fromPrompt(smartContext), 'main', smartContext.position); | 1001 | const message = await Message.fromPromptAsync(smartContext); |
| 1002 | chatCompletion.insert(message, 'main', smartContext.position); | ||
| 993 | } | 1003 | } |
| 994 | } | 1004 | } |
| 995 | 1005 | ||
| 996 | // Other relative extension prompts | 1006 | // Other relative extension prompts |
| 997 | for (const prompt of prompts.collection.filter(p => p.extension && p.position)) { | 1007 | for (const prompt of prompts.collection.filter(p => p.extension && p.position)) { |
| 998 | chatCompletion.insert(Message.fromPrompt(prompt), 'main', prompt.position); | 1008 | const message = await Message.fromPromptAsync(prompt); |
| 1009 | chatCompletion.insert(message, 'main', prompt.position); | ||
| 999 | } | 1010 | } |
| 1000 | 1011 | ||
| 1001 | // Pre-allocation of tokens for tool data | 1012 | // Pre-allocation of tokens for tool data |
| @@ -1003,7 +1014,7 @@ async function populateChatCompletion(prompts, chatCompletion, { bias, quietProm | |||
| 1003 | const toolData = {}; | 1014 | const toolData = {}; |
| 1004 | await ToolManager.registerFunctionToolsOpenAI(toolData); | 1015 | await ToolManager.registerFunctionToolsOpenAI(toolData); |
| 1005 | const toolMessage = [{ role: 'user', content: JSON.stringify(toolData) }]; | 1016 | const toolMessage = [{ role: 'user', content: JSON.stringify(toolData) }]; |
| 1006 | const toolTokens = tokenHandler.count(toolMessage); | 1017 | const toolTokens = await tokenHandler.countAsync(toolMessage); |
| 1007 | chatCompletion.reserveBudget(toolTokens); | 1018 | chatCompletion.reserveBudget(toolTokens); |
| 1008 | } | 1019 | } |
| 1009 | 1020 | ||
| @@ -1012,11 +1023,11 @@ async function populateChatCompletion(prompts, chatCompletion, { bias, quietProm | |||
| 1012 | 1023 | ||
| 1013 | // Decide whether dialogue examples should always be added | 1024 | // Decide whether dialogue examples should always be added |
| 1014 | if (power_user.pin_examples) { | 1025 | if (power_user.pin_examples) { |
| 1015 | populateDialogueExamples(prompts, chatCompletion, messageExamples); | 1026 | await populateDialogueExamples(prompts, chatCompletion, messageExamples); |
| 1016 | await populateChatHistory(messages, prompts, chatCompletion, type, cyclePrompt); | 1027 | await populateChatHistory(messages, prompts, chatCompletion, type, cyclePrompt); |
| 1017 | } else { | 1028 | } else { |
| 1018 | await populateChatHistory(messages, prompts, chatCompletion, type, cyclePrompt); | 1029 | await populateChatHistory(messages, prompts, chatCompletion, type, cyclePrompt); |
| 1019 | populateDialogueExamples(prompts, chatCompletion, messageExamples); | 1030 | await populateDialogueExamples(prompts, chatCompletion, messageExamples); |
| 1020 | } | 1031 | } |
| 1021 | 1032 | ||
| 1022 | chatCompletion.freeBudget(controlPrompts); | 1033 | chatCompletion.freeBudget(controlPrompts); |
| @@ -1281,7 +1292,7 @@ export async function prepareOpenAIMessages({ | |||
| 1281 | promptManager.setChatCompletion(chatCompletion); | 1292 | promptManager.setChatCompletion(chatCompletion); |
| 1282 | 1293 | ||
| 1283 | if (oai_settings.squash_system_messages && dryRun == false) { | 1294 | if (oai_settings.squash_system_messages && dryRun == false) { |
| 1284 | chatCompletion.squashSystemMessages(); | 1295 | await chatCompletion.squashSystemMessages(); |
| 1285 | } | 1296 | } |
| 1286 | 1297 | ||
| 1287 | // All information is up-to-date, render. | 1298 | // All information is up-to-date, render. |
| @@ -2127,8 +2138,11 @@ async function calculateLogitBias() { | |||
| 2127 | } | 2138 | } |
| 2128 | 2139 | ||
| 2129 | class TokenHandler { | 2140 | class TokenHandler { |
| 2130 | constructor(countTokenFn) { | 2141 | /** |
| 2131 | this.countTokenFn = countTokenFn; | 2142 | * @param {(messages: object[] | object, full?: boolean) => Promise<number>} countTokenAsyncFn Function to count tokens |
| 2143 | */ | ||
| 2144 | constructor(countTokenAsyncFn) { | ||
| 2145 | this.countTokenAsyncFn = countTokenAsyncFn; | ||
| 2132 | this.counts = { | 2146 | this.counts = { |
| 2133 | 'start_chat': 0, | 2147 | 'start_chat': 0, |
| 2134 | 'prompt': 0, | 2148 | 'prompt': 0, |
| @@ -2157,8 +2171,15 @@ class TokenHandler { | |||
| 2157 | this.counts[type] -= value; | 2171 | this.counts[type] -= value; |
| 2158 | } | 2172 | } |
| 2159 | 2173 | ||
| 2160 | count(messages, full, type) { | 2174 | /** |
| 2161 | const token_count = this.countTokenFn(messages, full); | 2175 | * Count tokens for a message or messages. |
| 2176 | * @param {object|any[]} messages Messages to count tokens for | ||
| 2177 | * @param {boolean} [full] Count full tokens | ||
| 2178 | * @param {string} [type] Identifier for the token count | ||
| 2179 | * @returns {Promise<number>} The token count | ||
| 2180 | */ | ||
| 2181 | async countAsync(messages, full, type) { | ||
| 2182 | const token_count = await this.countTokenAsyncFn(messages, full); | ||
| 2162 | this.counts[type] += token_count; | 2183 | this.counts[type] += token_count; |
| 2163 | 2184 | ||
| 2164 | return token_count; | 2185 | return token_count; |
| @@ -2178,7 +2199,7 @@ class TokenHandler { | |||
| 2178 | } | 2199 | } |
| 2179 | 2200 | ||
| 2180 | 2201 | ||
| 2181 | const tokenHandler = new TokenHandler(countTokensOpenAI); | 2202 | const tokenHandler = new TokenHandler(countTokensOpenAIAsync); |
| 2182 | 2203 | ||
| 2183 | // Thrown by ChatCompletion when a requested prompt couldn't be found. | 2204 | // Thrown by ChatCompletion when a requested prompt couldn't be found. |
| 2184 | class IdentifierNotFoundError extends Error { | 2205 | class IdentifierNotFoundError extends Error { |
| @@ -2228,6 +2249,7 @@ class Message { | |||
| 2228 | * @param {string} role - The role of the entity creating the message. | 2249 | * @param {string} role - The role of the entity creating the message. |
| 2229 | * @param {string} content - The actual content of the message. | 2250 | * @param {string} content - The actual content of the message. |
| 2230 | * @param {string} identifier - A unique identifier for the message. | 2251 | * @param {string} identifier - A unique identifier for the message. |
| 2252 | * @private Don't use this constructor directly. Use createAsync instead. | ||
| 2231 | */ | 2253 | */ |
| 2232 | constructor(role, content, identifier) { | 2254 | constructor(role, content, identifier) { |
| 2233 | this.identifier = identifier; | 2255 | this.identifier = identifier; |
| @@ -2239,18 +2261,32 @@ class Message { | |||
| 2239 | this.role = 'system'; | 2261 | this.role = 'system'; |
| 2240 | } | 2262 | } |
| 2241 | 2263 | ||
| 2242 | if (typeof this.content === 'string' && this.content.length > 0) { | 2264 | this.tokens = 0; |
| 2243 | this.tokens = tokenHandler.count({ role: this.role, content: this.content }); | 2265 | } |
| 2244 | } else { | 2266 | |
| 2245 | this.tokens = 0; | 2267 | /** |
| 2268 | * Create a new Message instance. | ||
| 2269 | * @param {string} role | ||
| 2270 | * @param {string} content | ||
| 2271 | * @param {string} identifier | ||
| 2272 | * @returns {Promise<Message>} Message instance | ||
| 2273 | */ | ||
| 2274 | static async createAsync(role, content, identifier) { | ||
| 2275 | const message = new Message(role, content, identifier); | ||
| 2276 | |||
| 2277 | if (typeof message.content === 'string' && message.content.length > 0) { | ||
| 2278 | message.tokens = await tokenHandler.countAsync({ role: message.role, content: message.content }); | ||
| 2246 | } | 2279 | } |
| 2280 | |||
| 2281 | return message; | ||
| 2247 | } | 2282 | } |
| 2248 | 2283 | ||
| 2249 | /** | 2284 | /** |
| 2250 | * Reconstruct the message from a tool invocation. | 2285 | * Reconstruct the message from a tool invocation. |
| 2251 | * @param {import('./tool-calling.js').ToolInvocation[]} invocations | 2286 | * @param {import('./tool-calling.js').ToolInvocation[]} invocations - The tool invocations to reconstruct the message from. |
| 2287 | * @returns {Promise<void>} | ||
| 2252 | */ | 2288 | */ |
| 2253 | setToolCalls(invocations) { | 2289 | async setToolCalls(invocations) { |
| 2254 | this.tool_calls = invocations.map(i => ({ | 2290 | this.tool_calls = invocations.map(i => ({ |
| 2255 | id: i.id, | 2291 | id: i.id, |
| 2256 | type: 'function', | 2292 | type: 'function', |
| @@ -2259,12 +2295,17 @@ class Message { | |||
| 2259 | name: i.name, | 2295 | name: i.name, |
| 2260 | }, | 2296 | }, |
| 2261 | })); | 2297 | })); |
| 2262 | this.tokens = tokenHandler.count({ role: this.role, tool_calls: JSON.stringify(this.tool_calls) }); | 2298 | this.tokens = await tokenHandler.countAsync({ role: this.role, tool_calls: JSON.stringify(this.tool_calls) }); |
| 2263 | } | 2299 | } |
| 2264 | 2300 | ||
| 2265 | setName(name) { | 2301 | /** |
| 2302 | * Add a name to the message. | ||
| 2303 | * @param {string} name Name to set for the message. | ||
| 2304 | * @returns {Promise<void>} | ||
| 2305 | */ | ||
| 2306 | async setName(name) { | ||
| 2266 | this.name = name; | 2307 | this.name = name; |
| 2267 | this.tokens = tokenHandler.count({ role: this.role, content: this.content, name: this.name }); | 2308 | this.tokens = await tokenHandler.countAsync({ role: this.role, content: this.content, name: this.name }); |
| 2268 | } | 2309 | } |
| 2269 | 2310 | ||
| 2270 | async addImage(image) { | 2311 | async addImage(image) { |
| @@ -2356,13 +2397,13 @@ class Message { | |||
| 2356 | } | 2397 | } |
| 2357 | 2398 | ||
| 2358 | /** | 2399 | /** |
| 2359 | * Create a new Message instance from a prompt. | 2400 | * Create a new Message instance from a prompt asynchronously. |
| 2360 | * @static | 2401 | * @static |
| 2361 | * @param {Object} prompt - The prompt object. | 2402 | * @param {Object} prompt - The prompt object. |
| 2362 | * @returns {Message} A new instance of Message. | 2403 | * @returns {Promise<Message>} A new instance of Message. |
| 2363 | */ | 2404 | */ |
| 2364 | static fromPrompt(prompt) { | 2405 | static async fromPromptAsync(prompt) { |
| 2365 | return new Message(prompt.role, prompt.content, prompt.identifier); | 2406 | return Message.createAsync(prompt.role, prompt.content, prompt.identifier); |
| 2366 | } | 2407 | } |
| 2367 | 2408 | ||
| 2368 | /** | 2409 | /** |
| @@ -2488,8 +2529,9 @@ export class ChatCompletion { | |||
| 2488 | 2529 | ||
| 2489 | /** | 2530 | /** |
| 2490 | * Combines consecutive system messages into one if they have no name attached. | 2531 | * Combines consecutive system messages into one if they have no name attached. |
| 2532 | * @returns {Promise<void>} | ||
| 2491 | */ | 2533 | */ |
| 2492 | squashSystemMessages() { | 2534 | async squashSystemMessages() { |
| 2493 | const excludeList = ['newMainChat', 'newChat', 'groupNudge']; | 2535 | const excludeList = ['newMainChat', 'newChat', 'groupNudge']; |
| 2494 | this.messages.collection = this.messages.flatten(); | 2536 | this.messages.collection = this.messages.flatten(); |
| 2495 | 2537 | ||
| @@ -2509,7 +2551,7 @@ export class ChatCompletion { | |||
| 2509 | if (shouldSquash(message)) { | 2551 | if (shouldSquash(message)) { |
| 2510 | if (lastMessage && shouldSquash(lastMessage)) { | 2552 | if (lastMessage && shouldSquash(lastMessage)) { |
| 2511 | lastMessage.content += '\n' + message.content; | 2553 | lastMessage.content += '\n' + message.content; |
| 2512 | lastMessage.tokens = tokenHandler.count({ role: lastMessage.role, content: lastMessage.content }); | 2554 | lastMessage.tokens = await tokenHandler.countAsync({ role: lastMessage.role, content: lastMessage.content }); |
| 2513 | } | 2555 | } |
| 2514 | else { | 2556 | else { |
| 2515 | squashedMessages.push(message); | 2557 | squashedMessages.push(message); |