diff --git a/apps/client/src/components/app_context.ts b/apps/client/src/components/app_context.ts index 8f6466e011..1c1389810a 100644 --- a/apps/client/src/components/app_context.ts +++ b/apps/client/src/components/app_context.ts @@ -101,8 +101,6 @@ export type CommandMappings = { showRevisions: CommandData & { noteId?: string | null; }; - showLlmChat: CommandData; - createAiChat: CommandData; showOptions: CommandData & { section: string; }; diff --git a/apps/client/src/components/root_command_executor.ts b/apps/client/src/components/root_command_executor.ts index 048aa4bd09..2aa5b90499 100644 --- a/apps/client/src/components/root_command_executor.ts +++ b/apps/client/src/components/root_command_executor.ts @@ -1,10 +1,8 @@ import dateNoteService from "../services/date_notes.js"; import froca from "../services/froca.js"; -import noteCreateService from "../services/note_create.js"; import openService from "../services/open.js"; import options from "../services/options.js"; import protectedSessionService from "../services/protected_session.js"; -import toastService from "../services/toast.js"; import treeService from "../services/tree.js"; import utils, { openInReusableSplit } from "../services/utils.js"; import appContext, { type CommandListenerData } from "./app_context.js"; @@ -248,34 +246,4 @@ export default class RootCommandExecutor extends Component { } } - async createAiChatCommand() { - try { - // Create a new AI Chat note at the root level - const rootNoteId = "root"; - - const result = await noteCreateService.createNote(rootNoteId, { - title: "New AI Chat", - type: "aiChat", - content: JSON.stringify({ - messages: [], - title: "New AI Chat" - }) - }); - - if (!result.note) { - toastService.showError("Failed to create AI Chat note"); - return; - } - - await appContext.tabManager.openTabWithNoteWithHoisting(result.note.noteId, { - activate: true - }); - - toastService.showMessage("Created new AI Chat note"); - } - catch (e) { - console.error("Error creating AI Chat note:", e); - toastService.showError(`Failed to create AI Chat note: ${(e as Error).message}`); - } - } } diff --git a/apps/client/src/entities/fnote.ts b/apps/client/src/entities/fnote.ts index 2b515f2412..07fc60ca31 100644 --- a/apps/client/src/entities/fnote.ts +++ b/apps/client/src/entities/fnote.ts @@ -18,7 +18,7 @@ const RELATION = "relation"; * end user. Those types should be used only for checking against, they are * not for direct use. */ -export type NoteType = "file" | "image" | "search" | "noteMap" | "launcher" | "doc" | "contentWidget" | "text" | "relationMap" | "render" | "canvas" | "mermaid" | "book" | "webView" | "code" | "mindMap" | "aiChat"; +export type NoteType = "file" | "image" | "search" | "noteMap" | "launcher" | "doc" | "contentWidget" | "text" | "relationMap" | "render" | "canvas" | "mermaid" | "book" | "webView" | "code" | "mindMap"; export interface NotePathRecord { isArchived: boolean; diff --git a/apps/client/src/services/in_app_help.ts b/apps/client/src/services/in_app_help.ts index 2f805783a3..46a89b0124 100644 --- a/apps/client/src/services/in_app_help.ts +++ b/apps/client/src/services/in_app_help.ts @@ -17,8 +17,7 @@ export const byNoteType: Record, string | null> = { render: null, search: null, text: null, - webView: null, - aiChat: null + webView: null }; export const byBookType: Record = { diff --git a/apps/client/src/services/note_types.ts b/apps/client/src/services/note_types.ts index 74b6f56659..48055c0548 100644 --- a/apps/client/src/services/note_types.ts +++ b/apps/client/src/services/note_types.ts @@ -53,7 +53,6 @@ export const NOTE_TYPES: NoteTypeMapping[] = [ { type: "file", title: t("note_types.file"), reserved: true }, { type: "image", title: t("note_types.image"), reserved: true }, { type: "launcher", mime: "", title: t("note_types.launcher"), reserved: true }, - { type: "aiChat", mime: "application/json", title: t("note_types.ai-chat"), reserved: true } ]; /** The maximum age in days for a template to be marked with the "New" badge */ diff --git a/apps/client/src/services/ws.ts b/apps/client/src/services/ws.ts index 488000ba16..1002abe9f7 100644 --- a/apps/client/src/services/ws.ts +++ b/apps/client/src/services/ws.ts @@ -133,49 +133,6 @@ async function handleMessage(event: MessageEvent) { appContext.triggerEvent("apiLogMessages", { noteId: message.noteId, messages: message.messages }); } else if (message.type === "toast") { toastService.showMessage(message.message); - } else if (message.type === "llm-stream") { - // ENHANCED LOGGING FOR DEBUGGING - console.log(`[WS-CLIENT] >>> RECEIVED LLM STREAM MESSAGE <<<`); - console.log(`[WS-CLIENT] Message details: sessionId=${message.sessionId}, hasContent=${!!message.content}, contentLength=${message.content ? message.content.length : 0}, hasThinking=${!!message.thinking}, hasToolExecution=${!!message.toolExecution}, isDone=${!!message.done}`); - - if (message.content) { - console.log(`[WS-CLIENT] CONTENT PREVIEW: "${message.content.substring(0, 50)}..."`); - } - - // Create the event with detailed logging - console.log(`[WS-CLIENT] Creating CustomEvent 'llm-stream-message'`); - const llmStreamEvent = new CustomEvent('llm-stream-message', { detail: message }); - - // Dispatch to multiple targets to ensure delivery - try { - console.log(`[WS-CLIENT] Dispatching event to window`); - window.dispatchEvent(llmStreamEvent); - console.log(`[WS-CLIENT] Event dispatched to window`); - - // Also try document for completeness - console.log(`[WS-CLIENT] Dispatching event to document`); - document.dispatchEvent(new CustomEvent('llm-stream-message', { detail: message })); - console.log(`[WS-CLIENT] Event dispatched to document`); - } catch (err) { - console.error(`[WS-CLIENT] Error dispatching event:`, err); - } - - // Debug current listeners (though we can't directly check for specific event listeners) - console.log(`[WS-CLIENT] Active event listeners should receive this message now`); - - // Detailed logging based on message type - if (message.content) { - console.log(`[WS-CLIENT] Content message: ${message.content.length} chars`); - } else if (message.thinking) { - console.log(`[WS-CLIENT] Thinking update: "${message.thinking}"`); - } else if (message.toolExecution) { - console.log(`[WS-CLIENT] Tool execution: action=${message.toolExecution.action}, tool=${message.toolExecution.tool || 'unknown'}`); - if (message.toolExecution.result) { - console.log(`[WS-CLIENT] Tool result preview: "${String(message.toolExecution.result).substring(0, 50)}..."`); - } - } else if (message.done) { - console.log(`[WS-CLIENT] Completion signal received`); - } } else if (message.type === "execute-script") { // TODO: Remove after porting the file // @ts-ignore diff --git a/apps/client/src/stylesheets/llm_chat.css b/apps/client/src/stylesheets/llm_chat.css deleted file mode 100644 index 5bb4ecd1b3..0000000000 --- a/apps/client/src/stylesheets/llm_chat.css +++ /dev/null @@ -1,450 +0,0 @@ -/* LLM Chat Panel Styles */ -.note-context-chat { - background-color: var(--main-background-color); -} - -/* Message Styling */ -.chat-message { - margin-bottom: 1rem; -} - -.message-avatar { - width: 36px; - height: 36px; - border-radius: 50%; - font-size: 1.25rem; - flex-shrink: 0; -} - -.user-avatar { - background-color: var(--input-background-color); - color: var(--cmd-button-icon-color); -} - -.assistant-avatar { - background-color: var(--subtle-border-color, var(--main-border-color)); - color: var(--hover-item-text-color); -} - -.message-content { - max-width: calc(100% - 50px); - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05); - color: var(--main-text-color); -} - -.user-content { - border-radius: 0.5rem 0.5rem 0 0.5rem !important; - background-color: var(--input-background-color) !important; -} - -.assistant-content { - border-radius: 0.5rem 0.5rem 0.5rem 0 !important; - background-color: var(--main-background-color); - border: 1px solid var(--subtle-border-color, var(--main-border-color)); -} - -/* Tool Execution Styling */ -.tool-execution-info { - margin-top: 0.75rem; - margin-bottom: 1.5rem; - border: 1px solid var(--subtle-border-color); - border-radius: 0.5rem; - overflow: hidden; - box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05); - background-color: var(--main-background-color); - /* Add a subtle transition effect */ - transition: all 0.2s ease-in-out; -} - -.tool-execution-status { - background-color: var(--accented-background-color, rgba(0, 0, 0, 0.03)) !important; - border-radius: 0 !important; - padding: 0.5rem !important; - max-height: 250px !important; - overflow-y: auto; -} - -.tool-execution-status .d-flex { - border-bottom: 1px solid var(--subtle-border-color); - padding-bottom: 0.5rem; - margin-bottom: 0.5rem; -} - -.tool-step { - padding: 0.5rem; - margin-bottom: 0.75rem; - border-radius: 0.375rem; - background-color: var(--main-background-color); - border: 1px solid var(--subtle-border-color); - transition: background-color 0.2s ease; -} - -.tool-step:hover { - background-color: rgba(0, 0, 0, 0.01); -} - -.tool-step:last-child { - margin-bottom: 0; -} - -/* Tool step specific styling */ -.tool-step.executing { - background-color: rgba(0, 123, 255, 0.05); - border-color: rgba(0, 123, 255, 0.2); -} - -.tool-step.result { - background-color: rgba(40, 167, 69, 0.05); - border-color: rgba(40, 167, 69, 0.2); -} - -.tool-step.error { - background-color: rgba(220, 53, 69, 0.05); - border-color: rgba(220, 53, 69, 0.2); -} - -/* Tool result formatting */ -.tool-result pre { - margin: 0.5rem 0; - padding: 0.5rem; - background-color: rgba(0, 0, 0, 0.03); - border-radius: 0.25rem; - overflow: auto; - max-height: 300px; -} - -.tool-result code { - font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace; - font-size: 0.9em; -} - -.tool-args code { - display: block; - padding: 0.5rem; - background-color: rgba(0, 0, 0, 0.03); - border-radius: 0.25rem; - margin-top: 0.25rem; - font-size: 0.85em; - color: var(--muted-text-color); - white-space: pre-wrap; - overflow: auto; - max-height: 100px; -} - -/* Tool Execution in Chat Styling */ -.chat-tool-execution { - padding: 0 0 0 36px; /* Aligned with message content, accounting for avatar width */ - width: 100%; - margin-bottom: 1rem; -} - -.tool-execution-container { - background-color: var(--accented-background-color, rgba(245, 247, 250, 0.7)); - border: 1px solid var(--subtle-border-color); - border-radius: 0.375rem; - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05); - overflow: hidden; - max-width: calc(100% - 20px); - transition: all 0.3s ease; -} - -.tool-execution-container.collapsed { - display: none; -} - -.tool-execution-header { - background-color: var(--main-background-color); - border-bottom: 1px solid var(--subtle-border-color); - margin-bottom: 0.5rem; - color: var(--muted-text-color); - font-weight: 500; - padding: 0.6rem 0.8rem; - cursor: pointer; - transition: background-color 0.2s ease; -} - -.tool-execution-header:hover { - background-color: var(--hover-item-background-color, rgba(0, 0, 0, 0.03)); -} - -.tool-execution-toggle { - color: var(--muted-text-color) !important; - background: transparent !important; - padding: 0.2rem 0.4rem !important; - transition: transform 0.2s ease; -} - -.tool-execution-toggle:hover { - color: var(--main-text-color) !important; -} - -.tool-execution-toggle i.bx-chevron-down { - transform: rotate(0deg); - transition: transform 0.3s ease; -} - -.tool-execution-toggle i.bx-chevron-right { - transform: rotate(-90deg); - transition: transform 0.3s ease; -} - -.tool-execution-chat-steps { - padding: 0.5rem; - max-height: 300px; - overflow-y: auto; -} - -/* Make error text more visible */ -.text-danger { - color: #dc3545 !important; -} - -/* Sources Styling */ -.sources-container { - background-color: var(--accented-background-color, var(--main-background-color)); - border-top: 1px solid var(--main-border-color); - color: var(--main-text-color); -} - -.source-item { - transition: all 0.2s ease; - background-color: var(--main-background-color); - border-color: var(--subtle-border-color, var(--main-border-color)) !important; -} - -.source-item:hover { - background-color: var(--link-hover-background, var(--hover-item-background-color)); -} - -.source-link { - color: var(--link-color, var(--hover-item-text-color)); - text-decoration: none; - display: block; - width: 100%; -} - -.source-link:hover { - color: var(--link-hover-color, var(--hover-item-text-color)); -} - -/* Input Area Styling */ -.note-context-chat-form { - background-color: var(--main-background-color); - border-top: 1px solid var(--main-border-color); -} - -.context-option-container { - padding: 0.5rem 0; - border-bottom: 1px solid var(--subtle-border-color, var(--main-border-color)); - color: var(--main-text-color); -} - -.chat-input-container { - padding-top: 0.5rem; -} - -.note-context-chat-input { - border-color: var(--subtle-border-color, var(--main-border-color)); - background-color: var(--input-background-color) !important; - color: var(--input-text-color) !important; - resize: none; - transition: all 0.2s ease; - min-height: 50px; - max-height: 150px; -} - -.note-context-chat-input:focus { - border-color: var(--input-focus-outline-color, var(--main-border-color)); - box-shadow: 0 0 0 0.25rem var(--input-focus-outline-color, rgba(13, 110, 253, 0.25)); -} - -.note-context-chat-send-button { - width: 40px; - height: 40px; - align-self: flex-end; - background-color: var(--cmd-button-background-color) !important; - color: var(--cmd-button-text-color) !important; -} - -/* Loading Indicator */ -.loading-indicator { - align-items: center; - justify-content: center; - padding: 1rem; - color: var(--muted-text-color); -} - -/* Thinking display styles */ -.llm-thinking-container { - margin: 1rem 0; - animation: fadeInUp 0.3s ease-out; -} - -.thinking-bubble { - background-color: var(--accented-background-color, var(--main-background-color)); - border: 1px solid var(--subtle-border-color, var(--main-border-color)); - border-radius: 0.75rem; - padding: 0.75rem; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05); - position: relative; - overflow: hidden; - transition: all 0.2s ease; -} - -.thinking-bubble:hover { - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08); -} - -.thinking-bubble::before { - content: ''; - position: absolute; - top: 0; - inset-inline-start: -100%; - width: 100%; - height: 100%; - background: linear-gradient(90deg, transparent, var(--hover-item-background-color, rgba(0, 0, 0, 0.03)), transparent); - animation: shimmer 2s infinite; - opacity: 0.5; -} - -.thinking-header { - cursor: pointer; - transition: all 0.2s ease; - border-radius: 0.375rem; -} - -.thinking-header:hover { - background-color: var(--hover-item-background-color, rgba(0, 0, 0, 0.03)); - padding: 0.25rem; - margin: -0.25rem; -} - -.thinking-dots { - display: flex; - gap: 3px; - align-items: center; -} - -.thinking-dots span { - width: 6px; - height: 6px; - background-color: var(--link-color, var(--hover-item-text-color)); - border-radius: 50%; - animation: thinkingPulse 1.4s infinite ease-in-out; -} - -.thinking-dots span:nth-child(1) { - animation-delay: -0.32s; -} - -.thinking-dots span:nth-child(2) { - animation-delay: -0.16s; -} - -.thinking-dots span:nth-child(3) { - animation-delay: 0s; -} - -.thinking-label { - font-weight: 500; - color: var(--link-color, var(--hover-item-text-color)) !important; -} - -.thinking-toggle { - color: var(--muted-text-color) !important; - transition: transform 0.2s ease; - background: transparent !important; - border: none !important; -} - -.thinking-toggle:hover { - color: var(--main-text-color) !important; -} - -.thinking-toggle.expanded { - transform: rotate(180deg); -} - -.thinking-content { - margin-top: 0.75rem; - padding-top: 0.75rem; - border-top: 1px solid var(--subtle-border-color, var(--main-border-color)); - animation: expandDown 0.3s ease-out; -} - -.thinking-text { - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif; - font-size: 0.875rem; - line-height: 1.5; - color: var(--main-text-color); - white-space: pre-wrap; - word-wrap: break-word; - background-color: var(--input-background-color); - padding: 0.75rem; - border-radius: 0.5rem; - border: 1px solid var(--subtle-border-color, var(--main-border-color)); - max-height: 300px; - overflow-y: auto; - transition: border-color 0.2s ease; -} - -.thinking-text:hover { - border-color: var(--main-border-color); -} - -/* Animations */ -@keyframes thinkingPulse { - 0%, 80%, 100% { - transform: scale(0.8); - opacity: 0.6; - } - 40% { - transform: scale(1); - opacity: 1; - } -} - -@keyframes shimmer { - 0% { - inset-inline-start: -100%; - } - 100% { - inset-inline-start: 100%; - } -} - -@keyframes fadeInUp { - from { - opacity: 0; - transform: translateY(10px); - } - to { - opacity: 1; - transform: translateY(0); - } -} - -@keyframes expandDown { - from { - opacity: 0; - max-height: 0; - } - to { - opacity: 1; - max-height: 300px; - } -} - -/* Responsive adjustments */ -@media (max-width: 768px) { - .thinking-bubble { - margin: 0.5rem 0; - padding: 0.5rem; - } - - .thinking-text { - font-size: 0.8rem; - padding: 0.5rem; - max-height: 200px; - } -} \ No newline at end of file diff --git a/apps/client/src/stylesheets/theme-next/llm-chat.css b/apps/client/src/stylesheets/theme-next/llm-chat.css deleted file mode 100644 index da5b478953..0000000000 --- a/apps/client/src/stylesheets/theme-next/llm-chat.css +++ /dev/null @@ -1,122 +0,0 @@ -/* LLM Chat Launcher Widget Styles */ -.note-context-chat { - display: flex; - flex-direction: column; - height: 100%; - width: 100%; -} - -.note-context-chat-container { - flex-grow: 1; - overflow-y: auto; - padding: 15px; -} - -.chat-message { - display: flex; - margin-bottom: 15px; - max-width: 85%; -} - -.chat-message.user-message { - margin-inline-start: auto; -} - -.chat-message.assistant-message { - margin-inline-end: auto; -} - -.message-avatar { - width: 32px; - height: 32px; - border-radius: 50%; - display: flex; - align-items: center; - justify-content: center; - margin-inline-end: 8px; -} - -.user-message .message-avatar { - background-color: var(--primary-color); - color: white; -} - -.assistant-message .message-avatar { - background-color: var(--secondary-color); - color: white; -} - -.message-content { - background-color: var(--more-accented-background-color); - border-radius: 12px; - padding: 10px 15px; - max-width: calc(100% - 40px); -} - -.user-message .message-content { - background-color: var(--accented-background-color); -} - -.message-content pre { - background-color: var(--code-background-color); - border-radius: 5px; - padding: 10px; - overflow-x: auto; - max-width: 100%; -} - -.message-content code { - background-color: var(--code-background-color); - padding: 2px 4px; - border-radius: 3px; -} - -.loading-indicator { - display: flex; - align-items: center; - margin: 10px 0; - color: var(--muted-text-color); -} - -.sources-container { - background-color: var(--accented-background-color); - border-top: 1px solid var(--main-border-color); - padding: 8px; -} - -.sources-list { - font-size: 0.9em; -} - -.source-item { - padding: 4px 0; -} - -.source-link { - color: var(--link-color); - text-decoration: none; -} - -.source-link:hover { - text-decoration: underline; -} - -.note-context-chat-form { - display: flex; - background-color: var(--main-background-color); - border-top: 1px solid var(--main-border-color); - padding: 10px; -} - -.note-context-chat-input { - resize: vertical; - min-height: 44px; - max-height: 200px; -} - -/* Responsive adjustments */ -@media (max-width: 768px) { - .chat-message { - max-width: 95%; - } -} \ No newline at end of file diff --git a/apps/client/src/translations/ar/translation.json b/apps/client/src/translations/ar/translation.json index 1321d6ebe1..3eaf9a8432 100644 --- a/apps/client/src/translations/ar/translation.json +++ b/apps/client/src/translations/ar/translation.json @@ -566,113 +566,6 @@ "enable-smooth-scroll": "تمكين التمرير السلس", "enable-motion": "تمكين الانتقالات والرسوم المتحركة" }, - "ai_llm": { - "progress": "تقدم", - "openai_tab": "OpenAI", - "actions": "أجراءات", - "retry": "أعد المحاولة", - "reprocessing_index": "جار اعادة البناء...", - "never": "ابدٱ", - "agent": { - "processing": "جار المعالجة...", - "thinking": "جار التفكير...", - "loading": "جار التحميل...", - "generating": "جار الانشاء..." - }, - "name": "الذكاء الأصطناعي", - "openai": "OpenAI", - "sources": "مصادر", - "temperature": "درجة الحرارة", - "model": "نموذج", - "refreshing_models": "جار التحديث...", - "error": "خطأ", - "refreshing": "جار التحديث...", - "ollama_tab": "Ollama", - "anthropic_tab": "انتروبيك", - "not_started": "لم يبدأ بعد", - "title": "اعدادات AI", - "processed_notes": "الملاحظات المعالجة", - "total_notes": "الملاحظات الكلية", - "queued_notes": "الملاحظات في قائمة الانتظار", - "failed_notes": "الملاحظات الفاشلة", - "last_processed": "اخر معالجة", - "refresh_stats": "تحديث الاحصائيات", - "voyage_tab": "استكشاف AI", - "provider_precedence": "اولوية المزود", - "system_prompt": "موجه النظام", - "openai_configuration": "اعدادات OpenAI", - "openai_settings": "اعدادات OpenAI", - "api_key": "مفتاح واجهة برمجة التطبيقات", - "url": "عنوان URL الاساسي", - "default_model": "النموذج الافتراضي", - "base_url": "عنوان URL الأساسي", - "openai_url_description": "افتراضيا: https://api.openai.com/v1", - "anthropic_settings": "اعدادات انتروبيك", - "ollama_settings": "اعدادات Ollama", - "anthropic_configuration": "تهيئة انتروبيك", - "voyage_url_description": "افتراضيا: https://api.voyageai.com/v1", - "ollama_configuration": "تهيئة Ollama", - "enable_ollama": "تمكين Ollama", - "last_attempt": "اخر محاولة", - "active_providers": "المزودون النشطون", - "disabled_providers": "المزودون المعطلون", - "similarity_threshold": "عتبة التشابه", - "complete": "اكتمل (100%)", - "ai_settings": "اعدادات AI", - "show_thinking": "عرض التفكير", - "index_status": "حالة الفهرس", - "indexed_notes": "الملاحظات المفهرسة", - "indexing_stopped": "تم ايقاف الفهرسة", - "last_indexed": "اخر فهرسة", - "note_chat": "دردشة الملاحظة", - "start_indexing": "بدء الفهرسة", - "chat": { - "root_note_title": "دردشات AI", - "new_chat_title": "دردشة جديدة", - "create_new_ai_chat": "انشاء دردشة AI جديدة" - }, - "selected_provider": "المزود المحدد", - "select_model": "اختر النموذج...", - "select_provider": "اختر المزود...", - "ollama_model": "نموذج Ollama", - "refresh_models": "تحديث النماذج", - "rebuild_index": "اعادة بناء الفهرس", - "note_title": "عنوان الملاحظة", - "processing": "جاري المعالجة ({{percentage}}%)", - "incomplete": "غير مكتمل ({{percentage}}%)", - "ollama_url": "عنوان URL الخاص ب Ollama", - "provider_configuration": "تكوين موفر AI", - "voyage_settings": "استكشاف اعدادات AI", - "enable_automatic_indexing": "تمكين الفهرسة التلقائية", - "index_rebuild_progress": "تقدم اعادة انشاء الفهرس", - "index_rebuild_complete": "اكتملت عملية تحسين الفهرس", - "use_enhanced_context": "استخدام السياق المحسن", - "enter_message": "ادخل رسالتك...", - "index_all_notes": "فهرسة جميع الملاحظات", - "indexing_in_progress": "جار فهرسة الملاحظات...", - "use_advanced_context": "استخدم السياق المتقدم", - "ai_enabled": "تمكين مميزات AI", - "ai_disabled": "الغاء تمكين مميزات AI", - "enable_ai_features": "تمكين خصائص AI/LLM", - "enable_ai": "تمكين خصائص AI/LLM", - "reprocess_index": "اعادة بناء فهرس البحث", - "index_rebuilding": "جار تحسين الفهرس {{percentage}}", - "voyage_configuration": "اعدادت Voyage AI", - "openai_model_description": "الامثلة: gpt-4o, gpt-4-turbo, gpt-3.5-turbo", - "partial": "{{ percentage }} % مكتمل", - "retry_queued": "تم جدولة الملاحظة لاعادة المحاولة", - "max_notes_per_llm_query": "اكبر عدد للملاحظات لكل استعلام", - "remove_provider": "احذف المزود من البحث", - "restore_provider": "استعادة المزود الى البحث", - "reprocess_index_error": "حدث خطأ اثناء اعادة بناء فهرس البحث", - "auto_refresh_notice": "تحديث تلقائي كل {{seconds}} ثانية", - "note_queued_for_retry": "الملاحظة جاهزة لاعادة المحاولة لاحقا", - "failed_to_retry_note": "‎فشل في اعادة محاولة معالجة المحاولة", - "failed_to_retry_all": "فشل في اعادة محاولة معالجة الملاحظة", - "error_generating_response": "‌فشل في توليد استجابة من ال AI", - "create_new_ai_chat": "انشاء دردشة AI جديدة", - "error_fetching": "فشل في استرجاع النماذج: {{error}}" - }, "code_auto_read_only_size": { "unit": "حروف", "title": "الحجم التلقائي للقراءه فقط" @@ -910,7 +803,6 @@ "web-view": "عرض الويب", "mind-map": "خريطة ذهنية", "geo-map": "خريطة جغرافية", - "ai-chat": "دردشة AI", "task-list": "قائمة المهام" }, "shared_switch": { diff --git a/apps/client/src/translations/cn/translation.json b/apps/client/src/translations/cn/translation.json index 58ac0f748a..9b27037faf 100644 --- a/apps/client/src/translations/cn/translation.json +++ b/apps/client/src/translations/cn/translation.json @@ -1532,7 +1532,6 @@ "geo-map": "地理地图", "beta-feature": "测试版", "task-list": "任务列表", - "ai-chat": "AI聊天", "new-feature": "新建", "collections": "集合", "book": "集合" @@ -1839,149 +1838,6 @@ "yesterday": "昨天" } }, - "ai_llm": { - "not_started": "未开始", - "title": "AI设置", - "processed_notes": "已处理笔记", - "total_notes": "笔记总数", - "progress": "进度", - "queued_notes": "排队中笔记", - "failed_notes": "失败笔记", - "last_processed": "最后处理时间", - "refresh_stats": "刷新统计数据", - "enable_ai_features": "启用AI/LLM功能", - "enable_ai_description": "启用笔记摘要、内容生成等AI功能及其他LLM能力", - "openai_tab": "OpenAI", - "anthropic_tab": "Anthropic", - "voyage_tab": "Voyage AI", - "ollama_tab": "Ollama", - "enable_ai": "启用AI/LLM功能", - "enable_ai_desc": "启用笔记摘要、内容生成等AI功能及其他LLM能力", - "provider_configuration": "AI提供商配置", - "provider_precedence": "提供商优先级", - "provider_precedence_description": "按优先级排序的提供商列表(用逗号分隔,例如:'openai,anthropic,ollama')", - "temperature": "温度参数", - "temperature_description": "控制响应的随机性(0 = 确定性输出,2 = 最大随机性)", - "system_prompt": "系统提示词", - "system_prompt_description": "所有AI交互使用的默认系统提示词", - "openai_configuration": "OpenAI配置", - "openai_settings": "OpenAI设置", - "api_key": "API密钥", - "url": "基础URL", - "model": "模型", - "openai_api_key_description": "用于访问OpenAI服务的API密钥", - "anthropic_api_key_description": "用于访问Claude模型的Anthropic API密钥", - "default_model": "默认模型", - "openai_model_description": "示例:gpt-4o、gpt-4-turbo、gpt-3.5-turbo", - "base_url": "基础URL", - "openai_url_description": "默认:https://api.openai.com/v1", - "anthropic_settings": "Anthropic设置", - "anthropic_url_description": "Anthropic API的基础URL(默认:https://api.anthropic.com)", - "anthropic_model_description": "用于聊天补全的Anthropic Claude模型", - "voyage_settings": "Voyage AI设置", - "ollama_settings": "Ollama设置", - "ollama_url_description": "Ollama API的URL(默认:http://localhost:11434)", - "ollama_model_description": "用于聊天补全的 Ollama 模型", - "anthropic_configuration": "Anthropic配置", - "voyage_configuration": "Voyage AI配置", - "voyage_url_description": "默认:https://api.voyageai.com/v1", - "ollama_configuration": "Ollama配置", - "enable_ollama": "启用Ollama", - "enable_ollama_description": "启用Ollama以使用本地AI模型", - "ollama_url": "Ollama URL", - "ollama_model": "Ollama模型", - "refresh_models": "刷新模型", - "refreshing_models": "刷新中...", - "enable_automatic_indexing": "启用自动索引", - "rebuild_index": "重建索引", - "rebuild_index_error": "启动索引重建失败。请查看日志了解详情。", - "note_title": "笔记标题", - "error": "错误", - "last_attempt": "最后尝试时间", - "actions": "操作", - "retry": "重试", - "partial": "{{ percentage }}% 已完成", - "retry_queued": "笔记已加入重试队列", - "retry_failed": "笔记加入重试队列失败", - "max_notes_per_llm_query": "每次查询的最大笔记数", - "max_notes_per_llm_query_description": "AI上下文包含的最大相似笔记数量", - "active_providers": "活跃提供商", - "disabled_providers": "已禁用提供商", - "remove_provider": "从搜索中移除提供商", - "restore_provider": "将提供商恢复到搜索中", - "similarity_threshold": "相似度阈值", - "similarity_threshold_description": "纳入LLM查询上下文的笔记最低相似度分数(0-1)", - "reprocess_index": "重建搜索索引", - "reprocessing_index": "重建中...", - "reprocess_index_started": "搜索索引优化已在后台启动", - "reprocess_index_error": "重建搜索索引失败", - "index_rebuild_progress": "索引重建进度", - "index_rebuilding": "正在优化索引({{percentage}}%)", - "index_rebuild_complete": "索引优化完成", - "index_rebuild_status_error": "检查索引重建状态失败", - "never": "从未", - "processing": "处理中({{percentage}}%)", - "incomplete": "未完成({{percentage}}%)", - "complete": "已完成(100%)", - "refreshing": "刷新中...", - "auto_refresh_notice": "每 {{seconds}} 秒自动刷新", - "note_queued_for_retry": "笔记已加入重试队列", - "failed_to_retry_note": "重试笔记失败", - "all_notes_queued_for_retry": "所有失败笔记已加入重试队列", - "failed_to_retry_all": "重试笔记失败", - "ai_settings": "AI设置", - "api_key_tooltip": "用于访问服务的API密钥", - "empty_key_warning": { - "anthropic": "Anthropic API密钥为空。请输入有效的API密钥。", - "openai": "OpenAI API密钥为空。请输入有效的API密钥。", - "voyage": "Voyage API密钥为空。请输入有效的API密钥。", - "ollama": "Ollama API密钥为空。请输入有效的API密钥。" - }, - "agent": { - "processing": "处理中...", - "thinking": "思考中...", - "loading": "加载中...", - "generating": "生成中..." - }, - "name": "AI", - "openai": "OpenAI", - "use_enhanced_context": "使用增强上下文", - "enhanced_context_description": "为AI提供来自笔记及其相关笔记的更多上下文,以获得更好的响应", - "show_thinking": "显示思考过程", - "show_thinking_description": "显示AI的思维链过程", - "enter_message": "输入你的消息...", - "error_contacting_provider": "联系AI提供商失败。请检查你的设置和网络连接。", - "error_generating_response": "生成AI响应失败", - "index_all_notes": "为所有笔记建立索引", - "index_status": "索引状态", - "indexed_notes": "已索引笔记", - "indexing_stopped": "索引已停止", - "indexing_in_progress": "索引进行中...", - "last_indexed": "最后索引时间", - "note_chat": "笔记聊天", - "sources": "来源", - "start_indexing": "开始索引", - "use_advanced_context": "使用高级上下文", - "ollama_no_url": "Ollama 未配置。请输入有效的URL。", - "chat": { - "root_note_title": "AI聊天记录", - "root_note_content": "此笔记包含你保存的AI聊天对话。", - "new_chat_title": "新聊天", - "create_new_ai_chat": "创建新的AI聊天" - }, - "create_new_ai_chat": "创建新的AI聊天", - "configuration_warnings": "你的AI配置存在一些问题。请检查你的设置。", - "experimental_warning": "LLM功能目前处于实验阶段 - 特此提醒。", - "selected_provider": "已选提供商", - "selected_provider_description": "选择用于聊天和补全功能的AI提供商", - "select_model": "选择模型...", - "select_provider": "选择提供商...", - "ai_enabled": "已启用 AI 功能", - "ai_disabled": "已禁用 AI 功能", - "no_models_found_online": "找不到模型。请检查您的 API 密钥及设置。", - "no_models_found_ollama": "找不到 Ollama 模型。请确认 Ollama 是否正在运行。", - "error_fetching": "获取模型失败:{{error}}" - }, "code-editor-options": { "title": "编辑器" }, diff --git a/apps/client/src/translations/de/translation.json b/apps/client/src/translations/de/translation.json index ff7cdce5da..b90f5f8ebf 100644 --- a/apps/client/src/translations/de/translation.json +++ b/apps/client/src/translations/de/translation.json @@ -1795,149 +1795,6 @@ "close": "Schließen", "help_title": "Zeige mehr Informationen zu diesem Fenster" }, - "ai_llm": { - "not_started": "Nicht gestartet", - "title": "KI Einstellungen", - "processed_notes": "Verarbeitete Notizen", - "total_notes": "Gesamt Notizen", - "progress": "Fortschritt", - "queued_notes": "Eingereihte Notizen", - "failed_notes": "Fehlgeschlagenen Notizen", - "last_processed": "Zuletzt verarbeitet", - "refresh_stats": "Statistiken neu laden", - "enable_ai_features": "Aktiviere KI/LLM Funktionen", - "enable_ai_description": "Aktiviere KI-Funktionen wie Notizzusammenfassungen, Inhaltserzeugung und andere LLM-Funktionen", - "openai_tab": "OpenAI", - "anthropic_tab": "Anthropic", - "voyage_tab": "Voyage AI", - "ollama_tab": "Ollama", - "enable_ai": "Aktiviere KI/LLM Funktionen", - "enable_ai_desc": "Aktiviere KI-Funktionen wie Notizzusammenfassungen, Inhaltserzeugung und andere LLM-Funktionen", - "provider_configuration": "KI-Anbieterkonfiguration", - "provider_precedence": "Anbieter Priorität", - "provider_precedence_description": "Komma-getrennte Liste von Anbieter in der Reihenfolge ihrer Priorität (z.B. 'openai, anthropic,ollama')", - "temperature": "Temperatur", - "temperature_description": "Regelt die Zufälligkeit in Antworten (0 = deterministisch, 2 = maximale Zufälligkeit)", - "system_prompt": "Systemaufforderung", - "system_prompt_description": "Standard Systemaufforderung für alle KI-Interaktionen", - "openai_configuration": "OpenAI Konfiguration", - "openai_settings": "OpenAI Einstellungen", - "api_key": "API Schlüssel", - "url": "Basis-URL", - "model": "Modell", - "anthropic_settings": "Anthropic Einstellungen", - "partial": "{{ percentage }}% verarbeitet", - "anthropic_api_key_description": "Dein Anthropic API-Key für den Zugriff auf Claude Modelle", - "anthropic_model_description": "Anthropic Claude Modell für Chat-Vervollständigung", - "voyage_settings": "Einstellungen für Voyage AI", - "ollama_url_description": "URL für die Ollama API (Standard: http://localhost:11434)", - "ollama_model_description": "Ollama Modell für Chat-Vervollständigung", - "anthropic_configuration": "Anthropic Konfiguration", - "voyage_configuration": "Voyage AI Konfiguration", - "voyage_url_description": "Standard: https://api.voyageai.com/v1", - "ollama_configuration": "Ollama Konfiguration", - "enable_ollama": "Aktiviere Ollama", - "enable_ollama_description": "Aktiviere Ollama für lokale KI Modell Nutzung", - "ollama_url": "Ollama URL", - "ollama_model": "Ollama Modell", - "refresh_models": "Aktualisiere Modelle", - "refreshing_models": "Aktualisiere...", - "enable_automatic_indexing": "Aktiviere automatische Indizierung", - "rebuild_index": "Index neu aufbauen", - "rebuild_index_error": "Fehler beim Neuaufbau des Index. Prüfe Log für mehr Informationen.", - "retry_failed": "Fehler: Notiz konnte nicht erneut eingereiht werden", - "max_notes_per_llm_query": "Max. Notizen je Abfrage", - "max_notes_per_llm_query_description": "Maximale Anzahl ähnlicher Notizen zum Einbinden als KI Kontext", - "active_providers": "Aktive Anbieter", - "disabled_providers": "Inaktive Anbieter", - "remove_provider": "Entferne Anbieter von Suche", - "restore_provider": "Anbieter zur Suche wiederherstellen", - "similarity_threshold": "Ähnlichkeitsschwelle", - "similarity_threshold_description": "Mindestähnlichkeitswert (0-1) für Notizen, die im Kontext für LLM-Abfragen berücksichtigt werden sollen", - "reprocess_index": "Suchindex neu erstellen", - "reprocessing_index": "Neuerstellung...", - "reprocess_index_started": "Suchindex-Optimierung wurde im Hintergrund gestartet", - "reprocess_index_error": "Fehler beim Wiederaufbau des Suchindex", - "index_rebuild_progress": "Fortschritt der Index-Neuerstellung", - "index_rebuilding": "Optimierung Index ({{percentage}}%)", - "index_rebuild_complete": "Index Optimierung abgeschlossen", - "index_rebuild_status_error": "Fehler bei Überprüfung Status Index Neuerstellung", - "never": "Niemals", - "processing": "Verarbeitung ({{percentage}}%)", - "refreshing": "Aktualisiere...", - "incomplete": "Unvollständig ({{percentage}}%)", - "complete": "Abgeschlossen (100%)", - "auto_refresh_notice": "Auto-Aktualisierung alle {{seconds}} Sekunden", - "note_queued_for_retry": "Notiz in Warteschlange für erneuten Versuch hinzugefügt", - "failed_to_retry_note": "Wiederholungsversuch fehlgeschlagen für Notiz", - "ai_settings": "KI Einstellungen", - "agent": { - "processing": "Verarbeite...", - "thinking": "Nachdenken...", - "loading": "Lade...", - "generating": "Generiere..." - }, - "name": "KI", - "openai": "OpenAI", - "use_enhanced_context": "Benutze verbesserten Kontext", - "openai_api_key_description": "Dein OpenAPI-Key für den Zugriff auf den KI-Dienst", - "default_model": "Standardmodell", - "openai_model_description": "Beispiele: gpt-4o, gpt-4-turbo, gpt-3.5-turbo", - "base_url": "Basis URL", - "openai_url_description": "Standard: https://api.openai.com/v1", - "anthropic_url_description": "Basis URL für Anthropic API (Standard: https://api.anthropic.com)", - "ollama_settings": "Ollama Einstellungen", - "note_title": "Notiz Titel", - "error": "Fehler", - "last_attempt": "Letzter Versuch", - "actions": "Aktionen", - "retry": "Erneut versuchen", - "retry_queued": "Notiz für weiteren Versuch eingereiht", - "empty_key_warning": { - "anthropic": "Anthropic API-Key ist leer. Bitte gültigen API-Key eingeben.", - "openai": "OpenAI API-Key ist leer. Bitte gültigen API-Key eingeben.", - "voyage": "Voyage API-Key ist leer. Bitte gültigen API-Key eingeben.", - "ollama": "Ollama API-Key ist leer. Bitte gültigen API-Key eingeben." - }, - "api_key_tooltip": "API-Key für den Zugriff auf den Dienst", - "failed_to_retry_all": "Wiederholungsversuch für Notizen fehlgeschlagen", - "all_notes_queued_for_retry": "Alle fehlgeschlagenen Notizen wurden zur Wiederholung in die Warteschlange gestellt", - "enhanced_context_description": "Versorgt die KI mit mehr Kontext aus der Notiz und den zugehörigen Notizen, um bessere Antworten zu ermöglichen", - "show_thinking": "Zeige Denkprozess", - "show_thinking_description": "Zeige den Denkprozess der KI", - "enter_message": "Geben Sie Ihre Nachricht ein...", - "error_contacting_provider": "Fehler beim Kontaktieren des KI-Anbieters. Bitte überprüfe die Einstellungen und die Internetverbindung.", - "error_generating_response": "Fehler beim Generieren der KI Antwort", - "index_all_notes": "Indiziere alle Notizen", - "index_status": "Indizierungsstatus", - "indexed_notes": "Indizierte Notizen", - "indexing_stopped": "Indizierung gestoppt", - "indexing_in_progress": "Indizierung in Bearbeitung...", - "last_indexed": "Zuletzt Indiziert", - "note_chat": "Notizen-Chat", - "sources": "Quellen", - "start_indexing": "Starte Indizierung", - "use_advanced_context": "Benutze erweiterten Kontext", - "ollama_no_url": "Ollama ist nicht konfiguriert. Bitte trage eine gültige URL ein.", - "chat": { - "root_note_title": "KI Chats", - "root_note_content": "Diese Notiz enthält gespeicherte KI-Chat-Unterhaltungen.", - "new_chat_title": "Neuer Chat", - "create_new_ai_chat": "Erstelle neuen KI Chat" - }, - "create_new_ai_chat": "Erstelle neuen KI Chat", - "configuration_warnings": "Es wurden Probleme mit der KI Konfiguration festgestellt. Bitte überprüfe die Einstellungen.", - "experimental_warning": "Die LLM-Funktionen sind aktuell experimentell - sei an dieser Stelle gewarnt.", - "selected_provider": "Ausgewählter Anbieter", - "selected_provider_description": "Wähle einen KI-Anbieter für Chat- und Vervollständigungsfunktionen", - "select_model": "Wähle Modell...", - "select_provider": "Wähle Anbieter...", - "ai_enabled": "KI Funktionen aktiviert", - "ai_disabled": "KI Funktionen deaktiviert", - "no_models_found_online": "Keine Modelle gefunden. Bitte überprüfe den API-Key und die Einstellungen.", - "no_models_found_ollama": "Kein Ollama Modell gefunden. Bitte prüfe, ob Ollama gerade läuft.", - "error_fetching": "Fehler beim Abrufen der Modelle: {{error}}" - }, "zen_mode": { "button_exit": "Verlasse Zen Modus" }, diff --git a/apps/client/src/translations/en-GB/translation.json b/apps/client/src/translations/en-GB/translation.json index aedd8f2a99..bdadeda59c 100644 --- a/apps/client/src/translations/en-GB/translation.json +++ b/apps/client/src/translations/en-GB/translation.json @@ -47,11 +47,6 @@ "attachment_detail_2": { "unrecognized_role": "Unrecognised attachment role '{{role}}'." }, - "ai_llm": { - "reprocess_index_started": "Search index optimisation started in the background", - "index_rebuilding": "Optimising index ({{percentage}}%)", - "index_rebuild_complete": "Index optimisation complete" - }, "highlighting": { "color-scheme": "Colour Scheme" }, diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json index 4b90a913f8..04a372ddae 100644 --- a/apps/client/src/translations/en/translation.json +++ b/apps/client/src/translations/en/translation.json @@ -1204,149 +1204,6 @@ "enable-smooth-scroll": "Enable smooth scrolling", "app-restart-required": "(a restart of the application is required for the change to take effect)" }, - "ai_llm": { - "not_started": "Not started", - "title": "AI Settings", - "processed_notes": "Processed Notes", - "total_notes": "Total Notes", - "progress": "Progress", - "queued_notes": "Queued Notes", - "failed_notes": "Failed Notes", - "last_processed": "Last Processed", - "refresh_stats": "Refresh Statistics", - "enable_ai_features": "Enable AI/LLM features", - "enable_ai_description": "Enable AI features like note summarization, content generation, and other LLM capabilities", - "openai_tab": "OpenAI", - "anthropic_tab": "Anthropic", - "voyage_tab": "Voyage AI", - "ollama_tab": "Ollama", - "enable_ai": "Enable AI/LLM features", - "enable_ai_desc": "Enable AI features like note summarization, content generation, and other LLM capabilities", - "provider_configuration": "AI Provider Configuration", - "provider_precedence": "Provider Precedence", - "provider_precedence_description": "Comma-separated list of providers in order of precedence (e.g., 'openai,anthropic,ollama')", - "temperature": "Temperature", - "temperature_description": "Controls randomness in responses (0 = deterministic, 2 = maximum randomness)", - "system_prompt": "System Prompt", - "system_prompt_description": "Default system prompt used for all AI interactions", - "openai_configuration": "OpenAI Configuration", - "openai_settings": "OpenAI Settings", - "api_key": "API Key", - "url": "Base URL", - "model": "Model", - "openai_api_key_description": "Your OpenAI API key for accessing their AI services", - "anthropic_api_key_description": "Your Anthropic API key for accessing Claude models", - "default_model": "Default Model", - "openai_model_description": "Examples: gpt-4o, gpt-4-turbo, gpt-3.5-turbo", - "base_url": "Base URL", - "openai_url_description": "Default: https://api.openai.com/v1", - "anthropic_settings": "Anthropic Settings", - "anthropic_url_description": "Base URL for the Anthropic API (default: https://api.anthropic.com)", - "anthropic_model_description": "Anthropic Claude models for chat completion", - "voyage_settings": "Voyage AI Settings", - "ollama_settings": "Ollama Settings", - "ollama_url_description": "URL for the Ollama API (default: http://localhost:11434)", - "ollama_model_description": "Ollama model to use for chat completion", - "anthropic_configuration": "Anthropic Configuration", - "voyage_configuration": "Voyage AI Configuration", - "voyage_url_description": "Default: https://api.voyageai.com/v1", - "ollama_configuration": "Ollama Configuration", - "enable_ollama": "Enable Ollama", - "enable_ollama_description": "Enable Ollama for local AI model usage", - "ollama_url": "Ollama URL", - "ollama_model": "Ollama Model", - "refresh_models": "Refresh Models", - "refreshing_models": "Refreshing...", - "enable_automatic_indexing": "Enable Automatic Indexing", - "rebuild_index": "Rebuild Index", - "rebuild_index_error": "Error starting index rebuild. Check logs for details.", - "note_title": "Note Title", - "error": "Error", - "last_attempt": "Last Attempt", - "actions": "Actions", - "retry": "Retry", - "partial": "{{ percentage }}% completed", - "retry_queued": "Note queued for retry", - "retry_failed": "Failed to queue note for retry", - "max_notes_per_llm_query": "Max Notes Per Query", - "max_notes_per_llm_query_description": "Maximum number of similar notes to include in AI context", - "active_providers": "Active Providers", - "disabled_providers": "Disabled Providers", - "remove_provider": "Remove provider from search", - "restore_provider": "Restore provider to search", - "similarity_threshold": "Similarity Threshold", - "similarity_threshold_description": "Minimum similarity score (0-1) for notes to be included in context for LLM queries", - "reprocess_index": "Rebuild Search Index", - "reprocessing_index": "Rebuilding...", - "reprocess_index_started": "Search index optimization started in the background", - "reprocess_index_error": "Error rebuilding search index", - "index_rebuild_progress": "Index Rebuild Progress", - "index_rebuilding": "Optimizing index ({{percentage}}%)", - "index_rebuild_complete": "Index optimization complete", - "index_rebuild_status_error": "Error checking index rebuild status", - "never": "Never", - "processing": "Processing ({{percentage}}%)", - "incomplete": "Incomplete ({{percentage}}%)", - "complete": "Complete (100%)", - "refreshing": "Refreshing...", - "auto_refresh_notice": "Auto-refreshes every {{seconds}} seconds", - "note_queued_for_retry": "Note queued for retry", - "failed_to_retry_note": "Failed to retry note", - "all_notes_queued_for_retry": "All failed notes queued for retry", - "failed_to_retry_all": "Failed to retry notes", - "ai_settings": "AI Settings", - "api_key_tooltip": "API key for accessing the service", - "empty_key_warning": { - "anthropic": "Anthropic API key is empty. Please enter a valid API key.", - "openai": "OpenAI API key is empty. Please enter a valid API key.", - "voyage": "Voyage API key is empty. Please enter a valid API key.", - "ollama": "Ollama API key is empty. Please enter a valid API key." - }, - "agent": { - "processing": "Processing...", - "thinking": "Thinking...", - "loading": "Loading...", - "generating": "Generating..." - }, - "name": "AI", - "openai": "OpenAI", - "use_enhanced_context": "Use enhanced context", - "enhanced_context_description": "Provides the AI with more context from the note and its related notes for better responses", - "show_thinking": "Show thinking", - "show_thinking_description": "Show the AI's chain of thought process", - "enter_message": "Enter your message...", - "error_contacting_provider": "Error contacting AI provider. Please check your settings and internet connection.", - "error_generating_response": "Error generating AI response", - "index_all_notes": "Index All Notes", - "index_status": "Index Status", - "indexed_notes": "Indexed Notes", - "indexing_stopped": "Indexing stopped", - "indexing_in_progress": "Indexing in progress...", - "last_indexed": "Last Indexed", - "note_chat": "Note Chat", - "sources": "Sources", - "start_indexing": "Start Indexing", - "use_advanced_context": "Use Advanced Context", - "ollama_no_url": "Ollama is not configured. Please enter a valid URL.", - "chat": { - "root_note_title": "AI Chats", - "root_note_content": "This note contains your saved AI chat conversations.", - "new_chat_title": "New Chat", - "create_new_ai_chat": "Create new AI Chat" - }, - "create_new_ai_chat": "Create new AI Chat", - "configuration_warnings": "There are some issues with your AI configuration. Please check your settings.", - "experimental_warning": "The LLM feature is currently experimental - you have been warned.", - "selected_provider": "Selected Provider", - "selected_provider_description": "Choose the AI provider for chat and completion features", - "select_model": "Select model...", - "select_provider": "Select provider...", - "ai_enabled": "AI features enabled", - "ai_disabled": "AI features disabled", - "no_models_found_online": "No models found. Please check your API key and settings.", - "no_models_found_ollama": "No Ollama models found. Please check if Ollama is running.", - "error_fetching": "Error fetching models: {{error}}" - }, "zoom_factor": { "title": "Zoom Factor (desktop build only)", "description": "Zooming can be controlled with CTRL+- and CTRL+= shortcuts as well." diff --git a/apps/client/src/translations/es/translation.json b/apps/client/src/translations/es/translation.json index a583bc2b0d..7f4538c6d5 100644 --- a/apps/client/src/translations/es/translation.json +++ b/apps/client/src/translations/es/translation.json @@ -1175,149 +1175,6 @@ "light_theme": "Heredado (Claro)", "dark_theme": "Heredado (Oscuro)" }, - "ai_llm": { - "not_started": "No iniciado", - "title": "IA y ajustes de embeddings", - "processed_notes": "Notas procesadas", - "total_notes": "Notas totales", - "progress": "Progreso", - "queued_notes": "Notas en fila", - "failed_notes": "Notas fallidas", - "last_processed": "Última procesada", - "refresh_stats": "Recargar estadísticas", - "enable_ai_features": "Habilitar características IA/LLM", - "enable_ai_description": "Habilitar características de IA como resumen de notas, generación de contenido y otras capacidades LLM", - "openai_tab": "OpenAI", - "anthropic_tab": "Anthropic", - "voyage_tab": "Voyage AI", - "ollama_tab": "Ollama", - "enable_ai": "Habilitar características IA/LLM", - "enable_ai_desc": "Habilitar características de IA como resumen de notas, generación de contenido y otras capacidades LLM", - "provider_configuration": "Configuración de proveedor de IA", - "provider_precedence": "Precedencia de proveedor", - "provider_precedence_description": "Lista de proveedores en orden de precedencia separada por comas (p.e., 'openai,anthropic,ollama')", - "temperature": "Temperatura", - "temperature_description": "Controla la aleatoriedad de las respuestas (0 = determinista, 2 = aleatoriedad máxima)", - "system_prompt": "Mensaje de sistema", - "system_prompt_description": "Mensaje de sistema predeterminado utilizado para todas las interacciones de IA", - "openai_configuration": "Configuración de OpenAI", - "openai_settings": "Ajustes de OpenAI", - "api_key": "Clave API", - "url": "URL base", - "model": "Modelo", - "openai_api_key_description": "Tu clave API de OpenAI para acceder a sus servicios de IA", - "anthropic_api_key_description": "Tu clave API de Anthropic para acceder a los modelos Claude", - "default_model": "Modelo por defecto", - "openai_model_description": "Ejemplos: gpt-4o, gpt-4-turbo, gpt-3.5-turbo", - "base_url": "URL base", - "openai_url_description": "Por defecto: https://api.openai.com/v1", - "anthropic_settings": "Ajustes de Anthropic", - "anthropic_url_description": "URL base para la API de Anthropic (por defecto: https://api.anthropic.com)", - "anthropic_model_description": "Modelos Claude de Anthropic para el completado de chat", - "voyage_settings": "Ajustes de Voyage AI", - "ollama_settings": "Ajustes de Ollama", - "ollama_url_description": "URL para la API de Ollama (por defecto: http://localhost:11434)", - "ollama_model_description": "Modelo de Ollama a usar para el completado de chat", - "anthropic_configuration": "Configuración de Anthropic", - "voyage_configuration": "Configuración de Voyage AI", - "voyage_url_description": "Por defecto: https://api.voyageai.com/v1", - "ollama_configuration": "Configuración de Ollama", - "enable_ollama": "Habilitar Ollama", - "enable_ollama_description": "Habilitar Ollama para uso de modelo de IA local", - "ollama_url": "URL de Ollama", - "ollama_model": "Modelo de Ollama", - "refresh_models": "Refrescar modelos", - "refreshing_models": "Refrescando...", - "enable_automatic_indexing": "Habilitar indexado automático", - "rebuild_index": "Recrear índice", - "rebuild_index_error": "Error al comenzar la reconstrucción del índice. Consulte los registros para más detalles.", - "note_title": "Título de nota", - "error": "Error", - "last_attempt": "Último intento", - "actions": "Acciones", - "retry": "Reintentar", - "partial": "{{ percentage }}% completado", - "retry_queued": "Nota en la cola para reintento", - "retry_failed": "Hubo un fallo al poner en la cola a la nota para reintento", - "max_notes_per_llm_query": "Máximo de notas por consulta", - "max_notes_per_llm_query_description": "Número máximo de notas similares a incluir en el contexto IA", - "active_providers": "Proveedores activos", - "disabled_providers": "Proveedores deshabilitados", - "remove_provider": "Eliminar proveedor de la búsqueda", - "restore_provider": "Restaurar proveedor a la búsqueda", - "similarity_threshold": "Bias de similaridad", - "similarity_threshold_description": "Puntuación de similaridad mínima (0-1) para incluir notas en el contexto para consultas LLM", - "reprocess_index": "Reconstruir el índice de búsqueda", - "reprocessing_index": "Reconstruyendo...", - "reprocess_index_started": "La optimización de índice de búsqueda comenzó en segundo plano", - "reprocess_index_error": "Error al reconstruir el índice de búsqueda", - "index_rebuild_progress": "Progreso de reconstrucción de índice", - "index_rebuilding": "Optimizando índice ({{percentage}}%)", - "index_rebuild_complete": "Optimización de índice completa", - "index_rebuild_status_error": "Error al comprobar el estado de reconstrucción del índice", - "never": "Nunca", - "processing": "Procesando ({{percentage}}%)", - "incomplete": "Incompleto ({{percentage}}%)", - "complete": "Completo (100%)", - "refreshing": "Refrescando...", - "auto_refresh_notice": "Refrescar automáticamente cada {{seconds}} segundos", - "note_queued_for_retry": "Nota en la cola para reintento", - "failed_to_retry_note": "Hubo un fallo al reintentar nota", - "all_notes_queued_for_retry": "Todas las notas con fallo agregadas a la cola para reintento", - "failed_to_retry_all": "Hubo un fallo al reintentar notas", - "ai_settings": "Ajustes de IA", - "api_key_tooltip": "Clave API para acceder al servicio", - "empty_key_warning": { - "anthropic": "La clave API de Anthropic está vacía. Por favor, ingrese una clave API válida.", - "openai": "La clave API de OpenAI está vacía. Por favor, ingrese una clave API válida.", - "voyage": "La clave API de Voyage está vacía. Por favor, ingrese una clave API válida.", - "ollama": "La clave API de Ollama está vacía. Por favor, ingrese una clave API válida." - }, - "agent": { - "processing": "Procesando...", - "thinking": "Pensando...", - "loading": "Cargando...", - "generating": "Generando..." - }, - "name": "IA", - "openai": "OpenAI", - "use_enhanced_context": "Utilizar contexto mejorado", - "enhanced_context_description": "Provee a la IA con más contexto de la nota y sus notas relacionadas para obtener mejores respuestas", - "show_thinking": "Mostrar pensamiento", - "show_thinking_description": "Mostrar la cadena del proceso de pensamiento de la IA", - "enter_message": "Ingrese su mensaje...", - "error_contacting_provider": "Error al contactar con su proveedor de IA. Por favor compruebe sus ajustes y conexión a internet.", - "error_generating_response": "Error al generar respuesta de IA", - "index_all_notes": "Indexar todas las notas", - "index_status": "Estado de índice", - "indexed_notes": "Notas indexadas", - "indexing_stopped": "Indexado detenido", - "indexing_in_progress": "Indexado en progreso...", - "last_indexed": "Último indexado", - "note_chat": "Chat de nota", - "sources": "Fuentes", - "start_indexing": "Comenzar indexado", - "use_advanced_context": "Usar contexto avanzado", - "ollama_no_url": "Ollama no está configurado. Por favor ingrese una URL válida.", - "chat": { - "root_note_title": "Chats de IA", - "root_note_content": "Esta nota contiene tus conversaciones de chat de IA guardadas.", - "new_chat_title": "Nuevo chat", - "create_new_ai_chat": "Crear nuevo chat de IA" - }, - "create_new_ai_chat": "Crear nuevo chat de IA", - "configuration_warnings": "Hay algunos problemas con su configuración de IA. Por favor compruebe sus ajustes.", - "experimental_warning": "La característica de LLM aún es experimental - ha sido advertido.", - "selected_provider": "Proveedor seleccionado", - "selected_provider_description": "Elija el proveedor de IA para el chat y características de completado", - "select_model": "Seleccionar modelo...", - "select_provider": "Seleccionar proveedor...", - "ai_enabled": "Características de IA activadas", - "ai_disabled": "Características de IA desactivadas", - "no_models_found_online": "No se encontraron modelos. Por favor, comprueba tu clave de API y la configuración.", - "no_models_found_ollama": "No se encontraron modelos de Ollama. Por favor, comprueba si Ollama se está ejecutando.", - "error_fetching": "Error al obtener los modelos: {{error}}" - }, "zoom_factor": { "title": "Factor de zoom (solo versión de escritorio)", "description": "El zoom también se puede controlar con los atajos CTRL+- y CTRL+=." diff --git a/apps/client/src/translations/fr/translation.json b/apps/client/src/translations/fr/translation.json index 69b4eee16a..c1d844ae99 100644 --- a/apps/client/src/translations/fr/translation.json +++ b/apps/client/src/translations/fr/translation.json @@ -1485,7 +1485,6 @@ "beta-feature": "Beta", "task-list": "Liste de tâches", "book": "Collection", - "ai-chat": "Chat IA", "new-feature": "Nouveau", "collections": "Collections" }, @@ -1798,149 +1797,6 @@ "close": "Fermer", "help_title": "Afficher plus d'informations sur cet écran" }, - "ai_llm": { - "not_started": "Non démarré", - "title": "Paramètres IA", - "processed_notes": "Notes traitées", - "anthropic_url_description": "URL de base pour l'API Anthropic (par défaut : https ://api.anthropic.com)", - "anthropic_model_description": "Modèles Anthropic Claude pour la complétion", - "voyage_settings": "Réglages d'IA Voyage", - "ollama_settings": "Réglages Ollama", - "ollama_url_description": "URL pour l'API Ollama (par défaut: http://localhost:11434)", - "ollama_model_description": "Model Ollama utilisé pour la complétion", - "anthropic_configuration": "Configuration Anthropic", - "voyage_configuration": "Configuration IA Voyage", - "voyage_url_description": "Défaut: https://api.voyageai.com/v1", - "ollama_configuration": "Configuration Ollama", - "total_notes": "Notes totales", - "progress": "Progrès", - "queued_notes": "Notes dans la file d'attente", - "refresh_stats": "Rafraîchir les statistiques", - "enable_ai_features": "Activer les fonctionnalités IA/LLM", - "enable_ai_description": "Activer les fonctionnalités IA telles que le résumé des notes, la génération de contenu et autres fonctionnalités LLM", - "openai_tab": "OpenAI", - "anthropic_tab": "Anthropic", - "voyage_tab": "Voyage AI", - "ollama_tab": "Ollama", - "enable_ai": "Activer les fonctionnalités IA/LLM", - "enable_ai_desc": "Activer les fonctionnalités IA telles que le résumé des notes, la génération de contenu et autres fonctionnalités LLM", - "provider_configuration": "Configuration du fournisseur IA", - "provider_precedence_description": "Liste de fournisseurs séparés par virgule, par ordre de préférence (ex. 'openai,anthopic,ollama')", - "temperature": "Température", - "temperature_description": "Contrôle de l'aléatoirité dans les réponses (0 = déterministe, 2 = hasard maximum)", - "system_prompt": "Prompt système", - "system_prompt_description": "Prompt système par défaut pour toutes les intéractions IA", - "openai_configuration": "Configuration OpenAI", - "openai_settings": "Options OpenAI", - "api_key": "Clef API", - "url": "URL de base", - "model": "Modèle", - "openai_api_key_description": "Votre clef API OpenAI pour accéder à leurs services IA", - "anthropic_api_key_description": "Votre clef API Anthropic pour accéder aux modèles Claude", - "default_model": "Modèle par défaut", - "openai_model_description": "Exemples : gpt-4o, gpt-4-turbo, gpt-3.5-turbo", - "base_url": "URL de base", - "openai_url_description": "Défaut : https://api.openai.com/v1", - "anthropic_settings": "Réglages Anthropic", - "enable_ollama": "Activer Ollama", - "enable_ollama_description": "Activer Ollama comme modèle d'IA local", - "ollama_url": "URL Ollama", - "ollama_model": "Modèle Ollama", - "refresh_models": "Rafraîchir les modèles", - "refreshing_models": "Mise à jour...", - "enable_automatic_indexing": "Activer l'indexage automatique", - "rebuild_index": "Rafraîchir l'index", - "rebuild_index_error": "Erreur dans le démarrage du rafraichissement de l'index. Veuillez consulter les logs pour plus de détails.", - "note_title": "Titre de la note", - "error": "Erreur", - "last_attempt": "Dernier essai", - "actions": "Actions", - "retry": "Réessayer", - "partial": "Complété à {{ percentage }}%", - "retry_queued": "Note ajoutée à la file d'attente", - "retry_failed": "Echec de l'ajout de la note à la file d'attente", - "max_notes_per_llm_query": "Notes maximum par requête", - "max_notes_per_llm_query_description": "Nombre maximum de notes similaires à inclure dans le contexte IA", - "active_providers": "Fournisseurs actifs", - "disabled_providers": "Fournisseurs désactivés", - "remove_provider": "Retirer le fournisseur de la recherche", - "similarity_threshold": "Seuil de similarité", - "similarity_threshold_description": "Seuil de similarité minimum (0-1) pour que inclure les notes dans le contexte d'une requête IA", - "reprocess_index": "Rafraîchir l'index de recherche", - "reprocessing_index": "Mise à jour...", - "reprocess_index_started": "L'optimisation de l'indice de recherche à commencer en arrière-plan", - "reprocess_index_error": "Erreur dans le rafraichissement de l'indice de recherche", - "failed_notes": "Notes en erreur", - "last_processed": "Dernier traitement", - "restore_provider": "Restaurer le fournisseur de recherche", - "index_rebuild_progress": "Progression de la reconstruction de l'index", - "index_rebuilding": "Optimisation de l'index ({{percentage}}%)", - "index_rebuild_complete": "Optimisation de l'index terminée", - "index_rebuild_status_error": "Erreur lors de la vérification de l'état de reconstruction de l'index", - "provider_precedence": "Priorité du fournisseur", - "never": "Jamais", - "processing": "Traitement en cours ({{percentage}}%)", - "incomplete": "Incomplet ({{percentage}}%)", - "complete": "Terminé (100%)", - "refreshing": "Mise à jour...", - "auto_refresh_notice": "Actualisation automatique toutes les {{seconds}} secondes", - "note_queued_for_retry": "Note mise en file d'attente pour une nouvelle tentative", - "failed_to_retry_note": "Échec de la nouvelle tentative de note", - "all_notes_queued_for_retry": "Toutes les notes ayant échoué sont mises en file d'attente pour une nouvelle tentative", - "failed_to_retry_all": "Échec du ré essai des notes", - "ai_settings": "Paramètres IA", - "api_key_tooltip": "Clé API pour accéder au service", - "empty_key_warning": { - "anthropic": "La clé API Anthropic est vide. Veuillez saisir une clé API valide.", - "openai": "La clé API OpenAI est vide. Veuillez saisir une clé API valide.", - "voyage": "La clé API Voyage est vide. Veuillez saisir une clé API valide.", - "ollama": "La clé API Ollama est vide. Veuillez saisir une clé API valide." - }, - "agent": { - "processing": "Traitement...", - "thinking": "Réflexion...", - "loading": "Chargement...", - "generating": "Génération..." - }, - "name": "IA", - "openai": "OpenAI", - "use_enhanced_context": "Utiliser un contexte amélioré", - "enhanced_context_description": "Fournit à l'IA plus de contexte à partir de la note et de ses notes associées pour de meilleures réponses", - "show_thinking": "Montrer la réflexion", - "show_thinking_description": "Montrer la chaîne de pensée de l'IA", - "enter_message": "Entrez votre message...", - "error_contacting_provider": "Erreur lors de la connexion au fournisseur d'IA. Veuillez vérifier vos paramètres et votre connexion Internet.", - "error_generating_response": "Erreur lors de la génération de la réponse de l'IA", - "index_all_notes": "Indexer toutes les notes", - "index_status": "Statut de l'index", - "indexed_notes": "Notes indexées", - "indexing_stopped": "Arrêt de l'indexation", - "indexing_in_progress": "Indexation en cours...", - "last_indexed": "Dernière indexée", - "note_chat": "Note discussion", - "sources": "Sources", - "start_indexing": "Démarrage de l'indexation", - "use_advanced_context": "Utiliser le contexte avancé", - "ollama_no_url": "Ollama n'est pas configuré. Veuillez saisir une URL valide.", - "chat": { - "root_note_title": "Discussions IA", - "root_note_content": "Cette note contient vos conversations de chat IA enregistrées.", - "new_chat_title": "Nouvelle discussion", - "create_new_ai_chat": "Créer une nouvelle discussion IA" - }, - "create_new_ai_chat": "Créer une nouvelle discussion IA", - "configuration_warnings": "Il y a quelques problèmes avec la configuration de votre IA. Veuillez vérifier vos paramètres.", - "experimental_warning": "La fonctionnalité LLM est actuellement expérimentale – vous êtes prévenu.", - "selected_provider": "Fournisseur sélectionné", - "selected_provider_description": "Choisissez le fournisseur d’IA pour les fonctionnalités de discussion et de complétion", - "select_model": "Sélectionner le modèle...", - "select_provider": "Sélectionnez un fournisseur...", - "ai_enabled": "Fonctionnalités d'IA activées", - "ai_disabled": "Fonctionnalités d'IA désactivées", - "no_models_found_online": "Aucun modèle trouvé. Veuillez vérifier votre clé API et vos paramètres.", - "no_models_found_ollama": "Aucun modèle Ollama trouvé. Veuillez vérifier si Ollama est en cours d'exécution.", - "error_fetching": "Erreur lors de la récupération des modèles : {{error}}" - }, "ui-performance": { "title": "Performance", "enable-motion": "Activer les transitions et animations", diff --git a/apps/client/src/translations/ga/translation.json b/apps/client/src/translations/ga/translation.json index 5592c8f3a9..c5020a4d0c 100644 --- a/apps/client/src/translations/ga/translation.json +++ b/apps/client/src/translations/ga/translation.json @@ -1193,149 +1193,6 @@ "enable-smooth-scroll": "Cumasaigh scrollú réidh", "app-restart-required": "(tá atosú an fheidhmchláir ag teastáil chun an t-athrú a chur i bhfeidhm)" }, - "ai_llm": { - "not_started": "Níor tosaíodh", - "title": "Socruithe AI", - "processed_notes": "Nótaí Próiseáilte", - "total_notes": "Nótaí Iomlána", - "progress": "Dul Chun Cinn", - "queued_notes": "Nótaí i gCiú", - "failed_notes": "Nótaí Theipthe", - "last_processed": "Próiseáilte Deiridh", - "refresh_stats": "Athnuachan Staitisticí", - "enable_ai_features": "Cumasaigh gnéithe AI/LLM", - "enable_ai_description": "Cumasaigh gnéithe AI cosúil le achoimre nótaí, giniúint ábhair, agus cumais LLM eile", - "openai_tab": "OpenAI", - "anthropic_tab": "Anthropic", - "voyage_tab": "Voyage AI", - "ollama_tab": "Ollama", - "enable_ai": "Cumasaigh gnéithe AI/LLM", - "enable_ai_desc": "Cumasaigh gnéithe AI cosúil le achoimre nótaí, giniúint ábhair, agus cumais LLM eile", - "provider_configuration": "Cumraíocht Soláthraí AI", - "provider_precedence": "Tosaíocht Soláthraí", - "provider_precedence_description": "Liosta soláthraithe scartha le camóga in ord tosaíochta (m.sh., 'openai, anthropic, ollama')", - "temperature": "Teocht", - "temperature_description": "Rialaíonn randamacht i bhfreagraí (0 = cinntitheach, 2 = uasmhéid randamachta)", - "system_prompt": "Pras Córais", - "system_prompt_description": "Leid réamhshocraithe an chórais a úsáidtear le haghaidh gach idirghníomhaíocht AI", - "openai_configuration": "Cumraíocht OpenAI", - "openai_settings": "Socruithe OpenAI", - "api_key": "Eochair API", - "url": "Bun-URL", - "model": "Samhail", - "openai_api_key_description": "D'eochair API OpenAI chun rochtain a fháil ar a gcuid seirbhísí AI", - "anthropic_api_key_description": "D'eochair API Anthropic chun rochtain a fháil ar mhúnlaí Claude", - "default_model": "Samhail Réamhshocraithe", - "openai_model_description": "Samplaí: gpt-4o, gpt-4-turbo, gpt-3.5-turbo", - "base_url": "Bun-URL", - "openai_url_description": "Réamhshocrú: https://api.openai.com/v1", - "anthropic_settings": "Socruithe Anthropic", - "anthropic_url_description": "Bun-URL don Anthropic API (réamhshocraithe: https://api.anthropic.com)", - "anthropic_model_description": "Samhlacha Anthropic Claude le haghaidh comhlánú comhrá", - "voyage_settings": "Socruithe Voyage AI", - "ollama_settings": "Socruithe Ollama", - "ollama_url_description": "URL don Ollama API (réamhshocrú: http://localhost:11434)", - "ollama_model_description": "Samhail Ollama le húsáid le haghaidh comhrá a chríochnú", - "anthropic_configuration": "Cumraíocht Anthropic", - "voyage_configuration": "Cumraíocht AI Voyage", - "voyage_url_description": "Réamhshocrú: https://api.voyageai.com/v1", - "ollama_configuration": "Cumraíocht Ollama", - "enable_ollama": "Cumasaigh Ollama", - "enable_ollama_description": "Cumasaigh Ollama le haghaidh úsáide áitiúla samhail AI", - "ollama_url": "URL Ollama", - "ollama_model": "Samhail Ollama", - "refresh_models": "Athnuachan Samhlacha", - "refreshing_models": "Ag athnuachan...", - "enable_automatic_indexing": "Cumasaigh Innéacsú Uathoibríoch", - "rebuild_index": "Innéacs Athchóirithe", - "rebuild_index_error": "Earráid ag tosú atógáil an innéacs. Seiceáil na logaí le haghaidh sonraí.", - "note_title": "Teideal an Nóta", - "error": "Earráid", - "last_attempt": "Iarracht Dheiridh", - "actions": "Gníomhartha", - "retry": "Déan iarracht eile", - "partial": "{{ percentage }}% críochnaithe", - "retry_queued": "Nóta curtha i scuaine le haghaidh athiarrachta", - "retry_failed": "Theip ar an nóta a chur sa scuaine le haghaidh athiarrachta", - "max_notes_per_llm_query": "Uasmhéid Nótaí In Aghaidh an Fhiosrúcháin", - "max_notes_per_llm_query_description": "Uasmhéid nótaí comhchosúla le cur san áireamh i gcomhthéacs na hintleachta saorga", - "active_providers": "Soláthraithe Gníomhacha", - "disabled_providers": "Soláthraithe faoi Mhíchumas", - "remove_provider": "Bain an soláthraí as an gcuardach", - "restore_provider": "Athchóirigh soláthraí chuig an gcuardach", - "similarity_threshold": "Tairseach Cosúlachta", - "similarity_threshold_description": "Scór cosúlachta íosta (0-1) le go n-áireofar nótaí i gcomhthéacs fiosrúcháin LLM", - "reprocess_index": "Athchruthaigh Innéacs Cuardaigh", - "reprocessing_index": "Atógáil...", - "reprocess_index_started": "Cuireadh tús le hoptamú innéacs cuardaigh sa chúlra", - "reprocess_index_error": "Earráid ag atógáil innéacs cuardaigh", - "index_rebuild_progress": "Dul Chun Cinn Athchóirithe Innéacs", - "index_rebuilding": "Innéacs optamaithe ({{percentage}}%)", - "index_rebuild_complete": "Uasmhéadú innéacs críochnaithe", - "index_rebuild_status_error": "Earráid ag seiceáil stádas athchóirithe innéacs", - "never": "Choíche", - "processing": "Próiseáil ({{percentage}}%)", - "incomplete": "Neamhchríochnaithe ({{percentage}}%)", - "complete": "Críochnaithe (100%)", - "refreshing": "Ag athnuachan...", - "auto_refresh_notice": "Athnuachan uathoibríoch gach {{seconds}} soicind", - "note_queued_for_retry": "Nóta curtha i scuaine le haghaidh athiarrachta", - "failed_to_retry_note": "Theip ar an nóta a athdhéanamh", - "all_notes_queued_for_retry": "Gach nóta nár éirigh leo curtha i scuaine le haghaidh athiarrachta", - "failed_to_retry_all": "Theip ar athiarracht nótaí", - "ai_settings": "Socruithe AI", - "api_key_tooltip": "Eochair API chun rochtain a fháil ar an tseirbhís", - "empty_key_warning": { - "anthropic": "Tá eochair API Anthropic folamh. Cuir isteach eochair API bhailí le do thoil.", - "openai": "Tá eochair API OpenAI folamh. Cuir isteach eochair API bhailí le do thoil.", - "voyage": "Tá eochair API Voyage folamh. Cuir isteach eochair API bhailí le do thoil.", - "ollama": "Tá eochair API Ollama folamh. Cuir isteach eochair API bhailí le do thoil." - }, - "agent": { - "processing": "Ag próiseáil...", - "thinking": "Ag smaoineamh...", - "loading": "Ag lódáil...", - "generating": "Ag giniúint..." - }, - "name": "Intleacht Shaorga", - "openai": "OpenAI", - "use_enhanced_context": "Úsáid comhthéacs feabhsaithe", - "enhanced_context_description": "Tugann sé níos mó comhthéacs don AI ón nóta agus a nótaí gaolmhara le haghaidh freagraí níos fearr", - "show_thinking": "Taispeáin smaointeoireacht", - "show_thinking_description": "Taispeáin slabhra phróiseas smaointeoireachta na hintleachta saorga", - "enter_message": "Cuir isteach do theachtaireacht...", - "error_contacting_provider": "Earráid ag teacht i dteagmháil leis an soláthraí AI. Seiceáil do shocruithe agus do nasc idirlín le do thoil.", - "error_generating_response": "Earráid ag giniúint freagra AI", - "index_all_notes": "Innéacs na Nótaí Uile", - "index_status": "Stádas Innéacs", - "indexed_notes": "Nótaí Innéacsaithe", - "indexing_stopped": "Stopadh an innéacsú", - "indexing_in_progress": "Innéacsú ar siúl...", - "last_indexed": "Innéacsaithe Deiridh", - "note_chat": "Comhrá Nótaí", - "sources": "Foinsí", - "start_indexing": "Tosaigh ag Innéacsú", - "use_advanced_context": "Úsáid Comhthéacs Ardleibhéil", - "ollama_no_url": "Níl Ollama cumraithe. Cuir isteach URL bailí le do thoil.", - "chat": { - "root_note_title": "Comhráite AI", - "root_note_content": "Tá do chomhráite comhrá AI sábháilte sa nóta seo.", - "new_chat_title": "Comhrá Nua", - "create_new_ai_chat": "Cruthaigh Comhrá AI nua" - }, - "create_new_ai_chat": "Cruthaigh Comhrá AI nua", - "configuration_warnings": "Tá roinnt fadhbanna le do chumraíocht AI. Seiceáil do shocruithe le do thoil.", - "experimental_warning": "Tá an ghné LLM turgnamhach faoi láthair - tugadh rabhadh duit.", - "selected_provider": "Soláthraí Roghnaithe", - "selected_provider_description": "Roghnaigh an soláthraí AI le haghaidh gnéithe comhrá agus comhlánaithe", - "select_model": "Roghnaigh samhail...", - "select_provider": "Roghnaigh soláthraí...", - "ai_enabled": "Gnéithe AI cumasaithe", - "ai_disabled": "Gnéithe AI díchumasaithe", - "no_models_found_online": "Níor aimsíodh aon mhúnlaí. Seiceáil d’eochair API agus do shocruithe le do thoil.", - "no_models_found_ollama": "Níor aimsíodh aon mhúnlaí Ollama. Seiceáil le do thoil an bhfuil Ollama ag rith.", - "error_fetching": "Earráid ag fáil samhlacha: {{error}}" - }, "zoom_factor": { "title": "Fachtóir Súmáil (leagan deisce amháin)", "description": "Is féidir súmáil a rialú le haicearraí CTRL+- agus CTRL+= chomh maith." diff --git a/apps/client/src/translations/it/translation.json b/apps/client/src/translations/it/translation.json index f24d449fcb..0ce70cbf62 100644 --- a/apps/client/src/translations/it/translation.json +++ b/apps/client/src/translations/it/translation.json @@ -663,149 +663,6 @@ "thursday": "Giovedì", "friday": "Venerdì" }, - "ai_llm": { - "not_started": "Non iniziato", - "title": "Impostazioni AI", - "processed_notes": "Note elaborate", - "total_notes": "Note totali", - "progress": "Progressi", - "queued_notes": "Note in coda", - "failed_notes": "Note non riuscite", - "last_processed": "Ultimo elaborato", - "refresh_stats": "Aggiorna statistiche", - "enable_ai_features": "Abilita le funzionalità AI/LLM", - "enable_ai_description": "Abilita funzionalità di intelligenza artificiale come il riepilogo delle note, la generazione di contenuti e altre funzionalità LLM", - "openai_tab": "OpenAI", - "anthropic_tab": "Antropico", - "voyage_tab": "Viaggio AI", - "ollama_tab": "Ollama", - "enable_ai": "Abilita le funzionalità AI/LLM", - "enable_ai_desc": "Abilita funzionalità di intelligenza artificiale come il riepilogo delle note, la generazione di contenuti e altre funzionalità LLM", - "provider_configuration": "Configurazione del fornitore di intelligenza artificiale", - "provider_precedence": "Precedenza del fornitore", - "provider_precedence_description": "Elenco dei provider separati da virgole in ordine di precedenza (ad esempio, 'openai,anthropic,ollama')", - "temperature": "Temperatura", - "temperature_description": "Controlla la casualità nelle risposte (0 = deterministico, 2 = casualità massima)", - "system_prompt": "Prompt di sistema", - "system_prompt_description": "Prompt di sistema predefinito utilizzato per tutte le interazioni con l'IA", - "openai_configuration": "Configurazione OpenAI", - "openai_settings": "Impostazioni OpenAI", - "api_key": "Chiave API", - "url": "URL di base", - "model": "Modello", - "openai_api_key_description": "La tua chiave API OpenAI per accedere ai loro servizi di intelligenza artificiale", - "anthropic_api_key_description": "La tua chiave API Anthropic per accedere ai modelli Claude", - "default_model": "Modello predefinito", - "openai_model_description": "Esempi: gpt-4o, gpt-4-turbo, gpt-3.5-turbo", - "base_url": "URL di base", - "openai_url_description": "Predefinito: https://api.openai.com/v1", - "anthropic_settings": "Ambientazioni antropiche", - "anthropic_url_description": "URL di base per l'API Anthropic (predefinito: https://api.anthropic.com)", - "anthropic_model_description": "Modelli di Anthropic Claude per il completamento della chat", - "voyage_settings": "Impostazioni AI di Voyage", - "ollama_settings": "Impostazioni Ollama", - "ollama_url_description": "URL per l'API Ollama (predefinito: http://localhost:11434)", - "ollama_model_description": "Modello Ollama da utilizzare per il completamento della chat", - "anthropic_configuration": "Configurazione antropica", - "voyage_configuration": "Configurazione AI di viaggio", - "voyage_url_description": "Predefinito: https://api.voyageai.com/v1", - "ollama_configuration": "Configurazione Ollama", - "enable_ollama": "Abilita Ollama", - "enable_ollama_description": "Abilita Ollama per l'utilizzo del modello AI locale", - "ollama_url": "URL di Ollama", - "ollama_model": "Modello Ollama", - "refresh_models": "Aggiorna modelli", - "refreshing_models": "Rinfrescante...", - "enable_automatic_indexing": "Abilita l'indicizzazione automatica", - "rebuild_index": "Ricostruisci indice", - "rebuild_index_error": "Errore durante l'avvio della ricostruzione dell'indice. Controllare i log per i dettagli.", - "note_title": "Titolo della nota", - "error": "Errore", - "last_attempt": "Ultimo tentativo", - "actions": "Azioni", - "retry": "Riprova", - "partial": "{{ percentage }}% completato", - "retry_queued": "Nota in coda per un nuovo tentativo", - "retry_failed": "Impossibile mettere in coda la nota per un nuovo tentativo", - "max_notes_per_llm_query": "Numero massimo di note per query", - "max_notes_per_llm_query_description": "Numero massimo di note simili da includere nel contesto AI", - "active_providers": "Fornitori attivi", - "disabled_providers": "Fornitori disabili", - "remove_provider": "Rimuovi il fornitore dalla ricerca", - "restore_provider": "Ripristina il provider per la ricerca", - "similarity_threshold": "Soglia di similarità", - "similarity_threshold_description": "Punteggio minimo di similarità (0-1) per le note da includere nel contesto per le query LLM", - "reprocess_index": "Ricostruisci l'indice di ricerca", - "reprocessing_index": "Ricostruzione...", - "reprocess_index_started": "Ottimizzazione dell'indice di ricerca avviata in background", - "reprocess_index_error": "Errore durante la ricostruzione dell'indice di ricerca", - "index_rebuild_progress": "Progresso nella ricostruzione dell'indice", - "index_rebuilding": "Indice di ottimizzazione ({{percentage}}%)", - "index_rebuild_complete": "Ottimizzazione dell'indice completata", - "index_rebuild_status_error": "Errore durante il controllo dello stato di ricostruzione dell'indice", - "never": "Mai", - "processing": "Elaborazione ({{percentage}}%)", - "incomplete": "Incompleto ({{percentage}}%)", - "complete": "Completato (100%)", - "refreshing": "Rinfrescante...", - "auto_refresh_notice": "Si aggiorna automaticamente ogni {{seconds}} secondi", - "note_queued_for_retry": "Nota in coda per un nuovo tentativo", - "failed_to_retry_note": "Impossibile riprovare nota", - "all_notes_queued_for_retry": "Tutte le note non riuscite sono in coda per un nuovo tentativo", - "failed_to_retry_all": "Impossibile riprovare le note", - "ai_settings": "Impostazioni AI", - "api_key_tooltip": "Chiave API per accedere al servizio", - "empty_key_warning": { - "anthropic": "La chiave API di Anthropic è vuota. Inserisci una chiave API valida.", - "openai": "La chiave API di OpenAI è vuota. Inserisci una chiave API valida.", - "voyage": "La chiave API di Voyage è vuota. Inserisci una chiave API valida.", - "ollama": "La chiave API di Ollama è vuota. Inserisci una chiave API valida." - }, - "agent": { - "processing": "Elaborazione in corso...", - "thinking": "Pensiero...", - "loading": "Caricamento...", - "generating": "Generazione in corso..." - }, - "name": "intelligenza artificiale", - "openai": "OpenAI", - "use_enhanced_context": "Utilizzare il contesto avanzato", - "enhanced_context_description": "Fornisce all'IA più contesto dalla nota e dalle note correlate per risposte migliori", - "show_thinking": "Mostra il pensiero", - "show_thinking_description": "Mostra la catena del processo di pensiero dell'IA", - "enter_message": "Inserisci il tuo messaggio...", - "error_contacting_provider": "Errore durante la connessione al fornitore dell'IA. Controlla le impostazioni e la connessione Internet.", - "error_generating_response": "Errore durante la generazione della risposta AI", - "index_all_notes": "Indice Tutte le note", - "index_status": "Stato dell'indice", - "indexed_notes": "Note indicizzate", - "indexing_stopped": "Indicizzazione interrotta", - "indexing_in_progress": "Indicizzazione in corso...", - "last_indexed": "Ultimo indicizzato", - "note_chat": "Nota Chat", - "sources": "Fonti", - "start_indexing": "Avvia l'indicizzazione", - "use_advanced_context": "Usa contesto avanzato", - "ollama_no_url": "Ollama non è configurato. Inserisci un URL valido.", - "chat": { - "root_note_title": "Chat AI", - "root_note_content": "Questa nota contiene le conversazioni della chat AI salvate.", - "new_chat_title": "Nuova chat", - "create_new_ai_chat": "Crea una nuova chat AI" - }, - "create_new_ai_chat": "Crea una nuova chat AI", - "configuration_warnings": "Ci sono alcuni problemi con la configurazione dell'IA. Controlla le impostazioni.", - "experimental_warning": "La funzionalità LLM è attualmente sperimentale: sei stato avvisato.", - "selected_provider": "Fornitore selezionato", - "selected_provider_description": "Scegli il fornitore di intelligenza artificiale per le funzionalità di chat e completamento", - "select_model": "Seleziona il modello...", - "select_provider": "Seleziona il fornitore...", - "ai_enabled": "Funzionalità AI abilitate", - "ai_disabled": "Funzionalità AI disabilitate", - "no_models_found_online": "Nessun modello trovato. Controlla la tua chiave API e le impostazioni.", - "no_models_found_ollama": "Nessun modello Ollama trovato. Controlla se Ollama è in esecuzione.", - "error_fetching": "Errore durante il recupero dei modelli: {{error}}" - }, "import": { "importIntoNote": "Importa nella nota", "chooseImportFile": "Scegli file di importazione", @@ -1856,7 +1713,6 @@ "confirm-change": "Si sconsiglia di cambiare tipo di nota quando il contenuto della nota non è vuoto. Vuoi continuare comunque?", "geo-map": "Mappa geografica", "beta-feature": "Beta", - "ai-chat": "Chat AI", "task-list": "Elenco delle attività", "new-feature": "Nuovo", "collections": "Collezioni" diff --git a/apps/client/src/translations/ja/translation.json b/apps/client/src/translations/ja/translation.json index fd7c5527e2..e040e7e6e1 100644 --- a/apps/client/src/translations/ja/translation.json +++ b/apps/client/src/translations/ja/translation.json @@ -597,7 +597,6 @@ "widget": "ウィジェット", "confirm-change": "ノートの内容が空ではない場合、ノートタイプを変更することは推奨されません。続行しますか?", "beta-feature": "Beta", - "ai-chat": "AI チャット", "task-list": "タスクリスト", "new-feature": "New", "collections": "コレクション" @@ -1425,149 +1424,6 @@ "content_renderer": { "open_externally": "外部で開く" }, - "ai_llm": { - "title": "AI 設定", - "enable_ai_features": "AI/LLM 機能を有効化", - "enable_ai_description": "ノートの要約、コンテンツ生成、その他のLLM機能などのAI機能を有効にする", - "openai_tab": "OpenAI", - "anthropic_tab": "Anthropic", - "voyage_tab": "Voyage AI", - "ollama_tab": "Ollama", - "enable_ai": "AI/LLM 機能を有効化", - "enable_ai_desc": "ノートの要約、コンテンツ生成、その他のLLM機能などのAI機能を有効にする", - "provider_configuration": "AI プロバイダーの設定", - "provider_precedence": "プロバイダーの優先順位", - "provider_precedence_description": "カンマで区切られたプロバイダーの優先順位リスト(例: 'openai,anthropic,ollama')", - "temperature_description": "応答のランダム性を制御する(0 = 決定的、2 = 最大ランダム性)", - "system_prompt_description": "すべてのAIとの対話に使用されるデフォルトのシステムプロンプト", - "system_prompt": "システムプロンプト", - "openai_configuration": "OpenAIの設定", - "openai_settings": "OpenAIの設定", - "api_key": "APIキー", - "model": "モデル", - "openai_api_key_description": "OpenAIのAIサービスにアクセスするためのAPIキー", - "anthropic_api_key_description": "AnthropicのClaudeモデルにアクセスするためのAPIキー", - "default_model": "デフォルトモデル", - "openai_model_description": "例: gpt-4o, gpt-4-turbo, gpt-3.5-turbo", - "openai_url_description": "デフォルト: https://api.openai.com/v1", - "anthropic_settings": "Anthropicの設定", - "anthropic_model_description": "チャット補完用のAnthropic Claudeモデル", - "voyage_settings": "Voyage AIの設定", - "ollama_settings": "Ollamaの設定", - "ollama_url_description": "Ollama API の URL(デフォルト: http://localhost:11434)", - "anthropic_url_description": "Anthropic APIのベースURL(デフォルト: https://api.anthropic.com)", - "ollama_model_description": "チャット補完用のOllamaモデル", - "anthropic_configuration": "Anthropicの設定", - "voyage_configuration": "Voyage AIの設定", - "voyage_url_description": "デフォルト: https://api.voyageai.com/v1", - "ollama_configuration": "Ollamaの設定", - "enable_ollama": "Ollamaを有効", - "enable_ollama_description": "ローカルAIモデルを利用するためOllamaを有効にする", - "ollama_url": "Ollama URL", - "ollama_model": "Ollamaモデル", - "refresh_models": "モデルを更新", - "refreshing_models": "更新中...", - "error": "エラー", - "retry": "再試行", - "partial": "{{ percentage }}%完了", - "processing": "処理中({{percentage}}%)", - "complete": "完了(100%)", - "refreshing": "更新中...", - "auto_refresh_notice": "{{seconds}}秒ごとに自動更新", - "ai_settings": "AI 設定", - "api_key_tooltip": "サービスにアクセスするためのAPIキー", - "empty_key_warning": { - "anthropic": "Anthropic APIキーが空です。有効な APIキーを入力してください。", - "openai": "OpenAI APIキーが空です。有効なAPIキーを入力してください。", - "voyage": "Voyage APIキーが空です。有効なAPIキーを入力してください。", - "ollama": "Ollama APIキーが空です。有効なAPIキーを入力してください。" - }, - "agent": { - "processing": "処理中...", - "loading": "読み込み中...", - "generating": "生成中...", - "thinking": "考え中..." - }, - "name": "AI", - "openai": "OpenAI", - "error_contacting_provider": "AIプロバイダーへの接続中にエラーが発生しました。設定とインターネット接続をご確認ください。", - "ollama_no_url": "Ollamaは設定されていません。有効なURLを入力してください。", - "chat": { - "root_note_title": "AIチャット", - "root_note_content": "このノートには、保存されたAIチャットの会話が含まれています。", - "new_chat_title": "新しいチャット", - "create_new_ai_chat": "新しいAIチャットを作成" - }, - "create_new_ai_chat": "新しいAIチャットを作成", - "configuration_warnings": "AIの設定に問題があります。設定を確認してください。", - "experimental_warning": "LLM機能は現在実験的です - ご注意ください。", - "selected_provider_description": "チャットおよび補完機能のAIプロバイダーを選択する", - "selected_provider": "プロバイダーを選択", - "select_model": "モデルを選択...", - "select_provider": "プロバイダーを選択...", - "not_started": "開始されていません", - "processed_notes": "処理済みノート", - "total_notes": "ノートの総数", - "progress": "進行状況", - "queued_notes": "キューに登録されたノート", - "failed_notes": "失敗したノート", - "last_processed": "最終処理日時", - "refresh_stats": "統計情報を更新", - "temperature": "Temperature(温度)", - "url": "ベースURL", - "base_url": "ベースURL", - "enable_automatic_indexing": "自動インデックス作成を有効にする", - "rebuild_index": "インデックスの再構築", - "rebuild_index_error": "インデックスの再構築開始時にエラーが発生しました。詳細はログをご確認ください。", - "note_title": "ノートのタイトル", - "last_attempt": "最終試行日時", - "actions": "アクション", - "retry_queued": "ノートが再試行キューに追加されました", - "retry_failed": "ノートを再試行キューに追加できませんでした", - "max_notes_per_llm_query": "クエリあたりの最大ノート数", - "max_notes_per_llm_query_description": "AIコンテキストに含める類似ノートの最大数", - "active_providers": "アクティブなプロバイダー", - "disabled_providers": "無効なプロバイダー", - "remove_provider": "検索からプロバイダーを削除", - "restore_provider": "検索にプロバイダーを復元", - "similarity_threshold": "類似度のしきい値", - "similarity_threshold_description": "LLMクエリのコンテキストに含めるノートの最小類似度スコア(0~1)", - "reprocess_index": "検索インデックスを再構築", - "reprocessing_index": "再構築中...", - "reprocess_index_started": "検索インデックスの最適化がバックグラウンドで開始されました", - "reprocess_index_error": "検索インデックスの再構築中にエラーが発生しました", - "index_rebuild_progress": "インデックスの再構築の進行状況", - "index_rebuilding": "インデックスの最適化中({{percentage}}%)", - "index_rebuild_complete": "インデックスの最適化が完了しました", - "index_rebuild_status_error": "インデックスの再構築ステータスの確認中にエラーが発生しました", - "never": "なし", - "incomplete": "未完了 ({{percentage}}%)", - "note_queued_for_retry": "ノートが再試行キューに追加されました", - "failed_to_retry_note": "ノートの再試行に失敗しました", - "all_notes_queued_for_retry": "失敗したすべてのノートは再試行のためにキューに入れられました", - "failed_to_retry_all": "ノートの再試行に失敗しました", - "use_enhanced_context": "拡張されたコンテキストを使用する", - "enhanced_context_description": "ノートとその関連ノートからより多くのコンテキストをAIに提供し、より良い応答を実現します", - "show_thinking": "思考を表示", - "show_thinking_description": "AIの思考プロセスをチェーン表示", - "enter_message": "メッセージを入力...", - "error_generating_response": "AI応答の生成中にエラーが発生しました", - "index_all_notes": "すべてのノートをインデックスに登録", - "index_status": "インデックスのステータス", - "indexed_notes": "インデックス登録済みのノート", - "indexing_stopped": "インデックス登録を停止しました", - "indexing_in_progress": "インデックス登録中です...", - "last_indexed": "最終インデックス作成日時", - "note_chat": "ノートチャット", - "sources": "ソース", - "start_indexing": "インデックス作成を開始", - "use_advanced_context": "高度なコンテキストを使用", - "ai_enabled": "AI 機能が有効", - "ai_disabled": "AI 機能が無効", - "no_models_found_online": "モデルが見つかりません。API キーと設定を確認してください。", - "no_models_found_ollama": "Ollama モデルが見つかりません。Ollama が実行中かどうかを確認してください。", - "error_fetching": "モデルの取得エラー: {{error}}" - }, "add_label": { "add_label": "ラベルを追加", "label_name_placeholder": "ラベル名", diff --git a/apps/client/src/translations/pl/translation.json b/apps/client/src/translations/pl/translation.json index 2c690b0370..982cb71c43 100644 --- a/apps/client/src/translations/pl/translation.json +++ b/apps/client/src/translations/pl/translation.json @@ -773,149 +773,6 @@ "target_note": "notatka docelowa", "create_relation_on_all_matched_notes": "Na wszystkich dopasowanych notatkach utwórz podaną relację." }, - "ai_llm": { - "actions": "Akcje", - "retry": "Ponów", - "partial": "{{ percentage }}% ukończono", - "retry_queued": "Notatka zakolejkowana do ponowienia", - "retry_failed": "Nie udało się zakolejkować notatki do ponowienia", - "max_notes_per_llm_query": "Maks. notatek na zapytanie", - "index_all_notes": "Indeksuj wszystkie notatki", - "index_status": "Status indeksu", - "indexed_notes": "Zaindeksowane notatki", - "indexing_stopped": "Indeksowanie zatrzymane", - "indexing_in_progress": "Indeksowanie w toku...", - "last_indexed": "Ostatnio zaindeksowane", - "note_chat": "Czat notatki", - "note_title": "Tytuł notatki", - "error": "Błąd", - "last_attempt": "Ostatnia próba", - "queued_notes": "Notatki w kolejce", - "failed_notes": "Notatki nieudane", - "last_processed": "Ostatnio przetworzone", - "refresh_stats": "Odśwież statystyki", - "enable_ai_features": "Włącz funkcje AI/LLM", - "enable_ai_description": "Włącz funkcje AI, takie jak podsumowywanie notatek, generowanie treści i inne możliwości LLM", - "openai_tab": "OpenAI", - "anthropic_tab": "Anthropic", - "voyage_tab": "Voyage AI", - "ollama_tab": "Ollama", - "enable_ai": "Włącz funkcje AI/LLM", - "enable_ai_desc": "Włącz funkcje AI, takie jak podsumowywanie notatek, generowanie treści i inne możliwości LLM", - "provider_configuration": "Konfiguracja dostawcy AI", - "provider_precedence": "Kolejność dostawców", - "provider_precedence_description": "Lista dostawców oddzielona przecinkami w kolejności pierwszeństwa (np. 'openai,anthropic,ollama')", - "temperature": "Temperatura", - "temperature_description": "Kontroluje losowość w odpowiedziach (0 = deterministyczne, 2 = maksymalna losowość)", - "system_prompt": "Prompt systemowy", - "system_prompt_description": "Domyślny prompt systemowy używany dla wszystkich interakcji AI", - "openai_configuration": "Konfiguracja OpenAI", - "openai_settings": "Ustawienia OpenAI", - "api_key": "Klucz API", - "url": "Bazowy URL", - "model": "Model", - "openai_api_key_description": "Twój klucz API OpenAI do dostępu do ich usług AI", - "anthropic_api_key_description": "Twój klucz API Anthropic do dostępu do modeli Claude", - "default_model": "Domyślny model", - "openai_model_description": "Przykłady: gpt-4o, gpt-4-turbo, gpt-3.5-turbo", - "base_url": "Bazowy URL", - "openai_url_description": "Domyślnie: https://api.openai.com/v1", - "anthropic_settings": "Ustawienia Anthropic", - "anthropic_url_description": "Bazowy URL dla API Anthropic (domyślnie: https://api.anthropic.com)", - "anthropic_model_description": "Modele Anthropic Claude do czatu", - "voyage_settings": "Ustawienia Voyage AI", - "ollama_settings": "Ustawienia Ollama", - "ollama_url_description": "URL dla API Ollama (domyślnie: http://localhost:11434)", - "ollama_model_description": "Model Ollama do użycia w czacie", - "anthropic_configuration": "Konfiguracja Anthropic", - "voyage_configuration": "Konfiguracja Voyage AI", - "voyage_url_description": "Domyślnie: https://api.voyageai.com/v1", - "ollama_configuration": "Konfiguracja Ollama", - "enable_ollama": "Włącz Ollama", - "enable_ollama_description": "Włącz Ollama do lokalnego użycia modeli AI", - "ollama_url": "URL Ollama", - "ollama_model": "Model Ollama", - "refresh_models": "Odśwież modele", - "refreshing_models": "Odświeżanie...", - "enable_automatic_indexing": "Włącz automatyczne indeksowanie", - "rebuild_index": "Przebuduj indeks", - "rebuild_index_error": "Błąd podczas rozpoczynania przebudowy indeksu. Sprawdź logi po szczegóły.", - "max_notes_per_llm_query_description": "Maksymalna liczba podobnych notatek do uwzględnienia w kontekście AI", - "active_providers": "Aktywni dostawcy", - "disabled_providers": "Wyłączeni dostawcy", - "remove_provider": "Usuń dostawcę z wyszukiwania", - "restore_provider": "Przywróć dostawcę do wyszukiwania", - "similarity_threshold": "Próg podobieństwa", - "not_started": "Nie rozpoczęto", - "title": "Ustawienia AI", - "processed_notes": "Przetworzone notatki", - "total_notes": "Łącznie notatek", - "progress": "Postęp", - "similarity_threshold_description": "Minimalny wynik podobieństwa (0-1) dla notatek, które mają być uwzględnione w kontekście zapytań LLM", - "reprocess_index": "Przebuduj indeks wyszukiwania", - "reprocessing_index": "Przebudowywanie...", - "reprocess_index_started": "Optymalizacja indeksu wyszukiwania rozpoczęta w tle", - "reprocess_index_error": "Błąd podczas przebudowy indeksu wyszukiwania", - "index_rebuild_progress": "Postęp przebudowy indeksu", - "index_rebuilding": "Optymalizacja indeksu ({{percentage}}%)", - "index_rebuild_complete": "Optymalizacja indeksu zakończona", - "index_rebuild_status_error": "Błąd podczas sprawdzania statusu przebudowy indeksu", - "never": "Nigdy", - "processing": "Przetwarzanie ({{percentage}}%)", - "incomplete": "Niekompletne ({{percentage}}%)", - "complete": "Zakończone (100%)", - "refreshing": "Odświeżanie...", - "auto_refresh_notice": "Automatyczne odświeżanie co {{seconds}} sekund", - "note_queued_for_retry": "Notatka zakolejkowana do ponowienia", - "failed_to_retry_note": "Nie udało się ponowić notatki", - "all_notes_queued_for_retry": "Wszystkie nieudane notatki zakolejkowane do ponowienia", - "failed_to_retry_all": "Nie udało się ponowić notatek", - "ai_settings": "Ustawienia AI", - "api_key_tooltip": "Klucz API do dostępu do usługi", - "empty_key_warning": { - "anthropic": "Klucz API Anthropic jest pusty. Proszę wprowadzić poprawny klucz API.", - "openai": "Klucz API OpenAI jest pusty. Proszę wprowadzić poprawny klucz API.", - "voyage": "Klucz API Voyage jest pusty. Proszę wprowadzić poprawny klucz API.", - "ollama": "Klucz API Ollama jest pusty. Proszę wprowadzić poprawny klucz API." - }, - "agent": { - "processing": "Przetwarzanie...", - "thinking": "Myślę...", - "loading": "Ładowanie...", - "generating": "Generowanie..." - }, - "name": "AI", - "openai": "OpenAI", - "use_enhanced_context": "Użyj rozszerzonego kontekstu", - "enhanced_context_description": "Dostarcza AI więcej kontekstu z notatki i jej powiązanych notatek dla lepszych odpowiedzi", - "show_thinking": "Pokaż proces myślenia", - "show_thinking_description": "Pokaż ciąg myślowy AI", - "enter_message": "Wpisz swoją wiadomość...", - "error_contacting_provider": "Błąd połączenia z dostawcą AI. Sprawdź swoje ustawienia i połączenie internetowe.", - "error_generating_response": "Błąd generowania odpowiedzi AI", - "sources": "Źródła", - "start_indexing": "Rozpocznij indeksowanie", - "use_advanced_context": "Użyj zaawansowanego kontekstu", - "ollama_no_url": "Ollama nie jest skonfigurowana. Proszę wprowadzić poprawny URL.", - "chat": { - "root_note_title": "Czaty AI", - "root_note_content": "Ta notatka zawiera twoje zapisane rozmowy czatu AI.", - "new_chat_title": "Nowy czat", - "create_new_ai_chat": "Utwórz nowy czat AI" - }, - "create_new_ai_chat": "Utwórz nowy czat AI", - "configuration_warnings": "Istnieją pewne problemy z twoją konfiguracją AI. Proszę sprawdzić ustawienia.", - "experimental_warning": "Funkcja LLM jest obecnie eksperymentalna - zostałeś ostrzeżony.", - "selected_provider": "Wybrany dostawca", - "selected_provider_description": "Wybierz dostawcę AI dla funkcji czatu i uzupełniania", - "select_model": "Wybierz model...", - "select_provider": "Wybierz dostawcę...", - "ai_enabled": "Funkcje AI włączone", - "ai_disabled": "Funkcje AI wyłączone", - "no_models_found_online": "Nie znaleziono modeli. Proszę sprawdzić klucz API i ustawienia.", - "no_models_found_ollama": "Nie znaleziono modeli Ollama. Proszę sprawdzić, czy Ollama jest uruchomiona.", - "error_fetching": "Błąd pobierania modeli: {{error}}" - }, "prompt": { "title": "Monit", "ok": "OK", diff --git a/apps/client/src/translations/pt/translation.json b/apps/client/src/translations/pt/translation.json index 7975182509..167d85e8c0 100644 --- a/apps/client/src/translations/pt/translation.json +++ b/apps/client/src/translations/pt/translation.json @@ -1179,149 +1179,6 @@ "enable-smooth-scroll": "Activar deslocamento suave", "app-restart-required": "(é necessário reiniciar a aplicação para aplicar as alterações)" }, - "ai_llm": { - "not_started": "Não iniciado", - "title": "Configurações de IA", - "processed_notes": "Notas Processadas", - "total_notes": "Total de Notas", - "progress": "Andamento", - "queued_notes": "Notas Enfileiradas", - "failed_notes": "Notas com Falha", - "last_processed": "Últimas Processadas", - "refresh_stats": "Atualizar Estatísticas", - "enable_ai_features": "Ativar recurso IA/LLM", - "enable_ai_description": "Ativar recursos IA como sumarização de notas, geração de conteúdo e outras capacidades de LLM", - "openai_tab": "OpenAI", - "anthropic_tab": "Anthropic", - "voyage_tab": "Voyage AI", - "ollama_tab": "Ollama", - "enable_ai": "Ativar recursos IA/LLM", - "enable_ai_desc": "Ativar recursos de IA como sumarização de notas, geração de conteúdo e outras capacidades de LLM", - "provider_configuration": "Configuração de Provedor de IA", - "provider_precedence": "Prioridade de provedor", - "provider_precedence_description": "Lista de provedores em ordem de prioridade, separados por vírgula (por exemplo, 'openai, anthropic, ollama')", - "temperature": "Temperatura", - "temperature_description": "Controla a aleatoriedade em respostas (0 = determinística, 2 = aleatoriedade máxima)", - "system_prompt": "Prompt de Sistema", - "system_prompt_description": "Prompt padrão de sistema usado para todas as interações de IA", - "openai_configuration": "Configuração OpenAI", - "openai_settings": "Opções OpenAI", - "api_key": "Chave de API", - "url": "URL Base", - "model": "Modelo", - "openai_api_key_description": "A sua API Key da OpenAI para aceder os serviços de IA", - "anthropic_api_key_description": "A sua API Key da Anthropic para aceder os modelos Claude", - "default_model": "Modelo Padrão", - "openai_model_description": "Exemplos: gpt-4o, gpt-4-turbo, gpt-3.5-turbo", - "base_url": "URL Base", - "openai_url_description": "Padrão: https://api.openai.com/v1", - "anthropic_settings": "Configurações Anthropic", - "anthropic_url_description": "URL Base da API Anthropic (padrão: https://api.anthropic.com)", - "anthropic_model_description": "Modelos Claude da Anthropic para completar conversas", - "voyage_settings": "Configurações Voyage AI", - "ollama_settings": "Configurações do Ollama", - "ollama_url_description": "URL para a API Ollama (padrão: http://localhost:11434)", - "ollama_model_description": "Modelo Ollama usado para complementação de chat", - "anthropic_configuration": "Configuração da Anthropic", - "voyage_configuration": "Configuração da Voyage IA", - "voyage_url_description": "Padrão: https://api.voyageai.com/v1", - "ollama_configuration": "Configuração da Ollama", - "enable_ollama": "Ativar Ollama", - "enable_ollama_description": "Ativar Ollama para uso do modelo local de IA", - "ollama_url": "URL da Ollama", - "ollama_model": "Modelo do Ollama", - "refresh_models": "Atualizar Modelos", - "refreshing_models": "A atualizar…", - "enable_automatic_indexing": "Ativar indexação automática", - "rebuild_index": "Reconstruir Índice", - "rebuild_index_error": "Ocorreu um erro ao iniciar a reconstrução do índice. Verifique os logs para obter pormenores.", - "note_title": "Título da nota", - "error": "Erro", - "last_attempt": "Última Tentativa", - "actions": "Ações", - "retry": "Tentar novamente", - "partial": "{{ percentage }}% concluído", - "retry_queued": "Nota enfileirada para nova tentativa", - "retry_failed": "Falha ao enfileirar nota para nova tentativa", - "max_notes_per_llm_query": "Máximo de Notas por Consulta", - "max_notes_per_llm_query_description": "Quantidade máxima de notas similares para incluir no contexto da IA", - "active_providers": "Provedores Ativos", - "disabled_providers": "Provedores Desativados", - "remove_provider": "Remover provedor da pesquisa", - "restore_provider": "Restaurar provedor na pesquisa", - "similarity_threshold": "Tolerância de Similaridade", - "similarity_threshold_description": "Pontuação máxima de similaridade (0-1) para notas a serem incluídas no contexto das consultas de LLM", - "reprocess_index": "Reconstruir Índice de Pesquisa", - "reprocessing_index": "A reconstruir…", - "reprocess_index_started": "Otimiação do índice de pesquisa iniciado em plano de fundo", - "reprocess_index_error": "Erro ao reconstruir índice de pesquisa", - "index_rebuild_progress": "Andamento da Reconstrução do Índice", - "index_rebuilding": "A otimizar índice ({{percentage}}%)", - "index_rebuild_complete": "Otimização de índice finalizada", - "index_rebuild_status_error": "Erro ao verificar o estado da reconstrução do índice", - "never": "Nunca", - "processing": "A processar ({{percentage}}%)", - "incomplete": "Incompleto ({{percentage}}%)", - "complete": "Completo (100%)", - "refreshing": "A atualizar…", - "auto_refresh_notice": "A atualizar automaticamente a cada {{seconds}} segundos", - "note_queued_for_retry": "Nota enfileirada para nova tentativa", - "failed_to_retry_note": "Falha ao retentar nota", - "all_notes_queued_for_retry": "Todas as notas com falha enfileiradas para nova tentativa", - "failed_to_retry_all": "Falha ao retentar notas", - "ai_settings": "Configurações IA", - "api_key_tooltip": "Chave de API para aceder o serviço", - "empty_key_warning": { - "anthropic": "A chave de API Anthropic está vazia. Por favor, digite uma chave de API válida.", - "openai": "A chave de API OpenAI está vazia. Por favor, digite uma chave de API válida.", - "voyage": "A chave de API da Voyage API está vazia. Por favor, digite uma chave de API válida.", - "ollama": "A chave de API da Ollama API está vazia. Por favor, digite uma chave de API válida." - }, - "agent": { - "processing": "A processar…", - "thinking": "A pensar…", - "loading": "A carregar…", - "generating": "A gerir…" - }, - "name": "IA", - "openai": "OpenAI", - "use_enhanced_context": "Usar contexto aprimorado", - "enhanced_context_description": "Alimentar IA com mais contexto sobre a nota e as suas notas relacionadas para melhores respostas", - "show_thinking": "Exibir pensamento", - "show_thinking_description": "Exibir o processo de linha de raciocínio da AI", - "enter_message": "Digite a sua mensagem…", - "error_contacting_provider": "Erro ao contactar o provedor de IA. Por favor, verifique as suas configurações e a sua conexão à internet.", - "error_generating_response": "Erro ao gerar resposta da IA", - "index_all_notes": "Indexar Todas as Notas", - "index_status": "Estado do Índice", - "indexed_notes": "Notas Indexadas", - "indexing_stopped": "Indexação interrompida", - "indexing_in_progress": "Indexação em andamento…", - "last_indexed": "Última Indexada", - "note_chat": "Conversa de Nota", - "sources": "Origens", - "start_indexing": "Iniciar Indexação", - "use_advanced_context": "Usar Contexto Avançado", - "ollama_no_url": "Ollama não está configurado. Por favor, digite uma URL válida.", - "chat": { - "root_note_title": "Conversas IA", - "root_note_content": "Esta nota contém as suas conversas com IA gravdas.", - "new_chat_title": "Nova Conversa", - "create_new_ai_chat": "Criar Conversa IA" - }, - "create_new_ai_chat": "Criar Conversa IA", - "configuration_warnings": "Há problemas com a sua configuração de IA. Por favor, verifique as suas configurações.", - "experimental_warning": "O recurso de LLM atualmente é experimental - você foi avisado.", - "selected_provider": "Provedor Selecionado", - "selected_provider_description": "Escolha o provedor de IA para conversas e recursos de completar", - "select_model": "Selecionar modelo…", - "select_provider": "Selecionar provedor…", - "ai_enabled": "Recursos de IA ativados", - "ai_disabled": "Recursos de IA desativados", - "no_models_found_online": "Nenhum modelo encontrado. Por favor, verifique a sua chave de API e as configurações.", - "no_models_found_ollama": "Nenhum modelo Ollama encontrado. Por favor, verifique se o Ollama está em execução.", - "error_fetching": "Erro ao obter modelos: {{error}}" - }, "zoom_factor": { "title": "Fator do Zoom (apenas versão de área de trabalho)", "description": "O zoom também pode ser controlado com atalhos CTRL+- e CTRL+=." @@ -1691,7 +1548,6 @@ "confirm-change": "Não é recomentado alterar o tipo da nota quando o conteúdo da nota não está vazio. Quer continuar assim mesmo?", "geo-map": "Mapa geográfico", "beta-feature": "Beta", - "ai-chat": "Chat IA", "task-list": "Lista de Tarefas", "new-feature": "Novo", "collections": "Coleções" diff --git a/apps/client/src/translations/pt_br/translation.json b/apps/client/src/translations/pt_br/translation.json index 2c57dab23b..3db8d8c8e1 100644 --- a/apps/client/src/translations/pt_br/translation.json +++ b/apps/client/src/translations/pt_br/translation.json @@ -85,149 +85,6 @@ "clone_to_selected_note": "Clonar para a nota selecionada", "note_cloned": "A nota \"{{clonedTitle}}\" foi clonada para \"{{targetTitle}}\"" }, - "ai_llm": { - "temperature": "Temperatura", - "retry_queued": "Nota enfileirada para nova tentativa", - "queued_notes": "Notas Enfileiradas", - "empty_key_warning": { - "voyage": "A chave de API da Voyage API está vazia. Por favor, digite uma chave de API válida.", - "ollama": "A chave de API da Ollama API está vazia. Por favor, digite uma chave de API válida.", - "anthropic": "A chave de API Anthropic está vazia. Por favor, digite uma chave de API válida.", - "openai": "A chave de API OpenAI está vazia. Por favor, digite uma chave de API válida." - }, - "not_started": "Não iniciado", - "title": "Configurações de IA", - "processed_notes": "Notas Processadas", - "total_notes": "Total de Notas", - "progress": "Andamento", - "failed_notes": "Notas com Falha", - "last_processed": "Últimas Processadas", - "refresh_stats": "Atualizar Estatísticas", - "enable_ai_features": "Ativar recurso IA/LLM", - "enable_ai_description": "Ativar recursos IA como sumarização de notas, geração de conteúdo, e outras capacidades de LLM", - "openai_tab": "OpenAI", - "anthropic_tab": "Anthropic", - "voyage_tab": "Voyage AI", - "enable_ai": "Ativar recursos IA/LLM", - "provider_configuration": "Configuração de Provedor de IA", - "system_prompt": "Prompt de Sistema", - "system_prompt_description": "Prompt padrão de sistema usado para todas as interações de IA", - "openai_configuration": "Configuração OpenAI", - "openai_settings": "Opções OpenAI", - "api_key": "Chave de API", - "url": "URL Base", - "model": "Modelo", - "openai_api_key_description": "Sua API Key da OpenAI para acessar os serviços de IA", - "anthropic_api_key_description": "Sua API Key da Anthropic para acessar os modelos Claude", - "default_model": "Modelo Padrão", - "openai_model_description": "Exemplos: gpt-4o, gpt-4-turbo, gpt-3.5-turbo", - "base_url": "URL Base", - "openai_url_description": "Padrão: https://api.openai.com/v1", - "anthropic_settings": "Configurações Anthropic", - "anthropic_url_description": "URL Base da API Anthropic (padrão: https://api.anthropic.com)", - "anthropic_model_description": "Modelos Claude da Anthropic para completar conversas", - "voyage_settings": "Configurações Voyage AI", - "retry": "Tentar novamente", - "retry_failed": "Falha ao enfileirar nota para nova tentativa", - "max_notes_per_llm_query": "Máximo de Notas por Consulta", - "max_notes_per_llm_query_description": "Número máximo de notas similares para incluir no contexto da IA", - "active_providers": "Provedores Ativos", - "disabled_providers": "Provedores Desativados", - "remove_provider": "Remover provedor da pesquisa", - "restore_provider": "Restaurar provedor na pesquisa", - "similarity_threshold": "Tolerância de Similaridade", - "similarity_threshold_description": "Pontuação máxima de similaridade (0-1) para notas a serem incluídas no contexto das consultas de LLM", - "reprocess_index": "Reconstruir Índice de Pesquisa", - "reprocessing_index": "Reconstruindo…", - "reprocess_index_started": "Otimiação do índice de pesquisa iniciado em plano de fundo", - "reprocess_index_error": "Erro ao reconstruir índice de pesquisa", - "index_rebuild_progress": "Andamento da Reconstrução do Índice", - "index_rebuilding": "Otimizando índice ({{percentage}}%)", - "index_rebuild_complete": "Otimização de índice finalizada", - "index_rebuild_status_error": "Erro ao verificar o estado da reconstrução do índice", - "never": "Nunca", - "processing": "Processando ({{percentage}}%)", - "incomplete": "Incompleto ({{percentage}}%)", - "complete": "Completo (100%)", - "refreshing": "Atualizando…", - "auto_refresh_notice": "Atualizando automaticamente a cada {{seconds}} segundos", - "note_queued_for_retry": "Nota enfileirada para nova tentativa", - "failed_to_retry_note": "Falha ao retentar nota", - "all_notes_queued_for_retry": "Todas as notas com falha enfileiradas para nova tentativa", - "failed_to_retry_all": "Falha ao retentar notas", - "ai_settings": "Configurações IA", - "api_key_tooltip": "Chave de API para acessar o serviço", - "agent": { - "processing": "Processando…", - "thinking": "Pensando…", - "loading": "Carregando…", - "generating": "Gerando…" - }, - "name": "IA", - "openai": "OpenAI", - "use_enhanced_context": "Usar contexto aprimorado", - "enhanced_context_description": "Alimentar IA com mais contexto sobre a nota e suas notas relacionadas para melhores respostas", - "show_thinking": "Exibir pensamento", - "enter_message": "Digite sua mensagem…", - "error_contacting_provider": "Erro ao contatar o provedor de IA. Por favor, verifique suas configurações e sua conexão de internet.", - "error_generating_response": "Erro ao gerar resposta da IA", - "index_all_notes": "Indexar Todas as Notas", - "index_status": "Estado do Índice", - "indexed_notes": "Notas Indexadas", - "indexing_stopped": "Indexação interrompida", - "indexing_in_progress": "Indexação em andamento…", - "last_indexed": "Última Indexada", - "note_chat": "Conversa de Nota", - "sources": "Origens", - "start_indexing": "Iniciar Indexação", - "use_advanced_context": "Usar Contexto Avançado", - "ollama_no_url": "Ollama não está configurado. Por favor, digite uma URL válida.", - "chat": { - "root_note_title": "Conversas IA", - "root_note_content": "Esta nota contém suas conversas com IA salvas.", - "new_chat_title": "Nova Conversa", - "create_new_ai_chat": "Criar nova Conversa IA" - }, - "create_new_ai_chat": "Criar nova Conversa IA", - "configuration_warnings": "Existem alguns problemas com sua configuração de IA. Por fovor, verifique suas configurações.", - "experimental_warning": "O recurso de LLM atualmente é experimental - você foi avisado.", - "selected_provider": "Provedor Selecionado", - "selected_provider_description": "Escolha o provedor de IA para conversas e recursos de completar", - "select_model": "Selecionar modelo…", - "select_provider": "Selecionar provedor…", - "ai_enabled": "Recursos de IA habilitados", - "ai_disabled": "Recursos de IA desabilitados", - "no_models_found_online": "Nenhum modelo encontrado. Por favor, verifique sua chave de API e as configurações.", - "no_models_found_ollama": "Nenhum modelo Ollama encontrado. Por favor, verifique se o Ollama está em execução.", - "error_fetching": "Erro ao obter modelos: {{error}}", - "ollama_tab": "Ollama", - "enable_ai_desc": "Habilitar recursos de IA como sumarização de notas, geração de conteúdo, e outras capacidades de LLM", - "provider_precedence": "Prioridade de provedor", - "provider_precedence_description": "Lista de provedores em ordem de prioridade, separados por vírgula (por exemplo, 'openai, anthropic, ollama')", - "temperature_description": "Controla a aleatoriedade em respostas (0 = determinística, 2 = aleatoriedade máxima)", - "ollama_settings": "Configurações do Ollama", - "ollama_url_description": "URL para a API Ollama (padrão: http://localhost:11434)", - "ollama_model_description": "Modelo Ollama usado para complementação de chat", - "anthropic_configuration": "Configuração da Anthropic", - "voyage_configuration": "Configuração da Voyage IA", - "voyage_url_description": "Padrão: https://api.voyageai.com/v1", - "ollama_configuration": "Configuração da Ollama", - "enable_ollama": "Habilitar Ollama", - "enable_ollama_description": "Habilitar Ollama para uso do modelo local de IA", - "ollama_url": "URL da Ollama", - "ollama_model": "Modelo do Ollama", - "refresh_models": "Atualizar Modelos", - "refreshing_models": "Atualizando…", - "enable_automatic_indexing": "Habilitar indexação automática", - "rebuild_index": "Reconstruir Índice", - "rebuild_index_error": "Ocorreu um erro ao iniciar a reconstrução do índice. Verifique os logs para obter detalhes.", - "note_title": "Título da nota", - "error": "Erro", - "last_attempt": "Última Tentativa", - "actions": "Ações", - "partial": "{{ percentage }}% concluído", - "show_thinking_description": "Exibir o processo de linha de raciocínio da AI" - }, "confirm": { "confirmation": "Confirmação", "cancel": "Cancelar", @@ -1575,7 +1432,6 @@ "confirm-change": "Não é recomentado alterar o tipo da nota quando o conteúdo da nota não está vazio. Quer continuar assim mesmo?", "geo-map": "Mapa geográfico", "beta-feature": "Beta", - "ai-chat": "Chat IA", "task-list": "Lista de Tarefas", "new-feature": "Novo", "collections": "Coleções", diff --git a/apps/client/src/translations/ro/translation.json b/apps/client/src/translations/ro/translation.json index e955dab73b..a7bfd1f00b 100644 --- a/apps/client/src/translations/ro/translation.json +++ b/apps/client/src/translations/ro/translation.json @@ -1457,7 +1457,6 @@ "geo-map": "Hartă geografică", "beta-feature": "Beta", "task-list": "Listă de sarcini", - "ai-chat": "Discuție cu AI-ul", "new-feature": "Nou", "collections": "Colecții" }, @@ -1835,149 +1834,6 @@ "lock-editing": "Blochează editarea", "unlock-editing": "Deblochează editarea" }, - "ai_llm": { - "not_started": "Nu s-a pornit", - "title": "Setări AI", - "processed_notes": "Notițe procesate", - "total_notes": "Totalul de notițe", - "progress": "Progres", - "queued_notes": "Notițe în curs de procesare", - "failed_notes": "Notițe ce au eșuat", - "last_processed": "Ultima procesare", - "refresh_stats": "Reîmprospătare statistici", - "enable_ai_features": "Activează funcțiile AI/LLM", - "enable_ai_description": "Activează funcțiile AI precum sumarizarea notițelor, generarea de conținut și alte capabilități ale LLM-ului", - "openai_tab": "OpenAI", - "anthropic_tab": "Anthropic", - "voyage_tab": "Voyage AI", - "ollama_tab": "Ollama", - "enable_ai": "Activează funcții AI/LLM", - "enable_ai_desc": "Activează funcțiile AI precum sumarizarea notițelor, generarea de conținut și alte capabilități ale LLM-ului", - "provider_configuration": "Setările furnizorului de AI", - "provider_precedence": "Ordinea de precedență a furnizorilor", - "provider_precedence_description": "Lista de furnizori în ordinea de precedență, separate de virgulă (ex. „openai,anthropic,ollama”)", - "temperature": "Temperatură", - "temperature_description": "Controlează aleatoritatea din răspunsuri (0 = deterministic, 2 = maxim aleator)", - "system_prompt": "Prompt-ul de sistem", - "system_prompt_description": "Prompt-ul de sistem folosit pentru toate interacțiunile cu AI-ul", - "openai_configuration": "Configurația OpenAI", - "openai_settings": "Setările OpenAI", - "api_key": "Cheie API", - "url": "URL de bază", - "model": "Model", - "openai_api_key_description": "Cheia de API din OpenAI pentru a putea accesa serviciile de AI", - "anthropic_api_key_description": "Cheia de API din Anthropic pentru a accesa modelele Claude", - "default_model": "Modelul implicit", - "openai_model_description": "Exemple: gpt-4o, gpt-4-turbo, gpt-3.5-turbo", - "base_url": "URL de bază", - "openai_url_description": "Implicit: https://api.openai.com/v1", - "anthropic_settings": "Setări Anthropic", - "anthropic_url_description": "URL de bază pentru API-ul Anthropic (implicit: https://api.anthropic.com)", - "anthropic_model_description": "Modele Anthropic Claude pentru auto-completare", - "voyage_settings": "Setări Voyage AI", - "ollama_settings": "Setări Ollama", - "ollama_url_description": "URL pentru API-ul Ollama (implicit: http://localhost:11434)", - "ollama_model_description": "Modelul Ollama pentru auto-completare", - "anthropic_configuration": "Configurația Anthropic", - "voyage_configuration": "Configurația Voyage AI", - "voyage_url_description": "Implicit: https://api.voyageai.com/v1", - "ollama_configuration": "Configurația Ollama", - "enable_ollama": "Activează Ollama", - "enable_ollama_description": "Activează Ollama pentru a putea utiliza modele AI locale", - "ollama_url": "URL Ollama", - "ollama_model": "Model Ollama", - "refresh_models": "Reîmprospătează modelele", - "refreshing_models": "Reîmprospătare...", - "enable_automatic_indexing": "Activează indexarea automată", - "rebuild_index": "Reconstruire index", - "rebuild_index_error": "Eroare la reconstruirea indexului. Verificați logurile pentru mai multe detalii.", - "note_title": "Titlul notiței", - "error": "Eroare", - "last_attempt": "Ultima încercare", - "actions": "Acțiuni", - "retry": "Reîncercare", - "partial": "{{ percentage }}% finalizat", - "retry_queued": "Notiță pusă în coadă pentru reîncercare", - "retry_failed": "Nu s-a putut pune notița în coada pentru reîncercare", - "max_notes_per_llm_query": "Număr maximum de notițe per interogare", - "max_notes_per_llm_query_description": "Numărul maxim de notițe similare incluse în contextul pentru AI", - "active_providers": "Furnizori activi", - "disabled_providers": "Furnizori dezactivați", - "remove_provider": "Șterge furnizorul din căutare", - "restore_provider": "Restaurează furnizorul pentru căutare", - "similarity_threshold": "Prag de similaritate", - "similarity_threshold_description": "Scorul minim de similaritate (0-1) pentru a include notițele în contextul interogărilor LLM", - "reprocess_index": "Reconstruiește indexul de căutare", - "reprocessing_index": "Reconstruire...", - "reprocess_index_started": "S-a pornit în fundal optimizarea indexului de căutare", - "reprocess_index_error": "Eroare la reconstruirea indexului de căutare", - "index_rebuild_progress": "Reconstruire index în curs", - "index_rebuilding": "Optimizare index ({{percentage}}%)", - "index_rebuild_complete": "Optimizarea indexului a avut loc cu succes", - "index_rebuild_status_error": "Eroare la verificarea stării reconstruirii indexului", - "never": "Niciodată", - "processing": "Procesare ({{percentage}}%)", - "incomplete": "Incomplet ({{percentage}}%)", - "complete": "Complet (100%)", - "refreshing": "Reîmprospătare...", - "auto_refresh_notice": "Reîmprospătare automată la fiecare {{seconds}} secunde", - "note_queued_for_retry": "Notiță pusă în coadă pentru reîncercare", - "failed_to_retry_note": "Eroare la reîncercarea notiței", - "all_notes_queued_for_retry": "Toate notițele eșuate au fost puse în coada de reîncercare", - "failed_to_retry_all": "Eroare la reîncercarea notițelor", - "ai_settings": "Setări AI", - "api_key_tooltip": "Cheia API pentru accesarea serviciului", - "empty_key_warning": { - "anthropic": "Cheia API pentru Anthropic lipsește. Introduceți o cheie API validă.", - "openai": "Cheia API pentru OpenAI lipsește. Introduceți o cheie API validă.", - "voyage": "Cheia API pentru Voyage lipsește. Introduceți o cheie API validă.", - "ollama": "Cheia API pentru Ollama lipsește. Introduceți o cheie API validă." - }, - "agent": { - "processing": "Procesare...", - "thinking": "Raționalizare...", - "loading": "Încărcare...", - "generating": "Generare..." - }, - "name": "AI", - "openai": "OpenAI", - "use_enhanced_context": "Folosește context îmbogățit", - "enhanced_context_description": "Oferă AI-ului mai multe informații de context din notiță și notițele similare pentru răspunsuri mai bune", - "show_thinking": "Afișează procesul de raționalizare", - "show_thinking_description": "Afișează lanțul de acțiuni din procesul de gândire al AI-ului", - "enter_message": "Introduceți mesajul...", - "error_contacting_provider": "Eroare la contactarea furnizorului de AI. Verificați setările și conexiunea la internet.", - "error_generating_response": "Eroare la generarea răspunsului AI", - "index_all_notes": "Indexează toate notițele", - "index_status": "Starea indexării", - "indexed_notes": "Notițe indexate", - "indexing_stopped": "Indexarea s-a oprit", - "indexing_in_progress": "Indexare în curs...", - "last_indexed": "Ultima indexare", - "note_chat": "Discuție pe baza notițelor", - "sources": "Surse", - "start_indexing": "Indexează", - "use_advanced_context": "Folosește context îmbogățit", - "ollama_no_url": "Ollama nu este configurat. Introduceți un URL corect.", - "chat": { - "root_note_title": "Discuții cu AI-ul", - "root_note_content": "Această notiță stochează conversația cu AI-ul.", - "new_chat_title": "Discuție nouă", - "create_new_ai_chat": "Crează o nouă discuție cu AI-ul" - }, - "create_new_ai_chat": "Crează o nouă discuție cu AI-ul", - "configuration_warnings": "Sunt câteva probleme la configurația AI-ului. Verificați setările.", - "experimental_warning": "Funcția LLM este experimentală.", - "selected_provider": "Furnizor selectat", - "selected_provider_description": "Selectați furnizorul de AI pentru funcțiile de discuție și completare", - "select_model": "Selectați modelul...", - "select_provider": "Selectați furnizorul...", - "ai_enabled": "Funcționalitățile AI au fost activate", - "ai_disabled": "Funcționalitățile AI au fost dezactivate", - "no_models_found_online": "Nu s-a găsit niciun model. Verificați cheia API și configurația.", - "no_models_found_ollama": "Nu s-a găsit niciun model Ollama. Verificați dacă Ollama rulează.", - "error_fetching": "Eroare la obținerea modelelor: {{error}}" - }, "custom_date_time_format": { "title": "Format dată/timp personalizat", "description": "Personalizați formatul de dată și timp inserat prin sau din bara de unelte. Vedeți Documentația Day.js pentru câmpurile de formatare disponibile.", diff --git a/apps/client/src/translations/ru/translation.json b/apps/client/src/translations/ru/translation.json index aa495eb287..f023814bdc 100644 --- a/apps/client/src/translations/ru/translation.json +++ b/apps/client/src/translations/ru/translation.json @@ -824,7 +824,6 @@ "web-view": "Веб-страница", "mind-map": "Mind Map", "geo-map": "Географическая карта", - "ai-chat": "ИИ Чат", "task-list": "Список задач", "confirm-change": "Не рекомендуется менять тип заметки, если её содержимое не пустое. Вы всё равно хотите продолжить?" }, @@ -1257,149 +1256,6 @@ "disabled": "выключено", "title": "Системная панель заголовка (требует перезапуска приложения)" }, - "ai_llm": { - "progress": "Прогресс", - "openai_tab": "OpenAI", - "anthropic_tab": "Anthropic", - "ollama_tab": "Ollama", - "temperature": "Температура", - "model": "Модель", - "refreshing_models": "Обновление...", - "error": "Ошибка", - "actions": "Действия", - "retry": "Повторить", - "never": "Никогда", - "refreshing": "Обновление...", - "agent": { - "processing": "Обработка...", - "thinking": "Думаю...", - "loading": "Загрузка...", - "generating": "Создание..." - }, - "name": "AI", - "openai": "OpenAI", - "sources": "Источники", - "reprocessing_index": "Перестройка индекса...", - "processed_notes": "Обработанные заметки", - "total_notes": "Всего заметок", - "queued_notes": "Заметок в очереди", - "failed_notes": "Заметки с ошибками обработки", - "last_processed": "Последние обработанные", - "refresh_stats": "Обновить статистику", - "voyage_tab": "Voyage AI", - "system_prompt": "Системный промпт", - "openai_configuration": "Конфигурация OpenAI", - "openai_settings": "Настройки OpenAI", - "api_key": "Ключ API", - "url": "Базовый URL", - "default_model": "Модель по умолчанию", - "base_url": "Базовый URL", - "openai_url_description": "По умолчанию: https://api.openai.com/v1", - "anthropic_settings": "Настройки Anthropic", - "ollama_settings": "Настройки Ollama", - "anthropic_configuration": "Конфигурация Anthropic", - "voyage_url_description": "По умолчанию: https://api.voyageai.com/v1", - "ollama_configuration": "Конфигурация Ollama", - "enable_ollama": "Включить Ollama", - "ollama_url": "URL Ollama", - "ollama_model": "Модель Ollama", - "refresh_models": "Обновить модели", - "rebuild_index": "Пересобрать индекс", - "note_title": "Название заметки", - "last_attempt": "Последняя попытка", - "active_providers": "Активные провайдеры", - "disabled_providers": "Отключенные провайдеры", - "similarity_threshold": "Порок сходства", - "processing": "Обработка ({{percentage}}%)", - "incomplete": "Не завершено ({{percentage}}%)", - "complete": "Завершено (100%)", - "ai_settings": "Настройки AI", - "show_thinking": "Отображать размышление", - "index_status": "Статус индексирования", - "indexed_notes": "Проиндексированные заметки", - "indexing_stopped": "Индексирование остановлено", - "last_indexed": "Индексировано в последний раз", - "note_chat": "Чат по заметке", - "start_indexing": "Начать индексирование", - "chat": { - "root_note_title": "Чаты с AI", - "new_chat_title": "Новый чат", - "create_new_ai_chat": "Создать новый чат с ИИ", - "root_note_content": "В этой заметке содержатся сохраненные вами разговоры в чате ИИ." - }, - "selected_provider": "Выбранный провайдер", - "select_model": "Выбрать модель...", - "select_provider": "Выбрать провайдера...", - "title": "Настройки AI", - "voyage_settings": "Настройки Voyage AI", - "error_contacting_provider": "Ошибка подключения к провайдеру AI. Проверьте настройки и подключение к интернету.", - "configuration_warnings": "Возникли проблемы с конфигурацией AI. Проверьте настройки.", - "selected_provider_description": "Выберите провайдер AI для функций чата и автодополнения", - "provider_configuration": "Конфигурация провайдера AI", - "ollama_url_description": "URL для API Ollama (по умолчанию: http://localhost:11434)", - "ollama_model_description": "Модель Ollama для автодополнения чата", - "temperature_description": "Контролирует случайность ответов (0 = детерминированный, 2 = максимальная случайность)", - "system_prompt_description": "Системный промпт по умолчанию, используемый для всех взаимодействий с ИИ", - "empty_key_warning": { - "openai": "Ключ API OpenAI пуст. Введите действительный ключ API.", - "ollama": "API-ключ Ollama пуст. Введите действительный API-ключ.", - "voyage": "Ключ API Voyage пуст. Введите действительный ключ API.", - "anthropic": "Ключ API Anthropic пуст. Введите действительный ключ API." - }, - "openai_api_key_description": "Ваш ключ API OpenAI для доступа к их службам ИИ", - "provider_precedence_description": "Список провайдеров, разделенных запятыми, в порядке приоритета (например, \"openai,anthropic,ollama\")", - "remove_provider": "Удалить провайдер из поиска", - "not_started": "Не запущено", - "provider_precedence": "Приоритет провайдера", - "enable_ai_features": "Включить функции AI/LLM", - "enable_ai": "Включить функции AI/LLM", - "voyage_configuration": "Конфигурация Voyage AI", - "enable_automatic_indexing": "Включить автоматическое индексирование", - "reprocess_index": "Перестроить индекс поиска", - "index_rebuild_progress": "Прогресс перестройки индекса", - "index_rebuilding": "Оптимизация индекса ({{percentage}}%)", - "index_rebuild_complete": "Оптимизация индекса завершена", - "use_enhanced_context": "Использовать улучшенный контекст", - "enter_message": "Введите ваше сообщение...", - "index_all_notes": "Индексировать все заметки", - "indexing_in_progress": "Индексация в процессе...", - "use_advanced_context": "Использовать расширенный контекст", - "openai_model_description": "Примеры: gpt-4o, gpt-4-turbo, gpt-3.5-turbo", - "partial": "{{ percentage }}% завершено", - "retry_queued": "Примечание поставлено в очередь на повторную попытку", - "max_notes_per_llm_query": "Макс. количество заметок на запрос", - "reprocess_index_error": "Ошибка перестройки поискового индекса", - "auto_refresh_notice": "Автоматически обновляется каждые {{seconds}} секунд", - "note_queued_for_retry": "Заметка поставлена в очередь на повторную попытку", - "failed_to_retry_note": "Не удалось повторить попытку", - "failed_to_retry_all": "Не удалось повторить попытку", - "error_generating_response": "Ошибка генерации ответа ИИ", - "create_new_ai_chat": "Создать новый чат с ИИ", - "ai_enabled": "Возможности ИИ активны", - "ai_disabled": "Возможности ИИ неактивны", - "restore_provider": "Восстановить значение провайдера", - "error_fetching": "Ошибка получения списка моделей: {{error}}", - "index_rebuild_status_error": "Ошибка проверки статуса перестроения индекса", - "enhanced_context_description": "Предоставляет ИИ больше контекста из заметки и связанных с ней заметок для более точных ответов", - "no_models_found_ollama": "Модели Ollama не найдены. Проверьте, запущена ли Ollama.", - "no_models_found_online": "Модели не найдены. Проверьте ваш ключ API и настройки.", - "experimental_warning": "Функция LLM в настоящее время является экспериментальной — вы предупреждены.", - "ollama_no_url": "Ollama не настроена. Введите корректный URL-адрес.", - "show_thinking_description": "Показать цепочку мыслительного процесса ИИ", - "api_key_tooltip": "API-ключ для доступа к сервису", - "all_notes_queued_for_retry": "Все неудачные заметки поставлены в очередь на повторную попытку", - "reprocess_index_started": "Оптимизация поискового индекса запущена в фоновом режиме", - "similarity_threshold_description": "Минимальный показатель сходства (similarity score, 0–1) для заметок, которые следует включить в контекст запросов LLM", - "max_notes_per_llm_query_description": "Максимальное количество похожих заметок для включения в контекст ИИ", - "retry_failed": "Не удалось поставить заметку в очередь для повторной попытки", - "rebuild_index_error": "Ошибка при запуске перестроения индекса. Подробности смотрите в логах.", - "enable_ollama_description": "Включить Ollama для использования локальной модели ИИ", - "anthropic_model_description": "Модели Anthropic Claude для автодополнения чата", - "anthropic_url_description": "Базовый URL для Anthropic API (по умолчанию: https://api.anthropic.com)", - "anthropic_api_key_description": "Ваш ключ Anthropic API для доступа к моделям Claude", - "enable_ai_desc": "Включить функции ИИ, такие как резюмирование заметок, генерация контента и другие возможности LLM", - "enable_ai_description": "Включить функции ИИ, такие как резюмирование заметок, генерация контента и другие возможности LLM" - }, "code-editor-options": { "title": "Редактор" }, diff --git a/apps/client/src/translations/sl/translation.json b/apps/client/src/translations/sl/translation.json index cbd702bc0e..8271815413 100644 --- a/apps/client/src/translations/sl/translation.json +++ b/apps/client/src/translations/sl/translation.json @@ -1,9 +1,9 @@ { - "about": { - "title": "Podrobnosti Trilium Notes", - "homepage": "Domača stran:", - "app_version": "Verzija aplikacije:", - "db_version": "Verzija DB:", - "sync_version": "Verzija Sync:" - } + "about": { + "title": "Podrobnosti Trilium Notes", + "homepage": "Domača stran:", + "app_version": "Verzija aplikacije:", + "db_version": "Verzija DB:", + "sync_version": "Verzija Sync:" + } } diff --git a/apps/client/src/translations/tw/translation.json b/apps/client/src/translations/tw/translation.json index 39d2f73d60..71eaf097a5 100644 --- a/apps/client/src/translations/tw/translation.json +++ b/apps/client/src/translations/tw/translation.json @@ -1493,7 +1493,6 @@ "book": "集合", "geo-map": "地理地圖", "beta-feature": "Beta", - "ai-chat": "AI 聊天", "task-list": "任務列表", "new-feature": "新增", "collections": "集合" @@ -1743,149 +1742,6 @@ "zen_mode": { "button_exit": "退出禪模式" }, - "ai_llm": { - "not_started": "未開始", - "title": "AI 設定", - "processed_notes": "已處理筆記", - "total_notes": "筆記總數", - "progress": "進度", - "queued_notes": "隊列中筆記", - "failed_notes": "失敗筆記", - "last_processed": "最後處理時間", - "refresh_stats": "更新統計資料", - "enable_ai_features": "啟用 AI/LLM 功能", - "enable_ai_description": "啟用筆記摘要、內容生成等 AI 功能及其他 LLM 能力", - "openai_tab": "OpenAI", - "anthropic_tab": "Anthropic", - "voyage_tab": "Voyage AI", - "ollama_tab": "Ollama", - "enable_ai": "啟用 AI/LLM 功能", - "enable_ai_desc": "啟用筆記摘要、內容生成等 AI 功能及其他 LLM 能力", - "provider_configuration": "AI 提供者設定", - "provider_precedence": "提供者優先級", - "provider_precedence_description": "依優先級排序的提供者列表(用逗號分隔,例如:'openai,anthropic,ollama')", - "temperature": "溫度", - "temperature_description": "控制回應的隨機性(0 = 確定性,2 = 最大隨機性)", - "system_prompt": "系統提示詞", - "system_prompt_description": "所有 AI 互動的預設系統提示詞", - "openai_configuration": "OpenAI 設定", - "openai_settings": "OpenAI 設定", - "api_key": "API 金鑰", - "url": "基礎 URL", - "model": "模型", - "openai_api_key_description": "用於存取 OpenAI 服務的 API 金鑰", - "anthropic_api_key_description": "用於存取 Claude 模型的 Anthropic API 金鑰", - "default_model": "預設模型", - "openai_model_description": "範例:gpt-4o, gpt-4-turbo, gpt-3.5-turbo", - "base_url": "基礎 URL", - "openai_url_description": "預設:https://api.openai.com/v1", - "anthropic_settings": "Anthropic 設定", - "anthropic_url_description": "Anthropic API 的基礎 URL(預設:https://api.anthropic.com)", - "anthropic_model_description": "用於聊天補全的 Anthropic Claude 模型", - "voyage_settings": "Voyage AI 設定", - "ollama_settings": "Ollama 設定", - "ollama_url_description": "Ollama API URL(預設:http://localhost:11434)", - "ollama_model_description": "用於聊天補全的 Ollama 模型", - "anthropic_configuration": "Anthropic 設定", - "voyage_configuration": "Voyage AI 設定", - "voyage_url_description": "預設:https://api.voyageai.com/v1", - "ollama_configuration": "Ollama 設定", - "enable_ollama": "啟用 Ollama", - "enable_ollama_description": "啟用 Ollama 以使用本地 AI 模型", - "ollama_url": "Ollama URL", - "ollama_model": "Ollama 模型", - "refresh_models": "重新整理模型", - "refreshing_models": "正在重新整理…", - "enable_automatic_indexing": "啟用自動索引", - "rebuild_index": "重建索引", - "rebuild_index_error": "啟動索引重建失敗。請查看日誌了解詳情。", - "note_title": "筆記標題", - "error": "錯誤", - "last_attempt": "最後嘗試時間", - "actions": "操作", - "retry": "重試", - "partial": "已完成 {{ percentage }}%", - "retry_queued": "筆記已加入重試隊列", - "retry_failed": "筆記加入重試隊列失敗", - "max_notes_per_llm_query": "每次查詢的最大筆記數", - "max_notes_per_llm_query_description": "AI 上下文包含的最大相似筆記數量", - "active_providers": "活躍提供者", - "disabled_providers": "已禁用的提供者", - "remove_provider": "從搜尋中移除提供者", - "restore_provider": "將提供者還原至搜尋中", - "similarity_threshold": "相似度閾值", - "similarity_threshold_description": "要包含在 LLM 查詢上下文中筆記的最低相似度分數(0-1)", - "reprocess_index": "重建搜尋索引", - "reprocessing_index": "正在重建…", - "reprocess_index_started": "搜尋索引最佳化已在背景啟動", - "reprocess_index_error": "重建搜尋索引失敗", - "index_rebuild_progress": "索引重建進度", - "index_rebuilding": "正在最佳化索引({{percentage}}%)", - "index_rebuild_complete": "完成索引最佳化", - "index_rebuild_status_error": "檢查索引重建狀態失敗", - "never": "從未", - "processing": "正在處理({{percentage}}%)", - "incomplete": "未完成({{percentage}}%)", - "complete": "已完成(100%)", - "refreshing": "正在重新整理…", - "auto_refresh_notice": "每 {{seconds}} 秒自動重新整理", - "note_queued_for_retry": "筆記已加入重試隊列", - "failed_to_retry_note": "重試筆記失敗", - "all_notes_queued_for_retry": "所有失敗筆記已加入重試隊列", - "failed_to_retry_all": "重試筆記失敗", - "ai_settings": "AI 設定", - "api_key_tooltip": "用於存取服務的 API 金鑰", - "empty_key_warning": { - "anthropic": "Anthropic API 金鑰為空。請輸入有效的 API 金鑰。", - "openai": "OpenAI API 金鑰為空。請輸入有效的 API 金鑰。", - "voyage": "Voyage API 金鑰為空。請輸入有效的 API 金鑰。", - "ollama": "Ollama API 金鑰為空。請輸入有效的 API 金鑰。" - }, - "agent": { - "processing": "正在處理…", - "thinking": "正在思考…", - "loading": "正在載入…", - "generating": "正在生成…" - }, - "name": "AI", - "openai": "OpenAI", - "use_enhanced_context": "使用增強的上下文", - "enhanced_context_description": "提供 AI 更多來自筆記及其相關筆記的內容,以獲得更好的回應", - "show_thinking": "顯示思考過程", - "show_thinking_description": "顯示 AI 的思維鏈過程", - "enter_message": "輸入您的訊息…", - "error_contacting_provider": "聯繫 AI 提供者失敗。請檢查您的設定和網路連接。", - "error_generating_response": "生成 AI 回應失敗", - "index_all_notes": "為所有筆記建立索引", - "index_status": "索引狀態", - "indexed_notes": "已索引筆記", - "indexing_stopped": "已停止索引", - "indexing_in_progress": "正在進行索引…", - "last_indexed": "最後索引時間", - "note_chat": "筆記聊天", - "sources": "來源", - "start_indexing": "開始索引", - "use_advanced_context": "使用進階上下文", - "ollama_no_url": "尚未設定 Ollama。請輸入有效的 URL。", - "chat": { - "root_note_title": "AI 聊天記錄", - "root_note_content": "此筆記包含您儲存的 AI 聊天對話。", - "new_chat_title": "新聊天", - "create_new_ai_chat": "建立新的 AI 聊天" - }, - "create_new_ai_chat": "建立新的 AI 聊天", - "configuration_warnings": "您的 AI 配置存在一些問題。請檢查您的設定。", - "experimental_warning": "特此提醒:LLM 功能目前正處於實驗階段。", - "selected_provider": "已選提供者", - "selected_provider_description": "選擇用於聊天和補全功能的 AI 提供者", - "select_model": "選擇模型…", - "select_provider": "選擇提供者…", - "ai_enabled": "已啟用 AI 功能", - "ai_disabled": "已禁用 AI 功能", - "no_models_found_online": "找不到模型。請檢查您的 API 金鑰及設定。", - "no_models_found_ollama": "找不到 Ollama 模型。請確認 Ollama 是否正在執行。", - "error_fetching": "獲取模型失敗:{{error}}" - }, "code-editor-options": { "title": "編輯器" }, diff --git a/apps/client/src/translations/uk/translation.json b/apps/client/src/translations/uk/translation.json index c2c8a5588d..8861fca0bc 100644 --- a/apps/client/src/translations/uk/translation.json +++ b/apps/client/src/translations/uk/translation.json @@ -1236,149 +1236,6 @@ "layout-vertical-description": "Панель запуску знаходиться ліворуч (за замовчуванням)", "layout-horizontal-description": "Панель запуску знаходиться під панеллю вкладок, панель вкладок тепер має повну ширину." }, - "ai_llm": { - "not_started": "Не розпочато", - "title": "Параметри AI", - "processed_notes": "Оброблені нотатки", - "total_notes": "Всього нотаток", - "progress": "Прогрес", - "queued_notes": "Черга нотаток", - "failed_notes": "Невдалі нотатки", - "last_processed": "Остання обробка", - "refresh_stats": "Оновити статистику", - "enable_ai_features": "Увімкнути функції AI/LLM", - "enable_ai_description": "Увімкніть функції AI, такі як підсумовування нотаток, генерація контенту та інші можливості LLM", - "openai_tab": "OpenAI", - "anthropic_tab": "Anthropic", - "voyage_tab": "Voyage AI", - "ollama_tab": "Ollama", - "enable_ai": "Увімкнути функції AI/LLM", - "enable_ai_desc": "Увімкнути функції AI, такі як підсумовування нотаток, генерація контенту та інші можливості LLM", - "provider_configuration": "Конфігурація постачальника AI", - "provider_precedence": "Пріоритет постачальника", - "provider_precedence_description": "Список постачальників, розділених комами, у порядку пріоритету (наприклад, «openai,anthropic,ollama»)", - "temperature": "Температура", - "temperature_description": "Контролює випадковість відповідей (0 = детермінований, 2 = максимальна випадковість)", - "system_prompt": "Системний Запит (Prompt)", - "system_prompt_description": "Системний запит (prompt) за замовчуванням використовується для всіх взаємодій з AI", - "openai_configuration": "Конфігурація OpenAI", - "openai_settings": "Налаштування OpenAI", - "api_key": "API Key", - "url": "Base URL", - "model": "Модель", - "openai_api_key_description": "Ваш ключ OpenAI API для доступу до служб AI", - "anthropic_api_key_description": "Ваш ключ Anthropic API для доступу до моделей Claude", - "default_model": "Модель за замовчуванням", - "openai_model_description": "Наприклад: gpt-4o, gpt-4-turbo, gpt-3.5-turbo", - "base_url": "Base URL", - "openai_url_description": "За замовчуванням: https://api.openai.com/v1", - "anthropic_settings": "Налаштування Anthropic", - "anthropic_url_description": "Базова URL-адреса для Anthropic API (за замовчуванням: https://api.anthropic.com)", - "anthropic_model_description": "Моделі Anthropic Claude для чату", - "voyage_settings": "Налаштування Voyage AI", - "ollama_settings": "Налаштування Ollama", - "ollama_url_description": "URL для Ollama API (default: http://localhost:11434)", - "ollama_model_description": "Модель Ollama для чату", - "anthropic_configuration": "Конфігурація Anthropic", - "voyage_configuration": "Конфігурація Voyage AI", - "voyage_url_description": "За замовчуванням: https://api.voyageai.com/v1", - "ollama_configuration": "Конфігурація Ollama", - "enable_ollama": "Увімкнути Ollama", - "enable_ollama_description": "Увімкнути Ollama для локальної моделі AI", - "ollama_url": "Ollama URL", - "ollama_model": "Модель Ollama", - "refresh_models": "Оновити моделі", - "refreshing_models": "Оновлення...", - "enable_automatic_indexing": "Увімкнути автоматичне індексування", - "rebuild_index": "Перебудувати індекс", - "rebuild_index_error": "Помилка початку перебудови індексу. Перегляньте logs для інформації.", - "note_title": "Заголовок нотатки", - "error": "Помилка", - "last_attempt": "Остання спроба", - "actions": "Дії", - "retry": "Повторити спробу", - "partial": "{{ percentage }}% completed", - "retry_queued": "Нотатка в черзі на повторну спробу", - "retry_failed": "Не вдалося додати нотатку до черги для повторної спроби", - "max_notes_per_llm_query": "Максимальна кількість нотаток на запит", - "max_notes_per_llm_query_description": "Максимальна кількість схожих нотаток для включення в контекст AI", - "active_providers": "Активні постачальники", - "disabled_providers": "Вимкнути постачальників", - "remove_provider": "Видалити постачальника з пошуку", - "restore_provider": "Відновити постачальника для пошуку", - "similarity_threshold": "Поріг схожості", - "similarity_threshold_description": "Мінімальна оцінка схожості (0-1) для нотаток, що включатимуться в контекст для запитів LLM", - "reprocess_index": "Перебудувати індекс пошуку", - "reprocessing_index": "Відбудова...", - "reprocess_index_started": "Оптимізація пошукового індексу розпочата у фоновому режимі", - "reprocess_index_error": "Помилка відбудови індексу пошуку", - "index_rebuild_progress": "Прогрес відбудови індексу", - "index_rebuilding": "Індекс оптимізації ({{percentage}}%)", - "index_rebuild_complete": "Оптимізацію індексу завершено", - "index_rebuild_status_error": "Помилка перевірки стану перебудови індексу", - "never": "Ніколи", - "processing": "Обробка ({{percentage}}%)", - "incomplete": "Незавершено ({{percentage}}%)", - "complete": "Завершено (100%)", - "refreshing": "Оновлення...", - "auto_refresh_notice": "Автоматичне оновлення кожні {{seconds}} секунд", - "note_queued_for_retry": "Нотатка в черзі на повторну спробу", - "failed_to_retry_note": "Не вдалося повторити спробу", - "all_notes_queued_for_retry": "Усі невдалі нотатки поставлені в чергу на повторну спробу", - "failed_to_retry_all": "Не вдалося повторити спробу", - "ai_settings": "Налаштування AI", - "api_key_tooltip": "Ключ API для доступу до сервісу", - "empty_key_warning": { - "anthropic": "Ключ API Anthropic порожній. Будь ласка, введіть дійсний ключ API.", - "openai": "Ключ API OpenAI порожній. Будь ласка, введіть дійсний ключ API.", - "voyage": "Ключ Voyage API подорожі порожній. Будь ласка, введіть дійсний ключ API.", - "ollama": "Ключ API Ollama порожній. Будь ласка, введіть дійсний ключ API." - }, - "agent": { - "processing": "Обробка...", - "thinking": "Думаю...", - "loading": "Завантаження...", - "generating": "Генерування..." - }, - "name": "AI", - "openai": "OpenAI", - "use_enhanced_context": "Використовувати покращений контекст", - "enhanced_context_description": "Надає AI більше контексту з нотатки та пов'язаних з нею нотаток для кращих відповідей", - "show_thinking": "Показати міркування", - "show_thinking_description": "Показати ланцюжок міркувань AI", - "enter_message": "Введіть ваше повідомлення...", - "error_contacting_provider": "Помилка зв’язку з постачальником AI. Перевірте налаштування та підключення до Інтернету.", - "error_generating_response": "Помилка створення відповіді AI", - "index_all_notes": "Індексація усіх нотаток", - "index_status": "Статус індексації", - "indexed_notes": "Індексовані нотатки", - "indexing_stopped": "Індексацію зупинено", - "indexing_in_progress": "Триває індексація...", - "last_indexed": "Остання індексація", - "note_chat": "Нотатка Чат", - "sources": "Джерела", - "start_indexing": "Почати індексацію", - "use_advanced_context": "Використовувати розширений контекст", - "ollama_no_url": "Ollama не налаштовано. Будь ласка, введіть дійсну URL-адресу.", - "chat": { - "root_note_title": "AI Чати", - "root_note_content": "Ця нотатка містить ваші збережені розмови в чаті з AI.", - "new_chat_title": "Новий Чат", - "create_new_ai_chat": "Створити новий AI Чат" - }, - "create_new_ai_chat": "Створити новий AI Чат", - "configuration_warnings": "Виникли деякі проблеми з конфігурацією AI. Перевірте налаштування.", - "experimental_warning": "Функція LLM наразі є експериментальною – вас попередили.", - "selected_provider": "Вибраний постачальник", - "selected_provider_description": "Вибрати постачальника послуг AI для функцій чату та автозаповнення", - "select_model": "Виберіть модель...", - "select_provider": "Виберіть постачальника...", - "ai_enabled": "Функції AI увімкнено", - "ai_disabled": "Функції AI вимкнено", - "no_models_found_online": "Моделей не знайдено. Будь ласка, перевірте свій ключ API та налаштування.", - "no_models_found_ollama": "Моделей Ollama не знайдено. Перевірте, чи працює Ollama.", - "error_fetching": "Помилка отримання моделей: {{error}}" - }, "backup": { "automatic_backup": "Автоматичне резервне копіювання", "automatic_backup_description": "Trilium може автоматично створювати резервні копії бази даних:", @@ -1957,7 +1814,6 @@ "confirm-change": "Не рекомендується змінювати тип нотатки, якщо її вміст не порожній. Ви все одно хочете продовжити?", "geo-map": "Географічна карта", "beta-feature": "Бета", - "ai-chat": "Чат AI", "task-list": "Список завдань", "new-feature": "Нова", "collections": "Колекції", diff --git a/apps/client/src/widgets/launch_bar/LauncherContainer.tsx b/apps/client/src/widgets/launch_bar/LauncherContainer.tsx index d202feaf35..79de559c98 100644 --- a/apps/client/src/widgets/launch_bar/LauncherContainer.tsx +++ b/apps/client/src/widgets/launch_bar/LauncherContainer.tsx @@ -10,7 +10,7 @@ import BookmarkButtons from "./BookmarkButtons"; import CalendarWidget from "./CalendarWidget"; import HistoryNavigationButton from "./HistoryNavigation"; import { LaunchBarContext } from "./launch_bar_widgets"; -import { AiChatButton, CommandButton, CustomWidget, NoteLauncher, QuickSearchLauncherWidget, ScriptLauncher, TodayLauncher } from "./LauncherDefinitions"; +import { CommandButton, CustomWidget, NoteLauncher, QuickSearchLauncherWidget, ScriptLauncher, TodayLauncher } from "./LauncherDefinitions"; import ProtectedSessionStatusWidget from "./ProtectedSessionStatusWidget"; import SpacerWidget from "./SpacerWidget"; import SyncStatus from "./SyncStatus"; @@ -67,7 +67,7 @@ function Launcher({ note, isHorizontalLayout }: { note: FNote, isHorizontalLayou case "builtinWidget": return initBuiltinWidget(note, isHorizontalLayout); default: - throw new Error(`Unrecognized launcher type '${launcherType}' for launcher '${note.noteId}' title '${note.title}'`); + console.warn(`Unrecognized launcher type '${launcherType}' for launcher '${note.noteId}' title '${note.title}'`); } } @@ -96,12 +96,10 @@ function initBuiltinWidget(note: FNote, isHorizontalLayout: boolean) { return ; case "quickSearch": return ; - case "aiChatLauncher": - return ; case "mobileTabSwitcher": return ; default: - throw new Error(`Unrecognized builtin widget ${builtinWidget} for launcher ${note.noteId} "${note.title}"`); + console.warn(`Unrecognized builtin widget ${builtinWidget} for launcher ${note.noteId} "${note.title}"`); } } diff --git a/apps/client/src/widgets/launch_bar/LauncherDefinitions.tsx b/apps/client/src/widgets/launch_bar/LauncherDefinitions.tsx index 5aa289257a..408780224c 100644 --- a/apps/client/src/widgets/launch_bar/LauncherDefinitions.tsx +++ b/apps/client/src/widgets/launch_bar/LauncherDefinitions.tsx @@ -11,7 +11,7 @@ import { getErrorMessage, isMobile } from "../../services/utils"; import BasicWidget from "../basic_widget"; import NoteContextAwareWidget from "../note_context_aware_widget"; import QuickSearchWidget from "../quick_search"; -import { useGlobalShortcut, useLegacyWidget, useNoteLabel, useNoteRelationTarget, useTriliumOptionBool } from "../react/hooks"; +import { useGlobalShortcut, useLegacyWidget, useNoteLabel, useNoteRelationTarget } from "../react/hooks"; import { ParentComponent } from "../react/react_utils"; import { CustomNoteLauncher } from "./GenericButtons"; import { LaunchBarActionButton, LaunchBarContext, LauncherNoteProps, useLauncherIconAndTitle } from "./launch_bar_widgets"; @@ -81,19 +81,6 @@ export function ScriptLauncher({ launcherNote }: LauncherNoteProps) { ); } -export function AiChatButton({ launcherNote }: LauncherNoteProps) { - const [ aiEnabled ] = useTriliumOptionBool("aiEnabled"); - const { icon, title } = useLauncherIconAndTitle(launcherNote); - - return aiEnabled && ( - - ); -} - export function TodayLauncher({ launcherNote }: LauncherNoteProps) { return ( { - try { - const resp = await server.post('llm/chat', { - title: 'Note Chat', - currentNoteId: currentNoteId // Pass the current note ID if available - }); - - if (resp && resp.id) { - // Backend returns the chat note ID as 'id' - return resp.id; - } - } catch (error) { - console.error('Failed to create chat session:', error); - } - - return null; -} - -/** - * Check if a chat note exists - * @param noteId - The ID of the chat note - */ -export async function checkSessionExists(noteId: string): Promise { - try { - const sessionCheck = await server.getWithSilentNotFound(`llm/chat/${noteId}`); - return !!(sessionCheck && sessionCheck.id); - } catch (error: any) { - console.log(`Error checking chat note ${noteId}:`, error); - return false; - } -} - -/** - * Set up streaming response via WebSocket - * @param noteId - The ID of the chat note - * @param messageParams - Message parameters - * @param onContentUpdate - Callback for content updates - * @param onThinkingUpdate - Callback for thinking updates - * @param onToolExecution - Callback for tool execution - * @param onComplete - Callback for completion - * @param onError - Callback for errors - */ -export async function setupStreamingResponse( - noteId: string, - messageParams: any, - onContentUpdate: (content: string, isDone?: boolean) => void, - onThinkingUpdate: (thinking: string) => void, - onToolExecution: (toolData: any) => void, - onComplete: () => void, - onError: (error: Error) => void -): Promise { - return new Promise((resolve, reject) => { - let assistantResponse = ''; - let receivedAnyContent = false; - let timeoutId: number | null = null; - let initialTimeoutId: number | null = null; - let cleanupTimeoutId: number | null = null; - let receivedAnyMessage = false; - let eventListener: ((event: Event) => void) | null = null; - let lastMessageTimestamp = 0; - - // Create a unique identifier for this response process - const responseId = `llm-stream-${Date.now()}-${Math.floor(Math.random() * 1000)}`; - console.log(`[${responseId}] Setting up WebSocket streaming for chat note ${noteId}`); - - // Send the initial request to initiate streaming - (async () => { - try { - const streamResponse = await server.post(`llm/chat/${noteId}/messages/stream`, { - content: messageParams.content, - useAdvancedContext: messageParams.useAdvancedContext, - showThinking: messageParams.showThinking, - options: { - temperature: 0.7, - maxTokens: 2000 - } - }); - - if (!streamResponse || !streamResponse.success) { - console.error(`[${responseId}] Failed to initiate streaming`); - reject(new Error('Failed to initiate streaming')); - return; - } - - console.log(`[${responseId}] Streaming initiated successfully`); - } catch (error) { - console.error(`[${responseId}] Error initiating streaming:`, error); - reject(error); - return; - } - })(); - - // Function to safely perform cleanup - const performCleanup = () => { - if (cleanupTimeoutId) { - window.clearTimeout(cleanupTimeoutId); - cleanupTimeoutId = null; - } - - console.log(`[${responseId}] Performing final cleanup of event listener`); - cleanupEventListener(eventListener); - onComplete(); - resolve(); - }; - - // Set initial timeout to catch cases where no message is received at all - initialTimeoutId = window.setTimeout(() => { - if (!receivedAnyMessage) { - console.error(`[${responseId}] No initial message received within timeout`); - performCleanup(); - reject(new Error('No response received from server')); - } - }, 10000); - - // Create a message handler for CustomEvents - eventListener = (event: Event) => { - const customEvent = event as CustomEvent; - const message = customEvent.detail; - - // Only process messages for our chat note - if (!message || message.chatNoteId !== noteId) { - return; - } - - // Update last message timestamp - lastMessageTimestamp = Date.now(); - - // Cancel any pending cleanup when we receive a new message - if (cleanupTimeoutId) { - console.log(`[${responseId}] Cancelling scheduled cleanup due to new message`); - window.clearTimeout(cleanupTimeoutId); - cleanupTimeoutId = null; - } - - console.log(`[${responseId}] LLM Stream message received: content=${!!message.content}, contentLength=${message.content?.length || 0}, thinking=${!!message.thinking}, toolExecution=${!!message.toolExecution}, done=${!!message.done}`); - - // Mark first message received - if (!receivedAnyMessage) { - receivedAnyMessage = true; - console.log(`[${responseId}] First message received for chat note ${noteId}`); - - // Clear the initial timeout since we've received a message - if (initialTimeoutId !== null) { - window.clearTimeout(initialTimeoutId); - initialTimeoutId = null; - } - } - - // Handle error - if (message.error) { - console.error(`[${responseId}] Stream error: ${message.error}`); - performCleanup(); - reject(new Error(message.error)); - return; - } - - // Handle thinking updates - only show if showThinking is enabled - if (message.thinking && messageParams.showThinking) { - console.log(`[${responseId}] Received thinking: ${message.thinking.substring(0, 100)}...`); - onThinkingUpdate(message.thinking); - } - - // Handle tool execution updates - if (message.toolExecution) { - console.log(`[${responseId}] Tool execution update:`, message.toolExecution); - onToolExecution(message.toolExecution); - } - - // Handle content updates - if (message.content) { - // Simply append the new content - no complex deduplication - assistantResponse += message.content; - - // Update the UI immediately with each chunk - onContentUpdate(assistantResponse, message.done || false); - receivedAnyContent = true; - - // Reset timeout since we got content - if (timeoutId !== null) { - window.clearTimeout(timeoutId); - } - - // Set new timeout - timeoutId = window.setTimeout(() => { - console.warn(`[${responseId}] Stream timeout for chat note ${noteId}`); - performCleanup(); - reject(new Error('Stream timeout')); - }, 30000); - } - - // Handle completion - if (message.done) { - console.log(`[${responseId}] Stream completed for chat note ${noteId}, final response: ${assistantResponse.length} chars`); - - // Clear all timeouts - if (timeoutId !== null) { - window.clearTimeout(timeoutId); - timeoutId = null; - } - - // Schedule cleanup after a brief delay to ensure all processing is complete - cleanupTimeoutId = window.setTimeout(() => { - performCleanup(); - }, 100); - } - }; - - // Register the event listener for WebSocket messages - window.addEventListener('llm-stream-message', eventListener); - - console.log(`[${responseId}] Event listener registered, waiting for messages...`); - }); -} - -/** - * Clean up an event listener - */ -function cleanupEventListener(listener: ((event: Event) => void) | null): void { - if (listener) { - try { - window.removeEventListener('llm-stream-message', listener); - console.log(`Successfully removed event listener`); - } catch (err) { - console.error(`Error removing event listener:`, err); - } - } -} - -/** - * Get a direct response from the server without streaming - */ -export async function getDirectResponse(noteId: string, messageParams: any): Promise { - try { - const postResponse = await server.post(`llm/chat/${noteId}/messages`, { - message: messageParams.content, - includeContext: messageParams.useAdvancedContext, - options: { - temperature: 0.7, - maxTokens: 2000 - } - }); - - return postResponse; - } catch (error) { - console.error('Error getting direct response:', error); - throw error; - } -} - diff --git a/apps/client/src/widgets/llm_chat/index.ts b/apps/client/src/widgets/llm_chat/index.ts deleted file mode 100644 index 8f0eb9f2d1..0000000000 --- a/apps/client/src/widgets/llm_chat/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -/** - * LLM Chat Panel Widget Module - */ -import LlmChatPanel from './llm_chat_panel.js'; - -export default LlmChatPanel; diff --git a/apps/client/src/widgets/llm_chat/llm_chat_panel.ts b/apps/client/src/widgets/llm_chat/llm_chat_panel.ts deleted file mode 100644 index 3187018cf5..0000000000 --- a/apps/client/src/widgets/llm_chat/llm_chat_panel.ts +++ /dev/null @@ -1,1791 +0,0 @@ -/** - * LLM Chat Panel Widget - */ -import BasicWidget from "../basic_widget.js"; -import toastService from "../../services/toast.js"; -import appContext from "../../components/app_context.js"; -import server from "../../services/server.js"; -import noteAutocompleteService from "../../services/note_autocomplete.js"; - -import { TPL, addMessageToChat, showSources, hideSources, showLoadingIndicator, hideLoadingIndicator } from "./ui.js"; -import { formatMarkdown } from "./utils.js"; -import { createChatSession, checkSessionExists, setupStreamingResponse, getDirectResponse } from "./communication.js"; -import { extractInChatToolSteps } from "./message_processor.js"; -import { validateProviders } from "./validation.js"; -import type { MessageData, ToolExecutionStep, ChatData } from "./types.js"; -import { formatCodeBlocks } from "../../services/syntax_highlight.js"; -import { ClassicEditor, type CKTextEditor, type MentionFeed } from "@triliumnext/ckeditor5"; -import type { Suggestion } from "../../services/note_autocomplete.js"; - -import "../../stylesheets/llm_chat.css"; - -export default class LlmChatPanel extends BasicWidget { - private noteContextChatMessages!: HTMLElement; - private noteContextChatForm!: HTMLFormElement; - private noteContextChatInput!: HTMLElement; - private noteContextChatInputEditor!: CKTextEditor; - private noteContextChatSendButton!: HTMLButtonElement; - private chatContainer!: HTMLElement; - private loadingIndicator!: HTMLElement; - private sourcesList!: HTMLElement; - private sourcesContainer!: HTMLElement; - private sourcesCount!: HTMLElement; - private useAdvancedContextCheckbox!: HTMLInputElement; - private showThinkingCheckbox!: HTMLInputElement; - private validationWarning!: HTMLElement; - private thinkingContainer!: HTMLElement; - private thinkingBubble!: HTMLElement; - private thinkingText!: HTMLElement; - private thinkingToggle!: HTMLElement; - - // Simplified to just use noteId - this represents the AI Chat note we're working with - private noteId: string | null = null; - private currentNoteId: string | null = null; // The note providing context (for regular notes) - private _messageHandlerId: number | null = null; - private _messageHandler: any = null; - - // Callbacks for data persistence - private onSaveData: ((data: any) => Promise) | null = null; - private onGetData: (() => Promise) | null = null; - private messages: MessageData[] = []; - private sources: Array<{noteId: string; title: string; similarity?: number; content?: string}> = []; - private metadata: { - model?: string; - provider?: string; - temperature?: number; - maxTokens?: number; - toolExecutions?: Array<{ - id: string; - name: string; - arguments: any; - result: any; - error?: string; - timestamp: string; - }>; - lastUpdated?: string; - usage?: { - promptTokens?: number; - completionTokens?: number; - totalTokens?: number; - }; - } = { - temperature: 0.7, - toolExecutions: [] - }; - - // Public getters and setters for private properties - public getCurrentNoteId(): string | null { - return this.currentNoteId; - } - - public setCurrentNoteId(noteId: string | null): void { - this.currentNoteId = noteId; - } - - public getMessages(): MessageData[] { - return this.messages; - } - - public setMessages(messages: MessageData[]): void { - this.messages = messages; - } - - public getNoteId(): string | null { - return this.noteId; - } - - public setNoteId(noteId: string | null): void { - this.noteId = noteId; - } - - // Deprecated - keeping for backward compatibility but mapping to noteId - public getChatNoteId(): string | null { - return this.noteId; - } - - public setChatNoteId(noteId: string | null): void { - this.noteId = noteId; - } - - public getNoteContextChatMessages(): HTMLElement { - return this.noteContextChatMessages; - } - - public clearNoteContextChatMessages(): void { - this.noteContextChatMessages.innerHTML = ''; - } - - doRender() { - this.$widget = $(TPL); - - const element = this.$widget[0]; - this.noteContextChatMessages = element.querySelector('.note-context-chat-messages') as HTMLElement; - this.noteContextChatForm = element.querySelector('.note-context-chat-form') as HTMLFormElement; - this.noteContextChatInput = element.querySelector('.note-context-chat-input') as HTMLElement; - this.noteContextChatSendButton = element.querySelector('.note-context-chat-send-button') as HTMLButtonElement; - this.chatContainer = element.querySelector('.note-context-chat-container') as HTMLElement; - this.loadingIndicator = element.querySelector('.loading-indicator') as HTMLElement; - this.sourcesList = element.querySelector('.sources-list') as HTMLElement; - this.sourcesContainer = element.querySelector('.sources-container') as HTMLElement; - this.sourcesCount = element.querySelector('.sources-count') as HTMLElement; - this.useAdvancedContextCheckbox = element.querySelector('.use-advanced-context-checkbox') as HTMLInputElement; - this.showThinkingCheckbox = element.querySelector('.show-thinking-checkbox') as HTMLInputElement; - this.validationWarning = element.querySelector('.provider-validation-warning') as HTMLElement; - this.thinkingContainer = element.querySelector('.llm-thinking-container') as HTMLElement; - this.thinkingBubble = element.querySelector('.thinking-bubble') as HTMLElement; - this.thinkingText = element.querySelector('.thinking-text') as HTMLElement; - this.thinkingToggle = element.querySelector('.thinking-toggle') as HTMLElement; - - // Set up event delegation for the settings link - this.validationWarning.addEventListener('click', (e) => { - const target = e.target as HTMLElement; - if (target.classList.contains('settings-link') || target.closest('.settings-link')) { - console.log('Settings link clicked, navigating to AI settings URL'); - window.location.href = '#root/_hidden/_options/_optionsAi'; - } - }); - - // Set up thinking toggle functionality - this.setupThinkingToggle(); - - // Initialize CKEditor with mention support (async) - this.initializeCKEditor().then(() => { - this.initializeEventListeners(); - }).catch(error => { - console.error('Failed to initialize CKEditor, falling back to basic event listeners:', error); - this.initializeBasicEventListeners(); - }); - - return this.$widget; - } - - private async initializeCKEditor() { - const mentionSetup: MentionFeed[] = [ - { - marker: "@", - feed: (queryText: string) => noteAutocompleteService.autocompleteSourceForCKEditor(queryText), - itemRenderer: (item) => { - const suggestion = item as Suggestion; - const itemElement = document.createElement("button"); - itemElement.innerHTML = `${suggestion.highlightedNotePathTitle} `; - return itemElement; - }, - minimumCharacters: 0 - } - ]; - - this.noteContextChatInputEditor = await ClassicEditor.create(this.noteContextChatInput, { - toolbar: { - items: [] // No toolbar for chat input - }, - placeholder: this.noteContextChatInput.getAttribute('data-placeholder') || 'Enter your message...', - mention: { - feeds: mentionSetup - }, - licenseKey: "GPL" - }); - - // Set minimal height - const editorElement = this.noteContextChatInputEditor.ui.getEditableElement(); - if (editorElement) { - editorElement.style.minHeight = '60px'; - editorElement.style.maxHeight = '200px'; - editorElement.style.overflowY = 'auto'; - } - - // Set up keybindings after editor is ready - this.setupEditorKeyBindings(); - - console.log('CKEditor initialized successfully for LLM chat input'); - } - - private initializeBasicEventListeners() { - // Fallback event listeners for when CKEditor fails to initialize - this.noteContextChatForm.addEventListener('submit', (e) => { - e.preventDefault(); - // In fallback mode, the noteContextChatInput should contain a textarea - const textarea = this.noteContextChatInput.querySelector('textarea'); - if (textarea) { - const content = textarea.value; - this.sendMessage(content); - } - }); - } - - cleanup() { - console.log(`LlmChatPanel cleanup called, removing any active WebSocket subscriptions`); - this._messageHandler = null; - this._messageHandlerId = null; - - // Clean up CKEditor instance - if (this.noteContextChatInputEditor) { - this.noteContextChatInputEditor.destroy().catch(error => { - console.error('Error destroying CKEditor:', error); - }); - } - } - - /** - * Set the callbacks for data persistence - */ - setDataCallbacks( - saveDataCallback: (data: any) => Promise, - getDataCallback: () => Promise - ) { - this.onSaveData = saveDataCallback; - this.onGetData = getDataCallback; - } - - /** - * Save current chat data to the note attribute - */ - async saveCurrentData() { - if (!this.onSaveData) { - return; - } - - try { - // Extract current tool execution steps if any exist - const toolSteps = extractInChatToolSteps(this.noteContextChatMessages); - - // Get tool executions from both UI and any cached executions in metadata - let toolExecutions: Array<{ - id: string; - name: string; - arguments: any; - result: any; - error?: string; - timestamp: string; - }> = []; - - // First include any tool executions already in metadata (from streaming events) - if (this.metadata?.toolExecutions && Array.isArray(this.metadata.toolExecutions)) { - toolExecutions = [...this.metadata.toolExecutions]; - console.log(`Including ${toolExecutions.length} tool executions from metadata`); - } - - // Also extract any visible tool steps from the UI - const extractedExecutions = toolSteps.map(step => { - // Parse tool execution information - if (step.type === 'tool-execution') { - try { - const content = JSON.parse(step.content); - return { - id: content.toolCallId || `tool-${Date.now()}-${Math.random().toString(36).substring(2, 7)}`, - name: content.tool || 'unknown', - arguments: content.args || {}, - result: content.result || {}, - error: content.error, - timestamp: new Date().toISOString() - }; - } catch (e) { - // If we can't parse it, create a basic record - return { - id: `tool-${Date.now()}-${Math.random().toString(36).substring(2, 7)}`, - name: 'unknown', - arguments: {}, - result: step.content, - timestamp: new Date().toISOString() - }; - } - } else if (step.type === 'result' && step.name) { - // Handle result steps with a name - return { - id: `tool-${Date.now()}-${Math.random().toString(36).substring(2, 7)}`, - name: step.name, - arguments: {}, - result: step.content, - timestamp: new Date().toISOString() - }; - } - return { - id: `tool-${Date.now()}-${Math.random().toString(36).substring(2, 7)}`, - name: 'unknown', - arguments: {}, - result: 'Unrecognized tool step', - timestamp: new Date().toISOString() - }; - }); - - // Merge the tool executions, keeping only unique IDs - const existingIds = new Set(toolExecutions.map((t: {id: string}) => t.id)); - for (const exec of extractedExecutions) { - if (!existingIds.has(exec.id)) { - toolExecutions.push(exec); - existingIds.add(exec.id); - } - } - - // Only save if we have a valid note ID - if (!this.noteId) { - console.warn('Cannot save chat data: no noteId available'); - return; - } - - const dataToSave = { - messages: this.messages, - noteId: this.noteId, - chatNoteId: this.noteId, // For backward compatibility - toolSteps: toolSteps, - // Add sources if we have them - sources: this.sources || [], - // Add metadata - metadata: { - model: this.metadata?.model || undefined, - provider: this.metadata?.provider || undefined, - temperature: this.metadata?.temperature || 0.7, - lastUpdated: new Date().toISOString(), - // Add tool executions - toolExecutions: toolExecutions - } - }; - - console.log(`Saving chat data with noteId: ${this.noteId}, ${toolSteps.length} tool steps, ${this.sources?.length || 0} sources, ${toolExecutions.length} tool executions`); - - // Save the data to the note attribute via the callback - // This is the ONLY place we should save data, letting the container widget handle persistence - await this.onSaveData(dataToSave); - } catch (error) { - console.error('Error saving chat data:', error); - } - } - - /** - * Save current chat data to a specific note ID - */ - async saveCurrentDataToSpecificNote(targetNoteId: string | null) { - if (!this.onSaveData || !targetNoteId) { - console.warn('Cannot save chat data: no saveData callback or no targetNoteId available'); - return; - } - - try { - // Extract current tool execution steps if any exist - const toolSteps = extractInChatToolSteps(this.noteContextChatMessages); - - // Get tool executions from both UI and any cached executions in metadata - let toolExecutions: Array<{ - id: string; - name: string; - arguments: any; - result: any; - error?: string; - timestamp: string; - }> = []; - - // First include any tool executions already in metadata (from streaming events) - if (this.metadata?.toolExecutions && Array.isArray(this.metadata.toolExecutions)) { - toolExecutions = [...this.metadata.toolExecutions]; - console.log(`Including ${toolExecutions.length} tool executions from metadata`); - } - - // Also extract any visible tool steps from the UI - const extractedExecutions = toolSteps.map(step => { - // Parse tool execution information - if (step.type === 'tool-execution') { - try { - const content = JSON.parse(step.content); - return { - id: content.toolCallId || `tool-${Date.now()}-${Math.random().toString(36).substring(2, 7)}`, - name: content.tool || 'unknown', - arguments: content.args || {}, - result: content.result || {}, - error: content.error, - timestamp: new Date().toISOString() - }; - } catch (e) { - // If we can't parse it, create a basic record - return { - id: `tool-${Date.now()}-${Math.random().toString(36).substring(2, 7)}`, - name: 'unknown', - arguments: {}, - result: step.content, - timestamp: new Date().toISOString() - }; - } - } else if (step.type === 'result' && step.name) { - // Handle result steps with a name - return { - id: `tool-${Date.now()}-${Math.random().toString(36).substring(2, 7)}`, - name: step.name, - arguments: {}, - result: step.content, - timestamp: new Date().toISOString() - }; - } - return { - id: `tool-${Date.now()}-${Math.random().toString(36).substring(2, 7)}`, - name: 'unknown', - arguments: {}, - result: 'Unrecognized tool step', - timestamp: new Date().toISOString() - }; - }); - - // Merge the tool executions, keeping only unique IDs - const existingIds = new Set(toolExecutions.map((t: {id: string}) => t.id)); - for (const exec of extractedExecutions) { - if (!existingIds.has(exec.id)) { - toolExecutions.push(exec); - existingIds.add(exec.id); - } - } - - const dataToSave = { - messages: this.messages, - noteId: targetNoteId, - chatNoteId: targetNoteId, // For backward compatibility - toolSteps: toolSteps, - // Add sources if we have them - sources: this.sources || [], - // Add metadata - metadata: { - model: this.metadata?.model || undefined, - provider: this.metadata?.provider || undefined, - temperature: this.metadata?.temperature || 0.7, - lastUpdated: new Date().toISOString(), - // Add tool executions - toolExecutions: toolExecutions - } - }; - - console.log(`Saving chat data to specific note ${targetNoteId}, ${toolSteps.length} tool steps, ${this.sources?.length || 0} sources, ${toolExecutions.length} tool executions`); - - // Save the data to the note attribute via the callback - // This is the ONLY place we should save data, letting the container widget handle persistence - await this.onSaveData(dataToSave); - } catch (error) { - console.error('Error saving chat data to specific note:', error); - } - } - - /** - * Load saved chat data from the note attribute - */ - async loadSavedData(): Promise { - if (!this.onGetData) { - return false; - } - - try { - const savedData = await this.onGetData() as ChatData; - - if (savedData?.messages?.length > 0) { - // Check if we actually have new content to avoid unnecessary UI rebuilds - const currentMessageCount = this.messages.length; - const savedMessageCount = savedData.messages.length; - - // If message counts are the same, check if content is different - const hasNewContent = savedMessageCount > currentMessageCount || - JSON.stringify(this.messages) !== JSON.stringify(savedData.messages); - - if (!hasNewContent) { - console.log("No new content detected, skipping UI rebuild"); - return true; - } - - console.log(`Loading saved data: ${currentMessageCount} -> ${savedMessageCount} messages`); - - // Store current scroll position if we need to preserve it - const shouldPreserveScroll = savedMessageCount > currentMessageCount && currentMessageCount > 0; - const currentScrollTop = shouldPreserveScroll ? this.chatContainer.scrollTop : 0; - const currentScrollHeight = shouldPreserveScroll ? this.chatContainer.scrollHeight : 0; - - // Load messages - const oldMessages = [...this.messages]; - this.messages = savedData.messages; - - // Only rebuild UI if we have significantly different content - if (savedMessageCount > currentMessageCount) { - // We have new messages - just add the new ones instead of rebuilding everything - const newMessages = savedData.messages.slice(currentMessageCount); - console.log(`Adding ${newMessages.length} new messages to UI`); - - newMessages.forEach(message => { - const role = message.role as 'user' | 'assistant'; - this.addMessageToChat(role, message.content); - }); - } else { - // Content changed but count is same - need to rebuild - console.log("Message content changed, rebuilding UI"); - - // Clear and rebuild the chat UI - this.noteContextChatMessages.innerHTML = ''; - - this.messages.forEach(message => { - const role = message.role as 'user' | 'assistant'; - this.addMessageToChat(role, message.content); - }); - } - - // Restore tool execution steps if they exist - if (savedData.toolSteps && Array.isArray(savedData.toolSteps) && savedData.toolSteps.length > 0) { - console.log(`Restoring ${savedData.toolSteps.length} saved tool steps`); - this.restoreInChatToolSteps(savedData.toolSteps); - } - - // Load sources if available - if (savedData.sources && Array.isArray(savedData.sources)) { - this.sources = savedData.sources; - console.log(`Loaded ${this.sources.length} sources from saved data`); - - // Show sources in the UI if they exist - if (this.sources.length > 0) { - this.showSources(this.sources); - } - } - - // Load metadata if available - if (savedData.metadata) { - this.metadata = { - ...this.metadata, - ...savedData.metadata - }; - - // Ensure tool executions are loaded - if (savedData.metadata.toolExecutions && Array.isArray(savedData.metadata.toolExecutions)) { - console.log(`Loaded ${savedData.metadata.toolExecutions.length} tool executions from saved data`); - - if (!this.metadata.toolExecutions) { - this.metadata.toolExecutions = []; - } - - // Make sure we don't lose any tool executions - this.metadata.toolExecutions = savedData.metadata.toolExecutions; - } - - console.log(`Loaded metadata from saved data:`, this.metadata); - } - - // Load Chat Note ID if available - if (savedData.noteId) { - console.log(`Using noteId as Chat Note ID: ${savedData.noteId}`); - this.noteId = savedData.noteId; - } else { - console.log(`No noteId found in saved data, cannot load chat session`); - return false; - } - - // Restore scroll position if we were preserving it - if (shouldPreserveScroll) { - // Calculate the new scroll position to maintain relative position - const newScrollHeight = this.chatContainer.scrollHeight; - const scrollDifference = newScrollHeight - currentScrollHeight; - const newScrollTop = currentScrollTop + scrollDifference; - - // Only scroll down if we're near the bottom, otherwise preserve exact position - const wasNearBottom = (currentScrollTop + this.chatContainer.clientHeight) >= (currentScrollHeight - 50); - - if (wasNearBottom) { - // User was at bottom, scroll to new bottom - this.chatContainer.scrollTop = newScrollHeight; - console.log("User was at bottom, scrolling to new bottom"); - } else { - // User was not at bottom, try to preserve their position - this.chatContainer.scrollTop = newScrollTop; - console.log(`Preserving scroll position: ${currentScrollTop} -> ${newScrollTop}`); - } - } - - return true; - } - } catch (error) { - console.error('Failed to load saved chat data', error); - } - - return false; - } - - /** - * Restore tool execution steps in the chat UI - */ - private restoreInChatToolSteps(steps: ToolExecutionStep[]) { - if (!steps || steps.length === 0) return; - - // Create the tool execution element - const toolExecutionElement = document.createElement('div'); - toolExecutionElement.className = 'chat-tool-execution mb-3'; - - // Insert before the assistant message if it exists - const assistantMessage = this.noteContextChatMessages.querySelector('.assistant-message:last-child'); - if (assistantMessage) { - this.noteContextChatMessages.insertBefore(toolExecutionElement, assistantMessage); - } else { - // Otherwise append to the end - this.noteContextChatMessages.appendChild(toolExecutionElement); - } - - // Fill with tool execution content - toolExecutionElement.innerHTML = ` -
- - Tool Execution - -
-
-
- ${this.renderToolStepsHtml(steps)} -
-
- `; - - // Add event listener for the toggle button - const toggleButton = toolExecutionElement.querySelector('.tool-execution-toggle'); - if (toggleButton) { - toggleButton.addEventListener('click', () => { - const stepsContainer = toolExecutionElement.querySelector('.tool-execution-container'); - const icon = toggleButton.querySelector('i'); - - if (stepsContainer) { - if (stepsContainer.classList.contains('collapsed')) { - // Expand - stepsContainer.classList.remove('collapsed'); - (stepsContainer as HTMLElement).style.display = 'block'; - if (icon) { - icon.className = 'bx bx-chevron-down'; - } - } else { - // Collapse - stepsContainer.classList.add('collapsed'); - (stepsContainer as HTMLElement).style.display = 'none'; - if (icon) { - icon.className = 'bx bx-chevron-right'; - } - } - } - }); - } - - // Add click handler for the header to toggle expansion as well - const header = toolExecutionElement.querySelector('.tool-execution-header'); - if (header) { - header.addEventListener('click', (e) => { - // Only toggle if the click isn't on the toggle button itself - const target = e.target as HTMLElement; - if (target && !target.closest('.tool-execution-toggle')) { - const toggleButton = toolExecutionElement.querySelector('.tool-execution-toggle'); - toggleButton?.dispatchEvent(new Event('click')); - } - }); - (header as HTMLElement).style.cursor = 'pointer'; - } - } - - /** - * Render HTML for tool execution steps - */ - private renderToolStepsHtml(steps: ToolExecutionStep[]): string { - if (!steps || steps.length === 0) return ''; - - return steps.map(step => { - let icon = 'bx-info-circle'; - let className = 'info'; - let content = ''; - - if (step.type === 'executing') { - icon = 'bx-code-block'; - className = 'executing'; - content = `
${step.content || 'Executing tools...'}
`; - } else if (step.type === 'result') { - icon = 'bx-terminal'; - className = 'result'; - content = ` -
Tool: ${step.name || 'unknown'}
-
${step.content || ''}
- `; - } else if (step.type === 'error') { - icon = 'bx-error-circle'; - className = 'error'; - content = ` -
Tool: ${step.name || 'unknown'}
-
${step.content || 'Error occurred'}
- `; - } else if (step.type === 'generating') { - icon = 'bx-message-dots'; - className = 'generating'; - content = `
${step.content || 'Generating response...'}
`; - } - - return ` -
-
- - ${content} -
-
- `; - }).join(''); - } - - async refresh() { - if (!this.isVisible()) { - return; - } - - // Check for any provider validation issues when refreshing - await validateProviders(this.validationWarning); - - // Get current note context if needed - const currentActiveNoteId = appContext.tabManager.getActiveContext()?.note?.noteId || null; - - // For AI Chat notes, the note itself IS the chat session - // So currentNoteId and noteId should be the same - if (this.noteId && currentActiveNoteId === this.noteId) { - // We're in an AI Chat note - don't reset, just load saved data - console.log(`Refreshing AI Chat note ${this.noteId} - loading saved data`); - await this.loadSavedData(); - return; - } - - // If we're switching to a different note, we need to reset - if (this.currentNoteId !== currentActiveNoteId) { - console.log(`Note ID changed from ${this.currentNoteId} to ${currentActiveNoteId}, resetting chat panel`); - - // Reset the UI and data - this.noteContextChatMessages.innerHTML = ''; - this.messages = []; - this.noteId = null; // Also reset the chat note ID - this.hideSources(); // Hide any sources from previous note - - // Update our current noteId - this.currentNoteId = currentActiveNoteId; - } - - // Always try to load saved data for the current note - const hasSavedData = await this.loadSavedData(); - - // Only create a new session if we don't have a session or saved data - if (!this.noteId || !hasSavedData) { - // Create a new chat session - await this.createChatSession(); - } - } - - /** - * Create a new chat session - */ - private async createChatSession() { - try { - // If we already have a noteId (for AI Chat notes), use it - const contextNoteId = this.noteId || this.currentNoteId; - - // Create a new chat session, passing the context note ID - const noteId = await createChatSession(contextNoteId ? contextNoteId : undefined); - - if (noteId) { - // Set the note ID for this chat - this.noteId = noteId; - console.log(`Created new chat session with noteId: ${this.noteId}`); - } else { - throw new Error("Failed to create chat session - no ID returned"); - } - - // Save the note ID as the session identifier - await this.saveCurrentData(); - } catch (error) { - console.error('Error creating chat session:', error); - toastService.showError('Failed to create chat session'); - } - } - - /** - * Handle sending a user message to the LLM service - */ - private async sendMessage(content: string) { - if (!content.trim()) return; - - // Extract mentions from the content if using CKEditor - let mentions: Array<{noteId: string; title: string; notePath: string}> = []; - let plainTextContent = content; - - if (this.noteContextChatInputEditor) { - const extracted = this.extractMentionsAndContent(content); - mentions = extracted.mentions; - plainTextContent = extracted.content; - } - - // Add the user message to the UI and data model - this.addMessageToChat('user', plainTextContent); - this.messages.push({ - role: 'user', - content: plainTextContent, - mentions: mentions.length > 0 ? mentions : undefined - }); - - // Save the data immediately after a user message - await this.saveCurrentData(); - - // Clear input and show loading state - if (this.noteContextChatInputEditor) { - this.noteContextChatInputEditor.setData(''); - } - showLoadingIndicator(this.loadingIndicator); - this.hideSources(); - - try { - const useAdvancedContext = this.useAdvancedContextCheckbox.checked; - const showThinking = this.showThinkingCheckbox.checked; - - // Add logging to verify parameters - console.log(`Sending message with: useAdvancedContext=${useAdvancedContext}, showThinking=${showThinking}, noteId=${this.currentNoteId}, sessionId=${this.noteId}`); - - // Create the message parameters - const messageParams = { - content: plainTextContent, - useAdvancedContext, - showThinking, - mentions: mentions.length > 0 ? mentions : undefined - }; - - // Try websocket streaming (preferred method) - try { - await this.setupStreamingResponse(messageParams); - } catch (streamingError) { - console.warn("WebSocket streaming failed, falling back to direct response:", streamingError); - - // If streaming fails, fall back to direct response - const handled = await this.handleDirectResponse(messageParams); - if (!handled) { - // If neither method works, show an error - throw new Error("Failed to get response from server"); - } - } - - // Note: We don't need to save here since the streaming completion and direct response methods - // both call saveCurrentData() when they're done - } catch (error) { - console.error('Error processing user message:', error); - toastService.showError('Failed to process message'); - - // Add a generic error message to the UI - this.addMessageToChat('assistant', 'Sorry, I encountered an error processing your message. Please try again.'); - this.messages.push({ - role: 'assistant', - content: 'Sorry, I encountered an error processing your message. Please try again.' - }); - - // Save the data even after error - await this.saveCurrentData(); - } - } - - /** - * Process a new user message - add to UI and save - */ - private async processUserMessage(content: string) { - // Check for validation issues first - await validateProviders(this.validationWarning); - - // Make sure we have a valid session - if (!this.noteId) { - // If no session ID, create a new session - await this.createChatSession(); - - if (!this.noteId) { - // If still no session ID, show error and return - console.error("Failed to create chat session"); - toastService.showError("Failed to create chat session"); - return; - } - } - - // Add user message to messages array if not already added - if (!this.messages.some(msg => msg.role === 'user' && msg.content === content)) { - this.messages.push({ - role: 'user', - content: content - }); - } - - // Clear input and show loading state - if (this.noteContextChatInputEditor) { - this.noteContextChatInputEditor.setData(''); - } - showLoadingIndicator(this.loadingIndicator); - this.hideSources(); - - try { - const useAdvancedContext = this.useAdvancedContextCheckbox.checked; - const showThinking = this.showThinkingCheckbox.checked; - - // Save current state to the Chat Note before getting a response - await this.saveCurrentData(); - - // Add logging to verify parameters - console.log(`Sending message with: useAdvancedContext=${useAdvancedContext}, showThinking=${showThinking}, noteId=${this.currentNoteId}, sessionId=${this.noteId}`); - - // Create the message parameters - const messageParams = { - content, - useAdvancedContext, - showThinking - }; - - // Try websocket streaming (preferred method) - try { - await this.setupStreamingResponse(messageParams); - } catch (streamingError) { - console.warn("WebSocket streaming failed, falling back to direct response:", streamingError); - - // If streaming fails, fall back to direct response - const handled = await this.handleDirectResponse(messageParams); - if (!handled) { - // If neither method works, show an error - throw new Error("Failed to get response from server"); - } - } - - // Save final state after getting the response - await this.saveCurrentData(); - } catch (error) { - this.handleError(error as Error); - // Make sure we save the current state even on error - await this.saveCurrentData(); - } - } - - /** - * Try to get a direct response from the server - */ - private async handleDirectResponse(messageParams: any): Promise { - try { - if (!this.noteId) return false; - - console.log(`Getting direct response using sessionId: ${this.noteId} (noteId: ${this.noteId})`); - - // Get a direct response from the server - const postResponse = await getDirectResponse(this.noteId, messageParams); - - // If the POST request returned content directly, display it - if (postResponse && postResponse.content) { - // Store metadata from the response - if (postResponse.metadata) { - console.log("Received metadata from response:", postResponse.metadata); - this.metadata = { - ...this.metadata, - ...postResponse.metadata - }; - } - - // Store sources from the response - if (postResponse.sources && postResponse.sources.length > 0) { - console.log(`Received ${postResponse.sources.length} sources from response`); - this.sources = postResponse.sources; - this.showSources(postResponse.sources); - } - - // Process the assistant response with original chat note ID - this.processAssistantResponse(postResponse.content, postResponse, this.noteId); - - hideLoadingIndicator(this.loadingIndicator); - return true; - } - - return false; - } catch (error) { - console.error("Error with direct response:", error); - return false; - } - } - - /** - * Process an assistant response - add to UI and save - */ - private async processAssistantResponse(content: string, fullResponse?: any, originalChatNoteId?: string | null) { - // Add the response to the chat UI - this.addMessageToChat('assistant', content); - - // Add to our local message array too - this.messages.push({ - role: 'assistant', - content, - timestamp: new Date() - }); - - // If we received tool execution information, add it to metadata - if (fullResponse?.metadata?.toolExecutions) { - console.log(`Storing ${fullResponse.metadata.toolExecutions.length} tool executions from response`); - // Make sure our metadata has toolExecutions - if (!this.metadata.toolExecutions) { - this.metadata.toolExecutions = []; - } - - // Add new tool executions - this.metadata.toolExecutions = [ - ...this.metadata.toolExecutions, - ...fullResponse.metadata.toolExecutions - ]; - } - - // Save to note - use original chat note ID if provided - this.saveCurrentDataToSpecificNote(originalChatNoteId || this.noteId).catch(err => { - console.error("Failed to save assistant response to note:", err); - }); - } - - /** - * Set up streaming response via WebSocket - */ - private async setupStreamingResponse(messageParams: any): Promise { - if (!this.noteId) { - throw new Error("No session ID available"); - } - - console.log(`Setting up streaming response using sessionId: ${this.noteId} (noteId: ${this.noteId})`); - - // Store tool executions captured during streaming - const toolExecutionsCache: Array<{ - id: string; - name: string; - arguments: any; - result: any; - error?: string; - timestamp: string; - }> = []; - - // Store the original chat note ID to ensure we save to the correct note even if user switches - const originalChatNoteId = this.noteId; - - return setupStreamingResponse( - this.noteId, - messageParams, - // Content update handler - (content: string, isDone: boolean = false) => { - this.updateStreamingUI(content, isDone, originalChatNoteId); - - // Update session data with additional metadata when streaming is complete - if (isDone) { - // Update our metadata with info from the server - server.get<{ - metadata?: { - model?: string; - provider?: string; - temperature?: number; - maxTokens?: number; - toolExecutions?: Array<{ - id: string; - name: string; - arguments: any; - result: any; - error?: string; - timestamp: string; - }>; - lastUpdated?: string; - usage?: { - promptTokens?: number; - completionTokens?: number; - totalTokens?: number; - }; - }; - sources?: Array<{ - noteId: string; - title: string; - similarity?: number; - content?: string; - }>; - }>(`llm/chat/${this.noteId}`) - .then((sessionData) => { - console.log("Got updated session data:", sessionData); - - // Store metadata - if (sessionData.metadata) { - this.metadata = { - ...this.metadata, - ...sessionData.metadata - }; - } - - // Store sources - if (sessionData.sources && sessionData.sources.length > 0) { - this.sources = sessionData.sources; - this.showSources(sessionData.sources); - } - - // Make sure we include the cached tool executions - if (toolExecutionsCache.length > 0) { - console.log(`Including ${toolExecutionsCache.length} cached tool executions in metadata`); - if (!this.metadata.toolExecutions) { - this.metadata.toolExecutions = []; - } - - // Add any tool executions from our cache that aren't already in metadata - const existingIds = new Set((this.metadata.toolExecutions || []).map((t: {id: string}) => t.id)); - for (const toolExec of toolExecutionsCache) { - if (!existingIds.has(toolExec.id)) { - this.metadata.toolExecutions.push(toolExec); - existingIds.add(toolExec.id); - } - } - } - - // DON'T save here - let the server handle saving the complete conversation - // to avoid race conditions between client and server saves - console.log("Updated metadata after streaming completion, server should save"); - }) - .catch(err => console.error("Error fetching session data after streaming:", err)); - } - }, - // Thinking update handler - (thinking: string) => { - this.showThinkingState(thinking); - }, - // Tool execution handler - (toolData: any) => { - this.showToolExecutionInfo(toolData); - - // Cache tools we see during streaming to include them in the final saved data - if (toolData && toolData.action === 'result' && toolData.tool) { - // Create a tool execution record - const toolExec = { - id: toolData.toolCallId || `tool-${Date.now()}-${Math.random().toString(36).substring(2, 7)}`, - name: toolData.tool, - arguments: toolData.args || {}, - result: toolData.result || {}, - error: toolData.error, - timestamp: new Date().toISOString() - }; - - // Add to both our local cache for immediate saving and to metadata for later saving - toolExecutionsCache.push(toolExec); - - // Initialize toolExecutions array if it doesn't exist - if (!this.metadata.toolExecutions) { - this.metadata.toolExecutions = []; - } - - // Add tool execution to our metadata - this.metadata.toolExecutions.push(toolExec); - - console.log(`Cached tool execution for ${toolData.tool} to be saved later`); - - // DON'T save immediately during streaming - let the server handle saving - // to avoid race conditions between client and server saves - console.log(`Tool execution cached, will be saved by server`); - } - }, - // Complete handler - () => { - hideLoadingIndicator(this.loadingIndicator); - }, - // Error handler - (error: Error) => { - this.handleError(error); - } - ); - } - - /** - * Update the UI with streaming content - */ - private updateStreamingUI(assistantResponse: string, isDone: boolean = false, originalChatNoteId?: string | null) { - // Track if we have a streaming message in progress - const hasStreamingMessage = !!this.noteContextChatMessages.querySelector('.assistant-message.streaming'); - - // Create a new message element or use the existing streaming one - let assistantMessageEl: HTMLElement; - - if (hasStreamingMessage) { - // Use the existing streaming message - assistantMessageEl = this.noteContextChatMessages.querySelector('.assistant-message.streaming')!; - } else { - // Create a new message element - assistantMessageEl = document.createElement('div'); - assistantMessageEl.className = 'assistant-message message mb-3 streaming'; - this.noteContextChatMessages.appendChild(assistantMessageEl); - - // Add assistant profile icon - const profileIcon = document.createElement('div'); - profileIcon.className = 'profile-icon'; - profileIcon.innerHTML = ''; - assistantMessageEl.appendChild(profileIcon); - - // Add message content container - const messageContent = document.createElement('div'); - messageContent.className = 'message-content'; - assistantMessageEl.appendChild(messageContent); - } - - // Update the content with the current response - const messageContent = assistantMessageEl.querySelector('.message-content') as HTMLElement; - messageContent.innerHTML = formatMarkdown(assistantResponse); - - // When the response is complete - if (isDone) { - // Remove the streaming class to mark this message as complete - assistantMessageEl.classList.remove('streaming'); - - // Apply syntax highlighting - formatCodeBlocks($(assistantMessageEl as HTMLElement)); - - // Hide the thinking display when response is complete - this.hideThinkingDisplay(); - - // Always add a new message to the data model - // This ensures we preserve all distinct assistant messages - this.messages.push({ - role: 'assistant', - content: assistantResponse, - timestamp: new Date() - }); - - // Save the updated message list to the original chat note - this.saveCurrentDataToSpecificNote(originalChatNoteId || this.noteId); - } - - // Scroll to bottom - this.chatContainer.scrollTop = this.chatContainer.scrollHeight; - } - - /** - * Handle general errors in the send message flow - */ - private handleError(error: Error) { - hideLoadingIndicator(this.loadingIndicator); - toastService.showError('Error sending message: ' + error.message); - } - - private addMessageToChat(role: 'user' | 'assistant', content: string) { - addMessageToChat(this.noteContextChatMessages, this.chatContainer, role, content); - } - - private showSources(sources: Array<{noteId: string, title: string}>) { - showSources( - this.sourcesList, - this.sourcesContainer, - this.sourcesCount, - sources, - (noteId: string) => { - // Open the note in a new tab but don't switch to it - appContext.tabManager.openTabWithNoteWithHoisting(noteId, { activate: false }); - } - ); - } - - private hideSources() { - hideSources(this.sourcesContainer); - } - - /** - * Handle tool execution updates - */ - private showToolExecutionInfo(toolExecutionData: any) { - console.log(`Showing tool execution info: ${JSON.stringify(toolExecutionData)}`); - - // Enhanced debugging for tool execution - if (!toolExecutionData) { - console.error('Tool execution data is missing or undefined'); - return; - } - - // Check for required properties - const actionType = toolExecutionData.action || ''; - const toolName = toolExecutionData.tool || 'unknown'; - console.log(`Tool execution details: action=${actionType}, tool=${toolName}, hasResult=${!!toolExecutionData.result}`); - - // Force action to 'result' if missing but result is present - if (!actionType && toolExecutionData.result) { - console.log('Setting missing action to "result" since result is present'); - toolExecutionData.action = 'result'; - } - - // Create or get the tool execution container - let toolExecutionElement = this.noteContextChatMessages.querySelector('.chat-tool-execution'); - if (!toolExecutionElement) { - toolExecutionElement = document.createElement('div'); - toolExecutionElement.className = 'chat-tool-execution mb-3'; - - // Create header with title and dropdown toggle - const header = document.createElement('div'); - header.className = 'tool-execution-header d-flex align-items-center p-2 rounded'; - header.innerHTML = ` - - Tool Execution - - `; - toolExecutionElement.appendChild(header); - - // Create container for tool steps - const stepsContainer = document.createElement('div'); - stepsContainer.className = 'tool-execution-container p-2 rounded mb-2'; - toolExecutionElement.appendChild(stepsContainer); - - // Add to chat messages - this.noteContextChatMessages.appendChild(toolExecutionElement); - - // Add click handler for toggle button - const toggleButton = toolExecutionElement.querySelector('.tool-execution-toggle'); - if (toggleButton) { - toggleButton.addEventListener('click', () => { - const stepsContainer = toolExecutionElement?.querySelector('.tool-execution-container'); - const icon = toggleButton.querySelector('i'); - - if (stepsContainer) { - if (stepsContainer.classList.contains('collapsed')) { - // Expand - stepsContainer.classList.remove('collapsed'); - (stepsContainer as HTMLElement).style.display = 'block'; - if (icon) { - icon.className = 'bx bx-chevron-down'; - } - } else { - // Collapse - stepsContainer.classList.add('collapsed'); - (stepsContainer as HTMLElement).style.display = 'none'; - if (icon) { - icon.className = 'bx bx-chevron-right'; - } - } - } - }); - } - - // Add click handler for the header to toggle expansion as well - header.addEventListener('click', (e) => { - // Only toggle if the click isn't on the toggle button itself - const target = e.target as HTMLElement; - if (target && !target.closest('.tool-execution-toggle')) { - const toggleButton = toolExecutionElement?.querySelector('.tool-execution-toggle'); - toggleButton?.dispatchEvent(new Event('click')); - } - }); - (header as HTMLElement).style.cursor = 'pointer'; - } - - // Get the steps container - const stepsContainer = toolExecutionElement.querySelector('.tool-execution-container'); - if (!stepsContainer) return; - - // Process based on action type - const action = toolExecutionData.action || ''; - - if (action === 'start' || action === 'executing') { - // Tool execution started - const step = document.createElement('div'); - step.className = 'tool-step executing p-2 mb-2 rounded'; - step.innerHTML = ` -
- - Executing tool: ${toolExecutionData.tool || 'unknown'} -
- ${toolExecutionData.args ? ` -
- Args: ${JSON.stringify(toolExecutionData.args || {}, null, 2)} -
` : ''} - `; - stepsContainer.appendChild(step); - } - else if (action === 'result' || action === 'complete') { - // Tool execution completed with results - const step = document.createElement('div'); - step.className = 'tool-step result p-2 mb-2 rounded'; - - let resultDisplay = ''; - - // Special handling for note search tools which have a specific structure - if ((toolExecutionData.tool === 'search_notes' || toolExecutionData.tool === 'keyword_search_notes') && - typeof toolExecutionData.result === 'object' && - toolExecutionData.result.results) { - - const results = toolExecutionData.result.results; - - if (results.length === 0) { - resultDisplay = `
No notes found matching the search criteria.
`; - } else { - resultDisplay = ` -
-
Found ${results.length} notes:
-
    - ${results.map((note: any) => ` -
  • - ${note.title} - ${note.similarity < 1 ? `(similarity: ${(note.similarity * 100).toFixed(0)}%)` : ''} -
  • - `).join('')} -
-
- `; - } - } - // Format the result based on type for other tools - else if (typeof toolExecutionData.result === 'object') { - // For objects, format as pretty JSON - resultDisplay = `
${JSON.stringify(toolExecutionData.result, null, 2)}
`; - } else { - // For simple values, display as text - resultDisplay = `
${String(toolExecutionData.result)}
`; - } - - step.innerHTML = ` -
- - Tool: ${toolExecutionData.tool || 'unknown'} -
-
- ${resultDisplay} -
- `; - stepsContainer.appendChild(step); - - // Add event listeners for note links if this is a note search result - if (toolExecutionData.tool === 'search_notes' || toolExecutionData.tool === 'keyword_search_notes') { - const noteLinks = step.querySelectorAll('.note-link'); - noteLinks.forEach(link => { - link.addEventListener('click', (e) => { - e.preventDefault(); - const noteId = (e.currentTarget as HTMLElement).getAttribute('data-note-id'); - if (noteId) { - // Open the note in a new tab but don't switch to it - appContext.tabManager.openTabWithNoteWithHoisting(noteId, { activate: false }); - } - }); - }); - } - } - else if (action === 'error') { - // Tool execution failed - const step = document.createElement('div'); - step.className = 'tool-step error p-2 mb-2 rounded'; - step.innerHTML = ` -
- - Error in tool: ${toolExecutionData.tool || 'unknown'} -
-
- ${toolExecutionData.error || 'Unknown error'} -
- `; - stepsContainer.appendChild(step); - } - else if (action === 'generating') { - // Generating final response with tool results - const step = document.createElement('div'); - step.className = 'tool-step generating p-2 mb-2 rounded'; - step.innerHTML = ` -
- - Generating response with tool results... -
- `; - stepsContainer.appendChild(step); - } - - // Make sure the loading indicator is shown during tool execution - this.loadingIndicator.style.display = 'flex'; - - // Scroll the chat container to show the tool execution - this.chatContainer.scrollTop = this.chatContainer.scrollHeight; - } - - /** - * Show thinking state in the UI - */ - private showThinkingState(thinkingData: string) { - // Parse the thinking content to extract text between tags - const thinkingContent = this.parseThinkingContent(thinkingData); - - if (thinkingContent) { - this.showThinkingDisplay(thinkingContent); - } else { - // Fallback: show raw thinking data - this.showThinkingDisplay(thinkingData); - } - - // Show the loading indicator as well - this.loadingIndicator.style.display = 'flex'; - } - - /** - * Parse thinking content from LLM response - */ - private parseThinkingContent(content: string): string | null { - if (!content) return null; - - // Look for content between and tags - const thinkRegex = /([\s\S]*?)<\/think>/gi; - const matches: string[] = []; - let match: RegExpExecArray | null; - - while ((match = thinkRegex.exec(content)) !== null) { - matches.push(match[1].trim()); - } - - if (matches.length > 0) { - return matches.join('\n\n--- Next thought ---\n\n'); - } - - // Check for incomplete thinking blocks (streaming in progress) - const incompleteThinkRegex = /([\s\S]*?)$/i; - const incompleteMatch = content.match(incompleteThinkRegex); - - if (incompleteMatch && incompleteMatch[1]) { - return incompleteMatch[1].trim() + '\n\n[Thinking in progress...]'; - } - - // If no think tags found, check if the entire content might be thinking - if (content.toLowerCase().includes('thinking') || - content.toLowerCase().includes('reasoning') || - content.toLowerCase().includes('let me think') || - content.toLowerCase().includes('i need to') || - content.toLowerCase().includes('first, ') || - content.toLowerCase().includes('step 1') || - content.toLowerCase().includes('analysis:')) { - return content; - } - - return null; - } - - private initializeEventListeners() { - this.noteContextChatForm.addEventListener('submit', (e) => { - e.preventDefault(); - - let content = ''; - - if (this.noteContextChatInputEditor && this.noteContextChatInputEditor.getData) { - // Use CKEditor content - content = this.noteContextChatInputEditor.getData(); - } else { - // Fallback: check if there's a textarea (fallback mode) - const textarea = this.noteContextChatInput.querySelector('textarea'); - if (textarea) { - content = textarea.value; - } else { - // Last resort: try to get text content from the div - content = this.noteContextChatInput.textContent || this.noteContextChatInput.innerText || ''; - } - } - - if (content.trim()) { - this.sendMessage(content); - } - }); - - // Handle Enter key (send on Enter, new line on Shift+Enter) via CKEditor - // We'll set this up after CKEditor is initialized - this.setupEditorKeyBindings(); - } - - private setupEditorKeyBindings() { - if (this.noteContextChatInputEditor && this.noteContextChatInputEditor.keystrokes) { - try { - this.noteContextChatInputEditor.keystrokes.set('Enter', (key, stop) => { - if (!key.shiftKey) { - stop(); - this.noteContextChatForm.dispatchEvent(new Event('submit')); - } - }); - console.log('CKEditor keybindings set up successfully'); - } catch (error) { - console.warn('Failed to set up CKEditor keybindings:', error); - } - } - } - - /** - * Extract note mentions and content from CKEditor - */ - private extractMentionsAndContent(editorData: string): { content: string; mentions: Array<{noteId: string; title: string; notePath: string}> } { - const mentions: Array<{noteId: string; title: string; notePath: string}> = []; - - // Parse the HTML content to extract mentions - const tempDiv = document.createElement('div'); - tempDiv.innerHTML = editorData; - - // Find all mention elements - CKEditor uses specific patterns for mentions - // Look for elements with data-mention attribute or specific mention classes - const mentionElements = tempDiv.querySelectorAll('[data-mention], .mention, span[data-id]'); - - mentionElements.forEach(mentionEl => { - try { - // Try different ways to extract mention data based on CKEditor's format - let mentionData: any = null; - - // Method 1: data-mention attribute (JSON format) - if (mentionEl.hasAttribute('data-mention')) { - mentionData = JSON.parse(mentionEl.getAttribute('data-mention') || '{}'); - } - // Method 2: data-id attribute (simple format) - else if (mentionEl.hasAttribute('data-id')) { - const dataId = mentionEl.getAttribute('data-id'); - const textContent = mentionEl.textContent || ''; - - // Parse the dataId to extract note information - if (dataId && dataId.startsWith('@')) { - const cleanId = dataId.substring(1); // Remove the @ - mentionData = { - id: cleanId, - name: textContent, - notePath: cleanId // Assume the ID contains the path - }; - } - } - // Method 3: Check if this is a reference link (href=#notePath) - else if (mentionEl.tagName === 'A' && mentionEl.hasAttribute('href')) { - const href = mentionEl.getAttribute('href'); - if (href && href.startsWith('#')) { - const notePath = href.substring(1); - mentionData = { - notePath: notePath, - noteTitle: mentionEl.textContent || 'Unknown Note' - }; - } - } - - if (mentionData && (mentionData.notePath || mentionData.link)) { - const notePath = mentionData.notePath || mentionData.link?.substring(1); // Remove # from link - const noteId = notePath ? notePath.split('/').pop() : null; - const title = mentionData.noteTitle || mentionData.name || mentionEl.textContent || 'Unknown Note'; - - if (noteId) { - mentions.push({ - noteId: noteId, - title: title, - notePath: notePath - }); - console.log(`Extracted mention: noteId=${noteId}, title=${title}, notePath=${notePath}`); - } - } - } catch (e) { - console.warn('Failed to parse mention data:', e, mentionEl); - } - }); - - // Convert to plain text for the LLM, but preserve the structure - const content = tempDiv.textContent || tempDiv.innerText || ''; - - console.log(`Extracted ${mentions.length} mentions from editor content`); - return { content, mentions }; - } - - private setupThinkingToggle() { - if (this.thinkingToggle) { - this.thinkingToggle.addEventListener('click', (e) => { - e.stopPropagation(); - this.toggleThinkingDetails(); - }); - } - - // Also make the entire header clickable - const thinkingHeader = this.thinkingBubble?.querySelector('.thinking-header'); - if (thinkingHeader) { - thinkingHeader.addEventListener('click', (e) => { - const target = e.target as HTMLElement; - if (!target.closest('.thinking-toggle')) { - this.toggleThinkingDetails(); - } - }); - } - } - - private toggleThinkingDetails() { - const content = this.thinkingBubble?.querySelector('.thinking-content') as HTMLElement; - const toggle = this.thinkingToggle?.querySelector('i'); - - if (content && toggle) { - const isVisible = content.style.display !== 'none'; - - if (isVisible) { - content.style.display = 'none'; - toggle.className = 'bx bx-chevron-down'; - this.thinkingToggle.classList.remove('expanded'); - } else { - content.style.display = 'block'; - toggle.className = 'bx bx-chevron-up'; - this.thinkingToggle.classList.add('expanded'); - } - } - } - - /** - * Show the thinking display with optional initial content - */ - private showThinkingDisplay(initialText: string = '') { - if (this.thinkingContainer) { - this.thinkingContainer.style.display = 'block'; - - if (initialText && this.thinkingText) { - this.updateThinkingText(initialText); - } - - // Scroll to show the thinking display - this.chatContainer.scrollTop = this.chatContainer.scrollHeight; - } - } - - /** - * Update the thinking text content - */ - private updateThinkingText(text: string) { - if (this.thinkingText) { - // Format the thinking text for better readability - const formattedText = this.formatThinkingText(text); - this.thinkingText.textContent = formattedText; - - // Auto-scroll if content is expanded - const content = this.thinkingBubble?.querySelector('.thinking-content') as HTMLElement; - if (content && content.style.display !== 'none') { - this.chatContainer.scrollTop = this.chatContainer.scrollHeight; - } - } - } - - /** - * Format thinking text for better presentation - */ - private formatThinkingText(text: string): string { - if (!text) return text; - - // Clean up the text - let formatted = text.trim(); - - // Add some basic formatting - formatted = formatted - // Add spacing around section markers - .replace(/(\d+\.\s)/g, '\n$1') - // Clean up excessive whitespace - .replace(/\n\s*\n\s*\n/g, '\n\n') - // Trim again - .trim(); - - return formatted; - } - - /** - * Hide the thinking display - */ - private hideThinkingDisplay() { - if (this.thinkingContainer) { - this.thinkingContainer.style.display = 'none'; - - // Reset the toggle state - const content = this.thinkingBubble?.querySelector('.thinking-content') as HTMLElement; - const toggle = this.thinkingToggle?.querySelector('i'); - - if (content && toggle) { - content.style.display = 'none'; - toggle.className = 'bx bx-chevron-down'; - this.thinkingToggle?.classList.remove('expanded'); - } - - // Clear the text content - if (this.thinkingText) { - this.thinkingText.textContent = ''; - } - } - } - - /** - * Append to existing thinking content (for streaming updates) - */ - private appendThinkingText(additionalText: string) { - if (this.thinkingText && additionalText) { - const currentText = this.thinkingText.textContent || ''; - const newText = currentText + additionalText; - this.updateThinkingText(newText); - } - } -} diff --git a/apps/client/src/widgets/llm_chat/message_processor.ts b/apps/client/src/widgets/llm_chat/message_processor.ts deleted file mode 100644 index 139a3d611f..0000000000 --- a/apps/client/src/widgets/llm_chat/message_processor.ts +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Message processing functions for LLM Chat - */ -import type { ToolExecutionStep } from "./types.js"; - -/** - * Extract tool execution steps from the DOM that are within the chat flow - */ -export function extractInChatToolSteps(chatMessagesElement: HTMLElement): ToolExecutionStep[] { - const steps: ToolExecutionStep[] = []; - - // Look for tool execution in the chat flow - const toolExecutionElement = chatMessagesElement.querySelector('.chat-tool-execution'); - - if (toolExecutionElement) { - // Find all tool step elements - const stepElements = toolExecutionElement.querySelectorAll('.tool-step'); - - stepElements.forEach(stepEl => { - const stepHtml = stepEl.innerHTML; - - // Determine the step type based on icons or classes present - let type = 'info'; - let name: string | undefined; - let content = ''; - - if (stepHtml.includes('bx-code-block')) { - type = 'executing'; - content = 'Executing tools...'; - } else if (stepHtml.includes('bx-terminal')) { - type = 'result'; - // Extract the tool name from the step - const nameMatch = stepHtml.match(/]*>Tool: ([^<]+)<\/span>/); - name = nameMatch ? nameMatch[1] : 'unknown'; - - // Extract the content from the div with class mt-1 ps-3 - const contentEl = stepEl.querySelector('.mt-1.ps-3'); - content = contentEl ? contentEl.innerHTML : ''; - } else if (stepHtml.includes('bx-error-circle')) { - type = 'error'; - const nameMatch = stepHtml.match(/]*>Tool: ([^<]+)<\/span>/); - name = nameMatch ? nameMatch[1] : 'unknown'; - - const contentEl = stepEl.querySelector('.mt-1.ps-3.text-danger'); - content = contentEl ? contentEl.innerHTML : ''; - } else if (stepHtml.includes('bx-message-dots')) { - type = 'generating'; - content = 'Generating response with tool results...'; - } else if (stepHtml.includes('bx-loader-alt')) { - // Skip the initializing spinner - return; - } - - steps.push({ type, name, content }); - }); - } - - return steps; -} diff --git a/apps/client/src/widgets/llm_chat/types.ts b/apps/client/src/widgets/llm_chat/types.ts deleted file mode 100644 index 7181651d04..0000000000 --- a/apps/client/src/widgets/llm_chat/types.ts +++ /dev/null @@ -1,60 +0,0 @@ -/** - * Types for LLM Chat Panel - */ - -export interface ChatResponse { - id: string; - messages: Array<{role: string; content: string}>; - sources?: Array<{noteId: string; title: string}>; -} - -export interface SessionResponse { - id: string; - title: string; - noteId: string; // The ID of the chat note -} - -export interface ToolExecutionStep { - type: string; - name?: string; - content: string; -} - -export interface MessageData { - role: string; - content: string; - timestamp?: Date; - mentions?: Array<{ - noteId: string; - title: string; - notePath: string; - }>; -} - -export interface ChatData { - messages: MessageData[]; - noteId: string; // The ID of the chat note - chatNoteId?: string; // Deprecated - kept for backward compatibility, should equal noteId - toolSteps: ToolExecutionStep[]; - sources?: Array<{ - noteId: string; - title: string; - similarity?: number; - content?: string; - }>; - metadata?: { - model?: string; - provider?: string; - temperature?: number; - maxTokens?: number; - lastUpdated?: string; - toolExecutions?: Array<{ - id: string; - name: string; - arguments: any; - result: any; - error?: string; - timestamp: string; - }>; - }; -} diff --git a/apps/client/src/widgets/llm_chat/ui.ts b/apps/client/src/widgets/llm_chat/ui.ts deleted file mode 100644 index 15d4802ca8..0000000000 --- a/apps/client/src/widgets/llm_chat/ui.ts +++ /dev/null @@ -1,272 +0,0 @@ -/** - * UI-related functions for LLM Chat - */ -import { t } from "../../services/i18n.js"; -import type { ToolExecutionStep } from "./types.js"; -import { formatMarkdown, applyHighlighting } from "./utils.js"; - -// Template for the chat widget -export const TPL = ` -
- - - -
-
- - - - - -
- - - -
-
-
- -
-
- Options: -
- - -
-
- - -
-
-
-
-`; - -/** - * Add a message to the chat UI - */ -export function addMessageToChat(messagesContainer: HTMLElement, chatContainer: HTMLElement, role: 'user' | 'assistant', content: string) { - const messageElement = document.createElement('div'); - messageElement.className = `chat-message ${role}-message mb-3 d-flex`; - - const avatarElement = document.createElement('div'); - avatarElement.className = 'message-avatar d-flex align-items-center justify-content-center me-2'; - - if (role === 'user') { - avatarElement.innerHTML = ''; - avatarElement.classList.add('user-avatar'); - } else { - avatarElement.innerHTML = ''; - avatarElement.classList.add('assistant-avatar'); - } - - const contentElement = document.createElement('div'); - contentElement.className = 'message-content p-3 rounded flex-grow-1'; - - if (role === 'user') { - contentElement.classList.add('user-content', 'bg-light'); - } else { - contentElement.classList.add('assistant-content'); - } - - // Format the content with markdown - contentElement.innerHTML = formatMarkdown(content); - - messageElement.appendChild(avatarElement); - messageElement.appendChild(contentElement); - - messagesContainer.appendChild(messageElement); - - // Apply syntax highlighting to any code blocks in the message - applyHighlighting(contentElement); - - // Scroll to bottom - chatContainer.scrollTop = chatContainer.scrollHeight; -} - -/** - * Show sources in the UI - */ -export function showSources( - sourcesList: HTMLElement, - sourcesContainer: HTMLElement, - sourcesCount: HTMLElement, - sources: Array<{noteId: string, title: string}>, - onSourceClick: (noteId: string) => void -) { - sourcesList.innerHTML = ''; - sourcesCount.textContent = sources.length.toString(); - - sources.forEach(source => { - const sourceElement = document.createElement('div'); - sourceElement.className = 'source-item p-2 mb-1 border rounded d-flex align-items-center'; - - // Create the direct link to the note - sourceElement.innerHTML = ` - `; - - // Add click handler - sourceElement.querySelector('.source-link')?.addEventListener('click', (e) => { - e.preventDefault(); - e.stopPropagation(); - onSourceClick(source.noteId); - return false; - }); - - sourcesList.appendChild(sourceElement); - }); - - sourcesContainer.style.display = 'block'; -} - -/** - * Hide sources in the UI - */ -export function hideSources(sourcesContainer: HTMLElement) { - sourcesContainer.style.display = 'none'; -} - -/** - * Show loading indicator - */ -export function showLoadingIndicator(loadingIndicator: HTMLElement) { - const logId = `ui-${Date.now()}`; - console.log(`[${logId}] Showing loading indicator`); - - try { - loadingIndicator.style.display = 'flex'; - const forceUpdate = loadingIndicator.offsetHeight; - console.log(`[${logId}] Loading indicator initialized`); - } catch (err) { - console.error(`[${logId}] Error showing loading indicator:`, err); - } -} - -/** - * Hide loading indicator - */ -export function hideLoadingIndicator(loadingIndicator: HTMLElement) { - const logId = `ui-${Date.now()}`; - console.log(`[${logId}] Hiding loading indicator`); - - try { - loadingIndicator.style.display = 'none'; - const forceUpdate = loadingIndicator.offsetHeight; - console.log(`[${logId}] Loading indicator hidden`); - } catch (err) { - console.error(`[${logId}] Error hiding loading indicator:`, err); - } -} - -/** - * Render tool steps as HTML for display in chat - */ -export function renderToolStepsHtml(steps: ToolExecutionStep[]): string { - if (!steps || steps.length === 0) return ''; - - let html = ''; - - steps.forEach(step => { - let icon, labelClass, content; - - switch (step.type) { - case 'executing': - icon = 'bx-code-block text-primary'; - labelClass = ''; - content = `
- - ${step.content} -
`; - break; - - case 'result': - icon = 'bx-terminal text-success'; - labelClass = 'fw-bold'; - content = `
- - Tool: ${step.name || 'unknown'} -
-
${step.content}
`; - break; - - case 'error': - icon = 'bx-error-circle text-danger'; - labelClass = 'fw-bold text-danger'; - content = `
- - Tool: ${step.name || 'unknown'} -
-
${step.content}
`; - break; - - case 'generating': - icon = 'bx-message-dots text-info'; - labelClass = ''; - content = `
- - ${step.content} -
`; - break; - - default: - icon = 'bx-info-circle text-muted'; - labelClass = ''; - content = `
- - ${step.content} -
`; - } - - html += `
${content}
`; - }); - - return html; -} diff --git a/apps/client/src/widgets/llm_chat/utils.ts b/apps/client/src/widgets/llm_chat/utils.ts deleted file mode 100644 index f7c68e9efb..0000000000 --- a/apps/client/src/widgets/llm_chat/utils.ts +++ /dev/null @@ -1,93 +0,0 @@ -/** - * Utility functions for LLM Chat - */ -import { marked } from "marked"; -import { formatCodeBlocks } from "../../services/syntax_highlight.js"; - -/** - * Format markdown content for display - */ -export function formatMarkdown(content: string): string { - if (!content) return ''; - - // First, extract HTML thinking visualization to protect it from replacements - const thinkingBlocks: string[] = []; - let processedContent = content.replace(/
/g, (match) => { - const placeholder = `__THINKING_BLOCK_${thinkingBlocks.length}__`; - thinkingBlocks.push(match); - return placeholder; - }); - - // Use marked library to parse the markdown - const markedContent = marked(processedContent, { - breaks: true, // Convert line breaks to
- gfm: true, // Enable GitHub Flavored Markdown - silent: true // Ignore errors - }); - - // Handle potential promise (though it shouldn't be with our options) - if (typeof markedContent === 'string') { - processedContent = markedContent; - } else { - console.warn('Marked returned a promise unexpectedly'); - // Use the original content as fallback - processedContent = content; - } - - // Restore thinking visualization blocks - thinkingBlocks.forEach((block, index) => { - processedContent = processedContent.replace(`__THINKING_BLOCK_${index}__`, block); - }); - - return processedContent; -} - -/** - * Simple HTML escaping for safer content display - */ -export function escapeHtml(text: string): string { - if (typeof text !== 'string') { - text = String(text || ''); - } - - return text - .replace(/&/g, '&') - .replace(//g, '>') - .replace(/"/g, '"') - .replace(/'/g, '''); -} - -/** - * Apply syntax highlighting to content - */ -export function applyHighlighting(element: HTMLElement): void { - formatCodeBlocks($(element)); -} - -/** - * Format tool arguments for display - */ -export function formatToolArgs(args: any): string { - if (!args || typeof args !== 'object') return ''; - - return Object.entries(args) - .map(([key, value]) => { - // Format the value based on its type - let displayValue; - if (typeof value === 'string') { - displayValue = value.length > 50 ? `"${value.substring(0, 47)}..."` : `"${value}"`; - } else if (value === null) { - displayValue = 'null'; - } else if (Array.isArray(value)) { - displayValue = '[...]'; // Simplified array representation - } else if (typeof value === 'object') { - displayValue = '{...}'; // Simplified object representation - } else { - displayValue = String(value); - } - - return `${escapeHtml(key)}: ${escapeHtml(displayValue)}`; - }) - .join(', '); -} diff --git a/apps/client/src/widgets/llm_chat/validation.ts b/apps/client/src/widgets/llm_chat/validation.ts deleted file mode 100644 index 2d287c4016..0000000000 --- a/apps/client/src/widgets/llm_chat/validation.ts +++ /dev/null @@ -1,92 +0,0 @@ -/** - * Validation functions for LLM Chat - */ -import options from "../../services/options.js"; -import { t } from "../../services/i18n.js"; - -/** - * Validate providers configuration - */ -export async function validateProviders(validationWarning: HTMLElement): Promise { - try { - // Check if AI is enabled - const aiEnabled = options.is('aiEnabled'); - if (!aiEnabled) { - validationWarning.style.display = 'none'; - return; - } - - // Get precedence list from options - const precedenceStr = options.get('aiProviderPrecedence') || 'openai,anthropic,ollama'; - let precedenceList: string[] = []; - - if (precedenceStr) { - if (precedenceStr.startsWith('[') && precedenceStr.endsWith(']')) { - try { - precedenceList = JSON.parse(precedenceStr); - } catch (e) { - console.error('Error parsing precedence list:', e); - precedenceList = ['openai']; // Default if parsing fails - } - } else if (precedenceStr.includes(',')) { - precedenceList = precedenceStr.split(',').map(p => p.trim()); - } else { - precedenceList = [precedenceStr]; - } - } - - // Check for configuration issues with providers in the precedence list - const configIssues: string[] = []; - - // Always add experimental warning as the first item - configIssues.push(t("ai_llm.experimental_warning")); - - // Check each provider in the precedence list for proper configuration - for (const provider of precedenceList) { - if (provider === 'openai') { - // Check OpenAI configuration - const apiKey = options.get('openaiApiKey'); - if (!apiKey) { - configIssues.push(`OpenAI API key is missing (optional for OpenAI-compatible endpoints)`); - } - } else if (provider === 'anthropic') { - // Check Anthropic configuration - const apiKey = options.get('anthropicApiKey'); - if (!apiKey) { - configIssues.push(`Anthropic API key is missing`); - } - } else if (provider === 'ollama') { - // Check Ollama configuration - const baseUrl = options.get('ollamaBaseUrl'); - if (!baseUrl) { - configIssues.push(`Ollama Base URL is missing`); - } - } - // Add checks for other providers as needed - } - - // Show warning if there are configuration issues - if (configIssues.length > 0) { - let message = 'AI Provider Configuration Issues'; - - message += '
    '; - - // Show configuration issues - for (const issue of configIssues) { - message += `
  • ${issue}
  • `; - } - - message += '
'; - message += ''; - - // Update HTML content - validationWarning.innerHTML = message; - validationWarning.style.display = 'block'; - } else { - validationWarning.style.display = 'none'; - } - } catch (error) { - console.error('Error validating providers:', error); - validationWarning.style.display = 'none'; - } -} diff --git a/apps/client/src/widgets/llm_chat_panel.ts b/apps/client/src/widgets/llm_chat_panel.ts deleted file mode 100644 index fd26850cc1..0000000000 --- a/apps/client/src/widgets/llm_chat_panel.ts +++ /dev/null @@ -1,7 +0,0 @@ -/** - * LLM Chat Panel Widget - * This file is preserved for backward compatibility. - * The actual implementation has been moved to the llm_chat/ folder. - */ -import LlmChatPanel from './llm_chat/index.js'; -export default LlmChatPanel; diff --git a/apps/client/src/widgets/note_types.tsx b/apps/client/src/widgets/note_types.tsx index d0209bf5a2..b5f4226582 100644 --- a/apps/client/src/widgets/note_types.tsx +++ b/apps/client/src/widgets/note_types.tsx @@ -12,7 +12,7 @@ import { TypeWidgetProps } from "./type_widgets/type_widget"; * A `NoteType` altered by the note detail widget, taking into consideration whether the note is editable or not and adding special note types such as an empty one, * for protected session or attachment information. */ -export type ExtendedNoteType = Exclude | "empty" | "readOnlyCode" | "readOnlyText" | "editableText" | "editableCode" | "attachmentDetail" | "attachmentList" | "protectedSession" | "aiChat" | "sqlConsole"; +export type ExtendedNoteType = Exclude | "empty" | "readOnlyCode" | "readOnlyText" | "editableText" | "editableCode" | "attachmentDetail" | "attachmentList" | "protectedSession" | "sqlConsole"; export type TypeWidget = ((props: TypeWidgetProps) => VNode | JSX.Element | undefined); type NoteTypeView = () => (Promise<{ default: TypeWidget } | TypeWidget> | TypeWidget); @@ -137,11 +137,6 @@ export const TYPE_MAPPINGS: Record = { printable: true, isFullHeight: true }, - aiChat: { - view: () => import("./type_widgets/AiChat"), - className: "ai-chat-widget-container", - isFullHeight: true - }, sqlConsole: { view: () => import("./type_widgets/SqlConsole"), className: "sql-console-widget-container", diff --git a/apps/client/src/widgets/ribbon/NoteActions.tsx b/apps/client/src/widgets/ribbon/NoteActions.tsx index b818672709..290306c340 100644 --- a/apps/client/src/widgets/ribbon/NoteActions.tsx +++ b/apps/client/src/widgets/ribbon/NoteActions.tsx @@ -85,7 +85,7 @@ export function NoteContextMenu({ note, noteContext, itemsAtStart, itemsNearNote ); const isElectron = getIsElectron(); const isMac = getIsMac(); - const hasSource = ["text", "code", "relationMap", "mermaid", "canvas", "mindMap", "aiChat"].includes(noteType); + const hasSource = ["text", "code", "relationMap", "mermaid", "canvas", "mindMap"].includes(noteType); const isSearchOrBook = ["search", "book"].includes(noteType); const isHelpPage = note.noteId.startsWith("_help"); const [syncServerHost] = useTriliumOption("syncServerHost"); diff --git a/apps/client/src/widgets/type_widgets/AiChat.tsx b/apps/client/src/widgets/type_widgets/AiChat.tsx deleted file mode 100644 index 88c5e9c821..0000000000 --- a/apps/client/src/widgets/type_widgets/AiChat.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import { useEffect, useRef } from "preact/hooks"; - -import LlmChatPanel from "../llm_chat"; -import { useEditorSpacedUpdate, useLegacyWidget } from "../react/hooks"; -import { type TypeWidgetProps } from "./type_widget"; - -export default function AiChat({ note, noteContext }: TypeWidgetProps) { - const dataRef = useRef(); - const spacedUpdate = useEditorSpacedUpdate({ - note, - noteContext, - noteType: "aiChat", - getData: async () => ({ - content: JSON.stringify(dataRef.current) - }), - onContentChange: (newContent) => { - try { - dataRef.current = JSON.parse(newContent); - llmChatPanel.refresh(); - } catch (e) { - dataRef.current = {}; - } - } - }); - const [ ChatWidget, llmChatPanel ] = useLegacyWidget(() => { - const llmChatPanel = new LlmChatPanel(); - llmChatPanel.setDataCallbacks( - async (data) => { - dataRef.current = data; - spacedUpdate.scheduleUpdate(); - }, - async () => dataRef.current - ); - return llmChatPanel; - }, { - noteContext, - containerStyle: { - height: "100%" - } - }); - - useEffect(() => { - llmChatPanel.setNoteId(note.noteId); - llmChatPanel.setCurrentNoteId(note.noteId); - }, [ note ]); - - return ChatWidget; -} diff --git a/apps/client/src/widgets/type_widgets/ContentWidget.tsx b/apps/client/src/widgets/type_widgets/ContentWidget.tsx index d7e2aad39f..2956c92ef5 100644 --- a/apps/client/src/widgets/type_widgets/ContentWidget.tsx +++ b/apps/client/src/widgets/type_widgets/ContentWidget.tsx @@ -11,7 +11,6 @@ import MultiFactorAuthenticationSettings from "./options/multi_factor_authentica import EtapiSettings from "./options/etapi"; import BackupSettings from "./options/backup"; import SyncOptions from "./options/sync"; -import AiSettings from "./options/ai_settings"; import OtherSettings from "./options/other"; import InternationalizationOptions from "./options/i18n"; import AdvancedSettings from "./options/advanced"; @@ -19,7 +18,7 @@ import "./ContentWidget.css"; import { t } from "../../services/i18n"; import BackendLog from "./code/BackendLog"; -export type OptionPages = "_optionsAppearance" | "_optionsShortcuts" | "_optionsTextNotes" | "_optionsCodeNotes" | "_optionsImages" | "_optionsSpellcheck" | "_optionsPassword" | "_optionsMFA" | "_optionsEtapi" | "_optionsBackup" | "_optionsSync" | "_optionsAi" | "_optionsOther" | "_optionsLocalization" | "_optionsAdvanced"; +export type OptionPages = "_optionsAppearance" | "_optionsShortcuts" | "_optionsTextNotes" | "_optionsCodeNotes" | "_optionsImages" | "_optionsSpellcheck" | "_optionsPassword" | "_optionsMFA" | "_optionsEtapi" | "_optionsBackup" | "_optionsSync" | "_optionsOther" | "_optionsLocalization" | "_optionsAdvanced"; const CONTENT_WIDGETS: Record JSX.Element> = { _optionsAppearance: AppearanceSettings, @@ -33,7 +32,6 @@ const CONTENT_WIDGETS: Record - - - - ); -} - -function EnableAiSettings() { - const [ aiEnabled, setAiEnabled ] = useTriliumOptionBool("aiEnabled"); - - return ( - <> - - - { - if (isEnabled) { - toast.showMessage(t("ai_llm.ai_enabled")); - } else { - toast.showMessage(t("ai_llm.ai_disabled")); - } - - setAiEnabled(isEnabled); - }} - /> - - {aiEnabled && {t("ai_llm.experimental_warning")}} - - - ); -} - -function ProviderSettings() { - const [ aiSelectedProvider, setAiSelectedProvider ] = useTriliumOption("aiSelectedProvider"); - const [ aiTemperature, setAiTemperature ] = useTriliumOption("aiTemperature"); - const [ aiSystemPrompt, setAiSystemPrompt ] = useTriliumOption("aiSystemPrompt"); - - return ( - - - - - - { - aiSelectedProvider === "openai" ? - - : aiSelectedProvider === "anthropic" ? - - : aiSelectedProvider === "ollama" ? - - : - <> - } - - - - - - - - - - ) -} - -interface SingleProviderSettingsProps { - provider: string; - title: string; - apiKeyDescription?: string; - baseUrlDescription: string; - modelDescription: string; - validationErrorMessage: string; - apiKeyOption?: OptionNames; - baseUrlOption: OptionNames; - modelOption: OptionNames; -} - -function SingleProviderSettings({ provider, title, apiKeyDescription, baseUrlDescription, modelDescription, validationErrorMessage, apiKeyOption, baseUrlOption, modelOption }: SingleProviderSettingsProps) { - const [ apiKey, setApiKey ] = useTriliumOption(apiKeyOption ?? baseUrlOption); - const [ baseUrl, setBaseUrl ] = useTriliumOption(baseUrlOption); - const isValid = (apiKeyOption ? !!apiKey : !!baseUrl); - - return ( -
-
-
-
{title}
-
- -
- {!isValid && {validationErrorMessage} } - - {apiKeyOption && ( - - - - )} - - - - - - {isValid && - - - - } -
-
-
- ) -} - -function ModelSelector({ provider, baseUrl, modelOption }: { provider: string; baseUrl: string, modelOption: OptionNames }) { - const [ model, setModel ] = useTriliumOption(modelOption); - const [ models, setModels ] = useState<{ name: string, id: string }[]>([]); - - const loadProviders = useCallback(async () => { - switch (provider) { - case "openai": - case "anthropic": { - try { - const response = await server.get(`llm/providers/${provider}/models?baseUrl=${encodeURIComponent(baseUrl)}`); - if (response.success) { - setModels(response.chatModels.toSorted((a, b) => a.name.localeCompare(b.name))); - } else { - toast.showError(t("ai_llm.no_models_found_online")); - } - } catch (e) { - toast.showError(t("ai_llm.error_fetching", { error: e })); - } - break; - } - case "ollama": { - try { - const response = await server.get(`llm/providers/ollama/models?baseUrl=${encodeURIComponent(baseUrl)}`); - if (response.success) { - setModels(response.models - .map(model => ({ - name: model.name, - id: model.model - })) - .toSorted((a, b) => a.name.localeCompare(b.name))); - } else { - toast.showError(t("ai_llm.no_models_found_ollama")); - } - } catch (e) { - toast.showError(t("ai_llm.error_fetching", { error: e })); - } - break; - } - } - }, [provider]); - - useEffect(() => { - loadProviders(); - }, [provider]); - - return ( - <> - - -