Merge branch 'staging' into immutable-config

This commit is contained in:
Cohee
2025-02-25 22:08:35 +02:00
30 changed files with 839 additions and 470 deletions

View File

@ -4,7 +4,7 @@ FROM node:lts-alpine3.19
ARG APP_HOME=/home/node/app ARG APP_HOME=/home/node/app
# Install system dependencies # Install system dependencies
RUN apk add gcompat tini git RUN apk add --no-cache gcompat tini git
# Create app directory # Create app directory
WORKDIR ${APP_HOME} WORKDIR ${APP_HOME}

View File

@ -2000,7 +2000,7 @@
</span> </span>
</div> </div>
</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"> <label for="openai_show_thoughts" class="checkbox_label widthFreeExpand">
<input id="openai_show_thoughts" type="checkbox" /> <input id="openai_show_thoughts" type="checkbox" />
<span> <span>
@ -2014,10 +2014,11 @@
</span> </span>
</div> </div>
</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.&#10;Currently supported values are low, medium, and high.&#10;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."> <div class="flex-container oneline-dropdown" title="Constrains effort on reasoning for reasoning models.&#10;Currently supported values are low, medium, and high.&#10;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"> <label for="openai_reasoning_effort">
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> </label>
<select id="openai_reasoning_effort"> <select id="openai_reasoning_effort">
<option data-i18n="openai_reasoning_effort_low" value="low">Low</option> <option data-i18n="openai_reasoning_effort_low" value="low">Low</option>
@ -2915,6 +2916,8 @@
<h4 data-i18n="Claude Model">Claude Model</h4> <h4 data-i18n="Claude Model">Claude Model</h4>
<select id="model_claude_select"> <select id="model_claude_select">
<optgroup label="Versions"> <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-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-20241022">claude-3-5-sonnet-20241022</option>
<option value="claude-3-5-sonnet-20240620">claude-3-5-sonnet-20240620</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" /> <input id="prefer_character_jailbreak" type="checkbox" />
<small data-i18n="Prefer Character Card Instructions">Prefer Char. Instructions</small> <small data-i18n="Prefer Character Card Instructions">Prefer Char. Instructions</small>
</label> </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.&#10;This will disable the upload cropping popup for avatars." data-i18n="[title]never_resize_avatars_tooltip">
<input id="never_resize_avatars" type="checkbox" /> <input id="never_resize_avatars" type="checkbox" />
<small data-i18n="Never resize avatars">Never resize avatars</small> <small data-i18n="Never resize avatars">Never resize avatars</small>
</label> </label>

View File

@ -633,7 +633,7 @@
"Prefer Character Card Prompt": "تفضيل التعليمات من بطاقة الشخصية", "Prefer Character Card Prompt": "تفضيل التعليمات من بطاقة الشخصية",
"If checked and the character card contains a jailbreak override (Post History Instruction), use that instead": "إذا تم التحقق وكانت بطاقة الشخصية تحتوي على تجاوز للكسر (تعليمات تاريخ المشاركة)، استخدم ذلك بدلاً من ذلك", "If checked and the character card contains a jailbreak override (Post History Instruction), use that instead": "إذا تم التحقق وكانت بطاقة الشخصية تحتوي على تجاوز للكسر (تعليمات تاريخ المشاركة)، استخدم ذلك بدلاً من ذلك",
"Prefer Character Card Jailbreak": "تفضيل كسر الحصار من بطاقة الشخصية", "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": "لا تغيير حجم الصور الرمزية أبدًا", "Never resize avatars": "لا تغيير حجم الصور الرمزية أبدًا",
"Show actual file names on the disk, in the characters list display only": "عرض الأسماء الفعلية للملفات على القرص، في عرض قائمة الشخصيات فقط", "Show actual file names on the disk, in the characters list display only": "عرض الأسماء الفعلية للملفات على القرص، في عرض قائمة الشخصيات فقط",
"Show avatar filenames": "عرض أسماء ملفات الصور الرمزية", "Show avatar filenames": "عرض أسماء ملفات الصور الرمزية",

View File

@ -633,7 +633,7 @@
"Prefer Character Card Prompt": "Bevorzuge Charakterkarten-Prompt", "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", "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", "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", "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 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", "Show avatar filenames": "Avatar-Dateinamen anzeigen",

View File

@ -633,7 +633,7 @@
"Prefer Character Card Prompt": "Preferir Indicaciones en Tarjeta de Personaje", "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", "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", "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", "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 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", "Show avatar filenames": "Mostrar nombres de archivo de avatares",

View File

@ -581,7 +581,7 @@
"Advanced Character Search": "Recherche de personnage avancée", "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", "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", "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", "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 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", "Show avatar filenames": "Afficher les noms de fichier des avatars",

View File

@ -633,7 +633,7 @@
"Prefer Character Card Prompt": "Kosstu kvenkortu fyrirspurn", "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", "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", "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", "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 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", "Show avatar filenames": "Sýna nöfn merkja",

View File

@ -633,7 +633,7 @@
"Prefer Character Card Prompt": "Preferisci Prompt della Scheda Personaggio", "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", "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", "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", "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 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", "Show avatar filenames": "Mostra nomi file avatar",

View File

@ -633,7 +633,7 @@
"Prefer Character Card Prompt": "キャラクターカードのプロンプトを優先", "Prefer Character Card Prompt": "キャラクターカードのプロンプトを優先",
"If checked and the character card contains a jailbreak override (Post History Instruction), use that instead": "チェックされていてキャラクターカードにジェイルブレイクオーバーライド(投稿履歴指示)が含まれている場合、それを代わりに使用します", "If checked and the character card contains a jailbreak override (Post History Instruction), use that instead": "チェックされていてキャラクターカードにジェイルブレイクオーバーライド(投稿履歴指示)が含まれている場合、それを代わりに使用します",
"Prefer Character Card Jailbreak": "キャラクターカードのJailbreakを優先", "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": "アバターを常にリサイズしない", "Never resize avatars": "アバターを常にリサイズしない",
"Show actual file names on the disk, in the characters list display only": "ディスク上の実際のファイル名を表示します。キャラクターリストの表示にのみ", "Show actual file names on the disk, in the characters list display only": "ディスク上の実際のファイル名を表示します。キャラクターリストの表示にのみ",
"Show avatar filenames": "アバターのファイル名を表示", "Show avatar filenames": "アバターのファイル名を表示",

View File

@ -646,7 +646,7 @@
"Prefer Character Card Prompt": "캐릭터 카드 프롬프트 선호", "Prefer Character Card Prompt": "캐릭터 카드 프롬프트 선호",
"If checked and the character card contains a jailbreak override (Post History Instruction), use that instead": "선택되어 있고 캐릭터 카드에 (Post-History 지시)탈옥 재정의가 포함 된 경우, 그것을 대신 사용합니다.", "If checked and the character card contains a jailbreak override (Post History Instruction), use that instead": "선택되어 있고 캐릭터 카드에 (Post-History 지시)탈옥 재정의가 포함 된 경우, 그것을 대신 사용합니다.",
"Prefer Character Card Jailbreak": "캐릭터 카드 탈옥 선호", "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": "아바타 크기 변경하지 않음", "Never resize avatars": "아바타 크기 변경하지 않음",
"Show actual file names on the disk, in the characters list display only": "실제 파일 이름을 디스크에 표시하며 캐릭터 목록 디스플레이에만", "Show actual file names on the disk, in the characters list display only": "실제 파일 이름을 디스크에 표시하며 캐릭터 목록 디스플레이에만",
"Show avatar filenames": "아바타 파일 이름 표시", "Show avatar filenames": "아바타 파일 이름 표시",

View File

@ -633,7 +633,7 @@
"Prefer Character Card Prompt": "Voorkeur karakterkaart prompt", "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", "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", "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", "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 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", "Show avatar filenames": "Toon avatar bestandsnamen",

View File

@ -633,7 +633,7 @@
"Prefer Character Card Prompt": "Preferir Prompt do Cartão de Personagem", "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", "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", "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", "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 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", "Show avatar filenames": "Mostrar nomes de arquivo de avatar",

View File

@ -995,7 +995,7 @@
"Set your custom avatar.": "Установить аватарку", "Set your custom avatar.": "Установить аватарку",
"Remove your custom avatar.": "Сбросить аватарку", "Remove your custom avatar.": "Сбросить аватарку",
"Make a Snapshot": "Сделать снимок", "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": "Доп. заголовок в списке персонажей", "Char List Subheader": "Доп. заголовок в списке персонажей",
"# Messages to Load": "Сколько сообщений загружать", "# Messages to Load": "Сколько сообщений загружать",
"(0 = All)": "(0 = все)", "(0 = All)": "(0 = все)",

View File

@ -633,7 +633,7 @@
"Prefer Character Card Prompt": "Перевага запиту персонажа", "Prefer Character Card Prompt": "Перевага запиту персонажа",
"If checked and the character card contains a jailbreak override (Post History Instruction), use that instead": "Якщо відмічено і картка персонажа містить заміну джейлбрейку (Інструкцію), використовуйте її замість цього", "If checked and the character card contains a jailbreak override (Post History Instruction), use that instead": "Якщо відмічено і картка персонажа містить заміну джейлбрейку (Інструкцію), використовуйте її замість цього",
"Prefer Character Card Jailbreak": "Перевага джейлбрейку персонажа", "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": "Ніколи не змінювати розмір аватарів", "Never resize avatars": "Ніколи не змінювати розмір аватарів",
"Show actual file names on the disk, in the characters list display only": "Показувати фактичні назви файлів на диску, тільки у відображенні списку персонажів", "Show actual file names on the disk, in the characters list display only": "Показувати фактичні назви файлів на диску, тільки у відображенні списку персонажів",
"Show avatar filenames": "Показувати імена файлів аватарів", "Show avatar filenames": "Показувати імена файлів аватарів",

View File

@ -633,7 +633,7 @@
"Prefer Character Card Prompt": "Ưu tiên Gợi ý từ Card", "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 đó", "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", "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", "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 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", "Show avatar filenames": "Hiển thị tên tệp hình đại diện",

View File

@ -722,7 +722,7 @@
"Prefer Character Card Prompt": "角色卡提示词优先", "Prefer Character Card Prompt": "角色卡提示词优先",
"If checked and the character card contains a Post-History Instructions override, use that instead": "开启后,如果角色卡包含后历史指令覆盖,则使用它。", "If checked and the character card contains a Post-History Instructions override, use that instead": "开启后,如果角色卡包含后历史指令覆盖,则使用它。",
"Prefer Character Card Instructions": "首选角色卡说明", "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": "永不调整头像大小", "Never resize avatars": "永不调整头像大小",
"Show actual file names on the disk, in the characters list display only": "在角色列表显示中,显示磁盘上实际的文件名。", "Show actual file names on the disk, in the characters list display only": "在角色列表显示中,显示磁盘上实际的文件名。",
"Show avatar filenames": "显示头像文件名", "Show avatar filenames": "显示头像文件名",

File diff suppressed because it is too large Load Diff

View File

@ -5708,14 +5708,15 @@ function parseAndSaveLogprobs(data, continueFrom) {
/** /**
* Extracts the message from the response data. * Extracts the message from the response data.
* @param {object} data Response data * @param {object} data Response data
* @param {string} activeApi If it's set, ignores active API
* @returns {string} Extracted message * @returns {string} Extracted message
*/ */
function extractMessageFromData(data) { export function extractMessageFromData(data, activeApi = null) {
if (typeof data === 'string') { if (typeof data === 'string') {
return data; return data;
} }
switch (main_api) { switch (activeApi ?? main_api) {
case 'kobold': case 'kobold':
return data.results[0].text; return data.results[0].text;
case 'koboldhorde': case 'koboldhorde':
@ -5725,7 +5726,7 @@ function extractMessageFromData(data) {
case 'novel': case 'novel':
return data.output; return data.output;
case 'openai': 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: default:
return ''; return '';
} }
@ -6085,17 +6086,52 @@ export async function saveReply(type, getMessage, fromStreaming, title, swipes,
return { type, getMessage }; 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) { 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 targetMessageId = messageId ?? chat.length - 1;
const swipeInfo = currentMessage.swipe_info[currentMessage.swipe_id]; if (chat.length > targetMessageId || targetMessageId < 0) {
if (swipeInfo && typeof swipeInfo === 'object') { console.warn(`[syncMesToSwipe] Invalid message ID: ${messageId}`);
swipeInfo.extra = structuredClone(currentMessage.extra); 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) { function saveImageToMessage(img, mes) {
@ -6400,6 +6436,7 @@ export function saveChatDebounced() {
if (chatSaveTimeout) { if (chatSaveTimeout) {
console.debug('Clearing chat save timeout'); console.debug('Clearing chat save timeout');
clearTimeout(chatSaveTimeout); clearTimeout(chatSaveTimeout);
chatSaveTimeout = null;
} }
chatSaveTimeout = setTimeout(async () => { chatSaveTimeout = setTimeout(async () => {
@ -6416,7 +6453,7 @@ export function saveChatDebounced() {
console.debug('Chat save timeout triggered'); console.debug('Chat save timeout triggered');
await saveChatConditional(); await saveChatConditional();
console.debug('Chat saved'); console.debug('Chat saved');
}, 1000); }, DEFAULT_SAVE_EDIT_TIMEOUT);
} }
export async function saveChat(chatName, withMetadata, mesId) { export async function saveChat(chatName, withMetadata, mesId) {
@ -8031,6 +8068,12 @@ export async function saveChatConditional() {
} }
try { try {
if (chatSaveTimeout) {
console.debug('Debounced chat save canceled');
clearTimeout(chatSaveTimeout);
chatSaveTimeout = null;
}
isChatSaving = true; isChatSaving = true;
if (selected_group) { 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 // Make sure ad-hoc changes to extras are saved before swiping away
syncCurrentSwipeInfoExtras(); syncMesToSwipe();
const swipe_duration = 120; const swipe_duration = 120;
const swipe_range = '700px'; const swipe_range = '700px';
@ -8705,7 +8748,7 @@ const swipe_right = () => {
} }
// Make sure ad-hoc changes to extras are saved before swiping away // Make sure ad-hoc changes to extras are saved before swiping away
syncCurrentSwipeInfoExtras(); syncMesToSwipe();
const swipe_duration = 200; const swipe_duration = 200;
const swipe_range = 700; 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 // Default APIs not contined inside text gen / chat gen
'kobold': { 'kobold': {
selected: 'kobold', selected: 'kobold',

View File

@ -45,6 +45,10 @@
<option data-type="openai" value="gpt-4o">gpt-4o</option> <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="gpt-4o-mini">gpt-4o-mini</option>
<option data-type="openai" value="chatgpt-4o-latest">chatgpt-4o-latest</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-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-20241022">claude-3-5-sonnet-20241022</option>
<option data-type="anthropic" value="claude-3-5-sonnet-20240620">claude-3-5-sonnet-20240620</option> <option data-type="anthropic" value="claude-3-5-sonnet-20240620">claude-3-5-sonnet-20240620</option>

View File

@ -32,9 +32,6 @@ export { MODULE_NAME };
const MODULE_NAME = '1_memory'; const MODULE_NAME = '1_memory';
let lastCharacterId = null;
let lastGroupId = null;
let lastChatId = null;
let lastMessageHash = null; let lastMessageHash = null;
let lastMessageId = null; let lastMessageId = null;
let inApiCall = false; let inApiCall = false;
@ -251,7 +248,7 @@ function onSummarySourceChange(event) {
} }
function switchSourceControls(value) { 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()); const source = element.dataset.summarySource.split(',').map(s => s.trim());
$(element).toggle(source.includes(value)); $(element).toggle(source.includes(value));
}); });
@ -349,15 +346,6 @@ function onMaxMessagesPerRequestInput() {
saveSettingsDebounced(); 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) { function getLatestMemoryFromChat(chat) {
if (!Array.isArray(chat) || !chat.length) { if (!Array.isArray(chat) || !chat.length) {
return ''; return '';
@ -390,6 +378,12 @@ function getIndexOfLatestChatSummary(chat) {
return -1; return -1;
} }
function onChatChanged() {
const context = getContext();
const latestMemory = getLatestMemoryFromChat(context.chat);
setMemoryContext(latestMemory, false);
}
async function onChatEvent() { async function onChatEvent() {
// Module not enabled // Module not enabled
if (extension_settings.memory.source === summary_sources.extras && !modules.includes('summarize')) { if (extension_settings.memory.source === summary_sources.extras && !modules.includes('summarize')) {
@ -401,32 +395,19 @@ async function onChatEvent() {
return; return;
} }
const context = getContext();
const chat = context.chat;
// no characters or group selected
if (!context.groupId && context.characterId === undefined) {
return;
}
// Streaming in-progress // Streaming in-progress
if (streamingProcessor && !streamingProcessor.isFinished) { if (streamingProcessor && !streamingProcessor.isFinished) {
return; 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 // Currently summarizing or frozen state - skip
if (inApiCall || extension_settings.memory.memoryFrozen) { if (inApiCall || extension_settings.memory.memoryFrozen) {
return; return;
} }
const context = getContext();
const chat = context.chat;
// No new messages - do nothing // No new messages - do nothing
if (chat.length === 0 || (lastMessageId === chat.length && getStringHash(chat[chat.length - 1].mes) === lastMessageHash)) { if (chat.length === 0 || (lastMessageId === chat.length && getStringHash(chat[chat.length - 1].mes) === lastMessageHash)) {
return; return;
@ -449,7 +430,10 @@ async function onChatEvent() {
summarizeChat(context) summarizeChat(context)
.catch(console.error) .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 context = getContext();
const skipWIAN = extension_settings.memory.SkipWIAN; 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 toast = quiet ? jQuery() : toastr.info('Summarizing chat...', 'Please wait', { timeOut: 0, extendedTimeOut: 0 });
const value = extension_settings.memory.source === summary_sources.main const value = extension_settings.memory.source === summary_sources.main
@ -993,7 +971,7 @@ function doPopout(e) {
.removeClass('zoomed_avatar') .removeClass('zoomed_avatar')
.addClass('draggable') .addClass('draggable')
.empty(); .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.empty();
originalElement.html('<div class="flex-container alignitemscenter justifyCenter wide100p"><small>Currently popped out</small></div>'); originalElement.html('<div class="flex-container alignitemscenter justifyCenter wide100p"><small>Currently popped out</small></div>');
newElement.append(controlBarHtml).append(originalHTMLClone); newElement.append(controlBarHtml).append(originalHTMLClone);
@ -1013,7 +991,7 @@ function doPopout(e) {
const summaryPopoutHTML = $('#summaryExtensionDrawerContents'); const summaryPopoutHTML = $('#summaryExtensionDrawerContents');
$('#summaryExtensionPopout').fadeOut(animation_duration, () => { $('#summaryExtensionPopout').fadeOut(animation_duration, () => {
originalElement.empty(); originalElement.empty();
originalElement.html(summaryPopoutHTML); originalElement.append(summaryPopoutHTML);
$('#summaryExtensionPopout').remove(); $('#summaryExtensionPopout').remove();
}); });
loadSettings(); loadSettings();
@ -1027,31 +1005,30 @@ function doPopout(e) {
function setupListeners() { function setupListeners() {
//setup shared listeners for popout and regular ext menu //setup shared listeners for popout and regular ext menu
$('#memory_restore').off('click').on('click', onMemoryRestoreClick); $('#memory_restore').off('click').on('click', onMemoryRestoreClick);
$('#memory_contents').off('click').on('input', onMemoryContentInput); $('#memory_contents').off('input').on('input', onMemoryContentInput);
$('#memory_frozen').off('click').on('input', onMemoryFrozenInput); $('#memory_frozen').off('input').on('input', onMemoryFrozenInput);
$('#memory_skipWIAN').off('click').on('input', onMemorySkipWIANInput); $('#memory_skipWIAN').off('input').on('input', onMemorySkipWIANInput);
$('#summary_source').off('click').on('change', onSummarySourceChange); $('#summary_source').off('change').on('change', onSummarySourceChange);
$('#memory_prompt_words').off('click').on('input', onMemoryPromptWordsInput); $('#memory_prompt_words').off('input').on('input', onMemoryPromptWordsInput);
$('#memory_prompt_interval').off('click').on('input', onMemoryPromptIntervalInput); $('#memory_prompt_interval').off('input').on('input', onMemoryPromptIntervalInput);
$('#memory_prompt').off('click').on('input', onMemoryPromptInput); $('#memory_prompt').off('input').on('input', onMemoryPromptInput);
$('#memory_force_summarize').off('click').on('click', () => forceSummarizeChat(false)); $('#memory_force_summarize').off('click').on('click', () => forceSummarizeChat(false));
$('#memory_template').off('click').on('input', onMemoryTemplateInput); $('#memory_template').off('input').on('input', onMemoryTemplateInput);
$('#memory_depth').off('click').on('input', onMemoryDepthInput); $('#memory_depth').off('input').on('input', onMemoryDepthInput);
$('#memory_role').off('click').on('input', onMemoryRoleInput); $('#memory_role').off('input').on('input', onMemoryRoleInput);
$('input[name="memory_position"]').off('click').on('change', onMemoryPositionChange); $('input[name="memory_position"]').off('change').on('change', onMemoryPositionChange);
$('#memory_prompt_words_force').off('click').on('input', onMemoryPromptWordsForceInput); $('#memory_prompt_words_force').off('input').on('input', onMemoryPromptWordsForceInput);
$('#memory_prompt_builder_default').off('click').on('input', onMemoryPromptBuilderInput); $('#memory_prompt_builder_default').off('input').on('input', onMemoryPromptBuilderInput);
$('#memory_prompt_builder_raw_blocking').off('click').on('input', onMemoryPromptBuilderInput); $('#memory_prompt_builder_raw_blocking').off('input').on('input', onMemoryPromptBuilderInput);
$('#memory_prompt_builder_raw_non_blocking').off('click').on('input', onMemoryPromptBuilderInput); $('#memory_prompt_builder_raw_non_blocking').off('input').on('input', onMemoryPromptBuilderInput);
$('#memory_prompt_restore').off('click').on('click', onMemoryPromptRestoreClick); $('#memory_prompt_restore').off('click').on('click', onMemoryPromptRestoreClick);
$('#memory_prompt_interval_auto').off('click').on('click', onPromptIntervalAutoClick); $('#memory_prompt_interval_auto').off('click').on('click', onPromptIntervalAutoClick);
$('#memory_prompt_words_auto').off('click').on('click', onPromptForceWordsAutoClick); $('#memory_prompt_words_auto').off('click').on('click', onPromptForceWordsAutoClick);
$('#memory_override_response_length').off('click').on('input', onOverrideResponseLengthInput); $('#memory_override_response_length').off('input').on('input', onOverrideResponseLengthInput);
$('#memory_max_messages_per_request').off('click').on('input', onMaxMessagesPerRequestInput); $('#memory_max_messages_per_request').off('input').on('input', onMaxMessagesPerRequestInput);
$('#memory_include_wi_scan').off('input').on('input', onMemoryIncludeWIScanInput); $('#memory_include_wi_scan').off('input').on('input', onMemoryIncludeWIScanInput);
$('#summarySettingsBlockToggle').off('click').on('click', function () { $('#summarySettingsBlockToggle').off('click').on('click', function () {
console.log('saw settings button click'); $('#summarySettingsBlock').slideToggle(200, 'swing');
$('#summarySettingsBlock').slideToggle(200, 'swing'); //toggleClass("hidden");
}); });
} }
@ -1068,11 +1045,11 @@ jQuery(async function () {
await addExtensionControls(); await addExtensionControls();
loadSettings(); loadSettings();
eventSource.on(event_types.CHAT_CHANGED, onChatChanged);
eventSource.makeLast(event_types.CHARACTER_MESSAGE_RENDERED, onChatEvent); eventSource.makeLast(event_types.CHARACTER_MESSAGE_RENDERED, onChatEvent);
eventSource.on(event_types.MESSAGE_DELETED, onChatEvent); for (const event of [event_types.MESSAGE_DELETED, event_types.MESSAGE_UPDATED, event_types.MESSAGE_SWIPED]) {
eventSource.on(event_types.MESSAGE_EDITED, onChatEvent); eventSource.on(event, onChatEvent);
eventSource.on(event_types.MESSAGE_SWIPED, onChatEvent); }
eventSource.on(event_types.CHAT_CHANGED, onChatEvent);
SlashCommandParser.addCommandObject(SlashCommand.fromProps({ SlashCommandParser.addCommandObject(SlashCommand.fromProps({
name: 'summarize', name: 'summarize',
callback: summarizeCallback, callback: summarizeCallback,

View File

@ -10,16 +10,18 @@
<div class="inline-drawer-content"> <div class="inline-drawer-content">
<div id="summaryExtensionDrawerContents"> <div id="summaryExtensionDrawerContents">
<label for="summary_source" data-i18n="ext_sum_with">Summarize with:</label> <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="main" data-i18n="ext_sum_main_api">Main API</option>
<option value="extras">Extras API (deprecated)</option> <option value="extras">Extras API (deprecated)</option>
<option value="webllm" data-i18n="ext_sum_webllm">WebLLM Extension</option> <option value="webllm" data-i18n="ext_sum_webllm">WebLLM Extension</option>
</select><br> </select><br>
<div class="flex-container justifyspacebetween alignitemscenter"> <div class="flex-container justifyspacebetween alignitemscenter">
<span class="flex1" data-i18n="ext_sum_current_summary">Current summary:</span> <span 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."> <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 data-i18n="ext_sum_restore_previous">Restore Previous</span> <span class="flex1">&nbsp;</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>
</div> </div>

View File

@ -3,9 +3,13 @@
flex-direction: column; flex-direction: column;
} }
#memory_settings textarea { #memory_contents {
font-size: calc(var(--mainFontSize) * 0.9); field-sizing: content;
line-height: 1.2; max-height: 50dvh;
}
#memory_restore {
width: max-content;
} }
label[for="memory_frozen"], label[for="memory_frozen"],
@ -35,3 +39,9 @@ label[for="memory_frozen"] input {
flex-direction: column; flex-direction: column;
row-gap: 5px; row-gap: 5px;
} }
#summaryExtensionPopout {
display: flex;
flex-direction: column;
padding-top: 25px;
}

View File

@ -393,8 +393,8 @@ export let proxies = [
]; ];
export let selected_proxy = proxies[0]; export let selected_proxy = proxies[0];
let openai_setting_names; export let openai_setting_names;
let openai_settings; export let openai_settings;
/** @type {import('./PromptManager.js').PromptManager} */ /** @type {import('./PromptManager.js').PromptManager} */
export let promptManager = null; 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: case chat_completion_sources.CLAUDE:
return oai_settings.claude_model; return oai_settings.claude_model;
case chat_completion_sources.OPENAI: case chat_completion_sources.OPENAI:
@ -1532,7 +1538,7 @@ export function getChatCompletionModel() {
case chat_completion_sources.DEEPSEEK: case chat_completion_sources.DEEPSEEK:
return oai_settings.deepseek_model; return oai_settings.deepseek_model;
default: 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) { function getStreamingReply(data, state) {
if (oai_settings.chat_completion_source === chat_completion_sources.CLAUDE) { if (oai_settings.chat_completion_source === chat_completion_sources.CLAUDE) {
if (oai_settings.show_thoughts) {
state.reasoning += data?.delta?.thinking || '';
}
return data?.delta?.text || ''; return data?.delta?.text || '';
} else if (oai_settings.chat_completion_source === chat_completion_sources.MAKERSUITE) { } else if (oai_settings.chat_completion_source === chat_completion_sources.MAKERSUITE) {
if (oai_settings.show_thoughts) { if (oai_settings.show_thoughts) {
@ -3990,6 +3999,8 @@ function onSettingsPresetChange() {
n: ['#n_openai', 'n', false], n: ['#n_openai', 'n', false],
}; };
const presetNameBefore = oai_settings.preset_settings_openai;
const presetName = $('#settings_preset_openai').find(':selected').text(); const presetName = $('#settings_preset_openai').find(':selected').text();
oai_settings.preset_settings_openai = presetName; oai_settings.preset_settings_openai = presetName;
@ -4015,6 +4026,7 @@ function onSettingsPresetChange() {
settingsToUpdate: settingsToUpdate, settingsToUpdate: settingsToUpdate,
settings: oai_settings, settings: oai_settings,
savePreset: saveOpenAIPreset, savePreset: saveOpenAIPreset,
presetNameBefore: presetNameBefore,
}).finally(r => { }).finally(r => {
$('.model_custom_select').empty(); $('.model_custom_select').empty();
@ -4981,6 +4993,7 @@ export function isImageInliningSupported() {
'gemini-1.5-pro-exp-0827', 'gemini-1.5-pro-exp-0827',
'claude-3', 'claude-3',
'claude-3-5', 'claude-3-5',
'claude-3-7',
'gpt-4-turbo', 'gpt-4-turbo',
'gpt-4o', 'gpt-4o',
'gpt-4o-mini', 'gpt-4o-mini',

View File

@ -21,6 +21,7 @@ import { groups, selected_group } from './group-chats.js';
import { instruct_presets } from './instruct-mode.js'; import { instruct_presets } from './instruct-mode.js';
import { kai_settings } from './kai-settings.js'; import { kai_settings } from './kai-settings.js';
import { convertNovelPreset } from './nai-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 { Popup, POPUP_RESULT, POPUP_TYPE } from './popup.js';
import { context_presets, getContextSettings, power_user } from './power-user.js'; import { context_presets, getContextSettings, power_user } from './power-user.js';
import { SlashCommand } from './slash-commands/SlashCommand.js'; import { SlashCommand } from './slash-commands/SlashCommand.js';
@ -456,6 +457,10 @@ class PresetManager {
presets = textgenerationwebui_presets; presets = textgenerationwebui_presets;
preset_names = textgenerationwebui_preset_names; preset_names = textgenerationwebui_preset_names;
break; break;
case 'openai':
presets = openai_settings;
preset_names = openai_setting_names;
break;
case 'context': case 'context':
presets = context_presets; presets = context_presets;
preset_names = context_presets.map(x => x.name); preset_names = context_presets.map(x => x.name);
@ -606,6 +611,30 @@ class PresetManager {
return settings; 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 // pass no arguments to delete current preset
async deletePreset(name) { async deletePreset(name) {
const { preset_names, presets } = this.getPresetList(); const { preset_names, presets } = this.getPresetList();

View File

@ -1,7 +1,7 @@
import { import {
moment, moment,
} from '../lib.js'; } 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 { getRegexedString, regex_placement } from './extensions/regex/engine.js';
import { getCurrentLocale, t, translate } from './i18n.js'; import { getCurrentLocale, t, translate } from './i18n.js';
import { MacrosParser } from './macros.js'; import { MacrosParser } from './macros.js';
@ -76,6 +76,8 @@ export function extractReasoningFromData(data) {
return data?.choices?.[0]?.message?.reasoning ?? ''; return data?.choices?.[0]?.message?.reasoning ?? '';
case chat_completion_sources.MAKERSUITE: case chat_completion_sources.MAKERSUITE:
return data?.responseContent?.parts?.filter(part => part.thought)?.map(part => part.text)?.join('\n\n') ?? ''; 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: { case chat_completion_sources.CUSTOM: {
return data?.choices?.[0]?.message?.reasoning_content return data?.choices?.[0]?.message?.reasoning_content
?? data?.choices?.[0]?.message?.reasoning ?? data?.choices?.[0]?.message?.reasoning
@ -755,6 +757,17 @@ function registerReasoningMacros() {
} }
function setReasoningEventHandlers() { 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) { $(document).on('click', '.mes_reasoning_details', function (e) {
if (!e.target.closest('.mes_reasoning_actions') && !e.target.closest('.mes_reasoning_header')) { if (!e.target.closest('.mes_reasoning_actions') && !e.target.closest('.mes_reasoning_header')) {
e.preventDefault(); e.preventDefault();
@ -835,9 +848,7 @@ function setReasoningEventHandlers() {
} }
const textarea = messageBlock.find('.reasoning_edit_textarea'); const textarea = messageBlock.find('.reasoning_edit_textarea');
const reasoning = getRegexedString(String(textarea.val()), regex_placement.REASONING, { isEdit: true }); updateReasoningFromValue(message, String(textarea.val()));
message.extra.reasoning = reasoning;
message.extra.reasoning_type = message.extra.reasoning_type ? ReasoningType.Edited : ReasoningType.Manual;
await saveChatConditional(); await saveChatConditional();
updateMessageBlock(messageId, message); updateMessageBlock(messageId, message);
textarea.remove(); textarea.remove();
@ -917,6 +928,20 @@ function setReasoningEventHandlers() {
await copyText(reasoning); await copyText(reasoning);
toastr.info(t`Copied!`, '', { timeOut: 2000 }); 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() { function registerReasoningAppEvents() {
eventSource.makeFirst(event_types.MESSAGE_RECEIVED, (/** @type {number} */ idx) => { const eventHandler = (/** @type {number} */ idx) => {
if (!power_user.reasoning.auto_parse) { if (!power_user.reasoning.auto_parse) {
return; return;
} }
@ -1021,15 +1046,22 @@ function registerReasoningAppEvents() {
message.mes = parsedReasoning.content; message.mes = parsedReasoning.content;
} }
// Find if a message already exists in DOM and must be updated
if (contentUpdated) { if (contentUpdated) {
syncMesToSwipe();
saveChatDebounced();
// Find if a message already exists in DOM and must be updated
const messageRendered = document.querySelector(`.mes[mesid="${idx}"]`) !== null; const messageRendered = document.querySelector(`.mes[mesid="${idx}"]`) !== null;
if (messageRendered) { if (messageRendered) {
console.debug('[Reasoning] Updating message block', idx); console.debug('[Reasoning] Updating message block', idx);
updateMessageBlock(idx, message); updateMessageBlock(idx, message);
} }
} }
}); };
for (const event of [event_types.MESSAGE_RECEIVED, event_types.MESSAGE_UPDATED]) {
eventSource.on(event, eventHandler);
}
} }
export function initReasoning() { export function initReasoning() {

View File

@ -42,7 +42,7 @@ import {
showMoreMessages, showMoreMessages,
stopGeneration, stopGeneration,
substituteParams, substituteParams,
syncCurrentSwipeInfoExtras, syncMesToSwipe,
system_avatar, system_avatar,
system_message_types, system_message_types,
this_chid, this_chid,
@ -2921,7 +2921,7 @@ async function addSwipeCallback(args, value) {
if (isTrueBoolean(args.switch)) { if (isTrueBoolean(args.switch)) {
// Make sure ad-hoc changes to extras are saved before swiping away // Make sure ad-hoc changes to extras are saved before swiping away
syncCurrentSwipeInfoExtras(); syncMesToSwipe();
lastMessage.swipe_id = newSwipeId; lastMessage.swipe_id = newSwipeId;
lastMessage.mes = lastMessage.swipes[newSwipeId]; lastMessage.mes = lastMessage.swipes[newSwipeId];
lastMessage.extra = structuredClone(lastMessage.swipe_info?.[newSwipeId]?.extra ?? lastMessage.extra ?? {}); lastMessage.extra = structuredClone(lastMessage.swipe_info?.[newSwipeId]?.extra ?? lastMessage.extra ?? {});

View File

@ -6,11 +6,13 @@ import {
characters, characters,
chat, chat,
chat_metadata, chat_metadata,
CONNECT_API_MAP,
create_save, create_save,
deactivateSendButtons, deactivateSendButtons,
event_types, event_types,
eventSource, eventSource,
extension_prompts, extension_prompts,
extractMessageFromData,
Generate, Generate,
generateQuietPrompt, generateQuietPrompt,
getCharacters, getCharacters,
@ -55,9 +57,10 @@ import { groups, openGroupChat, selected_group } from './group-chats.js';
import { t, translate } from './i18n.js'; import { t, translate } from './i18n.js';
import { hideLoader, showLoader } from './loader.js'; import { hideLoader, showLoader } from './loader.js';
import { MacrosParser } from './macros.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 { callGenericPopup, Popup, POPUP_RESULT, POPUP_TYPE } from './popup.js';
import { power_user, registerDebugFunction } from './power-user.js'; import { power_user, registerDebugFunction } from './power-user.js';
import { getPresetManager } from './preset-manager.js';
import { humanizedDateTime, isMobile, shouldSendOnEnter } from './RossAscends-mods.js'; import { humanizedDateTime, isMobile, shouldSendOnEnter } from './RossAscends-mods.js';
import { ScraperManager } from './scrapers.js'; import { ScraperManager } from './scrapers.js';
import { executeSlashCommands, executeSlashCommandsWithOptions, registerSlashCommand } from './slash-commands.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 { ARGUMENT_TYPE, SlashCommandArgument, SlashCommandNamedArgument } from './slash-commands/SlashCommandArgument.js';
import { SlashCommandParser } from './slash-commands/SlashCommandParser.js'; import { SlashCommandParser } from './slash-commands/SlashCommandParser.js';
import { tag_map, tags } from './tags.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 { tokenizers, getTextTokens, getTokenCount, getTokenCountAsync, getTokenizerModel } from './tokenizers.js';
import { ToolManager } from './tool-calling.js'; import { ToolManager } from './tool-calling.js';
import { accountStorage } from './util/AccountStorage.js'; import { accountStorage } from './util/AccountStorage.js';
@ -193,6 +196,11 @@ export function getContext() {
saveWorldInfo, saveWorldInfo,
updateWorldInfoList, updateWorldInfoList,
convertCharacterBook, convertCharacterBook,
CONNECT_API_MAP,
getTextGenServer,
extractMessageFromData,
getPresetManager,
getChatCompletionModel,
}; };
} }

View File

@ -318,8 +318,14 @@ export function validateTextGenUrl() {
control.val(formattedUrl); 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: case FEATHERLESS:
return FEATHERLESS_SERVER; return FEATHERLESS_SERVER;
case MANCER: case MANCER:
@ -333,7 +339,7 @@ export function getTextGenServer() {
case OPENROUTER: case OPENROUTER:
return OPENROUTER_SERVER; return OPENROUTER_SERVER;
default: 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); 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; return 5;
} }
@ -1228,7 +1240,7 @@ function getLogprobsNumber() {
* @param {string} str Input string * @param {string} str Input string
* @returns {string} Output string * @returns {string} Output string
*/ */
function replaceMacrosInList(str) { export function replaceMacrosInList(str) {
if (!str || typeof str !== 'string') { if (!str || typeof str !== 'string') {
return str; return str;
} }

View File

@ -28,6 +28,7 @@ import {
cachingAtDepthForOpenRouterClaude, cachingAtDepthForOpenRouterClaude,
cachingAtDepthForClaude, cachingAtDepthForClaude,
getPromptNames, getPromptNames,
calculateBudgetTokens,
} from '../../prompt-converters.js'; } from '../../prompt-converters.js';
import { readSecret, SECRET_KEYS } from '../secrets.js'; import { readSecret, SECRET_KEYS } from '../secrets.js';
@ -125,9 +126,12 @@ async function sendClaudeRequest(request, response) {
controller.abort(); controller.abort();
}); });
const additionalHeaders = {}; 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 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 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 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 // Add custom stop sequences
const stopSequences = []; const stopSequences = [];
if (Array.isArray(request.body.stop)) { if (Array.isArray(request.body.stop)) {
@ -155,16 +159,16 @@ async function sendClaudeRequest(request, response) {
delete requestBody.system; delete requestBody.system;
} }
if (useTools) { if (useTools) {
additionalHeaders['anthropic-beta'] = 'tools-2024-05-16'; betaHeaders.push('tools-2024-05-16');
requestBody.tool_choice = { type: request.body.tool_choice }; requestBody.tool_choice = { type: request.body.tool_choice };
requestBody.tools = request.body.tools requestBody.tools = request.body.tools
.filter(tool => tool.type === 'function') .filter(tool => tool.type === 'function')
.map(tool => tool.function) .map(tool => tool.function)
.map(fn => ({ name: fn.name, description: fn.description, input_schema: fn.parameters })); .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) {
if (requestBody.tools.length && convertedPrompt.messages.length && convertedPrompt.messages[convertedPrompt.messages.length - 1].role === 'assistant') { // No prefill when using tools
convertedPrompt.messages.push({ role: 'user', content: [{ type: 'text', text: '\u200b' }] }); voidPrefill = true;
} }
if (enableSystemPromptCache && requestBody.tools.length) { if (enableSystemPromptCache && requestBody.tools.length) {
requestBody.tools[requestBody.tools.length - 1]['cache_control'] = { type: 'ephemeral' }; requestBody.tools[requestBody.tools.length - 1]['cache_control'] = { type: 'ephemeral' };
@ -176,7 +180,38 @@ async function sendClaudeRequest(request, response) {
} }
if (enableSystemPromptCache || cachingAtDepth !== -1) { 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); console.debug('Claude request:', requestBody);
@ -979,6 +1014,7 @@ router.post('/generate', jsonParser, function (request, response) {
headers = { ...OPENROUTER_HEADERS }; headers = { ...OPENROUTER_HEADERS };
bodyParams = { bodyParams = {
'transforms': getOpenRouterTransforms(request), 'transforms': getOpenRouterTransforms(request),
'include_reasoning': Boolean(request.body.include_reasoning),
}; };
if (request.body.min_p !== undefined) { if (request.body.min_p !== undefined) {
@ -1004,10 +1040,6 @@ router.post('/generate', jsonParser, function (request, response) {
bodyParams['route'] = 'fallback'; bodyParams['route'] = 'fallback';
} }
if (request.body.include_reasoning) {
bodyParams['include_reasoning'] = true;
}
let cachingAtDepth = getConfigValue('claude.cachingAtDepth', -1, 'number'); let cachingAtDepth = getConfigValue('claude.cachingAtDepth', -1, 'number');
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); cachingAtDepthForOpenRouterClaude(request.body.messages, cachingAtDepth);

View File

@ -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;
}