diff --git a/public/sw.js b/public/sw.js index ed1172cb..6dc58c71 100644 --- a/public/sw.js +++ b/public/sw.js @@ -87,6 +87,13 @@ self.addEventListener('fetch', (event) => { async function checkCache(url){ const cache = await caches.open('risuCache') + if(url.pathname.startsWith("/sw/check")) { + url.pathname = "/sw/img" + url.pathname.slice(9); + return new Response(JSON.stringify({ + "able": !!(await cache.match(url)) + })) + } + return new Response(JSON.stringify({ "able": !!(await cache.match(url)) })) diff --git a/src/lang/cn.ts b/src/lang/cn.ts index 008ddce3..abcd8dc2 100644 --- a/src/lang/cn.ts +++ b/src/lang/cn.ts @@ -770,5 +770,50 @@ export const languageChinese = { "translatorPrompt": "翻译提示词", "translateBeforeHTMLFormatting": "於 HTML 格式化前翻译", "retranslate": "重新翻译", - "loading": "加载中" - } + "loading": "加载中", + "hypaV3Settings": { + "descriptionLabel": "HypaMemory V3 是一个使用总结和向量搜索的长期记忆系统。", + "supaMemoryPromptPlaceHolder": "留空以使用默认值", + "maxMemoryTokensRatioLabel": "最大记忆令牌比率(估计)", + "maxMemoryTokensRatioError": "无法计算最大记忆令牌比率", + "memoryTokensRatioLabel": "记忆令牌比率", + "extraSummarizationRatioLabel": "额外总结比率", + "maxChatsPerSummaryLabel": "每个总结的最大消息数", + "recentMemoryRatioLabel": "最近记忆比率", + "similarMemoryRatioLabel": "相似记忆比率", + "randomMemoryRatioLabel": "随机记忆比率", + "enableSimilarityCorrectionLabel": "启用相似度校正", + "preserveOrphanedMemoryLabel": "保留孤立记忆", + "applyRegexScriptWhenRerollingLabel": "重新生成时应用正则脚本", + "doNotSummarizeUserMessageLabel": "不要总结用户消息", + }, + "hypaV3Modal": { + "titleLabel": "HypaV3", + "resetConfirmMessage": "此操作无法撤销。您要重置 HypaV3 数据吗?", + "resetConfirmSecondMessage": "此操作不可逆。您确实要重置 HypaV3 数据吗?", + "convertLabel": "尚无总结,但您可以将 HypaV2 数据转换为 V3。", + "convertButton": "转换为 V3", + "convertSuccessMessage": "成功将 HypaV2 数据转换为 V3", + "convertErrorMessage": "将 HypaV2 数据转换为 V3 失败:{0}", + "noSummariesLabel": "尚无总结", + "searchPlaceholder": "输入 #N、ID 或搜索关键词", + "summaryNumberLabel": "总结 #{0}", + "deleteAfterConfirmMessage": "删除此后的所有总结?", + "deleteAfterConfirmSecondMessage": "此操作无法撤销。您确定吗?", + "translationLabel": "翻译", + "rerolledSummaryLabel": "重新生成的总结", + "rerolledTranslationLabel": "重新生成的总结翻译", + "connectedMessageCountLabel": "关联消息 ({0})", + "connectedFirstMessageLabel": "第一条消息", + "connectedMessageRoleLabel": "{0} 的消息", + "connectedMessageNotFoundLabel": "未找到消息", + "connectedMessageLoadingError": "加载关联消息时出错:{0}", + "connectedMessageTranslationLabel": "翻译", + "nextSummarizationFirstMessageLabel": "第一条消息", + "nextSummarizationNoMessageIdLabel": "无消息 ID", + "nextSummarizationLabel": "HypaV3 将总结 [{0}]", + "nextSummarizationNoMessagesFoundLabel": "警告:未找到消息", + "nextSummarizationLoadingError": "加载下一个总结目标时出错:{0}", + "emptySelectedFirstMessageLabel": "警告:所选的第一条消息为空" + }, +} diff --git a/src/lang/de.ts b/src/lang/de.ts index 8d0d8bc6..41144dc6 100644 --- a/src/lang/de.ts +++ b/src/lang/de.ts @@ -435,4 +435,49 @@ export const languageGerman = { appendNameNAI: "Namen an NAI anhängen", customStopWords: "Benutzerdefinierte Stoppwörter", useAdvancedEditor: "Erweiterten Editor verwenden", + "hypaV3Settings": { + "descriptionLabel": "HypaMemory V3 ist ein Langzeitgedächtnissystem, das sowohl Zusammenfassung als auch Vektorsuche verwendet.", + "supaMemoryPromptPlaceHolder": "Leer lassen für Standardeinstellung", + "maxMemoryTokensRatioLabel": "Maximales Gedächtnis-Token-Verhältnis (Geschätzt)", + "maxMemoryTokensRatioError": "Maximales Gedächtnis-Token-Verhältnis kann nicht berechnet werden", + "memoryTokensRatioLabel": "Gedächtnis-Token-Verhältnis", + "extraSummarizationRatioLabel": "Zusätzliches Zusammenfassungsverhältnis", + "maxChatsPerSummaryLabel": "Maximale Nachrichten pro Zusammenfassung", + "recentMemoryRatioLabel": "Verhältnis der jüngsten Erinnerungen", + "similarMemoryRatioLabel": "Verhältnis ähnlicher Erinnerungen", + "randomMemoryRatioLabel": "Verhältnis zufälliger Erinnerungen", + "enableSimilarityCorrectionLabel": "Ähnlichkeitskorrektur aktivieren", + "preserveOrphanedMemoryLabel": "Verwaiste Erinnerungen bewahren", + "applyRegexScriptWhenRerollingLabel": "Regex-Skript beim Neugenerieren anwenden", + "doNotSummarizeUserMessageLabel": "Benutzernachrichten nicht zusammenfassen", + }, + "hypaV3Modal": { + "titleLabel": "HypaV3", + "resetConfirmMessage": "Diese Aktion kann nicht rückgängig gemacht werden. Möchten Sie die HypaV3-Daten zurücksetzen?", + "resetConfirmSecondMessage": "Diese Aktion ist unwiderruflich. Möchten Sie die HypaV3-Daten wirklich zurücksetzen?", + "convertLabel": "Noch keine Zusammenfassungen, aber Sie können HypaV2-Daten zu V3 konvertieren.", + "convertButton": "Zu V3 konvertieren", + "convertSuccessMessage": "HypaV2-Daten erfolgreich zu V3 konvertiert", + "convertErrorMessage": "Konvertierung von HypaV2 zu V3 fehlgeschlagen: {0}", + "noSummariesLabel": "Noch keine Zusammenfassungen", + "searchPlaceholder": "Geben Sie #N, ID oder Suchanfrage ein", + "summaryNumberLabel": "Zusammenfassung #{0}", + "deleteAfterConfirmMessage": "Alle Zusammenfassungen nach dieser löschen?", + "deleteAfterConfirmSecondMessage": "Diese Aktion kann nicht rückgängig gemacht werden. Sind Sie wirklich sicher?", + "translationLabel": "Übersetzung", + "rerolledSummaryLabel": "Neu generierte Zusammenfassung", + "rerolledTranslationLabel": "Übersetzung der neu generierten Zusammenfassung", + "connectedMessageCountLabel": "Verbundene Nachrichten ({0})", + "connectedFirstMessageLabel": "Erste Nachricht", + "connectedMessageRoleLabel": "Nachricht von {0}", + "connectedMessageNotFoundLabel": "Nachricht nicht gefunden", + "connectedMessageLoadingError": "Fehler beim Laden der verbundenen Nachricht: {0}", + "connectedMessageTranslationLabel": "Übersetzung", + "nextSummarizationFirstMessageLabel": "Erste Nachricht", + "nextSummarizationNoMessageIdLabel": "Keine Nachrichten-ID", + "nextSummarizationLabel": "HypaV3 wird [{0}] zusammenfassen", + "nextSummarizationNoMessagesFoundLabel": "WARNUNG: Keine Nachrichten gefunden", + "nextSummarizationLoadingError": "Fehler beim Laden des nächsten Zusammenfassungsziels: {0}", + "emptySelectedFirstMessageLabel": "WARNUNG: Ausgewählte erste Nachricht ist leer" + }, } diff --git a/src/lang/en.ts b/src/lang/en.ts index 1f098789..1d552eca 100644 --- a/src/lang/en.ts +++ b/src/lang/en.ts @@ -170,7 +170,7 @@ export const languageEnglish = { summarizationPrompt: "The prompt that is used for summarization. if it is blank, it will use the default prompt. you can also use ChatML formating with {{slot}} for the chat data.", translatorPrompt: "The prompt that is used for translation. if it is blank, it will use the default prompt. you can also use ChatML formating with {{slot}} for the dest language, {{solt::content}} for the content, and {{slot::tnote}} for the translator note.", translateBeforeHTMLFormatting: "If enabled, it will translate the text before Regex scripts and HTML formatting. this could make the token lesser but could break the formatting.", - autoTranslateCachedOnly: "If enabled with Auto Translation option on, it will automatically translate only the text that the user has translated previously.", + autoTranslateCachedOnly: "If enabled with Auto Translation option on, it will automatically translate only the messages that the user has translated previously.", presetChain: "If it is not blank, the preset will be changed and applied randomly every time when user sends a message in the preset list in this input. preset list should be seperated by comma, for example, `preset1,preset2`.", legacyMediaFindings: "If enabled, it will use the old method to find media assets, without using the additional search algorithm.", comfyWorkflow: "Put the API workflow of comfy UI. you can get your API workflow in comfy UI by pressing the 'Workflow > Export (API)' button. you must also put {{risu_prompt}} in you workflow text. the {{risu_prompt}} will be replaced with the prompt provided by the Risu.", @@ -939,7 +939,7 @@ export const languageEnglish = { translateBeforeHTMLFormatting: "Translate Before HTML Formatting", retranslate: "Retranslate", loading: "Loading", - autoTranslateCachedOnly: "Auto Translate Cached Only", + autoTranslateCachedOnly: "Auto-translate Cached Messages Only", notification: "Notification", permissionDenied: "Permission Denied by Your Browser or OS", customFlags: "Custom Flags", @@ -964,7 +964,6 @@ export const languageEnglish = { banCharacterset: 'Auto Regenerate On Characterset', checkCorruption: "Check Corruption", showPromptComparison: "Show Prompt Comparison", - hypaV3Desc: "HypaMemory V3 is a long-term memory system that use both summarized data and vector search.", inlayErrorResponse: "Inlay Error Response", triggerOn: "Trigger On", noConfig: "No Config", @@ -984,4 +983,50 @@ export const languageEnglish = { max: "Max", delimiter: "Delimiter", deleteCount: "Delete Count", -} \ No newline at end of file + triggerEffRunAxLLM: "Run Auxiliary Model", + hypaV3Settings: { + descriptionLabel: "HypaMemory V3 is a long-term memory system that uses both summarization and vector search.", + supaMemoryPromptPlaceHolder: "Leave it blank to use default", + maxMemoryTokensRatioLabel: "Max Memory Tokens Ratio (Estimated)", + maxMemoryTokensRatioError: "Unable to calculate Max Memory Tokens Ratio", + memoryTokensRatioLabel: "Memory Tokens Ratio", + extraSummarizationRatioLabel: "Extra Summarization Ratio", + maxChatsPerSummaryLabel: "Max Messages Per Summary", + recentMemoryRatioLabel: "Recent Memory Ratio", + similarMemoryRatioLabel: "Similar Memory Ratio", + randomMemoryRatioLabel: "Random Memory Ratio", + enableSimilarityCorrectionLabel: "Enable Similarity Correction", + preserveOrphanedMemoryLabel: "Preserve Orphaned Memory", + applyRegexScriptWhenRerollingLabel: "Apply Regex Script When Rerolling", + doNotSummarizeUserMessageLabel: "Do Not Summarize User Message", + }, + hypaV3Modal: { + titleLabel: "HypaV3", + resetConfirmMessage: "This action cannot be undone. Do you want to reset HypaV3 data?", + resetConfirmSecondMessage: "This action is irreversible. Do you really, really want to reset HypaV3 data?", + convertLabel: "No summaries yet, but you may convert HypaV2 data to V3.", + convertButton: "Convert to V3", + convertSuccessMessage: "Successfully converted HypaV2 data to V3", + convertErrorMessage: "Failed to convert HypaV2 data to V3: {0}", + noSummariesLabel: "No summaries yet", + searchPlaceholder: "Enter #N, ID, or query", + summaryNumberLabel: "Summary #{0}", + deleteAfterConfirmMessage: "Delete all summaries after this one?", + deleteAfterConfirmSecondMessage: "This action cannot be undone. Are you really sure?", + translationLabel: "Translation", + rerolledSummaryLabel: "Rerolled Summary", + rerolledTranslationLabel: "Rerolled Summary Translation", + connectedMessageCountLabel: "Connected Messages ({0})", + connectedFirstMessageLabel: "First Message", + connectedMessageRoleLabel: "{0}'s Message", + connectedMessageNotFoundLabel: "Message not found", + connectedMessageLoadingError: "Error loading connected message: {0}", + connectedMessageTranslationLabel: "Translation", + nextSummarizationFirstMessageLabel: "First Message", + nextSummarizationNoMessageIdLabel: "No Message ID", + nextSummarizationLabel: "HypaV3 will summarize [{0}]", + nextSummarizationNoMessagesFoundLabel: "WARN: No messages found", + nextSummarizationLoadingError: "Error loading next summarization target: {0}", + emptySelectedFirstMessageLabel: "WARN: Selected first message is empty", + }, +} diff --git a/src/lang/es.ts b/src/lang/es.ts index 9d36fbcf..193d18a4 100644 --- a/src/lang/es.ts +++ b/src/lang/es.ts @@ -680,4 +680,49 @@ export const languageSpanish = { parameters: "Parámetros", sizeAndSpeed: "Tamaño y Velocidad", useLegacyGUI: "Usar Interfaz Legacy", + "hypaV3Settings": { + "descriptionLabel": "HypaMemory V3 es un sistema de memoria a largo plazo que utiliza tanto resúmenes como búsqueda vectorial.", + "supaMemoryPromptPlaceHolder": "Dejar en blanco para usar el valor predeterminado", + "maxMemoryTokensRatioLabel": "Ratio Máximo de Tokens de Memoria (Estimado)", + "maxMemoryTokensRatioError": "No se puede calcular el ratio máximo de tokens de memoria", + "memoryTokensRatioLabel": "Ratio de Tokens de Memoria", + "extraSummarizationRatioLabel": "Ratio de Resumen Adicional", + "maxChatsPerSummaryLabel": "Mensajes Máximos por Resumen", + "recentMemoryRatioLabel": "Ratio de Memoria Reciente", + "similarMemoryRatioLabel": "Ratio de Memoria Similar", + "randomMemoryRatioLabel": "Ratio de Memoria Aleatoria", + "enableSimilarityCorrectionLabel": "Activar Corrección de Similitud", + "preserveOrphanedMemoryLabel": "Preservar Memoria Huérfana", + "applyRegexScriptWhenRerollingLabel": "Aplicar Script Regex al Regenerar", + "doNotSummarizeUserMessageLabel": "No Resumir Mensajes del Usuario", + }, + "hypaV3Modal": { + "titleLabel": "HypaV3", + "resetConfirmMessage": "Esta acción no se puede deshacer. ¿Desea restablecer los datos de HypaV3?", + "resetConfirmSecondMessage": "Esta acción es irreversible. ¿Está realmente seguro de querer restablecer los datos de HypaV3?", + "convertLabel": "Aún no hay resúmenes, pero puede convertir datos de HypaV2 a V3.", + "convertButton": "Convertir a V3", + "convertSuccessMessage": "Datos de HypaV2 convertidos exitosamente a V3", + "convertErrorMessage": "Error al convertir datos de HypaV2 a V3: {0}", + "noSummariesLabel": "Aún no hay resúmenes", + "searchPlaceholder": "Ingrese #N, ID o búsqueda", + "summaryNumberLabel": "Resumen #{0}", + "deleteAfterConfirmMessage": "¿Eliminar todos los resúmenes después de este?", + "deleteAfterConfirmSecondMessage": "Esta acción no se puede deshacer. ¿Está realmente seguro?", + "translationLabel": "Traducción", + "rerolledSummaryLabel": "Resumen Regenerado", + "rerolledTranslationLabel": "Traducción del Resumen Regenerado", + "connectedMessageCountLabel": "Mensajes Conectados ({0})", + "connectedFirstMessageLabel": "Primer Mensaje", + "connectedMessageRoleLabel": "Mensaje de {0}", + "connectedMessageNotFoundLabel": "Mensaje no encontrado", + "connectedMessageLoadingError": "Error al cargar mensaje conectado: {0}", + "connectedMessageTranslationLabel": "Traducción", + "nextSummarizationFirstMessageLabel": "Primer Mensaje", + "nextSummarizationNoMessageIdLabel": "Sin ID de Mensaje", + "nextSummarizationLabel": "HypaV3 resumirá [{0}]", + "nextSummarizationNoMessagesFoundLabel": "ADVERTENCIA: No se encontraron mensajes", + "nextSummarizationLoadingError": "Error al cargar el siguiente objetivo de resumen: {0}", + "emptySelectedFirstMessageLabel": "ADVERTENCIA: El primer mensaje seleccionado está vacío" + }, } diff --git a/src/lang/ko.ts b/src/lang/ko.ts index 785f0b2b..2f631c6d 100644 --- a/src/lang/ko.ts +++ b/src/lang/ko.ts @@ -123,7 +123,8 @@ export const languageKorean = { "translatorNote": "여기에서 캐릭터마다 별도의 번역 프롬프트를 넣을 수 있습니다. 해당 옵션은 Ax. model 번역을 사용할 때만 적용됩니다. 언어 설정에서 `{{slot::tnote}}`를 넣으세요. 그룹챗에서는 작동하지 않습니다.", "groupInnerFormat": "This defines a format that is used in group chat for characters that isn't speaker. if it is not blank, it will use this format instead of the default format. if `Group Other Bot Role` is `assistant`, it will also be applied to the speaker.", "groupOtherBotRole": "This defines a role that is used in group chat for characters that isn't speaker.", - "chatHTML": "A HTML that would be inserted as each chat.\n\nYou can use CBS and special tags.\n- ``: a textbox that would be used to render text\n- ``: an icon for user or assistant\n- ``: icon buttons for chat edit, translations and etc.\n- ``: generation information button." + "chatHTML": "A HTML that would be inserted as each chat.\n\nYou can use CBS and special tags.\n- ``: a textbox that would be used to render text\n- ``: an icon for user or assistant\n- ``: icon buttons for chat edit, translations and etc.\n- ``: generation information button.", + "autoTranslateCachedOnly": "자동 번역 옵션이 켜진 상태에서 활성화하면, 사용자가 이전에 번역한 메시지만 자동으로 번역됩니다.", }, "setup": { "chooseProvider": "AI 제공자를 선택해 주세요", @@ -903,4 +904,51 @@ export const languageKorean = { "sysPromptEnd": "시스템 프롬프트 끝", "target": "목표", "addElse": "그 외에 경우 추가", -} \ No newline at end of file + "triggerEffRunAxLLM": "보조 모델 실행", + "autoTranslateCachedOnly": "캐시된 메시지만 자동 번역", + "hypaV3Settings": { + "descriptionLabel": "HypaMemory V3는 요약과 벡터 검색을 모두 사용하는 장기 기억 시스템입니다.", + "supaMemoryPromptPlaceHolder": "기본값을 사용하려면 비워두세요", + "maxMemoryTokensRatioLabel": "최대 메모리 토큰 비율 (추정)", + "maxMemoryTokensRatioError": "최대 메모리 토큰 비율을 계산할 수 없습니다", + "memoryTokensRatioLabel": "메모리 토큰 비율", + "extraSummarizationRatioLabel": "추가 요약 비율", + "maxChatsPerSummaryLabel": "요약당 최대 메시지 수", + "recentMemoryRatioLabel": "최근 메모리 비율", + "similarMemoryRatioLabel": "유사 메모리 비율", + "randomMemoryRatioLabel": "무작위 메모리 비율", + "enableSimilarityCorrectionLabel": "유사도 보정 활성화", + "preserveOrphanedMemoryLabel": "고아 메모리 보존", + "applyRegexScriptWhenRerollingLabel": "재생성 시 정규식 스크립트 적용", + "doNotSummarizeUserMessageLabel": "유저 메시지 요약하지 않기", + }, + "hypaV3Modal": { + "titleLabel": "HypaV3", + "resetConfirmMessage": "이 작업은 되돌릴 수 없습니다. HypaV3 데이터를 초기화하시겠습니까?", + "resetConfirmSecondMessage": "이 작업은 복구할 수 없습니다. 정말로, 정말로 HypaV3 데이터를 초기화하시겠습니까?", + "convertLabel": "아직 요약이 없지만, HypaV2 데이터를 V3로 변환할 수 있습니다.", + "convertButton": "V3로 변환", + "convertSuccessMessage": "HypaV2 데이터를 V3로 성공적으로 변환했습니다", + "convertErrorMessage": "HypaV2 데이터를 V3로 변환하는데 실패했습니다: {0}", + "noSummariesLabel": "아직 요약이 없습니다", + "searchPlaceholder": "#N, ID 또는 검색어 입력", + "summaryNumberLabel": "요약 #{0}", + "deleteAfterConfirmMessage": "이 요약 이후의 모든 요약을 삭제하시겠습니까?", + "deleteAfterConfirmSecondMessage": "이 작업은 되돌릴 수 없습니다. 정말 삭제하시겠습니까?", + "translationLabel": "번역", + "rerolledSummaryLabel": "재생성된 요약", + "rerolledTranslationLabel": "재생성된 요약 번역", + "connectedMessageCountLabel": "연결된 메시지 ({0})", + "connectedFirstMessageLabel": "첫 메시지", + "connectedMessageRoleLabel": "{0}의 메시지", + "connectedMessageNotFoundLabel": "메시지를 찾을 수 없습니다", + "connectedMessageLoadingError": "연결된 메시지를 불러오는 동안 오류 발생: {0}", + "connectedMessageTranslationLabel": "번역", + "nextSummarizationFirstMessageLabel": "첫 메시지", + "nextSummarizationNoMessageIdLabel": "메시지 ID 없음", + "nextSummarizationLabel": "HypaV3가 [{0}]를 요약할 예정입니다", + "nextSummarizationNoMessagesFoundLabel": "경고: 메시지를 찾을 수 없습니다", + "nextSummarizationLoadingError": "다음 요약 대상을 불러오는 동안 오류 발생: {0}", + "emptySelectedFirstMessageLabel": "경고: 선택된 첫 메시지가 비어있습니다" + }, +} diff --git a/src/lang/vi.ts b/src/lang/vi.ts index 6d7133a9..2d8a7275 100644 --- a/src/lang/vi.ts +++ b/src/lang/vi.ts @@ -409,4 +409,49 @@ export const LanguageVietnamese = { module: "Mô-đun", modules: "Mô-đun", useAdvancedEditor: "Sử dụng trình biên tập nâng cao", -} \ No newline at end of file + "hypaV3Settings": { + "descriptionLabel": "HypaMemory V3 là hệ thống bộ nhớ dài hạn sử dụng cả tóm tắt và tìm kiếm vector.", + "supaMemoryPromptPlaceHolder": "Để trống để sử dụng giá trị mặc định", + "maxMemoryTokensRatioLabel": "Tỷ lệ Token Bộ nhớ Tối đa (Ước tính)", + "maxMemoryTokensRatioError": "Không thể tính toán Tỷ lệ Token Bộ nhớ Tối đa", + "memoryTokensRatioLabel": "Tỷ lệ Token Bộ nhớ", + "extraSummarizationRatioLabel": "Tỷ lệ Tóm tắt Bổ sung", + "maxChatsPerSummaryLabel": "Số Tin nhắn Tối đa cho mỗi Tóm tắt", + "recentMemoryRatioLabel": "Tỷ lệ Bộ nhớ Gần đây", + "similarMemoryRatioLabel": "Tỷ lệ Bộ nhớ Tương tự", + "randomMemoryRatioLabel": "Tỷ lệ Bộ nhớ Ngẫu nhiên", + "enableSimilarityCorrectionLabel": "Bật Hiệu chỉnh Độ tương tự", + "preserveOrphanedMemoryLabel": "Giữ Bộ nhớ Mồ côi", + "applyRegexScriptWhenRerollingLabel": "Áp dụng Script Regex khi Tạo lại", + "doNotSummarizeUserMessageLabel": "Không Tóm tắt Tin nhắn Người dùng", + }, + "hypaV3Modal": { + "titleLabel": "HypaV3", + "resetConfirmMessage": "Hành động này không thể hoàn tác. Bạn có muốn đặt lại dữ liệu HypaV3 không?", + "resetConfirmSecondMessage": "Hành động này không thể khôi phục. Bạn có thực sự chắc chắn muốn đặt lại dữ liệu HypaV3 không?", + "convertLabel": "Chưa có tóm tắt nào, nhưng bạn có thể chuyển đổi dữ liệu HypaV2 sang V3.", + "convertButton": "Chuyển đổi sang V3", + "convertSuccessMessage": "Đã chuyển đổi thành công dữ liệu HypaV2 sang V3", + "convertErrorMessage": "Chuyển đổi dữ liệu HypaV2 sang V3 thất bại: {0}", + "noSummariesLabel": "Chưa có tóm tắt nào", + "searchPlaceholder": "Nhập #N, ID hoặc từ khóa", + "summaryNumberLabel": "Tóm tắt #{0}", + "deleteAfterConfirmMessage": "Xóa tất cả các tóm tắt sau tóm tắt này?", + "deleteAfterConfirmSecondMessage": "Hành động này không thể hoàn tác. Bạn có chắc chắn không?", + "translationLabel": "Bản dịch", + "rerolledSummaryLabel": "Tóm tắt đã Tạo lại", + "rerolledTranslationLabel": "Bản dịch Tóm tắt đã Tạo lại", + "connectedMessageCountLabel": "Tin nhắn Liên kết ({0})", + "connectedFirstMessageLabel": "Tin nhắn Đầu tiên", + "connectedMessageRoleLabel": "Tin nhắn của {0}", + "connectedMessageNotFoundLabel": "Không tìm thấy tin nhắn", + "connectedMessageLoadingError": "Lỗi khi tải tin nhắn liên kết: {0}", + "connectedMessageTranslationLabel": "Bản dịch", + "nextSummarizationFirstMessageLabel": "Tin nhắn Đầu tiên", + "nextSummarizationNoMessageIdLabel": "Không có ID Tin nhắn", + "nextSummarizationLabel": "HypaV3 sẽ tóm tắt [{0}]", + "nextSummarizationNoMessagesFoundLabel": "CẢNH BÁO: Không tìm thấy tin nhắn", + "nextSummarizationLoadingError": "Lỗi khi tải mục tiêu tóm tắt tiếp theo: {0}", + "emptySelectedFirstMessageLabel": "CẢNH BÁO: Tin nhắn đầu tiên được chọn trống" + }, +} diff --git a/src/lang/zh-Hant.ts b/src/lang/zh-Hant.ts index fb386234..fc47cc2a 100644 --- a/src/lang/zh-Hant.ts +++ b/src/lang/zh-Hant.ts @@ -129,7 +129,7 @@ export const languageChineseTraditional = { "summarizationPrompt": "用於摘要的提示詞。留空將使用預設提示詞。您也可以使用包含 {{slot}} 的 ChatML 格式來處理聊天數據。", "translatorPrompt": "用於翻譯的提示詞。留空將使用默認提示。您還可以使用帶有 {{slot}} 的 ChatML 格式表示目標語言:用 {{slot::content}} 表示內容,用 {{slot::tnote}} 表示翻譯註釋。", "translateBeforeHTMLFormatting": "啟用後,將在正規表達式和 HTML 格式化之前翻譯文本。這可能減少 Token 數,但可能破壞格式。", - "autoTranslateCachedOnly": "啟用後,僅會自動翻譯使用者之前已翻譯的內容。", + "autoTranslateCachedOnly": "啟用自動翻譯選項時,將只自動翻譯使用者先前已翻譯過的訊息。", "presetChain": "若欄位不為空,則每次使用者發送訊息時,系統會從此輸入中的預設列表中隨機變更並應用預設設定。預設列表應以逗號分隔,例如 `preset1,preset2`。", "legacyMediaFindings": "啟用後,將使用舊版方法尋找媒體資源,而不使用額外的搜尋演算法。", "comfyWorkflow": "請輸入 Comfy UI 的 API 工作流程。您可以在 Comfy UI 中點選「Workflow > Export (API)」按鈕以取得您的 API 工作流程。此外,您必須在工作流程文本中加入 {{risu_prompt}},該佔位符將被 Risu 提供的提示詞替換。", @@ -777,7 +777,7 @@ export const languageChineseTraditional = { "translateBeforeHTMLFormatting": "於 HTML 格式化前翻譯", "retranslate": "重新翻譯", "loading": "載入中", - "autoTranslateCachedOnly": "僅自動翻譯已快取的內容", + "autoTranslateCachedOnly": "僅自動翻譯已快取訊息", "notification": "使用系統通知", "permissionDenied": "權限被您的瀏覽器或操作系統拒絕", "customFlags": "自定義修飾詞(Flags)", @@ -802,7 +802,51 @@ export const languageChineseTraditional = { "banCharacterset": "自動重新生成字符集", "checkCorruption": "檢查損壞", "showPromptComparison": "顯示提示比較", - "hypaV3Desc": "HypaMemory V3 是一個長期記憶系統,使用摘要資料和向量搜尋。", "inlayErrorResponse": "嵌入錯誤回應", - "APIPool": "API 工具" + "APIPool": "API 工具", + "hypaV3Settings": { + "descriptionLabel": "HypaMemory V3 是一個使用摘要和向量搜索的長期記憶系統。", + "supaMemoryPromptPlaceHolder": "留空以使用預設值", + "maxMemoryTokensRatioLabel": "最大記憶標記比率(估計)", + "maxMemoryTokensRatioError": "無法計算最大記憶標記比率", + "memoryTokensRatioLabel": "記憶標記比率", + "extraSummarizationRatioLabel": "額外摘要比率", + "maxChatsPerSummaryLabel": "每個摘要的最大訊息數", + "recentMemoryRatioLabel": "最近記憶比率", + "similarMemoryRatioLabel": "相似記憶比率", + "randomMemoryRatioLabel": "隨機記憶比率", + "enableSimilarityCorrectionLabel": "啟用相似度校正", + "preserveOrphanedMemoryLabel": "保留孤立記憶", + "applyRegexScriptWhenRerollingLabel": "重新生成時應用正則表達式腳本", + "doNotSummarizeUserMessageLabel": "不要摘要用戶訊息" + }, + "hypaV3Modal": { + "titleLabel": "HypaV3 數據", + "resetConfirmMessage": "此操作無法撤銷。您要重置 HypaV3 數據嗎?", + "resetConfirmSecondMessage": "此操作不可逆。您真的確定要重置 HypaV3 數據嗎?", + "convertLabel": "尚無摘要,但您可以將 HypaV2 數據轉換為 V3。", + "convertButton": "轉換為 V3", + "convertSuccessMessage": "成功將 HypaV2 數據轉換為 V3", + "convertErrorMessage": "無法將 HypaV2 數據轉換為 V3:{0}", + "noSummariesLabel": "尚無摘要", + "searchPlaceholder": "輸入 #N、ID 或搜尋關鍵字", + "summaryNumberLabel": "摘要 #{0}", + "deleteAfterConfirmMessage": "刪除此摘要之後的所有摘要?", + "deleteAfterConfirmSecondMessage": "此操作無法撤銷。您確定要這樣做嗎?", + "translationLabel": "翻譯", + "rerolledSummaryLabel": "重新生成的摘要", + "rerolledTranslationLabel": "重新生成的摘要翻譯", + "connectedMessageCountLabel": "關聯訊息({0})", + "connectedFirstMessageLabel": "第一條訊息", + "connectedMessageRoleLabel": "{0} 的訊息", + "connectedMessageNotFoundLabel": "找不到訊息", + "connectedMessageLoadingError": "載入關聯訊息時出錯:{0}", + "connectedMessageTranslationLabel": "翻譯", + "nextSummarizationFirstMessageLabel": "第一條訊息", + "nextSummarizationNoMessageIdLabel": "無訊息 ID", + "nextSummarizationLabel": "HypaV3 將摘要 [{0}]", + "nextSummarizationNoMessagesFoundLabel": "警告:找不到訊息", + "nextSummarizationLoadingError": "載入下一個摘要目標時出錯:{0}", + "emptySelectedFirstMessageLabel": "警告:選定的第一條訊息為空" + }, } diff --git a/src/lib/Others/HypaV3Modal.svelte b/src/lib/Others/HypaV3Modal.svelte index ea9b6616..51451563 100644 --- a/src/lib/Others/HypaV3Modal.svelte +++ b/src/lib/Others/HypaV3Modal.svelte @@ -5,6 +5,8 @@ SettingsIcon, Trash2Icon, XIcon, + ChevronUpIcon, + ChevronDownIcon, LanguagesIcon, StarIcon, RefreshCw, @@ -28,6 +30,7 @@ import { summarize } from "../../ts/process/memory/hypav3"; import { type Message } from "../../ts/storage/database.svelte"; import { translateHTML } from "../../ts/translator/translator"; + import { language } from "../../lang"; interface SummaryUI { originalRef: HTMLTextAreaElement; @@ -50,20 +53,30 @@ translationRef: HTMLTextAreaElement; } - interface SearchResult { - element: HTMLElement; - matchType: "chatMemo" | "summary"; - summaryPosition?: { - start: number; - end: number; - }; + class SummarySearchResult { + constructor( + public summaryIndex: number, + public start: number, + public end: number + ) {} } + class ChatMemoSearchResult { + constructor( + public summaryIndex: number, + public memoIndex: number + ) {} + } + + type SearchResult = SummarySearchResult | ChatMemoSearchResult; + interface SearchUI { ref: HTMLInputElement; query: string; - currentIndex: number; results: SearchResult[]; + currentResultIndex: number; + requestedSearchFromIndex: number; + isNavigating: boolean; } const hypaV3DataState = $derived( @@ -75,6 +88,7 @@ let summaryUIStates = $state([]); let expandedMessageUIState = $state(null); let searchUIState = $state(null); + let showImportantOnly = $state(false); $effect.pre(() => { summaryUIStates = hypaV3DataState.summaries.map((summary) => ({ @@ -110,8 +124,10 @@ searchUIState = { ref: null, query: "", - currentIndex: -1, results: [], + currentResultIndex: -1, + requestedSearchFromIndex: -1, + isNavigating: false, }; // Focus on search element after it's rendered @@ -134,122 +150,187 @@ } if (e.key === "Enter") { - e.preventDefault(); // Prevent event default action + e.preventDefault?.(); // Prevent event default action const query = searchUIState.query.trim(); if (!query) return; - // Search summary index - if (query.match(/^#\d+$/)) { - const summaryNumber = parseInt(query.substring(1)) - 1; + // When received a new query + if (searchUIState.currentResultIndex === -1) { + const results = generateSearchResults(query); - if ( - summaryNumber >= 0 && - summaryNumber < hypaV3DataState.summaries.length - ) { - summaryUIStates[summaryNumber].originalRef.scrollIntoView({ - behavior: "instant", - block: "center", - }); - } - - return; - } - - const normalizedQuery = query.toLowerCase(); - - if (searchUIState.currentIndex === -1) { - const results: SearchResult[] = []; - - if (isGuidLike(query)) { - // Search chatMemo - summaryUIStates.forEach((summaryUI) => { - summaryUI.chatMemoRefs.forEach((buttonRef) => { - const buttonText = buttonRef.textContent?.toLowerCase() || ""; - - if (buttonText.includes(normalizedQuery)) { - results.push({ - element: buttonRef as HTMLButtonElement, - matchType: "chatMemo", - }); - } - }); - }); - } else { - // Search summary - summaryUIStates.forEach((summaryUI) => { - const textAreaText = summaryUI.originalRef.value?.toLowerCase(); - - let pos = -1; - while ( - (pos = textAreaText.indexOf(normalizedQuery, pos + 1)) !== -1 - ) { - results.push({ - element: summaryUI.originalRef as HTMLTextAreaElement, - matchType: "summary", - summaryPosition: { - start: pos, - end: pos + normalizedQuery.length, - }, - }); - } - }); - } + if (results.length === 0) return; searchUIState.results = results; } - if (searchUIState.results.length === 0) return; + const nextResult = getNextSearchResult(e.shiftKey); - // Move to next result - searchUIState.currentIndex = - (searchUIState.currentIndex + 1) % searchUIState.results.length; + if (nextResult) { + navigateToSearchResult(nextResult); + } + } + } - const result = searchUIState.results[searchUIState.currentIndex]; + function generateSearchResults(query: string): SearchResult[] { + const results: SearchResult[] = []; + const normalizedQuery = query.trim().toLowerCase(); + + // Search summary index + if (query.match(/^#\d+$/)) { + const summaryNumber = parseInt(query.substring(1)) - 1; + + if ( + summaryNumber >= 0 && + summaryNumber < hypaV3DataState.summaries.length && + (!showImportantOnly || + hypaV3DataState.summaries[summaryNumber].isImportant) + ) { + results.push(new SummarySearchResult(summaryNumber, 0, 0)); + } + + return results; + } + + if (isGuidLike(query)) { + // Search chatMemo + summaryUIStates.forEach((summaryUI, summaryIndex) => { + if ( + !showImportantOnly || + hypaV3DataState.summaries[summaryIndex].isImportant + ) { + summaryUI.chatMemoRefs.forEach((buttonRef, memoIndex) => { + const buttonText = buttonRef.textContent?.toLowerCase() || ""; + + if (buttonText.includes(normalizedQuery)) { + results.push(new ChatMemoSearchResult(summaryIndex, memoIndex)); + } + }); + } + }); + } else { + // Search summary + summaryUIStates.forEach((summaryUI, summaryIndex) => { + if ( + !showImportantOnly || + hypaV3DataState.summaries[summaryIndex].isImportant + ) { + const textAreaText = summaryUI.originalRef.value?.toLowerCase(); + let pos = -1; + + while ( + (pos = textAreaText.indexOf(normalizedQuery, pos + 1)) !== -1 + ) { + results.push( + new SummarySearchResult( + summaryIndex, + pos, + pos + normalizedQuery.length + ) + ); + } + } + }); + } + + return results; + } + + function isGuidLike(str: string): boolean { + const strTrimed = str.trim(); + + // Exclude too short inputs + if (strTrimed.length < 4) return false; + + return /^[0-9a-f]{4,12}(-[0-9a-f]{4,12}){0,4}-?$/i.test(strTrimed); + } + + function getNextSearchResult(backward: boolean): SearchResult | null { + if (!searchUIState || searchUIState.results.length === 0) return null; + + let nextIndex: number; + + if (searchUIState.requestedSearchFromIndex !== -1) { + const fromSummaryIndex = searchUIState.requestedSearchFromIndex; + + nextIndex = backward + ? searchUIState.results.findLastIndex( + (r) => r.summaryIndex <= fromSummaryIndex + ) + : searchUIState.results.findIndex( + (r) => r.summaryIndex >= fromSummaryIndex + ); + + if (nextIndex === -1) { + nextIndex = backward ? searchUIState.results.length - 1 : 0; + } + + searchUIState.requestedSearchFromIndex = -1; + } else { + const delta = backward ? -1 : 1; + + nextIndex = + (searchUIState.currentResultIndex + + delta + + searchUIState.results.length) % + searchUIState.results.length; + } + + searchUIState.currentResultIndex = nextIndex; + return searchUIState.results[nextIndex]; + } + + function navigateToSearchResult(result: SearchResult) { + searchUIState.isNavigating = true; + + if (result instanceof SummarySearchResult) { + const textarea = summaryUIStates[result.summaryIndex].originalRef; // Scroll to element - result.element.scrollIntoView({ + textarea.scrollIntoView({ behavior: "instant", block: "center", }); - if (result.matchType === "chatMemo") { - // Highlight chatMemo result - result.element.classList.add("ring-2", "ring-zinc-500"); + if (result.start === result.end) { + return; + } - // Remove highlight after a short delay - window.setTimeout(() => { - result.element.classList.remove("ring-2", "ring-zinc-500"); - }, 1000); - } else { - // Handle summary text selection - const textarea = result.element as HTMLTextAreaElement; + // Scroll to query + textarea.setSelectionRange(result.start, result.end); + scrollToSelection(textarea); + // Highlight query on desktop environment + if (!("ontouchend" in window)) { // Make readonly temporarily textarea.readOnly = true; - - // Select query - textarea.setSelectionRange( - result.summaryPosition.start, - result.summaryPosition.end - ); - textarea.focus(); - scrollToSelection(textarea); - - // This only works on firefox - //textarea.scrollTop = textarea.scrollHeight; // Scroll to the bottom - //textarea.blur(); // Collapse selection - //textarea.focus(); // This scrolls the textarea - - // Highlight textarea window.setTimeout(() => { searchUIState.ref.focus(); // Restore focus to search bar textarea.readOnly = false; // Remove readonly after focus moved }, 300); } + } else { + const button = + summaryUIStates[result.summaryIndex].chatMemoRefs[result.memoIndex]; + + // Scroll to element + button.scrollIntoView({ + behavior: "instant", + block: "center", + }); + + // Highlight chatMemo + button.classList.add("ring-2", "ring-zinc-500"); + + // Remove highlight after a short delay + window.setTimeout(() => { + button.classList.remove("ring-2", "ring-zinc-500"); + }, 1000); } + + searchUIState.isNavigating = false; } function scrollToSelection(textarea: HTMLTextAreaElement) { @@ -286,15 +367,6 @@ textarea.scrollTop = selectionTop - textarea.clientHeight / 2; } - function isGuidLike(str: string): boolean { - const strTrimed = str.trim(); - - // Exclude too short inputs - if (strTrimed.length < 4) return false; - - return /^[0-9a-f]{4,12}(-[0-9a-f]{4,12}){0,4}-?$/i.test(strTrimed); - } - async function toggleTranslate( summaryIndex: number, regenerate?: boolean @@ -759,21 +831,42 @@

- HypaV3 Data + {language.hypaV3Modal.titleLabel}

+ + +
-
+
{#if hypaV3DataState.summaries.length === 0} {#if isHypaV2ConversionPossible()} @@ -836,33 +931,37 @@ >
- No summaries yet, but you may convert HypaV2 data to V3. + {language.hypaV3Modal.convertLabel}
{:else}
- No summaries yet + {language.hypaV3Modal.noSummariesLabel}
{/if} @@ -880,13 +979,13 @@ > { if (searchUIState) { - searchUIState.currentIndex = -1; searchUIState.results = []; + searchUIState.currentResultIndex = -1; } }} onkeydown={(e) => onSearch(e)} @@ -897,16 +996,32 @@ - {searchUIState.currentIndex + 1}/{searchUIState.results - .length} + {searchUIState.currentResultIndex + 1}/{searchUIState + .results.length} {/if}
+ + + + +
@@ -914,256 +1029,295 @@ {#each hypaV3DataState.summaries as summary, i} - {#if summaryUIStates[i]} - -
- -
- Summary #{i + 1} - -
- - - - - - - - - - - -
-
- - -
- -
- - - {#if summaryUIStates[i].translation} -
-
- Translation -
- - -
- {/if} - - {#if summaryUIStates[i].rerolledText} - -
-
- Rerolled Summary -
- - - - - - - - -
-
-
- - -
- -
- - - {#if summaryUIStates[i].rerolledTranslation} -
-
- Rerolled Translation -
- - -
- {/if} - {/if} - - -
+ {#if !showImportantOnly || summary.isImportant} + {#if summaryUIStates[i]} + +
+
Connected Messages ({summary.chatMemos.length}){language.hypaV3Modal.summaryNumberLabel.replace( + "{0}", + (i + 1).toString() + )}
- + + + + + + + + + +
-
- -
- {#each summary.chatMemos as chatMemo, memoIndex} - - {/each} -
- - {#if expandedMessageUIState?.summaryIndex === i} - +
- - {#await getProcessedMessageFromChatMemo(expandedMessageUIState.selectedChatMemo) then expandedMessage} - {#if expandedMessage} - -
- {expandedMessage.role}'s Message -
- - - - {:else} - Message not found - {/if} - {:catch error} - Error loading expanded message: {error.message} - {/await} +
- - {#if expandedMessageUIState.translation} + + {#if summaryUIStates[i].translation}
- Translation + {language.hypaV3Modal.translationLabel}
{/if} - {/if} -
+ + {#if summaryUIStates[i].rerolledText} + +
+
+ {language.hypaV3Modal.rerolledSummaryLabel} +
+ + + + + + + + +
+
+
+ + +
+ +
+ + + {#if summaryUIStates[i].rerolledTranslation} +
+
+ {language.hypaV3Modal.rerolledTranslationLabel} +
+ + +
+ {/if} + {/if} + + +
+
+ {language.hypaV3Modal.connectedMessageCountLabel.replace( + "{0}", + summary.chatMemos.length.toString() + )} + +
+ + +
+
+
+ + +
+ {#each summary.chatMemos as chatMemo, memoIndex} + + {/each} +
+ + {#if expandedMessageUIState?.summaryIndex === i} + +
+ + {#await getProcessedMessageFromChatMemo(expandedMessageUIState.selectedChatMemo) then expandedMessage} + {#if expandedMessage} + +
+ {language.hypaV3Modal.connectedMessageRoleLabel.replace( + "{0}", + expandedMessage.role + )} +
+ + + + {:else} + {language.hypaV3Modal + .connectedMessageNotFoundLabel} + {/if} + {:catch error} + {language.hypaV3Modal.connectedMessageLoadingError.replace( + "{0}", + error.message + )} + {/await} +
+ + + {#if expandedMessageUIState.translation} +
+
+ {language.hypaV3Modal.connectedMessageTranslationLabel} +
+ + +
+ {/if} + {/if} +
+ {/if} {/if} {/each} @@ -1173,25 +1327,34 @@ {#if nextMessage} {@const chatId = nextMessage.chatId === "first" - ? "First Message" + ? language.hypaV3Modal.nextSummarizationFirstMessageLabel : nextMessage.chatId == null - ? "No Message ID" + ? language.hypaV3Modal.nextSummarizationNoMessageIdLabel : nextMessage.chatId}
- HypaV3 will summarize [{chatId}] + {language.hypaV3Modal.nextSummarizationLabel.replace( + "{0}", + chatId + )}
{:else} - WARN: No messages found + {language.hypaV3Modal + .nextSummarizationNoMessagesFoundLabel} {/if} {:catch error} Error loading next message: {error.message}{language.hypaV3Modal.nextSummarizationLoadingError.replace( + "{0}", + error.message + )} {/await} @@ -1200,7 +1363,7 @@ {#if !getFirstMessage()}
WARN: Selected first message is empty{language.hypaV3Modal.emptySelectedFirstMessageLabel}
{/if} diff --git a/src/lib/Setting/Pages/BotSettings.svelte b/src/lib/Setting/Pages/BotSettings.svelte index cb3e28f5..8033679f 100644 --- a/src/lib/Setting/Pages/BotSettings.svelte +++ b/src/lib/Setting/Pages/BotSettings.svelte @@ -643,6 +643,10 @@ {/if} + + + + diff --git a/src/lib/Setting/Pages/Module/ModuleMenu.svelte b/src/lib/Setting/Pages/Module/ModuleMenu.svelte index 6aae6b97..82e51fce 100644 --- a/src/lib/Setting/Pages/Module/ModuleMenu.svelte +++ b/src/lib/Setting/Pages/Module/ModuleMenu.svelte @@ -146,6 +146,8 @@
+ {language.customPromptTemplateToggle} + {/if} {#if submenu === 1 && (Array.isArray(currentModule.lorebook))}
diff --git a/src/lib/Setting/Pages/Module/ModuleSettings.svelte b/src/lib/Setting/Pages/Module/ModuleSettings.svelte index 4abaa0ed..a0adcb30 100644 --- a/src/lib/Setting/Pages/Module/ModuleSettings.svelte +++ b/src/lib/Setting/Pages/Module/ModuleSettings.svelte @@ -4,13 +4,14 @@ import { DBState } from 'src/ts/stores.svelte'; import Button from "src/lib/UI/GUI/Button.svelte"; import ModuleMenu from "src/lib/Setting/Pages/Module/ModuleMenu.svelte"; - import { exportModule, importModule, type RisuModule } from "src/ts/process/modules"; + import { exportModule, importModule, refreshModules, type RisuModule } from "src/ts/process/modules"; import { DownloadIcon, Edit, TrashIcon, Globe, Share2Icon } from "lucide-svelte"; import { v4 } from "uuid"; import { tooltip } from "src/ts/gui/tooltip"; import { alertCardExport, alertConfirm, alertError } from "src/ts/alert"; import TextInput from "src/lib/UI/GUI/TextInput.svelte"; import { ShowRealmFrameStore } from "src/ts/stores.svelte"; + import { onDestroy } from "svelte"; let tempModule:RisuModule = $state({ name: '', description: '', @@ -30,6 +31,12 @@ return score }) } + + onDestroy(() => { + if(DBState.db.moduleIntergration){ + refreshModules() + } + }) {#if mode === 0}

{language.modules}

@@ -48,9 +55,12 @@
{rmodule.name}
-
+
+ +
{:else if (DBState.db.supaModelType !== 'none' && DBState.db.hypav2 === false && DBState.db.hypaV3 === false)} {language.supaDesc} {language.SuperMemory} {language.model} diff --git a/src/lib/SideBars/CharConfig.svelte b/src/lib/SideBars/CharConfig.svelte index 93a4c98a..e0165d1a 100644 --- a/src/lib/SideBars/CharConfig.svelte +++ b/src/lib/SideBars/CharConfig.svelte @@ -30,7 +30,7 @@ import { updateInlayScreen } from "src/ts/process/inlayScreen"; import { registerOnnxModel } from "src/ts/process/transformers"; import MultiLangInput from "../UI/GUI/MultiLangInput.svelte"; - import { applyModule } from "src/ts/process/modules"; + import { applyModule, getModuleToggles } from "src/ts/process/modules"; import { exportRegex, importRegex } from "src/ts/process/scripts"; import Arcodion from "../UI/Arcodion.svelte"; import SliderInput from "../UI/GUI/SliderInput.svelte"; @@ -293,7 +293,7 @@
- {#each parseKeyValue(DBState.db.customPromptTemplateToggle) as toggle} + {#each parseKeyValue(DBState.db.customPromptTemplateToggle + getModuleToggles()) as toggle}
{ DBState.db.globalChatVariables[`toggle_${toggle[0]}`] = DBState.db.globalChatVariables[`toggle_${toggle[0]}`] === '1' ? '0' : '1' diff --git a/src/lib/SideBars/DevTool.svelte b/src/lib/SideBars/DevTool.svelte index 8c4076b7..a0263a2d 100644 --- a/src/lib/SideBars/DevTool.svelte +++ b/src/lib/SideBars/DevTool.svelte @@ -18,6 +18,7 @@ import { applyChatTemplate, chatTemplates } from "src/ts/process/templates/chatTemplate"; import OptionInput from "../UI/GUI/OptionInput.svelte"; import { loadLoreBookV3Prompt } from "src/ts/process/lorebook.svelte"; + import { getModules } from "src/ts/process/modules"; let previewMode = $state('chat') let previewJoin = $state('yes') @@ -281,6 +282,16 @@ }}>Match Sources + + \ No newline at end of file diff --git a/src/lib/SideBars/LoreBook/LoreBookSetting.svelte b/src/lib/SideBars/LoreBook/LoreBookSetting.svelte index 6e942df0..2454228d 100644 --- a/src/lib/SideBars/LoreBook/LoreBookSetting.svelte +++ b/src/lib/SideBars/LoreBook/LoreBookSetting.svelte @@ -2,13 +2,13 @@ import { DBState } from 'src/ts/stores.svelte'; import { language } from "../../../lang"; - import { DownloadIcon, FolderUpIcon, ImportIcon, PlusIcon } from "lucide-svelte"; + import { DownloadIcon, FolderUpIcon, ImportIcon, PlusIcon, SunIcon, LinkIcon } from "lucide-svelte"; import { addLorebook, exportLoreBook, importLoreBook } from "../../../ts/process/lorebook.svelte"; import Check from "../../UI/GUI/CheckInput.svelte"; import NumberInput from "../../UI/GUI/NumberInput.svelte"; import LoreBookList from "./LoreBookList.svelte"; import Help from "src/lib/Others/Help.svelte"; - import { selectedCharID } from "src/ts/stores.svelte"; + import { selectedCharID } from "src/ts/stores.svelte"; let submenu = $state(0) interface Props { @@ -16,6 +16,40 @@ } let { globalMode = $bindable(false) }: Props = $props(); + + function isAllCharacterLoreAlwaysActive() { + const globalLore = DBState.db.characters[$selectedCharID].globalLore; + return globalLore && globalLore.every((book) => book.alwaysActive); + } + + function isAllChatLoreAlwaysActive() { + const localLore = DBState.db.characters[$selectedCharID].chats[DBState.db.characters[$selectedCharID].chatPage].localLore; + return localLore && localLore.every((book) => book.alwaysActive); + } + + function toggleCharacterLoreAlwaysActive() { + const globalLore = DBState.db.characters[$selectedCharID].globalLore; + + if (!globalLore) return; + + const allActive = globalLore.every((book) => book.alwaysActive); + + globalLore.forEach((book) => { + book.alwaysActive = !allActive; + }); + } + + function toggleChatLoreAlwaysActive() { + const localLore = DBState.db.characters[$selectedCharID].chats[DBState.db.characters[$selectedCharID].chatPage].localLore; + + if (!localLore) return; + + const allActive = localLore.every((book) => book.alwaysActive); + + localLore.forEach((book) => { + book.alwaysActive = !allActive; + }); + } {#if !globalMode} @@ -99,5 +133,25 @@ }} class="hover:text-textcolor ml-2 cursor-pointer"> + +
{/if} \ No newline at end of file diff --git a/src/lib/SideBars/Scripts/TriggerData.svelte b/src/lib/SideBars/Scripts/TriggerData.svelte index 1f75fabb..b3906fec 100644 --- a/src/lib/SideBars/Scripts/TriggerData.svelte +++ b/src/lib/SideBars/Scripts/TriggerData.svelte @@ -304,6 +304,13 @@ index: '' } } + else if(effect.type === 'runAxLLM'){ + value.effect[i] = { + type: 'runAxLLM', + value: '', + inputVar: '' + } + } }}> {language.triggerEffSetVar} {language.triggerEffImperson} @@ -319,6 +326,7 @@ {language.runImgGen} {language.cutChat} {language.modifyChat} + {language.triggerEffRunAxLLM} {#if (value.type !== 'start' && (effect.type === 'systemprompt' || effect.type === 'stop')) || @@ -333,7 +341,8 @@ effect.type === 'showAlert' || effect.type === 'sendAIprompt' || effect.type === 'extractRegex' || - effect.type === 'runImgGen' + effect.type === 'runImgGen' || + effect.type === 'runAxLLM' ) } {language.triggerLowLevelOnly} @@ -459,6 +468,14 @@ {/if} + + {#if effect.type === 'runAxLLM'} + {language.prompt} + + + {language.resultStoredVar} + + {/if} {/each}
diff --git a/src/lib/SideBars/SideChatList.svelte b/src/lib/SideBars/SideChatList.svelte index 70f8a773..3c2a454e 100644 --- a/src/lib/SideBars/SideChatList.svelte +++ b/src/lib/SideBars/SideChatList.svelte @@ -16,6 +16,7 @@ import { onDestroy, onMount } from "svelte"; import { v4 } from "uuid"; import { getChatBranches } from "src/ts/gui/branches"; + import { getModuleToggles } from "src/ts/process/modules"; interface Props { chara: character|groupChat; @@ -221,12 +222,12 @@ {#if DBState.db.characters[$selectedCharID]?.chaId !== '§playground'} - {#if parseKeyValue(DBState.db.customPromptTemplateToggle).length > 4} + {#if parseKeyValue(DBState.db.customPromptTemplateToggle + getModuleToggles()).length > 4}
- {#each parseKeyValue(DBState.db.customPromptTemplateToggle) as toggle} + {#each parseKeyValue(DBState.db.customPromptTemplateToggle + getModuleToggles()) as toggle}
{ DBState.db.globalChatVariables[`toggle_${toggle[0]}`] = DBState.db.globalChatVariables[`toggle_${toggle[0]}`] === '1' ? '0' : '1' @@ -239,11 +240,11 @@
{/if}
- {:else if parseKeyValue(DBState.db.customPromptTemplateToggle).length > 0} + {:else if parseKeyValue(DBState.db.customPromptTemplateToggle + getModuleToggles()).length > 0}
- {#each parseKeyValue(DBState.db.customPromptTemplateToggle) as toggle} + {#each parseKeyValue(DBState.db.customPromptTemplateToggle + getModuleToggles()) as toggle}
{ DBState.db.globalChatVariables[`toggle_${toggle[0]}`] = DBState.db.globalChatVariables[`toggle_${toggle[0]}`] === '1' ? '0' : '1' diff --git a/src/ts/parser.svelte.ts b/src/ts/parser.svelte.ts index c6912d5e..87f46090 100644 --- a/src/ts/parser.svelte.ts +++ b/src/ts/parser.svelte.ts @@ -10,7 +10,7 @@ import { SizeStore, selectedCharID } from './stores.svelte'; import { calcString } from './process/infunctions'; import { findCharacterbyId, getPersonaPrompt, getUserIcon, getUserName, parseKeyValue, sfc32, sleep, uuidtoNumber } from './util'; import { getInlayAsset } from './process/files/inlays'; -import { getModuleAssets, getModuleLorebooks } from './process/modules'; +import { getModuleAssets, getModuleLorebooks, getModules } from './process/modules'; import type { OpenAIChat } from './process/index.svelte'; import hljs from 'highlight.js/lib/core' import 'highlight.js/styles/atom-one-dark.min.css' @@ -292,9 +292,38 @@ async function renderHighlightableMarkdown(data:string) { } - export const assetRegex = /{{(raw|path|img|image|video|audio|bg|emotion|asset|video-img|source)::(.+?)}}/gms +async function replaceAsync(string, regexp, replacerFunction) { + const replacements = await Promise.all( + Array.from(string.matchAll(regexp), + match => replacerFunction(...match as any))) + let i = 0; + return string.replace(regexp, () => replacements[i++]) +} + +async function getAssetSrc(assetArr: string[][], name: string, assetPaths: {[key: string]:{path: string, ext?: string}}) { + name = name.toLocaleLowerCase() + for (const asset of assetArr) { + if (trimmer(asset[0].toLocaleLowerCase()) !== trimmer(name)) continue + const assetPath = await getFileSrc(asset[1]) + assetPaths[asset[0].toLocaleLowerCase()] = { + path: assetPath, + ext: asset[2] + } + return + } +} + +async function getEmoSrc(emoArr: string[][], emoPaths: {[key: string]:{path: string}}) { + for (const emo of emoArr) { + const emoPath = await getFileSrc(emo[1]) + emoPaths[emo[0].toLocaleLowerCase()] = { + path: emoPath, + } + } +} + async function parseAdditionalAssets(data:string, char:simpleCharacterArgument|character, mode:'normal'|'back', mode2:'unset'|'pre'|'post' = 'unset'){ const assetWidthString = (DBState.db.assetWidth && DBState.db.assetWidth !== -1 || DBState.db.assetWidth === 0) ? `max-width:${DBState.db.assetWidth}rem;` : '' @@ -306,37 +335,20 @@ async function parseAdditionalAssets(data:string, char:simpleCharacterArgument|c path:string }} = {} - if(char.additionalAssets){ - for(const asset of char.additionalAssets){ - const assetPath = await getFileSrc(asset[1]) - assetPaths[asset[0].toLocaleLowerCase()] = { - path: assetPath, - ext: asset[2] - } - } - } - if(char.emotionImages){ - for(const emo of char.emotionImages){ - const emoPath = await getFileSrc(emo[1]) - emoPaths[emo[0].toLocaleLowerCase()] = { - path: emoPath, - } - } - } - const moduleAssets = getModuleAssets() - if(moduleAssets.length > 0){ - for(const asset of moduleAssets){ - const assetPath = await getFileSrc(asset[1]) - assetPaths[asset[0].toLocaleLowerCase()] = { - path: assetPath, - ext: asset[2] - } - } - } + if (char.emotionImages) await getEmoSrc(char.emotionImages, emoPaths) + const videoExtention = ['mp4', 'webm', 'avi', 'm4p', 'm4v'] let needsSourceAccess = false - data = data.replaceAll(assetRegex, (full:string, type:string, name:string) => { - name = name.toLocaleLowerCase() + + data = await replaceAsync(data, assetRegex, async (full:string, type:string, name:string) => { + const moduleAssets = getModuleAssets() + if (char.additionalAssets) { + await getAssetSrc(char.additionalAssets, name, assetPaths) + } + if (moduleAssets.length > 0) { + await getAssetSrc(moduleAssets, name, assetPaths) + } + if(type === 'emotion'){ const path = emoPaths[name]?.path if(!path){ @@ -344,6 +356,7 @@ async function parseAdditionalAssets(data:string, char:simpleCharacterArgument|c } return `${path}` } + if(type === 'source'){ needsSourceAccess = true switch(name){ @@ -355,13 +368,15 @@ async function parseAdditionalAssets(data:string, char:simpleCharacterArgument|c } } } + let path = assetPaths[name] + if(!path){ if(DBState.db.legacyMediaFindings){ return '' } - path = getClosestMatch(name, assetPaths) + path = await getClosestMatch(char, name, assetPaths) if(!path){ return '' @@ -411,63 +426,72 @@ async function parseAdditionalAssets(data:string, char:simpleCharacterArgument|c return data } -function getClosestMatch(name:string, assetPaths:{[key:string]:{path:string, ext?:string}}){ - - if(Object.keys(assetPaths).length === 0){ - return null - } - - //Levenshtein distance, new with 1d array - const dest = (a:string, b:string) => { - const h = a.length + 1 - const w = b.length + 1 - let d = new Int16Array(h * w) - for(let i=0;i DBState.db.assetMaxDifference){ return null } + + const assetPath = await getFileSrc(targetPath) + assetPaths[closest] = { + path: assetPath, + ext: targetExt + } + return assetPaths[closest] } +//Levenshtein distance, new with 1d array +function getDistance(a:string, b:string) { + const h = a.length + 1 + const w = b.length + 1 + let d = new Int16Array(h * w) + for(let i=0;i { try { let text = Buffer.from(txt, 'hex').toString('utf-8') @@ -1508,16 +1531,13 @@ function basicMatcher (p1:string,matcherArg:matcherArg,vars:{[key:string]:string return dateTimeFormat(arra[1],t) } case 'module_enabled':{ - const selchar = db.characters[get(selectedCharID)] - let enabledChatModules:string[] = [] - if(!selchar){ - enabledChatModules = selchar.chats[selchar.chatPage].modules ?? [] - + const modules = getModules() + for(const module of modules){ + if(module.namespace === arra[1]){ + return '1' + } } - const moduleId = db.modules.find((f) => { - return f.name === arra[1] - }).id - return (db.enabledModules.includes(moduleId) || enabledChatModules.includes(moduleId)) ? '1' : '0' + return '0' } case 'filter':{ const array = parseArray(arra[1]) diff --git a/src/ts/process/lua.ts b/src/ts/process/lua.ts index f4b27c32..1f2e4bdf 100644 --- a/src/ts/process/lua.ts +++ b/src/ts/process/lua.ts @@ -419,6 +419,64 @@ export async function runLua(code:string, arg:{ return true }) + luaEngine.global.set('axLLMMain', async (id:string, promptStr:string) => { + let prompt:{ + role: string, + content: string + }[] = JSON.parse(promptStr) + if(!LuaLowLevelIds.has(id)){ + return + } + let promptbody:OpenAIChat[] = prompt.map((dict) => { + let role:'system'|'user'|'assistant' = 'assistant' + switch(dict['role']){ + case 'system': + case 'sys': + role = 'system' + break + case 'user': + role = 'user' + break + case 'assistant': + case 'bot': + case 'char':{ + role = 'assistant' + break + } + } + + return { + content: dict['content'] ?? '', + role: role, + } + }) + const result = await requestChatData({ + formated: promptbody, + bias: {}, + useStreaming: false, + noMultiGen: true, + }, 'otherAx') + + if(result.type === 'fail'){ + return JSON.stringify({ + success: false, + result: 'Error: ' + result.result + }) + } + + if(result.type === 'streaming' || result.type === 'multiline'){ + return JSON.stringify({ + success: false, + result: result.result + }) + } + + return JSON.stringify({ + success: true, + result: result.result + }) + }) + await luaEngine.doString(luaCodeWarper(code)) luaEngineState.code = code } @@ -538,6 +596,10 @@ function LLM(id, prompt) return json.decode(LLMMain(id, json.encode(prompt)):await()) end +function axLLM(id, prompt) + return json.decode(axLLMMain(id, json.encode(prompt)):await()) +end + local editRequestFuncs = {} local editDisplayFuncs = {} local editInputFuncs = {} diff --git a/src/ts/process/memory/hypamemory.ts b/src/ts/process/memory/hypamemory.ts index 27ec3f96..deb2735c 100644 --- a/src/ts/process/memory/hypamemory.ts +++ b/src/ts/process/memory/hypamemory.ts @@ -106,7 +106,7 @@ export class HypaProcesser{ if(!gf.ok){ - throw gf.data + throw JSON.stringify(gf.data) } const result:number[][] = [] diff --git a/src/ts/process/memory/hypav3.ts b/src/ts/process/memory/hypav3.ts index 91bef8d6..1ce2489e 100644 --- a/src/ts/process/memory/hypav3.ts +++ b/src/ts/process/memory/hypav3.ts @@ -421,6 +421,12 @@ export async function hypaMemoryV3( continue; } + if (db.hypaV3Settings.doNotSummarizeUserMessage && chat.role === "user") { + console.log(`[HypaV3] Skipping user role at index ${i}`); + + continue; + } + toSummarize.push(chat); } @@ -436,23 +442,25 @@ export async function hypaMemoryV3( } // Attempt summarization - const summarizeResult = await retryableSummarize(toSummarize); + if (toSummarize.length > 0) { + const summarizeResult = await retryableSummarize(toSummarize); - if (!summarizeResult.success) { - return { - currentTokens, - chats, - error: `[HypaV3] Summarization failed after maximum retries: ${summarizeResult.data}`, - memory: toSerializableHypaV3Data(data), - }; + if (!summarizeResult.success) { + return { + currentTokens, + chats, + error: `[HypaV3] Summarization failed after maximum retries: ${summarizeResult.data}`, + memory: toSerializableHypaV3Data(data), + }; + } + + data.summaries.push({ + text: summarizeResult.data, + chatMemos: new Set(toSummarize.map((chat) => chat.memo)), + isImportant: false, + }); } - data.summaries.push({ - text: summarizeResult.data, - chatMemos: new Set(toSummarize.map((chat) => chat.memo)), - isImportant: false, - }); - currentTokens -= toSummarizeTokens; startIdx = endIdx; } @@ -469,6 +477,37 @@ export async function hypaMemoryV3( availableMemoryTokens ); + // Early return if no summaries + if (data.summaries.length === 0) { + // Generate final memory prompt + const memory = encapsulateMemoryPrompt(""); + + const newChats: OpenAIChat[] = [ + { + role: "system", + content: memory, + memo: "supaMemory", + }, + ...chats.slice(startIdx), + ]; + + console.log( + "[HypaV3] Exiting function:", + "\nCurrent Tokens:", + currentTokens, + "\nAll chats, including memory prompt:", + newChats, + "\nMemory Data:", + data + ); + + return { + currentTokens, + chats: newChats, + memory: toSerializableHypaV3Data(data), + }; + } + const selectedSummaries: Summary[] = []; const randomMemoryRatio = 1 - diff --git a/src/ts/process/modules.ts b/src/ts/process/modules.ts index 5afb4f00..73a9b80d 100644 --- a/src/ts/process/modules.ts +++ b/src/ts/process/modules.ts @@ -24,6 +24,7 @@ export interface RisuModule{ backgroundEmbedding?:string assets?:[string,string,string][] namespace?:string + customModuleToggle?:string } export async function exportModule(module:RisuModule, arg:{ @@ -267,7 +268,20 @@ function getModuleByIds(ids:string[]){ modules.push(module) } } - return modules + return deduplicateModuleById(modules) +} + +function deduplicateModuleById(modules:RisuModule[]){ + let ids:string[] = [] + let newModules:RisuModule[] = [] + for(let i=0;i s.trim()) + ids = ids.concat(intList) + } const idsJoined = ids.join('-') if(lastModules === idsJoined){ return lastModuleData @@ -352,6 +370,20 @@ export function getModuleRegexScripts() { return customscripts } +export function getModuleToggles() { + const modules = getModules() + let costomModuleToggles: string = '' + for (const module of modules) { + if(!module){ + continue + } + if (module.customModuleToggle) { + costomModuleToggles += '\n' + module.customModuleToggle + '\n' + } + } + return costomModuleToggles +} + export async function applyModule() { const sel = await alertModuleSelect() if (!sel) { @@ -425,4 +457,9 @@ export function moduleUpdate(){ ReloadGUIPointer.set(get(ReloadGUIPointer) + 1) lastModuleIds = ids } +} + +export function refreshModules(){ + lastModules = '' + lastModuleData = [] } \ No newline at end of file diff --git a/src/ts/process/triggers.ts b/src/ts/process/triggers.ts index ec295dc7..5b2d413f 100644 --- a/src/ts/process/triggers.ts +++ b/src/ts/process/triggers.ts @@ -152,6 +152,12 @@ export interface triggerEffectRunLLM{ inputVar: string } +export interface triggerEffectRunAxLLM{ + type: 'runAxLLM', + value: string, + inputVar: string +} + export type additonalSysPrompt = { start:string, historyend: string, @@ -1033,6 +1039,7 @@ export async function runTrigger(char:character,mode:triggerMode, arg:{ setVar(effect.inputVar, res) break } + case 'triggerlua':{ const triggerCodeResult = await runLua(effect.code,{ lowLevelAccess: trigger.lowLevelAccess, diff --git a/src/ts/storage/database.svelte.ts b/src/ts/storage/database.svelte.ts index 386be221..c5defa8b 100644 --- a/src/ts/storage/database.svelte.ts +++ b/src/ts/storage/database.svelte.ts @@ -474,13 +474,14 @@ export function setDatabase(data:Database){ data.reasoningEffort ??= 0 data.hypaV3Settings = { memoryTokensRatio: data.hypaV3Settings?.memoryTokensRatio ?? 0.2, - extraSummarizationRatio: data.hypaV3Settings?.extraSummarizationRatio ?? 0.2, + extraSummarizationRatio: data.hypaV3Settings?.extraSummarizationRatio ?? 0, maxChatsPerSummary: data.hypaV3Settings?.maxChatsPerSummary ?? 4, recentMemoryRatio: data.hypaV3Settings?.recentMemoryRatio ?? 0.4, similarMemoryRatio: data.hypaV3Settings?.similarMemoryRatio ?? 0.4, enableSimilarityCorrection: data.hypaV3Settings?.enableSimilarityCorrection ?? false, preserveOrphanedMemory: data.hypaV3Settings?.preserveOrphanedMemory ?? false, - processRegexScript: data.hypaV3Settings?.processRegexScript ?? false + processRegexScript: data.hypaV3Settings?.processRegexScript ?? false, + doNotSummarizeUserMessage: data.hypaV3Settings?.doNotSummarizeUserMessage ?? false } changeLanguage(data.language) setDatabaseLite(data) @@ -894,6 +895,7 @@ export interface Database{ enableSimilarityCorrection: boolean preserveOrphanedMemory: boolean processRegexScript: boolean + doNotSummarizeUserMessage: boolean }, OaiCompAPIKeys: {[key:string]:string} inlayErrorResponse:boolean diff --git a/src/ts/stores.svelte.ts b/src/ts/stores.svelte.ts index 3a20b4f2..6ac312ca 100644 --- a/src/ts/stores.svelte.ts +++ b/src/ts/stores.svelte.ts @@ -115,6 +115,7 @@ $effect.root(() => { DBState?.db?.characters?.[selIdState.selId]?.chats?.[DBState?.db?.characters?.[selIdState.selId]?.chatPage]?.modules?.length DBState?.db?.characters?.[selIdState.selId]?.hideChatIcon DBState?.db?.characters?.[selIdState.selId]?.backgroundHTML + DBState?.db?.moduleIntergration moduleUpdate() }) }) \ No newline at end of file diff --git a/src/ts/translator/translator.ts b/src/ts/translator/translator.ts index 542aae0b..1194ac4b 100644 --- a/src/ts/translator/translator.ts +++ b/src/ts/translator/translator.ts @@ -231,7 +231,9 @@ export async function translateHTML(html: string, reverse:boolean, charArg:simpl let DoingChat = get(doingChat) if(DoingChat){ if(isExpTranslator()){ - return html + if(!(db.translatorType === 'llm' && await getLLMCache(html) !== null)){ + return html + } } } if(db.translatorType === 'llm'){