mirror of
https://github.com/SillyTavern/SillyTavern.git
synced 2025-06-05 21:59:27 +02:00
Merge branch 'staging' into immutable-config
This commit is contained in:
@ -4,7 +4,7 @@ FROM node:lts-alpine3.19
|
||||
ARG APP_HOME=/home/node/app
|
||||
|
||||
# Install system dependencies
|
||||
RUN apk add gcompat tini git
|
||||
RUN apk add --no-cache gcompat tini git
|
||||
|
||||
# Create app directory
|
||||
WORKDIR ${APP_HOME}
|
||||
|
@ -2000,7 +2000,7 @@
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="range-block" data-source="deepseek,openrouter,custom">
|
||||
<div class="range-block" data-source="deepseek,openrouter,custom,claude">
|
||||
<label for="openai_show_thoughts" class="checkbox_label widthFreeExpand">
|
||||
<input id="openai_show_thoughts" type="checkbox" />
|
||||
<span>
|
||||
@ -2014,10 +2014,11 @@
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-container flexFlowColumn wide100p textAlignCenter marginTop10" data-source="openai,custom">
|
||||
<div class="flex-container flexFlowColumn wide100p textAlignCenter marginTop10" data-source="openai,custom,claude">
|
||||
<div class="flex-container oneline-dropdown" title="Constrains effort on reasoning for reasoning models. Currently supported values are low, medium, and high. Reducing reasoning effort can result in faster responses and fewer tokens used on reasoning in a response." data-i18n="[title]Constrains effort on reasoning for reasoning models.">
|
||||
<label for="openai_reasoning_effort" data-i18n="Reasoning Effort">
|
||||
Reasoning Effort
|
||||
<label for="openai_reasoning_effort">
|
||||
<span data-i18n="Reasoning Effort">Reasoning Effort</span>
|
||||
<i data-source="claude" class="opacity50p fa-solid fa-circle-info" title="Allocates a portion of the response length for thinking (low: 10%, medium: 25%, high: 50%), but minimum 1024 tokens."></i>
|
||||
</label>
|
||||
<select id="openai_reasoning_effort">
|
||||
<option data-i18n="openai_reasoning_effort_low" value="low">Low</option>
|
||||
@ -2915,6 +2916,8 @@
|
||||
<h4 data-i18n="Claude Model">Claude Model</h4>
|
||||
<select id="model_claude_select">
|
||||
<optgroup label="Versions">
|
||||
<option value="claude-3-7-sonnet-latest">claude-3-7-sonnet-latest</option>
|
||||
<option value="claude-3-7-sonnet-20250219">claude-3-7-sonnet-20250219</option>
|
||||
<option value="claude-3-5-sonnet-latest">claude-3-5-sonnet-latest</option>
|
||||
<option value="claude-3-5-sonnet-20241022">claude-3-5-sonnet-20241022</option>
|
||||
<option value="claude-3-5-sonnet-20240620">claude-3-5-sonnet-20240620</option>
|
||||
@ -4429,7 +4432,7 @@
|
||||
<input id="prefer_character_jailbreak" type="checkbox" />
|
||||
<small data-i18n="Prefer Character Card Instructions">Prefer Char. Instructions</small>
|
||||
</label>
|
||||
<label class="checkbox_label" for="never_resize_avatars" title="Avoid cropping and resizing imported character images. When off, crop/resize to 512x768." data-i18n="[title]Avoid cropping and resizing imported character images. When off, crop/resize to 512x768">
|
||||
<label class="checkbox_label" for="never_resize_avatars" title="Avoid cropping and resizing imported character images. When off, crop/resize to 512x768. This will disable the upload cropping popup for avatars." data-i18n="[title]never_resize_avatars_tooltip">
|
||||
<input id="never_resize_avatars" type="checkbox" />
|
||||
<small data-i18n="Never resize avatars">Never resize avatars</small>
|
||||
</label>
|
||||
|
@ -633,7 +633,7 @@
|
||||
"Prefer Character Card Prompt": "تفضيل التعليمات من بطاقة الشخصية",
|
||||
"If checked and the character card contains a jailbreak override (Post History Instruction), use that instead": "إذا تم التحقق وكانت بطاقة الشخصية تحتوي على تجاوز للكسر (تعليمات تاريخ المشاركة)، استخدم ذلك بدلاً من ذلك",
|
||||
"Prefer Character Card Jailbreak": "تفضيل كسر الحصار من بطاقة الشخصية",
|
||||
"Avoid cropping and resizing imported character images. When off, crop/resize to 512x768": "تجنب اقتصاص صور الأحرف المستوردة وتغيير حجمها. عند إيقاف التشغيل، قم بالقص/تغيير الحجم إلى 512 × 768.",
|
||||
"never_resize_avatars_tooltip": "تجنب اقتصاص صور الأحرف المستوردة وتغيير حجمها. عند إيقاف التشغيل، قم بالقص/تغيير الحجم إلى 512 × 768.",
|
||||
"Never resize avatars": "لا تغيير حجم الصور الرمزية أبدًا",
|
||||
"Show actual file names on the disk, in the characters list display only": "عرض الأسماء الفعلية للملفات على القرص، في عرض قائمة الشخصيات فقط",
|
||||
"Show avatar filenames": "عرض أسماء ملفات الصور الرمزية",
|
||||
|
@ -633,7 +633,7 @@
|
||||
"Prefer Character Card Prompt": "Bevorzuge Charakterkarten-Prompt",
|
||||
"If checked and the character card contains a jailbreak override (Post History Instruction), use that instead": "Wenn aktiviert und die Charakterkarte eine Jailbreak-Überschreibung enthält (Post-History-Instruction), verwende stattdessen diese",
|
||||
"Prefer Character Card Jailbreak": "Bevorzuge Charakterkarten-Jailbreak",
|
||||
"Avoid cropping and resizing imported character images. When off, crop/resize to 512x768": "Vermeiden Sie das Zuschneiden und Ändern der Größe importierter Zeichenbilder. Wenn deaktiviert, wird die Größe auf 512 x 768 zugeschnitten/angepasst.",
|
||||
"never_resize_avatars_tooltip": "Vermeiden Sie das Zuschneiden und Ändern der Größe importierter Zeichenbilder. Wenn deaktiviert, wird die Größe auf 512 x 768 zugeschnitten/angepasst.",
|
||||
"Never resize avatars": "Avatare niemals verkleinern",
|
||||
"Show actual file names on the disk, in the characters list display only": "Zeige tatsächliche Dateinamen auf der Festplatte, nur in der Anzeige der Charakterliste",
|
||||
"Show avatar filenames": "Avatar-Dateinamen anzeigen",
|
||||
|
@ -633,7 +633,7 @@
|
||||
"Prefer Character Card Prompt": "Preferir Indicaciones en Tarjeta de Personaje",
|
||||
"If checked and the character card contains a jailbreak override (Post History Instruction), use that instead": "Si está marcado y la tarjeta de personaje contiene una anulación de jailbreak (Instrucciones Post Historial), usar eso en su lugar",
|
||||
"Prefer Character Card Jailbreak": "Preferir Jailbreak en Tarjeta de Personaje",
|
||||
"Avoid cropping and resizing imported character images. When off, crop/resize to 512x768": "Evite recortar y cambiar el tamaño de las imágenes de personajes importados. Cuando esté desactivado, recorte/cambie el tamaño a 512x768.",
|
||||
"never_resize_avatars_tooltip": "Evite recortar y cambiar el tamaño de las imágenes de personajes importados. Cuando esté desactivado, recorte/cambie el tamaño a 512x768.",
|
||||
"Never resize avatars": "Nunca redimensionar avatares",
|
||||
"Show actual file names on the disk, in the characters list display only": "Mostrar nombres de archivo reales en el disco, solo en la visualización de la lista de personajes",
|
||||
"Show avatar filenames": "Mostrar nombres de archivo de avatares",
|
||||
|
@ -581,7 +581,7 @@
|
||||
"Advanced Character Search": "Recherche de personnage avancée",
|
||||
"If checked and the character card contains a prompt override (System Prompt), use that instead": "Si cochée et si la carte de personnage contient un prompt de remplacement (prompt système), l'utiliser à la place",
|
||||
"Prefer Character Card Prompt": "Préférer le prompt du personnage",
|
||||
"Avoid cropping and resizing imported character images. When off, crop/resize to 512x768": "Évitez de recadrer et de redimensionner les images de personnages importés. Lorsqu'il est désactivé, recadrez/redimensionnez à 512 x 768.",
|
||||
"never_resize_avatars_tooltip": "Évitez de recadrer et de redimensionner les images de personnages importés. Lorsqu'il est désactivé, recadrez/redimensionnez à 512 x 768.",
|
||||
"Never resize avatars": "Ne jamais redimensionner les avatars",
|
||||
"Show actual file names on the disk, in the characters list display only": "Afficher les noms de fichier réels sur le disque, dans l'affichage de la liste de personnages uniquement",
|
||||
"Show avatar filenames": "Afficher les noms de fichier des avatars",
|
||||
|
@ -633,7 +633,7 @@
|
||||
"Prefer Character Card Prompt": "Kosstu kvenkortu fyrirspurn",
|
||||
"If checked and the character card contains a jailbreak override (Post History Instruction), use that instead": "Ef merkt er og kortið inniheldur fangabrotsskil, notaðu það í staðinn",
|
||||
"Prefer Character Card Jailbreak": "Kosstu kvenkortu fangabrot",
|
||||
"Avoid cropping and resizing imported character images. When off, crop/resize to 512x768": "Forðastu að klippa og breyta stærð innfluttra stafamynda. Þegar slökkt er á því skaltu skera/breyta stærð í 512x768.",
|
||||
"never_resize_avatars_tooltip": "Forðastu að klippa og breyta stærð innfluttra stafamynda. Þegar slökkt er á því skaltu skera/breyta stærð í 512x768.",
|
||||
"Never resize avatars": "Aldrei breyta stærðinni á merkjum",
|
||||
"Show actual file names on the disk, in the characters list display only": "Sýna raunveruleg nöfn skráa á diskinum, í lista yfir persónur sýna aðeins",
|
||||
"Show avatar filenames": "Sýna nöfn merkja",
|
||||
|
@ -633,7 +633,7 @@
|
||||
"Prefer Character Card Prompt": "Preferisci Prompt della Scheda Personaggio",
|
||||
"If checked and the character card contains a jailbreak override (Post History Instruction), use that instead": "Se selezionato e la scheda del personaggio contiene una sovrascrittura jailbreak (Istruzione Storico Post), usalo invece",
|
||||
"Prefer Character Card Jailbreak": "Preferisci Jailbreak della Scheda Personaggio",
|
||||
"Avoid cropping and resizing imported character images. When off, crop/resize to 512x768": "Evita di ritagliare e ridimensionare le immagini dei personaggi importati. Quando è disattivato, ritaglia/ridimensiona a 512x768.",
|
||||
"never_resize_avatars_tooltip": "Evita di ritagliare e ridimensionare le immagini dei personaggi importati. Quando è disattivato, ritaglia/ridimensiona a 512x768.",
|
||||
"Never resize avatars": "Non ridimensionare mai gli avatar",
|
||||
"Show actual file names on the disk, in the characters list display only": "Mostra i nomi file effettivi sul disco, solo nella visualizzazione dell'elenco dei personaggi",
|
||||
"Show avatar filenames": "Mostra nomi file avatar",
|
||||
|
@ -633,7 +633,7 @@
|
||||
"Prefer Character Card Prompt": "キャラクターカードのプロンプトを優先",
|
||||
"If checked and the character card contains a jailbreak override (Post History Instruction), use that instead": "チェックされていてキャラクターカードにジェイルブレイクオーバーライド(投稿履歴指示)が含まれている場合、それを代わりに使用します",
|
||||
"Prefer Character Card Jailbreak": "キャラクターカードのJailbreakを優先",
|
||||
"Avoid cropping and resizing imported character images. When off, crop/resize to 512x768": "インポートした文字画像の切り取りやサイズ変更を避けます。オフにすると、512x768 に切り取り/サイズ変更されます。",
|
||||
"never_resize_avatars_tooltip": "インポートした文字画像の切り取りやサイズ変更を避けます。オフにすると、512x768 に切り取り/サイズ変更されます。",
|
||||
"Never resize avatars": "アバターを常にリサイズしない",
|
||||
"Show actual file names on the disk, in the characters list display only": "ディスク上の実際のファイル名を表示します。キャラクターリストの表示にのみ",
|
||||
"Show avatar filenames": "アバターのファイル名を表示",
|
||||
|
@ -646,7 +646,7 @@
|
||||
"Prefer Character Card Prompt": "캐릭터 카드 프롬프트 선호",
|
||||
"If checked and the character card contains a jailbreak override (Post History Instruction), use that instead": "선택되어 있고 캐릭터 카드에 (Post-History 지시)탈옥 재정의가 포함 된 경우, 그것을 대신 사용합니다.",
|
||||
"Prefer Character Card Jailbreak": "캐릭터 카드 탈옥 선호",
|
||||
"Avoid cropping and resizing imported character images. When off, crop/resize to 512x768": "가져온 캐릭터 이미지를 자르거나 크기를 조정하지 마세요. 꺼져 있으면 512x768로 자르거나 크기를 조정합니다.",
|
||||
"never_resize_avatars_tooltip": "가져온 캐릭터 이미지를 자르거나 크기를 조정하지 마세요. 꺼져 있으면 512x768로 자르거나 크기를 조정합니다.",
|
||||
"Never resize avatars": "아바타 크기 변경하지 않음",
|
||||
"Show actual file names on the disk, in the characters list display only": "실제 파일 이름을 디스크에 표시하며 캐릭터 목록 디스플레이에만",
|
||||
"Show avatar filenames": "아바타 파일 이름 표시",
|
||||
|
@ -633,7 +633,7 @@
|
||||
"Prefer Character Card Prompt": "Voorkeur karakterkaart prompt",
|
||||
"If checked and the character card contains a jailbreak override (Post History Instruction), use that instead": "Als aangevinkt en de karakterkaart bevat een jailbreak-override (Post History Instruction), gebruik die in plaats daarvan",
|
||||
"Prefer Character Card Jailbreak": "Voorkeur karakterkaart jailbreak",
|
||||
"Avoid cropping and resizing imported character images. When off, crop/resize to 512x768": "Vermijd het bijsnijden en vergroten/verkleinen van geïmporteerde karakterafbeeldingen. Indien uitgeschakeld, bijsnijden/formaat wijzigen naar 512 x 768.",
|
||||
"never_resize_avatars_tooltip": "Vermijd het bijsnijden en vergroten/verkleinen van geïmporteerde karakterafbeeldingen. Indien uitgeschakeld, bijsnijden/formaat wijzigen naar 512 x 768.",
|
||||
"Never resize avatars": "Avatars nooit verkleinen",
|
||||
"Show actual file names on the disk, in the characters list display only": "Toon de werkelijke bestandsnamen op de schijf, alleen in de weergave van de lijst met personages",
|
||||
"Show avatar filenames": "Toon avatar bestandsnamen",
|
||||
|
@ -633,7 +633,7 @@
|
||||
"Prefer Character Card Prompt": "Preferir Prompt do Cartão de Personagem",
|
||||
"If checked and the character card contains a jailbreak override (Post History Instruction), use that instead": "Se marcado e o cartão de personagem contiver uma substituição de jailbreak (Instrução de Histórico de Postagens), use isso em vez disso",
|
||||
"Prefer Character Card Jailbreak": "Preferir Jailbreak do Cartão de Personagem",
|
||||
"Avoid cropping and resizing imported character images. When off, crop/resize to 512x768": "Evite cortar e redimensionar imagens de personagens importados. Quando desativado, corte/redimensione para 512x768.",
|
||||
"never_resize_avatars_tooltip": "Evite cortar e redimensionar imagens de personagens importados. Quando desativado, corte/redimensione para 512x768.",
|
||||
"Never resize avatars": "Nunca redimensionar avatares",
|
||||
"Show actual file names on the disk, in the characters list display only": "Mostrar nomes de arquivo reais no disco, apenas na exibição da lista de personagens",
|
||||
"Show avatar filenames": "Mostrar nomes de arquivo de avatar",
|
||||
|
@ -995,7 +995,7 @@
|
||||
"Set your custom avatar.": "Установить аватарку",
|
||||
"Remove your custom avatar.": "Сбросить аватарку",
|
||||
"Make a Snapshot": "Сделать снимок",
|
||||
"Avoid cropping and resizing imported character images. When off, crop/resize to 512x768": "Не менять размер картинок у импортируемых персонажей. При отключении все картинки будут приводиться к размеру 512х768",
|
||||
"never_resize_avatars_tooltip": "Не менять размер картинок у импортируемых персонажей. При отключении все картинки будут приводиться к размеру 512х768",
|
||||
"Char List Subheader": "Доп. заголовок в списке персонажей",
|
||||
"# Messages to Load": "Сколько сообщений загружать",
|
||||
"(0 = All)": "(0 = все)",
|
||||
|
@ -633,7 +633,7 @@
|
||||
"Prefer Character Card Prompt": "Перевага запиту персонажа",
|
||||
"If checked and the character card contains a jailbreak override (Post History Instruction), use that instead": "Якщо відмічено і картка персонажа містить заміну джейлбрейку (Інструкцію), використовуйте її замість цього",
|
||||
"Prefer Character Card Jailbreak": "Перевага джейлбрейку персонажа",
|
||||
"Avoid cropping and resizing imported character images. When off, crop/resize to 512x768": "Уникайте обрізання та зміни розміру імпортованих зображень символів. Коли вимкнено, обрізати/змінити розмір до 512x768.",
|
||||
"never_resize_avatars_tooltip": "Уникайте обрізання та зміни розміру імпортованих зображень символів. Коли вимкнено, обрізати/змінити розмір до 512x768.",
|
||||
"Never resize avatars": "Ніколи не змінювати розмір аватарів",
|
||||
"Show actual file names on the disk, in the characters list display only": "Показувати фактичні назви файлів на диску, тільки у відображенні списку персонажів",
|
||||
"Show avatar filenames": "Показувати імена файлів аватарів",
|
||||
|
@ -633,7 +633,7 @@
|
||||
"Prefer Character Card Prompt": "Ưu tiên Gợi ý từ Card",
|
||||
"If checked and the character card contains a jailbreak override (Post History Instruction), use that instead": "Nếu được kiểm tra và thẻ nhân vật chứa một lệnh phá vỡ giam giữ (Hướng dẫn Lịch sử Bài viết), hãy sử dụng thay vào đó",
|
||||
"Prefer Character Card Jailbreak": "Ưu tiên Jailbreak từ Card",
|
||||
"Avoid cropping and resizing imported character images. When off, crop/resize to 512x768": "Tránh cắt xén và thay đổi kích thước hình ảnh ký tự đã nhập. Khi tắt, hãy cắt/thay đổi kích thước thành 512x768.",
|
||||
"never_resize_avatars_tooltip": "Tránh cắt xén và thay đổi kích thước hình ảnh ký tự đã nhập. Khi tắt, hãy cắt/thay đổi kích thước thành 512x768.",
|
||||
"Never resize avatars": "Không bao giờ thay đổi kích thước hình đại diện",
|
||||
"Show actual file names on the disk, in the characters list display only": "Hiển thị tên tệp thực tế trên đĩa, chỉ trong danh sách nhân vật",
|
||||
"Show avatar filenames": "Hiển thị tên tệp hình đại diện",
|
||||
|
@ -722,7 +722,7 @@
|
||||
"Prefer Character Card Prompt": "角色卡提示词优先",
|
||||
"If checked and the character card contains a Post-History Instructions override, use that instead": "开启后,如果角色卡包含后历史指令覆盖,则使用它。",
|
||||
"Prefer Character Card Instructions": "首选角色卡说明",
|
||||
"Avoid cropping and resizing imported character images. When off, crop/resize to 512x768": "避免裁剪和调整导入的角色图像的大小。关闭时,裁剪/调整大小为 512x768。",
|
||||
"never_resize_avatars_tooltip": "避免裁剪和调整导入的角色图像的大小。关闭时,裁剪/调整大小为 512x768。",
|
||||
"Never resize avatars": "永不调整头像大小",
|
||||
"Show actual file names on the disk, in the characters list display only": "在角色列表显示中,显示磁盘上实际的文件名。",
|
||||
"Show avatar filenames": "显示头像文件名",
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -5708,14 +5708,15 @@ function parseAndSaveLogprobs(data, continueFrom) {
|
||||
/**
|
||||
* Extracts the message from the response data.
|
||||
* @param {object} data Response data
|
||||
* @param {string} activeApi If it's set, ignores active API
|
||||
* @returns {string} Extracted message
|
||||
*/
|
||||
function extractMessageFromData(data) {
|
||||
export function extractMessageFromData(data, activeApi = null) {
|
||||
if (typeof data === 'string') {
|
||||
return data;
|
||||
}
|
||||
|
||||
switch (main_api) {
|
||||
switch (activeApi ?? main_api) {
|
||||
case 'kobold':
|
||||
return data.results[0].text;
|
||||
case 'koboldhorde':
|
||||
@ -5725,7 +5726,7 @@ function extractMessageFromData(data) {
|
||||
case 'novel':
|
||||
return data.output;
|
||||
case 'openai':
|
||||
return data?.choices?.[0]?.message?.content ?? data?.choices?.[0]?.text ?? data?.text ?? data?.message?.content?.[0]?.text ?? data?.message?.tool_plan ?? '';
|
||||
return data?.content?.find(p => p.type === 'text')?.text ?? data?.choices?.[0]?.message?.content ?? data?.choices?.[0]?.text ?? data?.text ?? data?.message?.content?.[0]?.text ?? data?.message?.tool_plan ?? '';
|
||||
default:
|
||||
return '';
|
||||
}
|
||||
@ -6085,17 +6086,52 @@ export async function saveReply(type, getMessage, fromStreaming, title, swipes,
|
||||
return { type, getMessage };
|
||||
}
|
||||
|
||||
export function syncCurrentSwipeInfoExtras() {
|
||||
/**
|
||||
* Syncs the current message and all its data into the swipe data at the given message ID (or the last message if no ID is given).
|
||||
*
|
||||
* If the swipe data is invalid in some way, this function will exit out without doing anything.
|
||||
* @param {number?} [messageId=null] - The ID of the message to sync with the swipe data. If no ID is given, the last message is used.
|
||||
* @returns {boolean} Whether the message was successfully synced
|
||||
*/
|
||||
export function syncMesToSwipe(messageId = null) {
|
||||
if (!chat.length) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
const currentMessage = chat[chat.length - 1];
|
||||
if (currentMessage && Array.isArray(currentMessage.swipe_info) && typeof currentMessage.swipe_id === 'number') {
|
||||
const swipeInfo = currentMessage.swipe_info[currentMessage.swipe_id];
|
||||
if (swipeInfo && typeof swipeInfo === 'object') {
|
||||
swipeInfo.extra = structuredClone(currentMessage.extra);
|
||||
|
||||
const targetMessageId = messageId ?? chat.length - 1;
|
||||
if (chat.length > targetMessageId || targetMessageId < 0) {
|
||||
console.warn(`[syncMesToSwipe] Invalid message ID: ${messageId}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
const targetMessage = chat[targetMessageId];
|
||||
|
||||
// No swipe data there yet, exit out
|
||||
if (typeof targetMessage.swipe_id !== 'number') {
|
||||
return false;
|
||||
}
|
||||
// If swipes structure is invalid, exit out (for now?)
|
||||
if (!Array.isArray(targetMessage.swipe_info) || !Array.isArray(targetMessage.swipes)) {
|
||||
return false;
|
||||
}
|
||||
// If the swipe is not present yet, exit out (will likely be copied later)
|
||||
if (!targetMessage.swipes[targetMessage.swipe_id] || !targetMessage.swipe_info[targetMessage.swipe_id]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const targetSwipeInfo = targetMessage.swipe_info[targetMessage.swipe_id];
|
||||
if (typeof targetSwipeInfo !== 'object') {
|
||||
return false;
|
||||
}
|
||||
|
||||
targetMessage.swipes[targetMessage.swipe_id] = targetMessage.mes;
|
||||
|
||||
targetSwipeInfo.send_date = targetMessage.send_date;
|
||||
targetSwipeInfo.gen_started = targetMessage.gen_started;
|
||||
targetSwipeInfo.gen_finished = targetMessage.gen_finished;
|
||||
targetSwipeInfo.extra = structuredClone(targetMessage.extra);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function saveImageToMessage(img, mes) {
|
||||
@ -6400,6 +6436,7 @@ export function saveChatDebounced() {
|
||||
if (chatSaveTimeout) {
|
||||
console.debug('Clearing chat save timeout');
|
||||
clearTimeout(chatSaveTimeout);
|
||||
chatSaveTimeout = null;
|
||||
}
|
||||
|
||||
chatSaveTimeout = setTimeout(async () => {
|
||||
@ -6416,7 +6453,7 @@ export function saveChatDebounced() {
|
||||
console.debug('Chat save timeout triggered');
|
||||
await saveChatConditional();
|
||||
console.debug('Chat saved');
|
||||
}, 1000);
|
||||
}, DEFAULT_SAVE_EDIT_TIMEOUT);
|
||||
}
|
||||
|
||||
export async function saveChat(chatName, withMetadata, mesId) {
|
||||
@ -8031,6 +8068,12 @@ export async function saveChatConditional() {
|
||||
}
|
||||
|
||||
try {
|
||||
if (chatSaveTimeout) {
|
||||
console.debug('Debounced chat save canceled');
|
||||
clearTimeout(chatSaveTimeout);
|
||||
chatSaveTimeout = null;
|
||||
}
|
||||
|
||||
isChatSaving = true;
|
||||
|
||||
if (selected_group) {
|
||||
@ -8567,7 +8610,7 @@ function swipe_left() { // when we swipe left..but no generation.
|
||||
}
|
||||
|
||||
// Make sure ad-hoc changes to extras are saved before swiping away
|
||||
syncCurrentSwipeInfoExtras();
|
||||
syncMesToSwipe();
|
||||
|
||||
const swipe_duration = 120;
|
||||
const swipe_range = '700px';
|
||||
@ -8705,7 +8748,7 @@ const swipe_right = () => {
|
||||
}
|
||||
|
||||
// Make sure ad-hoc changes to extras are saved before swiping away
|
||||
syncCurrentSwipeInfoExtras();
|
||||
syncMesToSwipe();
|
||||
|
||||
const swipe_duration = 200;
|
||||
const swipe_range = 700;
|
||||
@ -8873,7 +8916,7 @@ const swipe_right = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const CONNECT_API_MAP = {
|
||||
export const CONNECT_API_MAP = {
|
||||
// Default APIs not contined inside text gen / chat gen
|
||||
'kobold': {
|
||||
selected: 'kobold',
|
||||
|
@ -45,6 +45,10 @@
|
||||
<option data-type="openai" value="gpt-4o">gpt-4o</option>
|
||||
<option data-type="openai" value="gpt-4o-mini">gpt-4o-mini</option>
|
||||
<option data-type="openai" value="chatgpt-4o-latest">chatgpt-4o-latest</option>
|
||||
<option data-type="openai" value="o1">o1</option>
|
||||
<option data-type="openai" value="o1-2024-12-17">o1-2024-12-17</option>
|
||||
<option data-type="anthropic" value="claude-3-7-sonnet-latest">claude-3-7-sonnet-latest</option>
|
||||
<option data-type="anthropic" value="claude-3-7-sonnet-20250219">claude-3-7-sonnet-20250219</option>
|
||||
<option data-type="anthropic" value="claude-3-5-sonnet-latest">claude-3-5-sonnet-latest</option>
|
||||
<option data-type="anthropic" value="claude-3-5-sonnet-20241022">claude-3-5-sonnet-20241022</option>
|
||||
<option data-type="anthropic" value="claude-3-5-sonnet-20240620">claude-3-5-sonnet-20240620</option>
|
||||
|
@ -32,9 +32,6 @@ export { MODULE_NAME };
|
||||
|
||||
const MODULE_NAME = '1_memory';
|
||||
|
||||
let lastCharacterId = null;
|
||||
let lastGroupId = null;
|
||||
let lastChatId = null;
|
||||
let lastMessageHash = null;
|
||||
let lastMessageId = null;
|
||||
let inApiCall = false;
|
||||
@ -251,7 +248,7 @@ function onSummarySourceChange(event) {
|
||||
}
|
||||
|
||||
function switchSourceControls(value) {
|
||||
$('#memory_settings [data-summary-source]').each((_, element) => {
|
||||
$('#summaryExtensionDrawerContents [data-summary-source], #memory_settings [data-summary-source]').each((_, element) => {
|
||||
const source = element.dataset.summarySource.split(',').map(s => s.trim());
|
||||
$(element).toggle(source.includes(value));
|
||||
});
|
||||
@ -349,15 +346,6 @@ function onMaxMessagesPerRequestInput() {
|
||||
saveSettingsDebounced();
|
||||
}
|
||||
|
||||
function saveLastValues() {
|
||||
const context = getContext();
|
||||
lastGroupId = context.groupId;
|
||||
lastCharacterId = context.characterId;
|
||||
lastChatId = context.chatId;
|
||||
lastMessageId = context.chat?.length ?? null;
|
||||
lastMessageHash = getStringHash((context.chat.length && context.chat[context.chat.length - 1]['mes']) ?? '');
|
||||
}
|
||||
|
||||
function getLatestMemoryFromChat(chat) {
|
||||
if (!Array.isArray(chat) || !chat.length) {
|
||||
return '';
|
||||
@ -390,6 +378,12 @@ function getIndexOfLatestChatSummary(chat) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
function onChatChanged() {
|
||||
const context = getContext();
|
||||
const latestMemory = getLatestMemoryFromChat(context.chat);
|
||||
setMemoryContext(latestMemory, false);
|
||||
}
|
||||
|
||||
async function onChatEvent() {
|
||||
// Module not enabled
|
||||
if (extension_settings.memory.source === summary_sources.extras && !modules.includes('summarize')) {
|
||||
@ -401,32 +395,19 @@ async function onChatEvent() {
|
||||
return;
|
||||
}
|
||||
|
||||
const context = getContext();
|
||||
const chat = context.chat;
|
||||
|
||||
// no characters or group selected
|
||||
if (!context.groupId && context.characterId === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Streaming in-progress
|
||||
if (streamingProcessor && !streamingProcessor.isFinished) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Chat/character/group changed
|
||||
if ((context.groupId && lastGroupId !== context.groupId) || (context.characterId !== lastCharacterId) || (context.chatId !== lastChatId)) {
|
||||
const latestMemory = getLatestMemoryFromChat(chat);
|
||||
setMemoryContext(latestMemory, false);
|
||||
saveLastValues();
|
||||
return;
|
||||
}
|
||||
|
||||
// Currently summarizing or frozen state - skip
|
||||
if (inApiCall || extension_settings.memory.memoryFrozen) {
|
||||
return;
|
||||
}
|
||||
|
||||
const context = getContext();
|
||||
const chat = context.chat;
|
||||
|
||||
// No new messages - do nothing
|
||||
if (chat.length === 0 || (lastMessageId === chat.length && getStringHash(chat[chat.length - 1].mes) === lastMessageHash)) {
|
||||
return;
|
||||
@ -449,7 +430,10 @@ async function onChatEvent() {
|
||||
|
||||
summarizeChat(context)
|
||||
.catch(console.error)
|
||||
.finally(saveLastValues);
|
||||
.finally(() => {
|
||||
lastMessageId = context.chat?.length ?? null;
|
||||
lastMessageHash = getStringHash((context.chat.length && context.chat[context.chat.length - 1]['mes']) ?? '');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -464,13 +448,7 @@ async function forceSummarizeChat(quiet) {
|
||||
}
|
||||
|
||||
const context = getContext();
|
||||
|
||||
const skipWIAN = extension_settings.memory.SkipWIAN;
|
||||
console.log(`Skipping WIAN? ${skipWIAN}`);
|
||||
if (!context.chatId) {
|
||||
toastr.warning('No chat selected');
|
||||
return '';
|
||||
}
|
||||
|
||||
const toast = quiet ? jQuery() : toastr.info('Summarizing chat...', 'Please wait', { timeOut: 0, extendedTimeOut: 0 });
|
||||
const value = extension_settings.memory.source === summary_sources.main
|
||||
@ -993,7 +971,7 @@ function doPopout(e) {
|
||||
.removeClass('zoomed_avatar')
|
||||
.addClass('draggable')
|
||||
.empty();
|
||||
const prevSummaryBoxContents = $('#memory_contents').val(); //copy summary box before emptying
|
||||
const prevSummaryBoxContents = $('#memory_contents').val().toString(); //copy summary box before emptying
|
||||
originalElement.empty();
|
||||
originalElement.html('<div class="flex-container alignitemscenter justifyCenter wide100p"><small>Currently popped out</small></div>');
|
||||
newElement.append(controlBarHtml).append(originalHTMLClone);
|
||||
@ -1013,7 +991,7 @@ function doPopout(e) {
|
||||
const summaryPopoutHTML = $('#summaryExtensionDrawerContents');
|
||||
$('#summaryExtensionPopout').fadeOut(animation_duration, () => {
|
||||
originalElement.empty();
|
||||
originalElement.html(summaryPopoutHTML);
|
||||
originalElement.append(summaryPopoutHTML);
|
||||
$('#summaryExtensionPopout').remove();
|
||||
});
|
||||
loadSettings();
|
||||
@ -1027,31 +1005,30 @@ function doPopout(e) {
|
||||
function setupListeners() {
|
||||
//setup shared listeners for popout and regular ext menu
|
||||
$('#memory_restore').off('click').on('click', onMemoryRestoreClick);
|
||||
$('#memory_contents').off('click').on('input', onMemoryContentInput);
|
||||
$('#memory_frozen').off('click').on('input', onMemoryFrozenInput);
|
||||
$('#memory_skipWIAN').off('click').on('input', onMemorySkipWIANInput);
|
||||
$('#summary_source').off('click').on('change', onSummarySourceChange);
|
||||
$('#memory_prompt_words').off('click').on('input', onMemoryPromptWordsInput);
|
||||
$('#memory_prompt_interval').off('click').on('input', onMemoryPromptIntervalInput);
|
||||
$('#memory_prompt').off('click').on('input', onMemoryPromptInput);
|
||||
$('#memory_contents').off('input').on('input', onMemoryContentInput);
|
||||
$('#memory_frozen').off('input').on('input', onMemoryFrozenInput);
|
||||
$('#memory_skipWIAN').off('input').on('input', onMemorySkipWIANInput);
|
||||
$('#summary_source').off('change').on('change', onSummarySourceChange);
|
||||
$('#memory_prompt_words').off('input').on('input', onMemoryPromptWordsInput);
|
||||
$('#memory_prompt_interval').off('input').on('input', onMemoryPromptIntervalInput);
|
||||
$('#memory_prompt').off('input').on('input', onMemoryPromptInput);
|
||||
$('#memory_force_summarize').off('click').on('click', () => forceSummarizeChat(false));
|
||||
$('#memory_template').off('click').on('input', onMemoryTemplateInput);
|
||||
$('#memory_depth').off('click').on('input', onMemoryDepthInput);
|
||||
$('#memory_role').off('click').on('input', onMemoryRoleInput);
|
||||
$('input[name="memory_position"]').off('click').on('change', onMemoryPositionChange);
|
||||
$('#memory_prompt_words_force').off('click').on('input', onMemoryPromptWordsForceInput);
|
||||
$('#memory_prompt_builder_default').off('click').on('input', onMemoryPromptBuilderInput);
|
||||
$('#memory_prompt_builder_raw_blocking').off('click').on('input', onMemoryPromptBuilderInput);
|
||||
$('#memory_prompt_builder_raw_non_blocking').off('click').on('input', onMemoryPromptBuilderInput);
|
||||
$('#memory_template').off('input').on('input', onMemoryTemplateInput);
|
||||
$('#memory_depth').off('input').on('input', onMemoryDepthInput);
|
||||
$('#memory_role').off('input').on('input', onMemoryRoleInput);
|
||||
$('input[name="memory_position"]').off('change').on('change', onMemoryPositionChange);
|
||||
$('#memory_prompt_words_force').off('input').on('input', onMemoryPromptWordsForceInput);
|
||||
$('#memory_prompt_builder_default').off('input').on('input', onMemoryPromptBuilderInput);
|
||||
$('#memory_prompt_builder_raw_blocking').off('input').on('input', onMemoryPromptBuilderInput);
|
||||
$('#memory_prompt_builder_raw_non_blocking').off('input').on('input', onMemoryPromptBuilderInput);
|
||||
$('#memory_prompt_restore').off('click').on('click', onMemoryPromptRestoreClick);
|
||||
$('#memory_prompt_interval_auto').off('click').on('click', onPromptIntervalAutoClick);
|
||||
$('#memory_prompt_words_auto').off('click').on('click', onPromptForceWordsAutoClick);
|
||||
$('#memory_override_response_length').off('click').on('input', onOverrideResponseLengthInput);
|
||||
$('#memory_max_messages_per_request').off('click').on('input', onMaxMessagesPerRequestInput);
|
||||
$('#memory_override_response_length').off('input').on('input', onOverrideResponseLengthInput);
|
||||
$('#memory_max_messages_per_request').off('input').on('input', onMaxMessagesPerRequestInput);
|
||||
$('#memory_include_wi_scan').off('input').on('input', onMemoryIncludeWIScanInput);
|
||||
$('#summarySettingsBlockToggle').off('click').on('click', function () {
|
||||
console.log('saw settings button click');
|
||||
$('#summarySettingsBlock').slideToggle(200, 'swing'); //toggleClass("hidden");
|
||||
$('#summarySettingsBlock').slideToggle(200, 'swing');
|
||||
});
|
||||
}
|
||||
|
||||
@ -1068,11 +1045,11 @@ jQuery(async function () {
|
||||
|
||||
await addExtensionControls();
|
||||
loadSettings();
|
||||
eventSource.on(event_types.CHAT_CHANGED, onChatChanged);
|
||||
eventSource.makeLast(event_types.CHARACTER_MESSAGE_RENDERED, onChatEvent);
|
||||
eventSource.on(event_types.MESSAGE_DELETED, onChatEvent);
|
||||
eventSource.on(event_types.MESSAGE_EDITED, onChatEvent);
|
||||
eventSource.on(event_types.MESSAGE_SWIPED, onChatEvent);
|
||||
eventSource.on(event_types.CHAT_CHANGED, onChatEvent);
|
||||
for (const event of [event_types.MESSAGE_DELETED, event_types.MESSAGE_UPDATED, event_types.MESSAGE_SWIPED]) {
|
||||
eventSource.on(event, onChatEvent);
|
||||
}
|
||||
SlashCommandParser.addCommandObject(SlashCommand.fromProps({
|
||||
name: 'summarize',
|
||||
callback: summarizeCallback,
|
||||
|
@ -10,16 +10,18 @@
|
||||
<div class="inline-drawer-content">
|
||||
<div id="summaryExtensionDrawerContents">
|
||||
<label for="summary_source" data-i18n="ext_sum_with">Summarize with:</label>
|
||||
<select id="summary_source">
|
||||
<select id="summary_source" class="text_pole">
|
||||
<option value="main" data-i18n="ext_sum_main_api">Main API</option>
|
||||
<option value="extras">Extras API (deprecated)</option>
|
||||
<option value="webllm" data-i18n="ext_sum_webllm">WebLLM Extension</option>
|
||||
</select><br>
|
||||
|
||||
<div class="flex-container justifyspacebetween alignitemscenter">
|
||||
<span class="flex1" data-i18n="ext_sum_current_summary">Current summary:</span>
|
||||
<div id="memory_restore" class="menu_button flex1 margin0" data-i18n="[title]ext_sum_restore_tip" title="Restore a previous summary; use repeatedly to clear summarization state for this chat.">
|
||||
<span data-i18n="ext_sum_restore_previous">Restore Previous</span>
|
||||
<span data-i18n="ext_sum_current_summary">Current summary:</span>
|
||||
<i class="editor_maximize fa-solid fa-maximize right_menu_button" data-for="memory_contents" title="Expand the editor" data-i18n="[title]Expand the editor"></i>
|
||||
<span class="flex1"> </span>
|
||||
<div id="memory_restore" class="menu_button margin0" data-i18n="[title]ext_sum_restore_tip" title="Restore a previous summary; use repeatedly to clear summarization state for this chat.">
|
||||
<small data-i18n="ext_sum_restore_previous">Restore Previous</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -3,9 +3,13 @@
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#memory_settings textarea {
|
||||
font-size: calc(var(--mainFontSize) * 0.9);
|
||||
line-height: 1.2;
|
||||
#memory_contents {
|
||||
field-sizing: content;
|
||||
max-height: 50dvh;
|
||||
}
|
||||
|
||||
#memory_restore {
|
||||
width: max-content;
|
||||
}
|
||||
|
||||
label[for="memory_frozen"],
|
||||
@ -35,3 +39,9 @@ label[for="memory_frozen"] input {
|
||||
flex-direction: column;
|
||||
row-gap: 5px;
|
||||
}
|
||||
|
||||
#summaryExtensionPopout {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding-top: 25px;
|
||||
}
|
||||
|
@ -393,8 +393,8 @@ export let proxies = [
|
||||
];
|
||||
export let selected_proxy = proxies[0];
|
||||
|
||||
let openai_setting_names;
|
||||
let openai_settings;
|
||||
export let openai_setting_names;
|
||||
export let openai_settings;
|
||||
|
||||
/** @type {import('./PromptManager.js').PromptManager} */
|
||||
export let promptManager = null;
|
||||
@ -1497,8 +1497,14 @@ async function sendWindowAIRequest(messages, signal, stream) {
|
||||
}
|
||||
}
|
||||
|
||||
export function getChatCompletionModel() {
|
||||
switch (oai_settings.chat_completion_source) {
|
||||
/**
|
||||
* Gets the API model for the selected chat completion source.
|
||||
* @param {string} source If it's set, ignores active source
|
||||
* @returns {string} API model
|
||||
*/
|
||||
export function getChatCompletionModel(source = null) {
|
||||
const activeSource = source ?? oai_settings.chat_completion_source;
|
||||
switch (activeSource) {
|
||||
case chat_completion_sources.CLAUDE:
|
||||
return oai_settings.claude_model;
|
||||
case chat_completion_sources.OPENAI:
|
||||
@ -1532,7 +1538,7 @@ export function getChatCompletionModel() {
|
||||
case chat_completion_sources.DEEPSEEK:
|
||||
return oai_settings.deepseek_model;
|
||||
default:
|
||||
throw new Error(`Unknown chat completion source: ${oai_settings.chat_completion_source}`);
|
||||
throw new Error(`Unknown chat completion source: ${activeSource}`);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2149,6 +2155,9 @@ async function sendOpenAIRequest(type, messages, signal) {
|
||||
*/
|
||||
function getStreamingReply(data, state) {
|
||||
if (oai_settings.chat_completion_source === chat_completion_sources.CLAUDE) {
|
||||
if (oai_settings.show_thoughts) {
|
||||
state.reasoning += data?.delta?.thinking || '';
|
||||
}
|
||||
return data?.delta?.text || '';
|
||||
} else if (oai_settings.chat_completion_source === chat_completion_sources.MAKERSUITE) {
|
||||
if (oai_settings.show_thoughts) {
|
||||
@ -3990,6 +3999,8 @@ function onSettingsPresetChange() {
|
||||
n: ['#n_openai', 'n', false],
|
||||
};
|
||||
|
||||
const presetNameBefore = oai_settings.preset_settings_openai;
|
||||
|
||||
const presetName = $('#settings_preset_openai').find(':selected').text();
|
||||
oai_settings.preset_settings_openai = presetName;
|
||||
|
||||
@ -4015,6 +4026,7 @@ function onSettingsPresetChange() {
|
||||
settingsToUpdate: settingsToUpdate,
|
||||
settings: oai_settings,
|
||||
savePreset: saveOpenAIPreset,
|
||||
presetNameBefore: presetNameBefore,
|
||||
}).finally(r => {
|
||||
$('.model_custom_select').empty();
|
||||
|
||||
@ -4981,6 +4993,7 @@ export function isImageInliningSupported() {
|
||||
'gemini-1.5-pro-exp-0827',
|
||||
'claude-3',
|
||||
'claude-3-5',
|
||||
'claude-3-7',
|
||||
'gpt-4-turbo',
|
||||
'gpt-4o',
|
||||
'gpt-4o-mini',
|
||||
|
@ -21,6 +21,7 @@ import { groups, selected_group } from './group-chats.js';
|
||||
import { instruct_presets } from './instruct-mode.js';
|
||||
import { kai_settings } from './kai-settings.js';
|
||||
import { convertNovelPreset } from './nai-settings.js';
|
||||
import { openai_settings, openai_setting_names, oai_settings } from './openai.js';
|
||||
import { Popup, POPUP_RESULT, POPUP_TYPE } from './popup.js';
|
||||
import { context_presets, getContextSettings, power_user } from './power-user.js';
|
||||
import { SlashCommand } from './slash-commands/SlashCommand.js';
|
||||
@ -456,6 +457,10 @@ class PresetManager {
|
||||
presets = textgenerationwebui_presets;
|
||||
preset_names = textgenerationwebui_preset_names;
|
||||
break;
|
||||
case 'openai':
|
||||
presets = openai_settings;
|
||||
preset_names = openai_setting_names;
|
||||
break;
|
||||
case 'context':
|
||||
presets = context_presets;
|
||||
preset_names = context_presets.map(x => x.name);
|
||||
@ -606,6 +611,30 @@ class PresetManager {
|
||||
return settings;
|
||||
}
|
||||
|
||||
getCompletionPresetByName(name) {
|
||||
// Retrieve a completion preset by name. Return undefined if not found.
|
||||
let { presets, preset_names } = this.getPresetList();
|
||||
let preset;
|
||||
|
||||
// Some APIs use an array of names, others use an object of {name: index}
|
||||
if (Array.isArray(preset_names)) { // array of names
|
||||
if (preset_names.includes(name)) {
|
||||
preset = presets[preset_names.indexOf(name)];
|
||||
}
|
||||
} else { // object of {names: index}
|
||||
if (preset_names[name] !== undefined) {
|
||||
preset = presets[preset_names[name]];
|
||||
}
|
||||
}
|
||||
|
||||
if (preset === undefined) {
|
||||
console.error(`Preset ${name} not found`);
|
||||
}
|
||||
|
||||
// if the preset isn't found, returns undefined
|
||||
return preset;
|
||||
}
|
||||
|
||||
// pass no arguments to delete current preset
|
||||
async deletePreset(name) {
|
||||
const { preset_names, presets } = this.getPresetList();
|
||||
|
@ -1,7 +1,7 @@
|
||||
import {
|
||||
moment,
|
||||
} from '../lib.js';
|
||||
import { chat, closeMessageEditor, event_types, eventSource, main_api, messageFormatting, saveChatConditional, saveSettingsDebounced, substituteParams, updateMessageBlock } from '../script.js';
|
||||
import { chat, closeMessageEditor, event_types, eventSource, main_api, messageFormatting, saveChatConditional, saveChatDebounced, saveSettingsDebounced, substituteParams, syncMesToSwipe, updateMessageBlock } from '../script.js';
|
||||
import { getRegexedString, regex_placement } from './extensions/regex/engine.js';
|
||||
import { getCurrentLocale, t, translate } from './i18n.js';
|
||||
import { MacrosParser } from './macros.js';
|
||||
@ -76,6 +76,8 @@ export function extractReasoningFromData(data) {
|
||||
return data?.choices?.[0]?.message?.reasoning ?? '';
|
||||
case chat_completion_sources.MAKERSUITE:
|
||||
return data?.responseContent?.parts?.filter(part => part.thought)?.map(part => part.text)?.join('\n\n') ?? '';
|
||||
case chat_completion_sources.CLAUDE:
|
||||
return data?.content?.find(part => part.type === 'thinking')?.thinking ?? '';
|
||||
case chat_completion_sources.CUSTOM: {
|
||||
return data?.choices?.[0]?.message?.reasoning_content
|
||||
?? data?.choices?.[0]?.message?.reasoning
|
||||
@ -755,6 +757,17 @@ function registerReasoningMacros() {
|
||||
}
|
||||
|
||||
function setReasoningEventHandlers() {
|
||||
/**
|
||||
* Updates the reasoning block of a message from a value.
|
||||
* @param {object} message Message object
|
||||
* @param {string} value Reasoning value
|
||||
*/
|
||||
function updateReasoningFromValue(message, value) {
|
||||
const reasoning = getRegexedString(value, regex_placement.REASONING, { isEdit: true });
|
||||
message.extra.reasoning = reasoning;
|
||||
message.extra.reasoning_type = message.extra.reasoning_type ? ReasoningType.Edited : ReasoningType.Manual;
|
||||
}
|
||||
|
||||
$(document).on('click', '.mes_reasoning_details', function (e) {
|
||||
if (!e.target.closest('.mes_reasoning_actions') && !e.target.closest('.mes_reasoning_header')) {
|
||||
e.preventDefault();
|
||||
@ -835,9 +848,7 @@ function setReasoningEventHandlers() {
|
||||
}
|
||||
|
||||
const textarea = messageBlock.find('.reasoning_edit_textarea');
|
||||
const reasoning = getRegexedString(String(textarea.val()), regex_placement.REASONING, { isEdit: true });
|
||||
message.extra.reasoning = reasoning;
|
||||
message.extra.reasoning_type = message.extra.reasoning_type ? ReasoningType.Edited : ReasoningType.Manual;
|
||||
updateReasoningFromValue(message, String(textarea.val()));
|
||||
await saveChatConditional();
|
||||
updateMessageBlock(messageId, message);
|
||||
textarea.remove();
|
||||
@ -917,6 +928,20 @@ function setReasoningEventHandlers() {
|
||||
await copyText(reasoning);
|
||||
toastr.info(t`Copied!`, '', { timeOut: 2000 });
|
||||
});
|
||||
|
||||
$(document).on('input', '.reasoning_edit_textarea', function () {
|
||||
if (!power_user.auto_save_msg_edits) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { message } = getMessageFromJquery(this);
|
||||
if (!message?.extra) {
|
||||
return;
|
||||
}
|
||||
|
||||
updateReasoningFromValue(message, String($(this).val()));
|
||||
saveChatDebounced();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -973,7 +998,7 @@ function parseReasoningFromString(str, { strict = true } = {}) {
|
||||
}
|
||||
|
||||
function registerReasoningAppEvents() {
|
||||
eventSource.makeFirst(event_types.MESSAGE_RECEIVED, (/** @type {number} */ idx) => {
|
||||
const eventHandler = (/** @type {number} */ idx) => {
|
||||
if (!power_user.reasoning.auto_parse) {
|
||||
return;
|
||||
}
|
||||
@ -1021,15 +1046,22 @@ function registerReasoningAppEvents() {
|
||||
message.mes = parsedReasoning.content;
|
||||
}
|
||||
|
||||
// Find if a message already exists in DOM and must be updated
|
||||
if (contentUpdated) {
|
||||
syncMesToSwipe();
|
||||
saveChatDebounced();
|
||||
|
||||
// Find if a message already exists in DOM and must be updated
|
||||
const messageRendered = document.querySelector(`.mes[mesid="${idx}"]`) !== null;
|
||||
if (messageRendered) {
|
||||
console.debug('[Reasoning] Updating message block', idx);
|
||||
updateMessageBlock(idx, message);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
for (const event of [event_types.MESSAGE_RECEIVED, event_types.MESSAGE_UPDATED]) {
|
||||
eventSource.on(event, eventHandler);
|
||||
}
|
||||
}
|
||||
|
||||
export function initReasoning() {
|
||||
|
@ -42,7 +42,7 @@ import {
|
||||
showMoreMessages,
|
||||
stopGeneration,
|
||||
substituteParams,
|
||||
syncCurrentSwipeInfoExtras,
|
||||
syncMesToSwipe,
|
||||
system_avatar,
|
||||
system_message_types,
|
||||
this_chid,
|
||||
@ -2921,7 +2921,7 @@ async function addSwipeCallback(args, value) {
|
||||
|
||||
if (isTrueBoolean(args.switch)) {
|
||||
// Make sure ad-hoc changes to extras are saved before swiping away
|
||||
syncCurrentSwipeInfoExtras();
|
||||
syncMesToSwipe();
|
||||
lastMessage.swipe_id = newSwipeId;
|
||||
lastMessage.mes = lastMessage.swipes[newSwipeId];
|
||||
lastMessage.extra = structuredClone(lastMessage.swipe_info?.[newSwipeId]?.extra ?? lastMessage.extra ?? {});
|
||||
|
@ -6,11 +6,13 @@ import {
|
||||
characters,
|
||||
chat,
|
||||
chat_metadata,
|
||||
CONNECT_API_MAP,
|
||||
create_save,
|
||||
deactivateSendButtons,
|
||||
event_types,
|
||||
eventSource,
|
||||
extension_prompts,
|
||||
extractMessageFromData,
|
||||
Generate,
|
||||
generateQuietPrompt,
|
||||
getCharacters,
|
||||
@ -55,9 +57,10 @@ import { groups, openGroupChat, selected_group } from './group-chats.js';
|
||||
import { t, translate } from './i18n.js';
|
||||
import { hideLoader, showLoader } from './loader.js';
|
||||
import { MacrosParser } from './macros.js';
|
||||
import { oai_settings } from './openai.js';
|
||||
import { getChatCompletionModel, oai_settings } from './openai.js';
|
||||
import { callGenericPopup, Popup, POPUP_RESULT, POPUP_TYPE } from './popup.js';
|
||||
import { power_user, registerDebugFunction } from './power-user.js';
|
||||
import { getPresetManager } from './preset-manager.js';
|
||||
import { humanizedDateTime, isMobile, shouldSendOnEnter } from './RossAscends-mods.js';
|
||||
import { ScraperManager } from './scrapers.js';
|
||||
import { executeSlashCommands, executeSlashCommandsWithOptions, registerSlashCommand } from './slash-commands.js';
|
||||
@ -65,7 +68,7 @@ import { SlashCommand } from './slash-commands/SlashCommand.js';
|
||||
import { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from './slash-commands/SlashCommandArgument.js';
|
||||
import { SlashCommandParser } from './slash-commands/SlashCommandParser.js';
|
||||
import { tag_map, tags } from './tags.js';
|
||||
import { textgenerationwebui_settings } from './textgen-settings.js';
|
||||
import { getTextGenServer, textgenerationwebui_settings } from './textgen-settings.js';
|
||||
import { tokenizers, getTextTokens, getTokenCount, getTokenCountAsync, getTokenizerModel } from './tokenizers.js';
|
||||
import { ToolManager } from './tool-calling.js';
|
||||
import { accountStorage } from './util/AccountStorage.js';
|
||||
@ -193,6 +196,11 @@ export function getContext() {
|
||||
saveWorldInfo,
|
||||
updateWorldInfoList,
|
||||
convertCharacterBook,
|
||||
CONNECT_API_MAP,
|
||||
getTextGenServer,
|
||||
extractMessageFromData,
|
||||
getPresetManager,
|
||||
getChatCompletionModel,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -318,8 +318,14 @@ export function validateTextGenUrl() {
|
||||
control.val(formattedUrl);
|
||||
}
|
||||
|
||||
export function getTextGenServer() {
|
||||
switch (settings.type) {
|
||||
/**
|
||||
* Gets the API URL for the selected text generation type.
|
||||
* @param {string} type If it's set, ignores active type
|
||||
* @returns {string} API URL
|
||||
*/
|
||||
export function getTextGenServer(type = null) {
|
||||
const selectedType = type ?? settings.type;
|
||||
switch (selectedType) {
|
||||
case FEATHERLESS:
|
||||
return FEATHERLESS_SERVER;
|
||||
case MANCER:
|
||||
@ -333,7 +339,7 @@ export function getTextGenServer() {
|
||||
case OPENROUTER:
|
||||
return OPENROUTER_SERVER;
|
||||
default:
|
||||
return settings.server_urls[settings.type] ?? '';
|
||||
return settings.server_urls[selectedType] ?? '';
|
||||
}
|
||||
}
|
||||
|
||||
@ -1215,8 +1221,14 @@ function isDynamicTemperatureSupported() {
|
||||
return settings.dynatemp && DYNATEMP_BLOCK?.dataset?.tgType?.includes(settings.type);
|
||||
}
|
||||
|
||||
function getLogprobsNumber() {
|
||||
if (settings.type === VLLM || settings.type === INFERMATICAI) {
|
||||
/**
|
||||
* Gets the number of logprobs to request based on the selected type.
|
||||
* @param {string} type If it's set, ignores active type
|
||||
* @returns {number} Number of logprobs to request
|
||||
*/
|
||||
export function getLogprobsNumber(type = null) {
|
||||
const selectedType = type ?? settings.type;
|
||||
if (selectedType === VLLM || selectedType === INFERMATICAI) {
|
||||
return 5;
|
||||
}
|
||||
|
||||
@ -1228,7 +1240,7 @@ function getLogprobsNumber() {
|
||||
* @param {string} str Input string
|
||||
* @returns {string} Output string
|
||||
*/
|
||||
function replaceMacrosInList(str) {
|
||||
export function replaceMacrosInList(str) {
|
||||
if (!str || typeof str !== 'string') {
|
||||
return str;
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ import {
|
||||
cachingAtDepthForOpenRouterClaude,
|
||||
cachingAtDepthForClaude,
|
||||
getPromptNames,
|
||||
calculateBudgetTokens,
|
||||
} from '../../prompt-converters.js';
|
||||
|
||||
import { readSecret, SECRET_KEYS } from '../secrets.js';
|
||||
@ -125,9 +126,12 @@ async function sendClaudeRequest(request, response) {
|
||||
controller.abort();
|
||||
});
|
||||
const additionalHeaders = {};
|
||||
const betaHeaders = ['output-128k-2025-02-19'];
|
||||
const useTools = request.body.model.startsWith('claude-3') && Array.isArray(request.body.tools) && request.body.tools.length > 0;
|
||||
const useSystemPrompt = (request.body.model.startsWith('claude-2') || request.body.model.startsWith('claude-3')) && request.body.claude_use_sysprompt;
|
||||
const convertedPrompt = convertClaudeMessages(request.body.messages, request.body.assistant_prefill, useSystemPrompt, useTools, getPromptNames(request));
|
||||
const useThinking = request.body.model.startsWith('claude-3-7') && Boolean(request.body.include_reasoning);
|
||||
let voidPrefill = false;
|
||||
// Add custom stop sequences
|
||||
const stopSequences = [];
|
||||
if (Array.isArray(request.body.stop)) {
|
||||
@ -155,16 +159,16 @@ async function sendClaudeRequest(request, response) {
|
||||
delete requestBody.system;
|
||||
}
|
||||
if (useTools) {
|
||||
additionalHeaders['anthropic-beta'] = 'tools-2024-05-16';
|
||||
betaHeaders.push('tools-2024-05-16');
|
||||
requestBody.tool_choice = { type: request.body.tool_choice };
|
||||
requestBody.tools = request.body.tools
|
||||
.filter(tool => tool.type === 'function')
|
||||
.map(tool => tool.function)
|
||||
.map(fn => ({ name: fn.name, description: fn.description, input_schema: fn.parameters }));
|
||||
|
||||
// Claude doesn't do prefills on function calls, and doesn't allow empty messages
|
||||
if (requestBody.tools.length && convertedPrompt.messages.length && convertedPrompt.messages[convertedPrompt.messages.length - 1].role === 'assistant') {
|
||||
convertedPrompt.messages.push({ role: 'user', content: [{ type: 'text', text: '\u200b' }] });
|
||||
if (requestBody.tools.length) {
|
||||
// No prefill when using tools
|
||||
voidPrefill = true;
|
||||
}
|
||||
if (enableSystemPromptCache && requestBody.tools.length) {
|
||||
requestBody.tools[requestBody.tools.length - 1]['cache_control'] = { type: 'ephemeral' };
|
||||
@ -176,7 +180,38 @@ async function sendClaudeRequest(request, response) {
|
||||
}
|
||||
|
||||
if (enableSystemPromptCache || cachingAtDepth !== -1) {
|
||||
additionalHeaders['anthropic-beta'] = 'prompt-caching-2024-07-31';
|
||||
betaHeaders.push('prompt-caching-2024-07-31');
|
||||
}
|
||||
|
||||
if (useThinking) {
|
||||
// No prefill when thinking
|
||||
voidPrefill = true;
|
||||
const reasoningEffort = request.body.reasoning_effort;
|
||||
const budgetTokens = calculateBudgetTokens(requestBody.max_tokens, reasoningEffort, requestBody.stream);
|
||||
const minThinkTokens = 1024;
|
||||
if (requestBody.max_tokens <= minThinkTokens) {
|
||||
const newValue = requestBody.max_tokens + minThinkTokens;
|
||||
console.warn(color.yellow(`Claude thinking requires a minimum of ${minThinkTokens} response tokens.`));
|
||||
console.info(color.blue(`Increasing response length to ${newValue}.`));
|
||||
requestBody.max_tokens = newValue;
|
||||
}
|
||||
requestBody.thinking = {
|
||||
type: 'enabled',
|
||||
budget_tokens: budgetTokens,
|
||||
};
|
||||
|
||||
// NO I CAN'T SILENTLY IGNORE THE TEMPERATURE.
|
||||
delete requestBody.temperature;
|
||||
delete requestBody.top_p;
|
||||
delete requestBody.top_k;
|
||||
}
|
||||
|
||||
if (voidPrefill && convertedPrompt.messages.length && convertedPrompt.messages[convertedPrompt.messages.length - 1].role === 'assistant') {
|
||||
convertedPrompt.messages.push({ role: 'user', content: [{ type: 'text', text: '\u200b' }] });
|
||||
}
|
||||
|
||||
if (betaHeaders.length) {
|
||||
additionalHeaders['anthropic-beta'] = betaHeaders.join(',');
|
||||
}
|
||||
|
||||
console.debug('Claude request:', requestBody);
|
||||
@ -979,6 +1014,7 @@ router.post('/generate', jsonParser, function (request, response) {
|
||||
headers = { ...OPENROUTER_HEADERS };
|
||||
bodyParams = {
|
||||
'transforms': getOpenRouterTransforms(request),
|
||||
'include_reasoning': Boolean(request.body.include_reasoning),
|
||||
};
|
||||
|
||||
if (request.body.min_p !== undefined) {
|
||||
@ -1004,10 +1040,6 @@ router.post('/generate', jsonParser, function (request, response) {
|
||||
bodyParams['route'] = 'fallback';
|
||||
}
|
||||
|
||||
if (request.body.include_reasoning) {
|
||||
bodyParams['include_reasoning'] = true;
|
||||
}
|
||||
|
||||
let cachingAtDepth = getConfigValue('claude.cachingAtDepth', -1, 'number');
|
||||
if (Number.isInteger(cachingAtDepth) && cachingAtDepth >= 0 && request.body.model?.startsWith('anthropic/claude-3')) {
|
||||
cachingAtDepthForOpenRouterClaude(request.body.messages, cachingAtDepth);
|
||||
|
@ -862,3 +862,34 @@ export function cachingAtDepthForOpenRouterClaude(messages, cachingAtDepth) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the budget tokens for a given reasoning effort.
|
||||
* @param {number} maxTokens Maximum tokens
|
||||
* @param {string} reasoningEffort Reasoning effort
|
||||
* @param {boolean} stream If streaming is enabled
|
||||
* @returns {number} Budget tokens
|
||||
*/
|
||||
export function calculateBudgetTokens(maxTokens, reasoningEffort, stream) {
|
||||
let budgetTokens = 0;
|
||||
|
||||
switch (reasoningEffort) {
|
||||
case 'low':
|
||||
budgetTokens = Math.floor(maxTokens * 0.1);
|
||||
break;
|
||||
case 'medium':
|
||||
budgetTokens = Math.floor(maxTokens * 0.25);
|
||||
break;
|
||||
case 'high':
|
||||
budgetTokens = Math.floor(maxTokens * 0.5);
|
||||
break;
|
||||
}
|
||||
|
||||
budgetTokens = Math.max(budgetTokens, 1024);
|
||||
|
||||
if (!stream) {
|
||||
budgetTokens = Math.min(budgetTokens, 21333);
|
||||
}
|
||||
|
||||
return budgetTokens;
|
||||
}
|
||||
|
Reference in New Issue
Block a user