diff --git a/package-lock.json b/package-lock.json index 2419fe002..fa23bf5a6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -63,6 +63,7 @@ "showdown": "^2.1.0", "sillytavern-transformers": "2.14.6", "simple-git": "^3.19.1", + "slidetoggle": "^4.0.0", "tiktoken": "^1.0.16", "vectra": "^0.2.2", "wavefile": "^11.0.0", @@ -6531,6 +6532,12 @@ "integrity": "sha512-VZBmZP8WU3sMOZm1bdgTadsQbcscK0UM8oKxKVBs4XAhUo2Xxzm/OFMGBkPusxw9xL3Uy8LrzEqGqJhclsr0yA==", "license": "MIT" }, + "node_modules/slidetoggle": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slidetoggle/-/slidetoggle-4.0.0.tgz", + "integrity": "sha512-6qvrOS1dnDFEr41UEEFFRQE8nswaAFIYZAHer6dVlznRIjHyCISjNJoxIn5U5QlAbZfBBxTELQk4jS7miHto1A==", + "license": "MIT" + }, "node_modules/smart-buffer": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", diff --git a/package.json b/package.json index 777d99076..26d979a7f 100644 --- a/package.json +++ b/package.json @@ -53,6 +53,7 @@ "showdown": "^2.1.0", "sillytavern-transformers": "2.14.6", "simple-git": "^3.19.1", + "slidetoggle": "^4.0.0", "tiktoken": "^1.0.16", "vectra": "^0.2.2", "wavefile": "^11.0.0", diff --git a/public/img/generic.svg b/public/img/generic.svg new file mode 100644 index 000000000..2411ed069 --- /dev/null +++ b/public/img/generic.svg @@ -0,0 +1,15 @@ + + + + + diff --git a/public/index.html b/public/index.html index 076c7553b..6f0d40dcc 100644 --- a/public/index.html +++ b/public/index.html @@ -1736,6 +1736,7 @@
Tail Free Sampling
Min P
Exclude Top Choices
+
DRY
-
@@ -3220,16 +3217,17 @@

Cohere Model

+ diff --git a/public/scripts/macros.js b/public/scripts/macros.js index 0e4d7a8da..ea6b64b67 100644 --- a/public/scripts/macros.js +++ b/public/scripts/macros.js @@ -516,7 +516,11 @@ export function evaluateMacros(content, env, postProcessFn) { break; } - content = content.replace(macro.regex, (...args) => postProcessFn(macro.replace(...args))); + try { + content = content.replace(macro.regex, (...args) => postProcessFn(macro.replace(...args))); + } catch (e) { + console.warn(`Macro content can't be replaced: ${macro.regex} in ${content}`, e); + } } return content; diff --git a/public/scripts/openai.js b/public/scripts/openai.js index f50126abc..8edc3c682 100644 --- a/public/scripts/openai.js +++ b/public/scripts/openai.js @@ -4098,13 +4098,8 @@ async function onModelChange() { $('#openai_max_context').attr('max', max_2mil); } else if (value.includes('gemini-1.5-flash') || value.includes('gemini-2.0-flash-exp')) { $('#openai_max_context').attr('max', max_1mil); - } else if (value.includes('gemini-1.0-pro-vision') || value === 'gemini-pro-vision') { - $('#openai_max_context').attr('max', max_16k); } else if (value.includes('gemini-1.0-pro') || value === 'gemini-pro') { $('#openai_max_context').attr('max', max_32k); - } else if (value === 'text-bison-001') { - $('#openai_max_context').attr('max', max_8k); - // The ultra endpoints are possibly dead: } else if (value.includes('gemini-1.0-ultra') || value === 'gemini-ultra') { $('#openai_max_context').attr('max', max_32k); } else { @@ -4226,16 +4221,13 @@ async function onModelChange() { if (oai_settings.max_context_unlocked) { $('#openai_max_context').attr('max', unlocked_max); } - else if (['command-light', 'command'].includes(oai_settings.cohere_model)) { + else if (['command-light-nightly', 'command-light', 'command'].includes(oai_settings.cohere_model)) { $('#openai_max_context').attr('max', max_4k); } - else if (['command-light-nightly', 'command-nightly'].includes(oai_settings.cohere_model)) { - $('#openai_max_context').attr('max', max_8k); - } - else if (oai_settings.cohere_model.includes('command-r') || ['c4ai-aya-expanse-32b'].includes(oai_settings.cohere_model)) { + else if (oai_settings.cohere_model.includes('command-r') || ['c4ai-aya-23', 'c4ai-aya-expanse-32b', 'command-nightly'].includes(oai_settings.cohere_model)) { $('#openai_max_context').attr('max', max_128k); } - else if (['c4ai-aya-23', 'c4ai-aya-expanse-8b'].includes(oai_settings.cohere_model)) { + else if (['c4ai-aya-23-8b', 'c4ai-aya-expanse-8b'].includes(oai_settings.cohere_model)) { $('#openai_max_context').attr('max', max_8k); } else { @@ -4787,7 +4779,6 @@ export function isImageInliningSupported() { 'gemini-1.5-pro-002', 'gemini-1.5-pro-exp-0801', 'gemini-1.5-pro-exp-0827', - 'gemini-pro-vision', 'claude-3', 'claude-3-5', 'gpt-4-turbo', diff --git a/public/scripts/textgen-settings.js b/public/scripts/textgen-settings.js index 9b7db4444..44153d05e 100644 --- a/public/scripts/textgen-settings.js +++ b/public/scripts/textgen-settings.js @@ -55,6 +55,7 @@ const { } = textgen_types; const LLAMACPP_DEFAULT_ORDER = [ + 'dry', 'top_k', 'tfs_z', 'typical_p', @@ -1038,11 +1039,13 @@ export function parseTextgenLogprobs(token, logprobs) { return { token, topLogprobs: candidates }; } case LLAMACPP: { - /** @type {Record[]} */ if (!logprobs?.length) { return null; } - const candidates = logprobs[0].probs.map(x => [x.tok_str, x.prob]); + const candidates = logprobs?.[0]?.probs?.map(x => [x.tok_str, x.prob]); + if (!candidates) { + return null; + } return { token, topLogprobs: candidates }; } default: diff --git a/public/scripts/tool-calling.js b/public/scripts/tool-calling.js index deda15289..b56e3027c 100644 --- a/public/scripts/tool-calling.js +++ b/public/scripts/tool-calling.js @@ -25,6 +25,7 @@ import { isTrueBoolean } from './utils.js'; * @typedef {object} ToolInvocationResult * @property {ToolInvocation[]} invocations Successful tool invocations * @property {Error[]} errors Errors that occurred during tool invocation + * @property {string[]} stealthCalls Names of stealth tools that were invoked */ /** @@ -36,6 +37,7 @@ import { isTrueBoolean } from './utils.js'; * @property {function} action - The action to perform when the tool is invoked. * @property {function} [formatMessage] - A function to format the tool call message. * @property {function} [shouldRegister] - A function to determine if the tool should be registered. + * @property {boolean} [stealth] - A tool call result will not be shown in the chat. No follow-up generation will be performed. */ /** @@ -147,6 +149,12 @@ class ToolDefinition { */ #shouldRegister; + /** + * A tool call result will not be shown in the chat. No follow-up generation will be performed. + * @type {boolean} + */ + #stealth; + /** * Creates a new ToolDefinition. * @param {string} name A unique name for the tool. @@ -156,8 +164,9 @@ class ToolDefinition { * @param {function} action A function that will be called when the tool is executed. * @param {function} formatMessage A function that will be called to format the tool call toast. * @param {function} shouldRegister A function that will be called to determine if the tool should be registered. + * @param {boolean} stealth A tool call result will not be shown in the chat. No follow-up generation will be performed. */ - constructor(name, displayName, description, parameters, action, formatMessage, shouldRegister) { + constructor(name, displayName, description, parameters, action, formatMessage, shouldRegister, stealth) { this.#name = name; this.#displayName = displayName; this.#description = description; @@ -165,6 +174,7 @@ class ToolDefinition { this.#action = action; this.#formatMessage = formatMessage; this.#shouldRegister = shouldRegister; + this.#stealth = stealth; } /** @@ -214,6 +224,10 @@ class ToolDefinition { get displayName() { return this.#displayName; } + + get stealth() { + return this.#stealth; + } } /** @@ -246,7 +260,7 @@ export class ToolManager { * Registers a new tool with the tool registry. * @param {ToolRegistration} tool The tool to register. */ - static registerFunctionTool({ name, displayName, description, parameters, action, formatMessage, shouldRegister }) { + static registerFunctionTool({ name, displayName, description, parameters, action, formatMessage, shouldRegister, stealth }) { // Convert WIP arguments if (typeof arguments[0] !== 'object') { [name, description, parameters, action] = arguments; @@ -256,7 +270,16 @@ export class ToolManager { console.warn(`[ToolManager] A tool with the name "${name}" has already been registered. The definition will be overwritten.`); } - const definition = new ToolDefinition(name, displayName, description, parameters, action, formatMessage, shouldRegister); + const definition = new ToolDefinition( + name, + displayName, + description, + parameters, + action, + formatMessage, + shouldRegister, + stealth, + ); this.#tools.set(name, definition); console.log('[ToolManager] Registered function tool:', definition); } @@ -302,6 +325,20 @@ export class ToolManager { } } + /** + * Checks if a tool is a stealth tool. + * @param {string} name The name of the tool to check. + * @returns {boolean} Whether the tool is a stealth tool. + */ + static isStealthTool(name) { + if (!this.#tools.has(name)) { + return false; + } + + const tool = this.#tools.get(name); + return !!tool.stealth; + } + /** * Formats a message for a tool call by name. * @param {string} name The name of the tool to format the message for. @@ -608,6 +645,7 @@ export class ToolManager { const result = { invocations: [], errors: [], + stealthCalls: [], }; const toolCalls = ToolManager.#getToolCallsFromData(data); @@ -625,7 +663,7 @@ export class ToolManager { const parameters = toolCall.function.arguments; const name = toolCall.function.name; const displayName = ToolManager.getDisplayName(name); - + const isStealth = ToolManager.isStealthTool(name); const message = await ToolManager.formatToolCallMessage(name, parameters); const toast = message && toastr.info(message, 'Tool Calling', { timeOut: 0 }); const toolResult = await ToolManager.invokeFunctionTool(name, parameters); @@ -638,6 +676,12 @@ export class ToolManager { continue; } + // Don't save stealth tool invocations + if (isStealth) { + result.stealthCalls.push(name); + continue; + } + const invocation = { id, displayName, @@ -860,6 +904,14 @@ export class ToolManager { isRequired: false, acceptsMultiple: false, }), + SlashCommandNamedArgument.fromProps({ + name: 'stealth', + description: 'If true, a tool call result will not be shown in the chat and no follow-up generation will be performed.', + typeList: [ARGUMENT_TYPE.BOOLEAN], + isRequired: false, + acceptsMultiple: false, + defaultValue: String(false), + }), ], unnamedArgumentList: [ SlashCommandArgument.fromProps({ @@ -891,7 +943,7 @@ export class ToolManager { }; } - const { name, displayName, description, parameters, formatMessage, shouldRegister } = args; + const { name, displayName, description, parameters, formatMessage, shouldRegister, stealth } = args; if (!(action instanceof SlashCommandClosure)) { throw new Error('The unnamed argument must be a closure.'); @@ -927,6 +979,7 @@ export class ToolManager { action: actionFunc, formatMessage: formatMessageFunc, shouldRegister: shouldRegisterFunc, + stealth: stealth && isTrueBoolean(String(stealth)), }); return ''; diff --git a/public/scripts/variables.js b/public/scripts/variables.js index df22db8ad..fd533f656 100644 --- a/public/scripts/variables.js +++ b/public/scripts/variables.js @@ -46,6 +46,10 @@ function getLocalVariable(name, args = {}) { } function setLocalVariable(name, value, args = {}) { + if (!name) { + throw new Error('Variable name cannot be empty or undefined.'); + } + if (!chat_metadata.variables) { chat_metadata.variables = {}; } @@ -99,6 +103,10 @@ function getGlobalVariable(name, args = {}) { } function setGlobalVariable(name, value, args = {}) { + if (!name) { + throw new Error('Variable name cannot be empty or undefined.'); + } + if (args.index !== undefined) { try { let globalVariable = JSON.parse(extension_settings.variables.global[name] ?? 'null'); diff --git a/public/style.css b/public/style.css index 3246b2ca2..c9033bcad 100644 --- a/public/style.css +++ b/public/style.css @@ -2874,6 +2874,7 @@ input[type=search]:focus::-webkit-search-cancel-button { .mes_block .ch_name { max-width: 100%; + min-height: 22px; } /*applies to both groups and solos chars in the char list*/ @@ -4043,7 +4044,7 @@ input[type="range"]::-webkit-slider-thumb { .mes_button, .extraMesButtons>div { cursor: pointer; - transition: 0.3s ease-in-out; + transition: opacity 0.2s ease-in-out; filter: drop-shadow(0px 0px 2px black); opacity: 0.3; padding: 1px 3px; diff --git a/src/constants.js b/src/constants.js index d2c40daeb..7fb98eef9 100644 --- a/src/constants.js +++ b/src/constants.js @@ -159,33 +159,6 @@ export const GEMINI_SAFETY = [ }, ]; -export const BISON_SAFETY = [ - { - category: 'HARM_CATEGORY_DEROGATORY', - threshold: 'BLOCK_NONE', - }, - { - category: 'HARM_CATEGORY_TOXICITY', - threshold: 'BLOCK_NONE', - }, - { - category: 'HARM_CATEGORY_VIOLENCE', - threshold: 'BLOCK_NONE', - }, - { - category: 'HARM_CATEGORY_SEXUAL', - threshold: 'BLOCK_NONE', - }, - { - category: 'HARM_CATEGORY_MEDICAL', - threshold: 'BLOCK_NONE', - }, - { - category: 'HARM_CATEGORY_DANGEROUS', - threshold: 'BLOCK_NONE', - }, -]; - export const CHAT_COMPLETION_SOURCES = { OPENAI: 'openai', WINDOWAI: 'windowai', diff --git a/src/endpoints/backends/chat-completions.js b/src/endpoints/backends/chat-completions.js index f30a5163d..519371ad4 100644 --- a/src/endpoints/backends/chat-completions.js +++ b/src/endpoints/backends/chat-completions.js @@ -6,7 +6,6 @@ import { jsonParser } from '../../express-common.js'; import { CHAT_COMPLETION_SOURCES, GEMINI_SAFETY, - BISON_SAFETY, OPENROUTER_HEADERS, } from '../../constants.js'; import { @@ -262,9 +261,7 @@ async function sendMakerSuiteRequest(request, response) { } const model = String(request.body.model); - const isGemini = model.includes('gemini'); - const isText = model.includes('text'); - const stream = Boolean(request.body.stream) && isGemini; + const stream = Boolean(request.body.stream); const generationConfig = { stopSequences: request.body.stop, @@ -301,39 +298,7 @@ async function sendMakerSuiteRequest(request, response) { return body; } - function getBisonBody() { - const prompt = isText - ? ({ text: convertTextCompletionPrompt(request.body.messages) }) - : ({ messages: convertGooglePrompt(request.body.messages, model).contents }); - - /** @type {any} Shut the lint up */ - const bisonBody = { - ...generationConfig, - safetySettings: BISON_SAFETY, - candidate_count: 1, // lewgacy spelling - prompt: prompt, - }; - - if (!isText) { - delete bisonBody.stopSequences; - delete bisonBody.maxOutputTokens; - delete bisonBody.safetySettings; - - if (Array.isArray(prompt.messages)) { - for (const msg of prompt.messages) { - msg.author = msg.role; - msg.content = msg.parts[0].text; - delete msg.parts; - delete msg.role; - } - } - } - - delete bisonBody.candidateCount; - return bisonBody; - } - - const body = isGemini ? getGeminiBody() : getBisonBody(); + const body = getGeminiBody(); console.log('Google AI Studio request:', body); try { @@ -343,10 +308,8 @@ async function sendMakerSuiteRequest(request, response) { controller.abort(); }); - const apiVersion = isGemini ? 'v1beta' : 'v1beta2'; - const responseType = isGemini - ? (stream ? 'streamGenerateContent' : 'generateContent') - : (isText ? 'generateText' : 'generateMessage'); + const apiVersion = 'v1beta'; + const responseType = (stream ? 'streamGenerateContent' : 'generateContent'); const generateResponse = await fetch(`${apiUrl.toString().replace(/\/$/, '')}/${apiVersion}/models/${model}:${responseType}?key=${apiKey}${stream ? '&alt=sse' : ''}`, { body: JSON.stringify(body), @@ -897,7 +860,7 @@ router.post('/generate', jsonParser, function (request, response) { } let cachingAtDepth = getConfigValue('claude.cachingAtDepth', -1); - if (Number.isInteger(cachingAtDepth) && cachingAtDepth >= 0 && request.body.model.startsWith('anthropic/claude-3')) { + if (Number.isInteger(cachingAtDepth) && cachingAtDepth >= 0 && request.body.model?.startsWith('anthropic/claude-3')) { cachingAtDepthForOpenRouterClaude(request.body.messages, cachingAtDepth); } } else if (request.body.chat_completion_source === CHAT_COMPLETION_SOURCES.CUSTOM) { diff --git a/src/prompt-converters.js b/src/prompt-converters.js index c23dd6a69..47f5989cc 100644 --- a/src/prompt-converters.js +++ b/src/prompt-converters.js @@ -333,8 +333,6 @@ export function convertCohereMessages(messages, charName = '', userName = '') { * @returns {{contents: *[], system_instruction: {parts: {text: string}}}} Prompt for Google MakerSuite models */ export function convertGooglePrompt(messages, model, useSysPrompt = false, charName = '', userName = '') { - // This is a 1x1 transparent PNG - const PNG_PIXEL = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII='; const visionSupportedModels = [ 'gemini-2.0-flash-exp', @@ -355,13 +353,6 @@ export function convertGooglePrompt(messages, model, useSysPrompt = false, charN 'gemini-1.5-pro-002', 'gemini-1.5-pro-exp-0801', 'gemini-1.5-pro-exp-0827', - 'gemini-1.0-pro-vision-latest', - 'gemini-pro-vision', - ]; - - const dummyRequiredModels = [ - 'gemini-1.0-pro-vision-latest', - 'gemini-pro-vision', ]; const isMultimodal = visionSupportedModels.includes(model); @@ -452,16 +443,6 @@ export function convertGooglePrompt(messages, model, useSysPrompt = false, charN } }); - // pro 1.5 doesn't require a dummy image to be attached, other vision models do - if (isMultimodal && dummyRequiredModels.includes(model) && !hasImage) { - contents[0].parts.push({ - inlineData: { - mimeType: 'image/png', - data: PNG_PIXEL, - }, - }); - } - return { contents: contents, system_instruction: system_instruction }; }