Expressions: Add WebLLM extension classification
| @@ -15,6 +15,7 @@ import { SlashCommandEnumValue, enumTypes } from '../../slash-commands/SlashComm | ||
| 15 | 15 | import { commonEnumProviders } from '../../slash-commands/SlashCommandCommonEnumsProvider.js'; |
| 16 | 16 | import { slashCommandReturnHelper } from '../../slash-commands/SlashCommandReturnHelper.js'; |
| 17 | 17 | import { SlashCommandClosure } from '../../slash-commands/SlashCommandClosure.js'; |
| 18 | +import { generateWebLlmChatPrompt, isWebLlmSupported } from '../shared.js'; | |
| 18 | 19 | export { MODULE_NAME }; |
| 19 | 20 | |
| 20 | 21 | const MODULE_NAME = 'expressions'; |
| @@ -59,6 +60,7 @@ const EXPRESSION_API = { | ||
| 59 | 60 | local: 0, |
| 60 | 61 | extras: 1, |
| 61 | 62 | llm: 2, |
| 63 | + webllm: 3, | |
| 62 | 64 | }; |
| 63 | 65 | |
| 64 | 66 | let expressionsList = null; |
| @@ -698,8 +700,8 @@ async function moduleWorker() { | ||
| 698 | 700 | } |
| 699 | 701 | |
| 700 | 702 | // If using LLM api then check if streamingProcessor is finished to avoid sending multiple requests to the API |
| 701 | 703 | if (extension_settings.expressions.api === EXPRESSION_API.llm && context.streamingProcessor && !context.streamingProcessor.isFinished) { |
| 702 | 704 | return; |
| 703 | 705 | } |
| 704 | 706 | |
| 705 | 707 | // API is busy |
| @@ -852,7 +854,7 @@ function setTalkingHeadState(newState) { | ||
| 852 | 854 | extension_settings.expressions.talkinghead = newState; // Store setting |
| 853 | 855 | saveSettingsDebounced(); |
| 854 | 856 | |
| 855 | 857 | if (extension_settings.expressions.api == [EXPRESSION_API.local, ||EXPRESSION_API.llm, EXPRESSION_API.webllm].includes(extension_settings.expressions.api == EXPRESSION_API.llm)) { |
| 856 | 858 | return; |
| 857 | 859 | } |
| 858 | 860 | |
| @@ -1057,11 +1059,39 @@ function parseLlmResponse(emotionResponse, labels) { | ||
| 1057 | 1059 | console.debug(`fuzzy search found: ${result[0].item} as closest for the LLM response:`, emotionResponse); |
| 1058 | 1060 | return result[0].item; |
| 1059 | 1061 | } |
| 1062 | + const lowerCaseResponse = String(emotionResponse || '').toLowerCase(); | |
| 1063 | + for (const label of labels) { | |
| 1064 | + if (lowerCaseResponse.includes(label.toLowerCase())) { | |
| 1065 | + console.debug(`Found label ${label} in the LLM response:`, emotionResponse); | |
| 1066 | + return label; | |
| 1067 | + } | |
| 1068 | + } | |
| 1060 | 1069 | } |
| 1061 | 1070 | |
| 1062 | 1071 | throw new Error('Could not parse emotion response ' + emotionResponse); |
| 1063 | 1072 | } |
| 1064 | 1073 | |
| 1074 | +/** | |
| 1075 | + * Gets the JSON schema for the LLM API. | |
| 1076 | + * @param {string[]} emotions A list of emotions to search for. | |
| 1077 | + * @returns {object} The JSON schema for the LLM API. | |
| 1078 | + */ | |
| 1079 | +function getJsonSchema(emotions) { | |
| 1080 | + return { | |
| 1081 | + $schema: 'http://json-schema.org/draft-04/schema#', | |
| 1082 | + type: 'object', | |
| 1083 | + properties: { | |
| 1084 | + emotion: { | |
| 1085 | + type: 'string', | |
| 1086 | + enum: emotions, | |
| 1087 | + }, | |
| 1088 | + }, | |
| 1089 | + required: [ | |
| 1090 | + 'emotion', | |
| 1091 | + ], | |
| 1092 | + }; | |
| 1093 | +} | |
| 1094 | + | |
| 1065 | 1095 | function onTextGenSettingsReady(args) { |
| 1066 | 1096 | // Only call if inside an API call |
| 1067 | 1097 | if (inApiCall && extension_settings.expressions.api === EXPRESSION_API.llm && isJsonSchemaSupported()) { |
| @@ -1071,19 +1101,7 @@ function onTextGenSettingsReady(args) { | ||
| 1071 | 1101 | stop: [], |
| 1072 | 1102 | stopping_strings: [], |
| 1073 | 1103 | custom_token_bans: [], |
| 1074 | 1104 | json_schema: {getJsonSchema(emotions), |
| 1075 | - $schema: 'http://json-schema.org/draft-04/schema#', | |
| 1076 | - type: 'object', | |
| 1077 | - properties: { | |
| 1078 | - emotion: { | |
| 1079 | - type: 'string', | |
| 1080 | - enum: emotions, | |
| 1081 | - }, | |
| 1082 | - }, | |
| 1083 | - required: [ | |
| 1084 | - 'emotion', | |
| 1085 | - ], | |
| 1086 | - }, | |
| 1087 | 1105 | }); |
| 1088 | 1106 | } |
| 1089 | 1107 | } |
| @@ -1139,6 +1157,22 @@ export async function getExpressionLabel(text, expressionsApi = extension_settin | ||
| 1139 | 1157 | const emotionResponse = await generateRaw(text, main_api, false, false, prompt); |
| 1140 | 1158 | return parseLlmResponse(emotionResponse, expressionsList); |
| 1141 | 1159 | } |
| 1160 | + // Using WebLLM | |
| 1161 | + case EXPRESSION_API.webllm: { | |
| 1162 | + if (!isWebLlmSupported()) { | |
| 1163 | + console.warn('WebLLM is not supported. Using fallback expression'); | |
| 1164 | + return getFallbackExpression(); | |
| 1165 | + } | |
| 1166 | + | |
| 1167 | + const expressionsList = await getExpressionsList(); | |
| 1168 | + const prompt = substituteParamsExtended(customPrompt, { labels: expressionsList }) || await getLlmPrompt(expressionsList); | |
| 1169 | + const messages = [ | |
| 1170 | + { role: 'user', content: text + '\n\n' + prompt }, | |
| 1171 | + ]; | |
| 1172 | + | |
| 1173 | + const emotionResponse = await generateWebLlmChatPrompt(messages); | |
| 1174 | + return parseLlmResponse(emotionResponse, expressionsList); | |
| 1175 | + } | |
| 1142 | 1176 | // Extras |
| 1143 | 1177 | default: { |
| 1144 | 1178 | const url = new URL(getApiUrl()); |
| @@ -1603,7 +1637,7 @@ function onExpressionApiChanged() { | ||
| 1603 | 1637 | const tempApi = this.value; |
| 1604 | 1638 | if (tempApi) { |
| 1605 | 1639 | extension_settings.expressions.api = Number(tempApi); |
| 1606 | 1640 | $('.expression_llm_prompt_block').toggle([EXPRESSION_API.llm, EXPRESSION_API.webllm].includes(extension_settings.expressions.api === EXPRESSION_API.llm)); |
| 1607 | 1641 | expressionsList = null; |
| 1608 | 1642 | spriteCache = {}; |
| 1609 | 1643 | moduleWorker(); |
| @@ -1940,7 +1974,7 @@ function migrateSettings() { | ||
| 1940 | 1974 | |
| 1941 | 1975 | await renderAdditionalExpressionSettings(); |
| 1942 | 1976 | $('#expression_api').val(extension_settings.expressions.api ?? EXPRESSION_API.extras); |
| 1943 | 1977 | $('.expression_llm_prompt_block').toggle([EXPRESSION_API.llm, EXPRESSION_API.webllm].includes(extension_settings.expressions.api === EXPRESSION_API.llm)); |
| 1944 | 1978 | $('#expression_llm_prompt').val(extension_settings.expressions.llmPrompt ?? ''); |
| 1945 | 1979 | $('#expression_llm_prompt').on('input', function () { |
| 1946 | 1980 | extension_settings.expressions.llmPrompt = $(this).val(); |
| @@ -24,7 +24,8 @@ | ||
| 24 | 24 | <select id="expression_api" class="flex1 margin0"> |
| 25 | 25 | <option value="0" data-i18n="Local">Local</option> |
| 26 | 26 | <option value="1" data-i18n="Extras">Extras</option> |
| 27 | 27 | <option value="2" data-i18n="LLMMain API">LLMMain API</option> |
| 28 | + <option value="3" data-i18n="WebLLM Extension">WebLLM Extension</option> | |
| 28 | 29 | </select> |
| 29 | 30 | </div> |
| 30 | 31 | <div class="expression_llm_prompt_block m-b-1 m-t-1"> |