diff --git a/packages/mcp-trace/trace-core/types/config.ts b/packages/mcp-trace/trace-core/types/config.ts index abb98ec657..37f705eb8d 100644 --- a/packages/mcp-trace/trace-core/types/config.ts +++ b/packages/mcp-trace/trace-core/types/config.ts @@ -20,7 +20,6 @@ export interface TelemetryConfig { endpoint?: string headers?: Record defaultTracerName?: string - isDevModel?: boolean } export interface TraceConfig extends TelemetryConfig { @@ -62,6 +61,5 @@ export interface SpanEntity { export const defaultConfig: TelemetryConfig = { serviceName: 'default', headers: {}, - defaultTracerName: 'default', - isDevModel: true + defaultTracerName: 'default' } diff --git a/src/main/services/ConfigManager.ts b/src/main/services/ConfigManager.ts index cf3185ad70..5f5be2c723 100644 --- a/src/main/services/ConfigManager.ts +++ b/src/main/services/ConfigManager.ts @@ -26,7 +26,8 @@ export enum ConfigKeys { SelectionAssistantFilterMode = 'selectionAssistantFilterMode', SelectionAssistantFilterList = 'selectionAssistantFilterList', DisableHardwareAcceleration = 'disableHardwareAcceleration', - Proxy = 'proxy' + Proxy = 'proxy', + EnableDeveloperMode = 'enableDeveloperMode' } export class ConfigManager { @@ -232,6 +233,14 @@ export class ConfigManager { this.set(key, value, true) } + getEnableDeveloperMode(): boolean { + return this.get(ConfigKeys.EnableDeveloperMode, false) + } + + setEnableDeveloperMode(value: boolean) { + this.set(ConfigKeys.EnableDeveloperMode, value) + } + set(key: string, value: unknown, isNotify: boolean = false) { this.store.set(key, value) isNotify && this.notifySubscribers(key, value) diff --git a/src/main/services/NodeTraceService.ts b/src/main/services/NodeTraceService.ts index a11880949c..c3b7e9c0dc 100644 --- a/src/main/services/NodeTraceService.ts +++ b/src/main/services/NodeTraceService.ts @@ -1,22 +1,22 @@ import { isDev } from '@main/constant' -import { CacheBatchSpanProcessor, defaultConfig, FunctionSpanExporter } from '@mcp-trace/trace-core' +import { CacheBatchSpanProcessor, FunctionSpanExporter } from '@mcp-trace/trace-core' import { NodeTracer as MCPNodeTracer } from '@mcp-trace/trace-node/nodeTracer' import { context, SpanContext, trace } from '@opentelemetry/api' import { BrowserWindow, ipcMain } from 'electron' import * as path from 'path' import { ConfigKeys, configManager } from './ConfigManager' +import { loggerService } from './LoggerService' import { spanCacheService } from './SpanCacheService' export const TRACER_NAME = 'CherryStudio' +const logger = loggerService.withContext('NodeTraceService') + export class NodeTraceService { init() { - // TODO get developer mode setting from config - defaultConfig.isDevModel = true - const exporter = new FunctionSpanExporter(async (spans) => { - console.log(`Spans length:`, spans.length) + logger.info(`Spans length: ${spans.length}`) }) MCPNodeTracer.init( @@ -74,8 +74,7 @@ export function openTraceWindow(topicId: string, traceId: string, autoOpen = tru minimizable: true, resizable: true, title: 'Call Chain Window', - frame: false, - titleBarStyle: 'hidden', + frame: true, titleBarOverlay: { height: 40 }, webPreferences: { preload: path.join(__dirname, '../preload/index.js'), diff --git a/src/main/services/SpanCacheService.ts b/src/main/services/SpanCacheService.ts index 1529815ceb..9ebd9adf7d 100644 --- a/src/main/services/SpanCacheService.ts +++ b/src/main/services/SpanCacheService.ts @@ -1,17 +1,15 @@ -import { - Attributes, - convertSpanToSpanEntity, - defaultConfig, - SpanEntity, - TokenUsage, - TraceCache -} from '@mcp-trace/trace-core' +import { loggerService } from '@logger' +import { Attributes, convertSpanToSpanEntity, SpanEntity, TokenUsage, TraceCache } from '@mcp-trace/trace-core' import { SpanStatusCode } from '@opentelemetry/api' import { ReadableSpan } from '@opentelemetry/sdk-trace-base' import fs from 'fs/promises' import * as os from 'os' import * as path from 'path' +import { configManager } from './ConfigManager' + +const logger = loggerService.withContext('SpanCacheService') + class SpanCacheService implements TraceCache { private topicMap: Map = new Map() private fileDir: string @@ -23,7 +21,7 @@ class SpanCacheService implements TraceCache { } createSpan: (span: ReadableSpan) => void = (span: ReadableSpan) => { - if (!defaultConfig.isDevModel) { + if (!configManager.getEnableDeveloperMode()) { return } const spanEntity = convertSpanToSpanEntity(span) @@ -33,7 +31,7 @@ class SpanCacheService implements TraceCache { } endSpan: (span: ReadableSpan) => void = (span: ReadableSpan) => { - if (!defaultConfig.isDevModel) { + if (!configManager.getEnableDeveloperMode()) { return } const spanId = span.spanContext().spanId @@ -84,12 +82,12 @@ class SpanCacheService implements TraceCache { }) ) .catch((err) => { - console.error('Error cleaning local data:', err) + logger.error('Error cleaning local data:', err) }) } async saveSpans(topicId: string) { - if (!defaultConfig.isDevModel) { + if (!configManager.getEnableDeveloperMode()) { return } let traceId: string | undefined @@ -140,7 +138,7 @@ class SpanCacheService implements TraceCache { } saveEntity(entity: SpanEntity) { - if (!defaultConfig.isDevModel) { + if (!configManager.getEnableDeveloperMode()) { return } if (this.cache.has(entity.id)) { @@ -216,7 +214,7 @@ class SpanCacheService implements TraceCache { try { await fs.rm(filePath, { recursive: true }) } catch (error) { - console.error(error) + logger.error('Error cleaning local data:', error) } } } @@ -361,7 +359,7 @@ class SpanCacheService implements TraceCache { try { yield JSON.parse(trimmed) as SpanEntity } catch (e) { - console.error(`JSON解析失败: ${trimmed}`, e) + logger.error(`JSON解析失败: ${trimmed}`, e) } } } @@ -371,7 +369,7 @@ class SpanCacheService implements TraceCache { .filter((span) => span.topicId === topicId && span.traceId === traceId && span.modelName) .filter((span) => !modelName || span.modelName === modelName) } catch (err) { - console.error('Error parsing JSON:', err) + logger.error('Error parsing JSON:', err) throw err } } @@ -389,7 +387,7 @@ class SpanCacheService implements TraceCache { await fs.access(filePath) return true } catch (err) { - console.log('delete trace file error:', err) + logger.error('delete trace file error:', err) return false } } diff --git a/src/preload/index.ts b/src/preload/index.ts index 0aaa6b2c2b..84cff34e64 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -28,10 +28,10 @@ import { Notification } from 'src/renderer/src/types/notification' import { CreateDirectoryOptions } from 'webdav' import type { ActionItem } from '../renderer/src/types/selectionTypes' + export function tracedInvoke(channel: string, spanContext: SpanContext | undefined, ...args: any[]) { if (spanContext) { const data = { type: 'trace', context: spanContext } - console.log(`tracedInvoke data`, data) return ipcRenderer.invoke(channel, ...args, data) } return ipcRenderer.invoke(channel, ...args) diff --git a/src/renderer/src/aiCore/index.ts b/src/renderer/src/aiCore/index.ts index af300a0f13..f0e3e88f49 100644 --- a/src/renderer/src/aiCore/index.ts +++ b/src/renderer/src/aiCore/index.ts @@ -2,9 +2,9 @@ import { loggerService } from '@logger' import { ApiClientFactory } from '@renderer/aiCore/clients/ApiClientFactory' import { BaseApiClient } from '@renderer/aiCore/clients/BaseApiClient' import { isDedicatedImageGenerationModel, isFunctionCallingModel } from '@renderer/config/models' +import { getProviderByModel } from '@renderer/services/AssistantService' import { withSpanResult } from '@renderer/services/SpanManagerService' import { StartSpanParams } from '@renderer/trace/types/ModelSpanEntity' -import { getProviderByModel } from '@renderer/services/AssistantService' import type { GenerateImageParams, Model, Provider } from '@renderer/types' import type { RequestOptions, SdkModel } from '@renderer/types/sdk' import { isEnabledToolUse } from '@renderer/utils/mcp-tools' diff --git a/src/renderer/src/components/CodeBlockView/HtmlArtifactsPopup.tsx b/src/renderer/src/components/CodeBlockView/HtmlArtifactsPopup.tsx index 98c90d4faf..24a9749021 100644 --- a/src/renderer/src/components/CodeBlockView/HtmlArtifactsPopup.tsx +++ b/src/renderer/src/components/CodeBlockView/HtmlArtifactsPopup.tsx @@ -58,7 +58,7 @@ const HtmlArtifactsPopup: React.FC = ({ open, title, ht clearInterval(intervalRef.current) } } - }, [open, previewHtml]) + }, [currentHtml, open, previewHtml]) // 全屏时防止 body 滚动 useEffect(() => { diff --git a/src/renderer/src/components/QuickPanel/view.tsx b/src/renderer/src/components/QuickPanel/view.tsx index dae10e9260..4f91a729ae 100644 --- a/src/renderer/src/components/QuickPanel/view.tsx +++ b/src/renderer/src/components/QuickPanel/view.tsx @@ -128,7 +128,7 @@ export const QuickPanelView: React.FC = ({ setInputText }) => { prevSymbolRef.current = ctx.symbol return newList - }, [ctx.defaultIndex, ctx.isVisible, ctx.list, ctx.symbol, searchText]) + }, [ctx.isVisible, ctx.list, ctx.symbol, searchText]) const canForwardAndBackward = useMemo(() => { return list.some((item) => item.isMenu) || historyPanel.length > 0 diff --git a/src/renderer/src/hooks/useSettings.ts b/src/renderer/src/hooks/useSettings.ts index 43f9e41135..c44eff419e 100644 --- a/src/renderer/src/hooks/useSettings.ts +++ b/src/renderer/src/hooks/useSettings.ts @@ -5,6 +5,7 @@ import { setAssistantIconType, setAutoCheckUpdate as _setAutoCheckUpdate, setDisableHardwareAcceleration, + setEnableDeveloperMode, setLaunchOnBoot, setLaunchToTray, setPinTopicsToTop, @@ -121,3 +122,20 @@ export function useMessageStyle() { export const getStoreSetting = (key: keyof SettingsState) => { return store.getState().settings[key] } + +export const useEnableDeveloperMode = () => { + const enableDeveloperMode = useAppSelector((state) => state.settings.enableDeveloperMode) + const dispatch = useAppDispatch() + + return { + enableDeveloperMode, + setEnableDeveloperMode: (enableDeveloperMode: boolean) => { + dispatch(setEnableDeveloperMode(enableDeveloperMode)) + window.api.config.set('enableDeveloperMode', enableDeveloperMode) + } + } +} + +export const getEnableDeveloperMode = () => { + return store.getState().settings.enableDeveloperMode +} diff --git a/src/renderer/src/i18n/locales/en-us.json b/src/renderer/src/i18n/locales/en-us.json index 953aaf47c1..974bfc892b 100644 --- a/src/renderer/src/i18n/locales/en-us.json +++ b/src/renderer/src/i18n/locales/en-us.json @@ -1801,6 +1801,10 @@ "token_placeholder": "Please enter the Yuque Token" } }, + "developer": { + "enable_developer_mode": "Enable Developer Mode", + "title": "Developer Mode" + }, "display.assistant.title": "Assistant Settings", "display.custom.css": "Custom CSS", "display.custom.css.cherrycss": "Get from cherrycss.com", @@ -2503,6 +2507,23 @@ "title": "Page Zoom" } }, + "trace": { + "backList": "Back To List", + "edasSupport": "Powered by Alibaba Cloud EDAS", + "endTime": "End Time", + "inputs": "Inputs", + "label": "Call Chain", + "name": "Node Name", + "noTraceList": "No trace information found", + "outputs": "Outputs", + "parentId": "Parent Id", + "spanDetail": "Span Details", + "spendTime": "Spend Time", + "startTime": "Start Time", + "tag": "Tag", + "tokenUsage": "Token Usage", + "traceWindow": "Call Chain Window" + }, "translate": { "alter_language": "Alternative Language", "any.language": "Any language", @@ -2564,129 +2585,6 @@ "quit": "Quit", "show_window": "Show Window", "visualization": "Visualization" - }, - "memory": { - "title": "Memories", - "actions": "Actions", - "description": "Memory allows you to store and manage information about your interactions with the assistant. You can add, edit, and delete memories, as well as filter and search through them.", - "add_memory": "Add Memory", - "edit_memory": "Edit Memory", - "memory_content": "Memory Content", - "please_enter_memory": "Please enter memory content", - "memory_placeholder": "Enter memory content...", - "user_id": "User ID", - "user_id_placeholder": "Enter user ID (optional)", - "load_failed": "Failed to load memories", - "add_success": "Memory added successfully", - "add_failed": "Failed to add memory", - "update_success": "Memory updated successfully", - "update_failed": "Failed to update memory", - "delete_success": "Memory deleted successfully", - "delete_failed": "Failed to delete memory", - "delete_confirm_title": "Delete Memories", - "delete_confirm_content": "Are you sure you want to delete {{count}} memories?", - "delete_confirm": "Are you sure you want to delete this memory?", - "time": "Time", - "user": "User", - "content": "Content", - "score": "Score", - "memories_description": "Showing {{count}} of {{total}} memories", - "search_placeholder": "Search memories...", - "start_date": "Start Date", - "end_date": "End Date", - "all_users": "All Users", - "users": "users", - "delete_selected": "Delete Selected", - "reset_filters": "Reset Filters", - "pagination_total": "{{start}}-{{end}} of {{total}} items", - "current_user": "Current User", - "select_user": "Select User", - "default_user": "Default User", - "switch_user": "Switch User", - "user_switched": "User context switched to {{user}}", - "switch_user_confirm": "Switch user context to {{user}}?", - "add_user": "Add User", - "add_new_user": "Add New User", - "new_user_id": "New User ID", - "new_user_id_placeholder": "Enter a unique user ID", - "user_id_required": "User ID is required", - "user_id_reserved": "'default-user' is reserved, please use a different ID", - "user_id_exists": "This user ID already exists", - "user_id_too_long": "User ID cannot exceed 50 characters", - "user_id_invalid_chars": "User ID can only contain letters, numbers, hyphens and underscores", - "user_id_rules": "User ID must be unique and contain only letters, numbers, hyphens (-) and underscores (_)", - "user_created": "User {{user}} created and switched successfully", - "add_user_failed": "Failed to add user", - "memory": "memory", - "reset_user_memories": "Reset User Memories", - "reset_memories": "Reset Memories", - "delete_user": "Delete User", - "loading_memories": "Loading memories...", - "no_memories": "No memories yet", - "no_matching_memories": "No matching memories found", - "no_memories_description": "Start by adding your first memory to get started", - "try_different_filters": "Try adjusting your search criteria", - "add_first_memory": "Add Your First Memory", - "user_switch_failed": "Failed to switch user", - "cannot_delete_default_user": "Cannot delete the default user", - "delete_user_confirm_title": "Delete User", - "delete_user_confirm_content": "Are you sure you want to delete user {{user}} and all their memories?", - "user_deleted": "User {{user}} deleted successfully", - "delete_user_failed": "Failed to delete user", - "reset_user_memories_confirm_title": "Reset User Memories", - "reset_user_memories_confirm_content": "Are you sure you want to reset all memories for {{user}}?", - "user_memories_reset": "All memories for {{user}} have been reset", - "reset_user_memories_failed": "Failed to reset user memories", - "reset_memories_confirm_title": "Reset All Memories", - "reset_memories_confirm_content": "Are you sure you want to permanently delete all memories for {{user}}? This action cannot be undone.", - "memories_reset_success": "All memories for {{user}} have been reset successfully", - "reset_memories_failed": "Failed to reset memories", - "delete_confirm_single": "Are you sure you want to delete this memory?", - "total_memories": "total memories", - "default": "Default", - "custom": "Custom", - "global_memory_enabled": "Global memory enabled", - "global_memory": "Global Memory", - "enable_global_memory_first": "Please enable global memory first", - "configure_memory_first": "Please configure memory settings first", - "global_memory_disabled_title": "Global Memory Disabled", - "global_memory_disabled_desc": "To use memory features, please enable global memory in assistant settings first.", - "not_configured_title": "Memory Not Configured", - "not_configured_desc": "Please configure embedding and LLM models in memory settings to enable memory functionality.", - "go_to_memory_page": "Go to Memory Page", - "settings": "Settings", - "user_management": "User Management", - "statistics": "Statistics", - "search": "Search", - "initial_memory_content": "Welcome! This is your first memory.", - "loading": "Loading memories...", - "settings_title": "Memory Settings", - "llm_model": "LLM Model", - "please_select_llm_model": "Please select an LLM model", - "select_llm_model_placeholder": "Select LLM Model", - "embedding_model": "Embedding Model", - "please_select_embedding_model": "Please select an embedding model", - "select_embedding_model_placeholder": "Select Embedding Model", - "embedding_dimensions": "Embedding Dimensions", - "stored_memories": "Stored Memories", - "global_memory_description": "To use memory features, please enable global memory in assistant settings." - }, - "trace": { - "label": "Call Chain", - "traceWindow": "Call Chain Window", - "backList": "Back To List", - "spanDetail": "Span Details", - "name": "Node Name", - "tag": "Tag", - "startTime": "Start Time", - "endTime": "End Time", - "tokenUsage": "Token Usage", - "spendTime": "Spend Time", - "parentId": "Parent Id", - "inputs": "Inputs", - "outputs": "Outputs", - "noTraceList": "No trace information found", - "edasSupport": "Powered by Alibaba Cloud EDAS" } } } diff --git a/src/renderer/src/i18n/locales/ja-jp.json b/src/renderer/src/i18n/locales/ja-jp.json index b16d9d685e..7833e11a09 100644 --- a/src/renderer/src/i18n/locales/ja-jp.json +++ b/src/renderer/src/i18n/locales/ja-jp.json @@ -1801,6 +1801,10 @@ "token_placeholder": "Yuqueトークンを入力してください" } }, + "developer": { + "enable_developer_mode": "開発者モードを有効にする", + "title": "開発者モード" + }, "display.assistant.title": "アシスタント設定", "display.custom.css": "カスタムCSS", "display.custom.css.cherrycss": "cherrycss.comから取得", @@ -2503,6 +2507,23 @@ "title": "ページズーム" } }, + "trace": { + "backList": "リストに戻る", + "edasSupport": "Powered by Alibaba Cloud EDAS", + "endTime": "終了時間", + "inputs": "入力", + "label": "呼び出しチェーン", + "name": "ノード名", + "noTraceList": "トレース情報が見つかりません", + "outputs": "出力", + "parentId": "親ID", + "spanDetail": "スパンの詳細", + "spendTime": "時間を過ごす", + "startTime": "開始時間", + "tag": "Tagラベル", + "tokenUsage": "トークンの使用", + "traceWindow": "呼び出しチェーンウィンドウ" + }, "translate": { "alter_language": "備用言語", "any.language": "任意の言語", @@ -2564,129 +2585,6 @@ "quit": "終了", "show_window": "ウィンドウを表示", "visualization": "可視化" - }, - "trace": { - "label": "呼び出しチェーン", - "traceWindow": "呼び出しチェーンウィンドウ", - "backList": "リストに戻る", - "spanDetail": "スパンの詳細", - "name": "ノード名", - "tag": "Tagラベル", - "startTime": "開始時間", - "endTime": "終了時間", - "tokenUsage": "トークンの使用", - "spendTime": "時間を過ごす", - "parentId": "親ID", - "inputs": "入力", - "outputs": "出力", - "noTraceList": "トレース情報が見つかりません", - "edasSupport": "Powered by Alibaba Cloud EDAS" - }, - "memory": { - "title": "グローバルメモリ", - "add_memory": "メモリーを追加", - "edit_memory": "メモリーを編集", - "memory_content": "メモリー内容", - "please_enter_memory": "メモリー内容を入力してください", - "memory_placeholder": "メモリー内容を入力...", - "user_id": "ユーザーID", - "user_id_placeholder": "ユーザーIDを入力(オプション)", - "load_failed": "メモリーの読み込みに失敗しました", - "add_success": "メモリーが正常に追加されました", - "add_failed": "メモリーの追加に失敗しました", - "update_success": "メモリーが正常に更新されました", - "update_failed": "メモリーの更新に失敗しました", - "delete_success": "メモリーが正常に削除されました", - "delete_failed": "メモリーの削除に失敗しました", - "delete_confirm_title": "メモリーを削除", - "delete_confirm_content": "{{count}}件のメモリーを削除してもよろしいですか?", - "delete_confirm": "このメモリーを削除してもよろしいですか?", - "time": "時間", - "user": "ユーザー", - "content": "内容", - "score": "スコア", - "memories_description": "{{total}}件中{{count}}件のメモリーを表示", - "search_placeholder": "メモリーを検索...", - "start_date": "開始日", - "end_date": "終了日", - "all_users": "すべてのユーザー", - "users": "ユーザー", - "delete_selected": "選択したものを削除", - "reset_filters": "フィルターをリセット", - "pagination_total": "{{total}}件中{{start}}-{{end}}件", - "current_user": "現在のユーザー", - "select_user": "ユーザーを選択", - "default_user": "デフォルトユーザー", - "switch_user": "ユーザーを切り替え", - "user_switched": "ユーザーコンテキストが{{user}}に切り替わりました", - "switch_user_confirm": "ユーザーコンテキストを{{user}}に切り替えますか?", - "add_user": "ユーザーを追加", - "add_new_user": "新しいユーザーを追加", - "new_user_id": "新しいユーザーID", - "new_user_id_placeholder": "一意のユーザーIDを入力", - "user_id_required": "ユーザーIDは必須です", - "user_id_reserved": "'default-user'は予約済みです。別のIDを使用してください", - "user_id_exists": "このユーザーIDはすでに存在します", - "user_id_too_long": "ユーザーIDは50文字を超えられません", - "user_id_invalid_chars": "ユーザーIDには文字、数字、ハイフン、アンダースコアのみ使用できます", - "user_id_rules": "ユーザーIDは一意であり、文字、数字、ハイフン(-)、アンダースコア(_)のみ含む必要があります", - "user_created": "ユーザー{{user}}が作成され、切り替えが成功しました", - "add_user_failed": "ユーザーの追加に失敗しました", - "memory": "個のメモリ", - "reset_user_memories": "ユーザーメモリをリセット", - "reset_memories": "メモリをリセット", - "delete_user": "ユーザーを削除", - "loading_memories": "メモリを読み込み中...", - "no_memories": "メモリがありません", - "no_matching_memories": "一致するメモリが見つかりません", - "no_memories_description": "最初のメモリを追加してください", - "try_different_filters": "検索条件を調整してください", - "add_first_memory": "最初のメモリを追加", - "user_switch_failed": "ユーザーの切り替えに失敗しました", - "cannot_delete_default_user": "デフォルトユーザーは削除できません", - "delete_user_confirm_title": "ユーザーを削除", - "delete_user_confirm_content": "ユーザー{{user}}とそのすべてのメモリを削除してもよろしいですか?", - "user_deleted": "ユーザー{{user}}が正常に削除されました", - "delete_user_failed": "ユーザーの削除に失敗しました", - "reset_user_memories_confirm_title": "ユーザーメモリをリセット", - "reset_user_memories_confirm_content": "{{user}}のすべてのメモリをリセットしてもよろしいですか?", - "user_memories_reset": "{{user}}のすべてのメモリがリセットされました", - "reset_user_memories_failed": "ユーザーメモリのリセットに失敗しました", - "reset_memories_confirm_title": "すべてのメモリをリセット", - "reset_memories_confirm_content": "{{user}}のすべてのメモリを完全に削除してもよろしいですか?この操作は元に戻せません。", - "memories_reset_success": "{{user}}のすべてのメモリが正常にリセットされました", - "reset_memories_failed": "メモリのリセットに失敗しました", - "delete_confirm_single": "このメモリを削除してもよろしいですか?", - "total_memories": "個のメモリ", - "default": "デフォルト", - "custom": "カスタム", - "description": "メモリは、アシスタントとのやりとりに関する情報を保存・管理する機能です。メモリの追加、編集、削除のほか、フィルタリングや検索を行うことができます。", - "global_memory_enabled": "グローバルメモリが有効化されました", - "global_memory": "グローバルメモリ", - "enable_global_memory_first": "最初にグローバルメモリを有効にしてください", - "configure_memory_first": "最初にメモリ設定を構成してください", - "global_memory_disabled_title": "グローバルメモリが無効です", - "global_memory_disabled_desc": "メモリ機能を使用するには、まずアシスタント設定でグローバルメモリを有効にしてください。", - "not_configured_title": "メモリが設定されていません", - "not_configured_desc": "メモリ機能を有効にするには、メモリ設定で埋め込みとLLMモデルを設定してください。", - "go_to_memory_page": "メモリページに移動", - "settings": "設定", - "statistics": "統計", - "search": "検索", - "actions": "アクション", - "user_management": "ユーザー管理", - "initial_memory_content": "ようこそ!これはあなたの最初の記憶です。", - "loading": "思い出を読み込み中...", - "settings_title": "メモリ設定", - "llm_model": "LLMモデル", - "please_select_llm_model": "LLMモデルを選択してください", - "select_llm_model_placeholder": "LLMモデルを選択", - "embedding_model": "埋め込みモデル", - "please_select_embedding_model": "埋め込みモデルを選択してください", - "select_embedding_model_placeholder": "埋め込みモデルを選択", - "embedding_dimensions": "埋め込み次元", - "stored_memories": "保存された記憶", - "global_memory_description": "メモリ機能を使用するには、アシスタント設定でグローバルメモリを有効にしてください。" } } } diff --git a/src/renderer/src/i18n/locales/ru-ru.json b/src/renderer/src/i18n/locales/ru-ru.json index 896cbfa0f3..e5a8029335 100644 --- a/src/renderer/src/i18n/locales/ru-ru.json +++ b/src/renderer/src/i18n/locales/ru-ru.json @@ -1801,6 +1801,10 @@ "token_placeholder": "Введите токен Yuque" } }, + "developer": { + "enable_developer_mode": "Включить разработчик", + "title": "Разработчик" + }, "display.assistant.title": "Настройки ассистентов", "display.custom.css": "Пользовательский CSS", "display.custom.css.cherrycss": "Получить из cherrycss.com", @@ -2503,6 +2507,23 @@ "title": "Масштаб страницы" } }, + "trace": { + "backList": "Вернуться к списку", + "edasSupport": "Powered by Alibaba Cloud EDAS", + "endTime": "время окончания", + "inputs": "входы", + "label": "Цепочка вызовов", + "name": "Имя узла", + "noTraceList": "Информация о следах не найдена", + "outputs": "выходы", + "parentId": "Родительский идентификатор", + "spanDetail": "Span Подробнее", + "spendTime": "тратитьВремя", + "startTime": "время начала", + "tag": "ярлык", + "tokenUsage": "Использование токена", + "traceWindow": "Окно цепочки вызовов" + }, "translate": { "alter_language": "Альтернативный язык", "any.language": "Любой язык", @@ -2564,129 +2585,6 @@ "quit": "Выйти", "show_window": "Показать окно", "visualization": "Визуализация" - }, - "memory": { - "title": "Глобальная память", - "add_memory": "Добавить память", - "edit_memory": "Редактировать память", - "memory_content": "Содержимое памяти", - "please_enter_memory": "Пожалуйста, введите содержимое памяти", - "memory_placeholder": "Введите содержимое памяти...", - "user_id": "ID пользователя", - "user_id_placeholder": "Введите ID пользователя (необязательно)", - "load_failed": "Не удалось загрузить память", - "add_success": "Память успешно добавлена", - "add_failed": "Не удалось добавить память", - "update_success": "Память успешно обновлена", - "update_failed": "Не удалось обновить память", - "delete_success": "Память успешно удалена", - "delete_failed": "Не удалось удалить память", - "delete_confirm_title": "Удалить память", - "delete_confirm_content": "Вы уверены, что хотите удалить {{count}} записей памяти?", - "delete_confirm": "Вы уверены, что хотите удалить эту запись памяти?", - "time": "Время", - "user": "Пользователь", - "content": "Содержимое", - "score": "Оценка", - "memories_description": "Показано {{count}} из {{total}} записей памяти", - "search_placeholder": "Поиск памяти...", - "start_date": "Дата начала", - "end_date": "Дата окончания", - "all_users": "Все пользователи", - "users": "пользователи", - "delete_selected": "Удалить выбранные", - "reset_filters": "Сбросить фильтры", - "pagination_total": "{{start}}-{{end}} из {{total}} элементов", - "current_user": "Текущий пользователь", - "select_user": "Выбрать пользователя", - "default_user": "Пользователь по умолчанию", - "switch_user": "Переключить пользователя", - "user_switched": "Контекст пользователя переключен на {{user}}", - "switch_user_confirm": "Переключить контекст пользователя на {{user}}?", - "add_user": "Добавить пользователя", - "add_new_user": "Добавить нового пользователя", - "new_user_id": "Новый ID пользователя", - "new_user_id_placeholder": "Введите уникальный ID пользователя", - "user_id_required": "ID пользователя обязателен", - "user_id_reserved": "'default-user' зарезервирован, используйте другой ID", - "user_id_exists": "Этот ID пользователя уже существует", - "user_id_too_long": "ID пользователя не может превышать 50 символов", - "user_id_invalid_chars": "ID пользователя может содержать только буквы, цифры, дефисы и подчёркивания", - "user_id_rules": "ID пользователя должен быть уникальным и содержать только буквы, цифры, дефисы (-) и подчёркивания (_)", - "user_created": "Пользователь {{user}} создан и переключен успешно", - "add_user_failed": "Не удалось добавить пользователя", - "memory": "воспоминаний", - "reset_user_memories": "Сбросить воспоминания пользователя", - "reset_memories": "Сбросить воспоминания", - "delete_user": "Удалить пользователя", - "loading_memories": "Загрузка воспоминаний...", - "no_memories": "Нет воспоминаний", - "no_matching_memories": "Подходящие воспоминания не найдены", - "no_memories_description": "Начните с добавления вашего первого воспоминания", - "try_different_filters": "Попробуйте изменить критерии поиска", - "add_first_memory": "Добавить первое воспоминание", - "user_switch_failed": "Не удалось переключить пользователя", - "cannot_delete_default_user": "Нельзя удалить пользователя по умолчанию", - "delete_user_confirm_title": "Удалить пользователя", - "delete_user_confirm_content": "Вы уверены, что хотите удалить пользователя {{user}} и все его воспоминания?", - "user_deleted": "Пользователь {{user}} успешно удален", - "delete_user_failed": "Не удалось удалить пользователя", - "reset_user_memories_confirm_title": "Сбросить воспоминания пользователя", - "reset_user_memories_confirm_content": "Вы уверены, что хотите сбросить все воспоминания пользователя {{user}}?", - "user_memories_reset": "Все воспоминания пользователя {{user}} сброшены", - "reset_user_memories_failed": "Не удалось сбросить воспоминания пользователя", - "reset_memories_confirm_title": "Сбросить все воспоминания", - "reset_memories_confirm_content": "Вы уверены, что хотите навсегда удалить все воспоминания пользователя {{user}}? Это действие нельзя отменить.", - "memories_reset_success": "Все воспоминания пользователя {{user}} успешно сброшены", - "reset_memories_failed": "Не удалось сбросить воспоминания", - "delete_confirm_single": "Вы уверены, что хотите удалить это воспоминание?", - "total_memories": "всего воспоминаний", - "default": "По умолчанию", - "custom": "Пользовательский", - "description": "Память позволяет хранить и управлять информацией о ваших взаимодействиях с ассистентом. Вы можете добавлять, редактировать и удалять воспоминания, а также фильтровать и искать их.", - "global_memory_enabled": "Глобальная память включена", - "global_memory": "Глобальная память", - "enable_global_memory_first": "Сначала включите глобальную память", - "configure_memory_first": "Сначала настройте параметры памяти", - "global_memory_disabled_title": "Глобальная память отключена", - "global_memory_disabled_desc": "Чтобы использовать функции памяти, сначала включите глобальную память в настройках ассистента.", - "not_configured_title": "Память не настроена", - "not_configured_desc": "Пожалуйста, настройте модели встраивания и LLM в настройках памяти, чтобы включить функциональность памяти.", - "go_to_memory_page": "Перейти на страницу памяти", - "settings": "Настройки", - "statistics": "Статистика", - "search": "Поиск", - "actions": "Действия", - "user_management": "Управление пользователями", - "initial_memory_content": "Добро пожаловать! Это ваше первое воспоминание.", - "loading": "Загрузка воспоминаний...", - "settings_title": "Настройки памяти", - "llm_model": "Модель LLM", - "please_select_llm_model": "Пожалуйста, выберите модель LLM", - "select_llm_model_placeholder": "Выбор модели LLM", - "embedding_model": "Модель встраивания", - "please_select_embedding_model": "Пожалуйста, выберите модель для внедрения", - "select_embedding_model_placeholder": "Выберите модель внедрения", - "embedding_dimensions": "Размерность вложения", - "stored_memories": "Запасённые воспоминания", - "global_memory_description": "Для использования функций памяти необходимо включить глобальную память в настройках ассистента." - }, - "trace": { - "label": "Цепочка вызовов", - "traceWindow": "Окно цепочки вызовов", - "backList": "Вернуться к списку", - "spanDetail": "Span Подробнее", - "name": "Имя узла", - "tag": "ярлык", - "startTime": "время начала", - "endTime": "время окончания", - "tokenUsage": "Использование токена", - "spendTime": "тратитьВремя", - "parentId": "Родительский идентификатор", - "inputs": "входы", - "outputs": "выходы", - "noTraceList": "Информация о следах не найдена", - "edasSupport": "Powered by Alibaba Cloud EDAS" } } } diff --git a/src/renderer/src/i18n/locales/zh-cn.json b/src/renderer/src/i18n/locales/zh-cn.json index 90dbfd94b0..ede9a74274 100644 --- a/src/renderer/src/i18n/locales/zh-cn.json +++ b/src/renderer/src/i18n/locales/zh-cn.json @@ -1801,6 +1801,10 @@ "token_placeholder": "请输入语雀 Token" } }, + "developer": { + "enable_developer_mode": "启用开发者模式", + "title": "开发者模式" + }, "display.assistant.title": "助手设置", "display.custom.css": "自定义 CSS", "display.custom.css.cherrycss": "从 cherrycss.com 获取", @@ -2503,6 +2507,23 @@ "title": "缩放" } }, + "trace": { + "backList": "返回列表", + "edasSupport": "Powered by Alibaba Cloud EDAS", + "endTime": "结束时间", + "inputs": "输入", + "label": "调用链", + "name": "节点名称", + "noTraceList": "没有找到Trace信息", + "outputs": "输出", + "parentId": "上级Id", + "spanDetail": "Span详情", + "spendTime": "消耗时间", + "startTime": "开始时间", + "tag": "标签", + "tokenUsage": "Token使用量", + "traceWindow": "调用链窗口" + }, "translate": { "alter_language": "备用语言", "any.language": "任意语言", @@ -2564,129 +2585,6 @@ "quit": "退出", "show_window": "显示窗口", "visualization": "可视化" - }, - "memory": { - "title": "全局记忆", - "settings": "设置", - "statistics": "统计", - "search": "搜索", - "actions": "操作", - "add_memory": "添加记忆", - "edit_memory": "编辑记忆", - "memory_content": "记忆内容", - "please_enter_memory": "请输入记忆内容", - "memory_placeholder": "输入记忆内容...", - "user_id": "用户 ID", - "user_id_placeholder": "输入用户 ID(可选)", - "load_failed": "加载记忆失败", - "add_success": "记忆添加成功", - "add_failed": "添加记忆失败", - "update_success": "记忆更新成功", - "update_failed": "更新记忆失败", - "delete_success": "记忆删除成功", - "delete_failed": "删除记忆失败", - "delete_confirm_title": "删除记忆", - "delete_confirm_content": "确定要删除 {{count}} 条记忆吗?", - "delete_confirm": "确定要删除这条记忆吗?", - "time": "时间", - "user": "用户", - "content": "内容", - "score": "分数", - "memories_description": "显示 {{count}} / {{total}} 条记忆", - "search_placeholder": "搜索记忆...", - "start_date": "开始日期", - "end_date": "结束日期", - "all_users": "所有用户", - "users": "用户", - "delete_selected": "删除选中", - "reset_filters": "重置筛选", - "pagination_total": "第 {{start}}-{{end}} 项,共 {{total}} 项", - "current_user": "当前用户", - "select_user": "选择用户", - "default_user": "默认用户", - "switch_user": "切换用户", - "user_switched": "用户上下文已切换到 {{user}}", - "switch_user_confirm": "将用户上下文切换到 {{user}}?", - "add_user": "添加用户", - "add_new_user": "添加新用户", - "new_user_id": "新用户ID", - "new_user_id_placeholder": "输入唯一的用户ID", - "user_management": "用户管理", - "user_id_required": "用户ID为必填项", - "user_id_reserved": "'default-user' 为保留字,请使用其他ID", - "user_id_exists": "该用户ID已存在", - "user_id_too_long": "用户ID不能超过50个字符", - "user_id_invalid_chars": "用户ID只能包含字母、数字、连字符和下划线", - "user_id_rules": "用户ID必须唯一,只能包含字母、数字、连字符(-)和下划线(_)", - "user_created": "用户 {{user}} 创建并切换成功", - "add_user_failed": "添加用户失败", - "memory": "条记忆", - "reset_user_memories": "重置用户记忆", - "reset_memories": "重置记忆", - "delete_user": "删除用户", - "loading_memories": "正在加载记忆...", - "no_memories": "暂无记忆", - "no_matching_memories": "未找到匹配的记忆", - "no_memories_description": "开始添加您的第一条记忆吧", - "try_different_filters": "尝试调整搜索条件", - "add_first_memory": "添加您的第一条记忆", - "user_switch_failed": "切换用户失败", - "cannot_delete_default_user": "不能删除默认用户", - "delete_user_confirm_title": "删除用户", - "delete_user_confirm_content": "确定要删除用户 {{user}} 及其所有记忆吗?", - "user_deleted": "用户 {{user}} 删除成功", - "delete_user_failed": "删除用户失败", - "reset_user_memories_confirm_title": "重置用户记忆", - "reset_user_memories_confirm_content": "确定要重置 {{user}} 的所有记忆吗?", - "user_memories_reset": "{{user}} 的所有记忆已重置", - "reset_user_memories_failed": "重置用户记忆失败", - "reset_memories_confirm_title": "重置所有记忆", - "reset_memories_confirm_content": "确定要永久删除 {{user}} 的所有记忆吗?此操作无法撤销。", - "memories_reset_success": "{{user}} 的所有记忆已成功重置", - "reset_memories_failed": "重置记忆失败", - "delete_confirm_single": "确定要删除这条记忆吗?", - "total_memories": "条记忆", - "default": "默认", - "custom": "自定义", - "description": "记忆功能允许您存储和管理与助手交互的信息。您可以添加、编辑和删除记忆,也可以对它们进行过滤和搜索。", - "global_memory_enabled": "全局记忆已启用", - "global_memory": "全局记忆", - "enable_global_memory_first": "请先启用全局记忆", - "configure_memory_first": "请先配置记忆设置", - "global_memory_disabled_title": "全局记忆已禁用", - "global_memory_disabled_desc": "要使用记忆功能,请先在助手设置中启用全局记忆。", - "not_configured_title": "记忆未配置", - "not_configured_desc": "请在记忆设置中配置嵌入和LLM模型以启用记忆功能。", - "go_to_memory_page": "前往记忆页面", - "initial_memory_content": "欢迎!这是您的第一条记忆。", - "loading": "正在加载记忆...", - "settings_title": "记忆设置", - "llm_model": "LLM 模型", - "please_select_llm_model": "请选择 LLM 模型", - "select_llm_model_placeholder": "选择 LLM 模型", - "embedding_model": "嵌入模型", - "please_select_embedding_model": "请选择嵌入模型", - "select_embedding_model_placeholder": "选择嵌入模型", - "embedding_dimensions": "嵌入维度", - "stored_memories": "已存储记忆", - "global_memory_description": "需要开启助手设置中的全局记忆才能使用" - }, - "trace": { - "label": "调用链", - "traceWindow": "调用链窗口", - "backList": "返回列表", - "spanDetail": "Span详情", - "name": "节点名称", - "tag": "标签", - "startTime": "开始时间", - "endTime": "结束时间", - "tokenUsage": "Token使用量", - "spendTime": "消耗时间", - "parentId": "上级Id", - "inputs": "输入", - "outputs": "输出", - "noTraceList": "没有找到Trace信息", - "edasSupport": "Powered by Alibaba Cloud EDAS" } } } diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json index 2b9a285b38..5071b840b3 100644 --- a/src/renderer/src/i18n/locales/zh-tw.json +++ b/src/renderer/src/i18n/locales/zh-tw.json @@ -1801,6 +1801,10 @@ "token_placeholder": "請輸入語雀 Token" } }, + "developer": { + "enable_developer_mode": "啟用開發者模式", + "title": "開發者模式" + }, "display.assistant.title": "助手設定", "display.custom.css": "自訂 CSS", "display.custom.css.cherrycss": "從 cherrycss.com 取得", @@ -2503,6 +2507,23 @@ "title": "縮放" } }, + "trace": { + "backList": "返回清單", + "edasSupport": "Powered by Alibaba Cloud EDAS", + "endTime": "結束時間", + "inputs": "輸入", + "label": "呼叫鏈", + "name": "節點名稱", + "noTraceList": "沒有找到Trace資訊", + "outputs": "輸出", + "parentId": "上級Id", + "spanDetail": "Span詳情", + "spendTime": "消耗時間", + "startTime": "開始時間", + "tag": "標籤", + "tokenUsage": "Token使用量", + "traceWindow": "呼叫鏈視窗" + }, "translate": { "alter_language": "備用語言", "any.language": "任意語言", @@ -2564,129 +2585,6 @@ "quit": "結束", "show_window": "顯示視窗", "visualization": "視覺化" - }, - "memory": { - "title": "全域記憶", - "add_memory": "新增記憶", - "edit_memory": "編輯記憶", - "memory_content": "記憶內容", - "please_enter_memory": "請輸入記憶內容", - "memory_placeholder": "輸入記憶內容...", - "user_id": "使用者ID", - "user_id_placeholder": "輸入使用者ID(可選)", - "load_failed": "載入記憶失敗", - "add_success": "記憶新增成功", - "add_failed": "新增記憶失敗", - "update_success": "記憶更新成功", - "update_failed": "更新記憶失敗", - "delete_success": "記憶刪除成功", - "delete_failed": "刪除記憶失敗", - "delete_confirm_title": "刪除記憶", - "delete_confirm_content": "確定要刪除 {{count}} 條記憶嗎?", - "delete_confirm": "確定要刪除這條記憶嗎?", - "time": "時間", - "user": "使用者", - "content": "內容", - "score": "分數", - "memories_description": "顯示 {{count}} / {{total}} 條記憶", - "search_placeholder": "搜尋記憶...", - "start_date": "開始日期", - "end_date": "結束日期", - "all_users": "所有使用者", - "users": "使用者", - "delete_selected": "刪除選取", - "reset_filters": "重設篩選", - "pagination_total": "第 {{start}}-{{end}} 項,共 {{total}} 項", - "current_user": "目前使用者", - "select_user": "選擇使用者", - "default_user": "預設使用者", - "switch_user": "切換使用者", - "user_switched": "使用者內容已切換至 {{user}}", - "switch_user_confirm": "將使用者內容切換至 {{user}}?", - "add_user": "新增使用者", - "add_new_user": "新增新使用者", - "new_user_id": "新使用者ID", - "new_user_id_placeholder": "輸入唯一的使用者ID", - "user_id_required": "使用者ID為必填欄位", - "user_id_reserved": "'default-user' 為保留字,請使用其他ID", - "user_id_exists": "此使用者ID已存在", - "user_id_too_long": "使用者ID不能超過50個字元", - "user_id_invalid_chars": "使用者ID只能包含字母、數字、連字符和底線", - "user_id_rules": "使用者ID必须唯一,只能包含字母、數字、連字符(-)和底線(_)", - "user_created": "使用者 {{user}} 建立並切換成功", - "add_user_failed": "新增使用者失敗", - "memory": "個記憶", - "reset_user_memories": "重置使用者記憶", - "reset_memories": "重置記憶", - "delete_user": "刪除使用者", - "loading_memories": "正在載入記憶...", - "no_memories": "暫無記憶", - "no_matching_memories": "未找到符合的記憶", - "no_memories_description": "開始新增您的第一個記憶吧", - "try_different_filters": "嘗試調整搜尋條件", - "add_first_memory": "新增您的第一個記憶", - "user_switch_failed": "切換使用者失敗", - "cannot_delete_default_user": "不能刪除預設使用者", - "delete_user_confirm_title": "刪除使用者", - "delete_user_confirm_content": "確定要刪除使用者 {{user}} 及其所有記憶嗎?", - "user_deleted": "使用者 {{user}} 刪除成功", - "delete_user_failed": "刪除使用者失敗", - "reset_user_memories_confirm_title": "重置使用者記憶", - "reset_user_memories_confirm_content": "確定要重置 {{user}} 的所有記憶嗎?", - "user_memories_reset": "{{user}} 的所有記憶已重置", - "reset_user_memories_failed": "重置使用者記憶失敗", - "reset_memories_confirm_title": "重置所有記憶", - "reset_memories_confirm_content": "確定要永久刪除 {{user}} 的所有記憶嗎?此操作無法復原。", - "memories_reset_success": "{{user}} 的所有記憶已成功重置", - "reset_memories_failed": "重置記憶失敗", - "delete_confirm_single": "確定要刪除這個記憶嗎?", - "total_memories": "個記憶", - "default": "預設", - "custom": "自定義", - "description": "記憶功能讓您儲存和管理與助手互動的資訊。您可以新增、編輯和刪除記憶,也可以對它們進行篩選和搜尋。", - "global_memory_enabled": "全域記憶已啟用", - "global_memory": "全域記憶", - "enable_global_memory_first": "請先啟用全域記憶", - "configure_memory_first": "請先配置記憶設定", - "global_memory_disabled_title": "全域記憶已停用", - "global_memory_disabled_desc": "要使用記憶功能,請先在助手設定中啟用全域記憶。", - "not_configured_title": "記憶未配置", - "not_configured_desc": "請在記憶設定中配置嵌入和LLM模型以啟用記憶功能。", - "go_to_memory_page": "前往記憶頁面", - "settings": "設定", - "statistics": "統計", - "search": "搜尋", - "actions": "操作", - "user_management": "使用者管理", - "initial_memory_content": "歡迎!這是你的第一個記憶。", - "loading": "載入記憶中...", - "settings_title": "記憶體設定", - "llm_model": "LLM 模型", - "please_select_llm_model": "請選擇一個LLM模型", - "select_llm_model_placeholder": "選擇LLM模型", - "embedding_model": "嵌入模型", - "please_select_embedding_model": "請選擇一個嵌入模型", - "select_embedding_model_placeholder": "選擇嵌入模型", - "embedding_dimensions": "嵌入維度", - "stored_memories": "儲存的記憶", - "global_memory_description": "需要開啟助手設定中的全域記憶才能使用" - }, - "trace": { - "label": "呼叫鏈", - "traceWindow": "呼叫鏈視窗", - "backList": "返回清單", - "spanDetail": "Span詳情", - "name": "節點名稱", - "tag": "標籤", - "startTime": "開始時間", - "endTime": "結束時間", - "tokenUsage": "Token使用量", - "spendTime": "消耗時間", - "parentId": "上級Id", - "inputs": "輸入", - "outputs": "輸出", - "noTraceList": "沒有找到Trace資訊", - "edasSupport": "Powered by Alibaba Cloud EDAS" } } } diff --git a/src/renderer/src/pages/home/Messages/MessageMenubar.tsx b/src/renderer/src/pages/home/Messages/MessageMenubar.tsx index c241a8cac1..f79eeb62a1 100644 --- a/src/renderer/src/pages/home/Messages/MessageMenubar.tsx +++ b/src/renderer/src/pages/home/Messages/MessageMenubar.tsx @@ -1,5 +1,4 @@ import { CheckOutlined, EditOutlined, QuestionCircleOutlined, SyncOutlined } from '@ant-design/icons' -import { defaultConfig } from '@mcp-trace/trace-core' import ObsidianExportPopup from '@renderer/components/Popups/ObsidianExportPopup' import SaveToKnowledgePopup from '@renderer/components/Popups/SaveToKnowledgePopup' import SelectModelPopup from '@renderer/components/Popups/SelectModelPopup' @@ -8,7 +7,7 @@ import { translateLanguageOptions } from '@renderer/config/translate' import { useMessageEditing } from '@renderer/context/MessageEditingContext' import { useChatContext } from '@renderer/hooks/useChatContext' import { useMessageOperations, useTopicLoading } from '@renderer/hooks/useMessageOperations' -import { useMessageStyle } from '@renderer/hooks/useSettings' +import { useEnableDeveloperMode, useMessageStyle } from '@renderer/hooks/useSettings' import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService' import { getMessageTitle } from '@renderer/services/MessagesService' import { translateText } from '@renderer/services/TranslateService' @@ -47,7 +46,7 @@ import { ThumbsUp, Trash } from 'lucide-react' -import { FC, memo, useCallback, useEffect, useMemo, useState } from 'react' +import { FC, memo, useCallback, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import { useSelector } from 'react-redux' import styled from 'styled-components' @@ -88,6 +87,7 @@ const MessageMenubar: FC = (props) => { } = useMessageOperations(topic) const { isBubbleStyle } = useMessageStyle() + const { enableDeveloperMode } = useEnableDeveloperMode() const loading = useTopicLoading(topic) @@ -179,14 +179,7 @@ const MessageMenubar: FC = (props) => { [isTranslating, message, getTranslationUpdater, mainTextContent] ) - const [isDevelopModel, setIsDevelopModel] = useState(true) - - useEffect(() => { - setIsDevelopModel(defaultConfig.isDevModel || false) - }, []) - const handleTraceUserMessage = useCallback(async () => { - console.log('current traceId', message.traceId, 'start send') if (message.traceId) { window.api.trace.openWindow( message.topicId, @@ -594,7 +587,7 @@ const MessageMenubar: FC = (props) => { - {isDevelopModel && message.traceId && ( + {enableDeveloperMode && message.traceId && ( handleTraceUserMessage()}> diff --git a/src/renderer/src/pages/settings/GeneralSettings.tsx b/src/renderer/src/pages/settings/GeneralSettings.tsx index 32e9e335ce..497cfca0c8 100644 --- a/src/renderer/src/pages/settings/GeneralSettings.tsx +++ b/src/renderer/src/pages/settings/GeneralSettings.tsx @@ -1,7 +1,7 @@ import { InfoCircleOutlined } from '@ant-design/icons' import Selector from '@renderer/components/Selector' import { useTheme } from '@renderer/context/ThemeProvider' -import { useSettings } from '@renderer/hooks/useSettings' +import { useEnableDeveloperMode, useSettings } from '@renderer/hooks/useSettings' import i18n from '@renderer/i18n' import { RootState, useAppDispatch } from '@renderer/store' import { @@ -42,6 +42,7 @@ const GeneralSettings: FC = () => { } = useSettings() const [proxyUrl, setProxyUrl] = useState(storeProxyUrl) const { theme } = useTheme() + const { enableDeveloperMode, setEnableDeveloperMode } = useEnableDeveloperMode() const updateTray = (isShowTray: boolean) => { setTray(isShowTray) @@ -321,6 +322,14 @@ const GeneralSettings: FC = () => { /> + + {t('settings.developer.title')} + + + {t('settings.developer.enable_developer_mode')} + + + ) } diff --git a/src/renderer/src/services/SpanManagerService.ts b/src/renderer/src/services/SpanManagerService.ts index 401ff58101..13b056f7ea 100644 --- a/src/renderer/src/services/SpanManagerService.ts +++ b/src/renderer/src/services/SpanManagerService.ts @@ -1,9 +1,11 @@ import { MessageStream } from '@anthropic-ai/sdk/resources/messages/messages' -import { defaultConfig, SpanEntity, TokenUsage } from '@mcp-trace/trace-core' +import { loggerService } from '@logger' +import { SpanEntity, TokenUsage } from '@mcp-trace/trace-core' import { cleanContext, endContext, getContext, startContext } from '@mcp-trace/trace-web' import { Context, context, Span, SpanStatusCode, trace } from '@opentelemetry/api' import { isAsyncIterable } from '@renderer/aiCore/middleware/utils' import { db } from '@renderer/databases' +import { getEnableDeveloperMode } from '@renderer/hooks/useSettings' import { EVENT_NAMES, EventEmitter } from '@renderer/services/EventService' import { handleAsyncIterable } from '@renderer/trace/dataHandler/AsyncIterableHandler' import { handleResult } from '@renderer/trace/dataHandler/CommonResultHandler' @@ -16,6 +18,8 @@ import { MessageBlockType } from '@renderer/types/newMessage' import { SdkRawChunk } from '@renderer/types/sdk' import { Stream } from 'openai/streaming' +const logger = loggerService.withContext('SpanManagerService') + class SpanManagerService { private spanMap: Map = new Map() @@ -35,10 +39,11 @@ class SpanManagerService { } startTrace(params: StartSpanParams, models?: Model[]) { - if (!defaultConfig.isDevModel) { - console.warn('Trace is enabled in developer mode.') + if (!getEnableDeveloperMode()) { + logger.warn('Trace is enabled in developer mode.') return } + const span = webTracer.startSpan(params.name || 'root', { root: true, attributes: { @@ -60,8 +65,8 @@ class SpanManagerService { } async restartTrace(message: Message, text?: string) { - if (!defaultConfig.isDevModel) { - console.warn('Trace is enabled in developer mode.') + if (!getEnableDeveloperMode()) { + logger.warn('Trace is enabled in developer mode.') return } @@ -95,8 +100,8 @@ class SpanManagerService { } async appendTrace(message: Message, model: Model) { - if (!defaultConfig.isDevModel) { - console.warn('Trace is enabled in developer mode.') + if (!getEnableDeveloperMode()) { + logger.warn('Trace is enabled in developer mode.') return } if (!message.traceId) { @@ -189,8 +194,8 @@ class SpanManagerService { } addSpan(params: StartSpanParams) { - if (!defaultConfig.isDevModel) { - console.warn('Trace is enabled in developer mode.') + if (!getEnableDeveloperMode()) { + logger.warn('Trace is enabled in developer mode.') return } const entity = this.getModelSpanEntity(params.topicId, params.modelName) @@ -230,7 +235,7 @@ class SpanManagerService { rootEntity.addModelError(params.error) } if (!span) { - console.info(`No active span found for topicId: ${params.topicId}-modelName: ${params.modelName}.`) + logger.info(`No active span found for topicId: ${params.topicId}-modelName: ${params.modelName}.`) return } diff --git a/src/renderer/src/services/WebTraceService.ts b/src/renderer/src/services/WebTraceService.ts index 40f8f97e77..135e75b0a8 100644 --- a/src/renderer/src/services/WebTraceService.ts +++ b/src/renderer/src/services/WebTraceService.ts @@ -1,7 +1,10 @@ +import { loggerService } from '@logger' import { convertSpanToSpanEntity, FunctionSpanExporter, FunctionSpanProcessor } from '@mcp-trace/trace-core' import { WebTracer } from '@mcp-trace/trace-web' import { ReadableSpan } from '@opentelemetry/sdk-trace-base' +const logger = loggerService.withContext('WebTraceService') + const TRACER_NAME = 'CherryStudio' class WebTraceService { @@ -9,9 +12,10 @@ class WebTraceService { const exporter = new FunctionSpanExporter((spans: ReadableSpan[]): Promise => { // Implement your save logic here if needed // For now, just resolve immediately - console.log('Saving spans:', spans) + logger.info(`Saving spans: ${spans.length}`) return Promise.resolve() }) + const processor = new FunctionSpanProcessor( exporter, (span: ReadableSpan) => { diff --git a/src/renderer/src/store/settings.ts b/src/renderer/src/store/settings.ts index 1d30d9d9fa..2e24f9681a 100644 --- a/src/renderer/src/store/settings.ts +++ b/src/renderer/src/store/settings.ts @@ -198,6 +198,8 @@ export interface SettingsState { localBackupSkipBackupFile: boolean defaultPaintingProvider: PaintingProvider s3: S3Config + // Developer mode + enableDeveloperMode: boolean } export type MultiModelMessageStyle = 'horizontal' | 'vertical' | 'fold' | 'grid' @@ -364,7 +366,9 @@ export const initialState: SettingsState = { syncInterval: 0, maxBackups: 0, skipBackupFile: false - } + }, + // Developer mode + enableDeveloperMode: false } const settingsSlice = createSlice({ @@ -756,6 +760,9 @@ const settingsSlice = createSlice({ }, setS3Partial: (state, action: PayloadAction>) => { state.s3 = { ...state.s3, ...action.payload } + }, + setEnableDeveloperMode: (state, action: PayloadAction) => { + state.enableDeveloperMode = action.payload } } }) @@ -874,7 +881,8 @@ export const { setLocalBackupSkipBackupFile, setDefaultPaintingProvider, setS3, - setS3Partial + setS3Partial, + setEnableDeveloperMode } = settingsSlice.actions export default settingsSlice.reducer diff --git a/src/renderer/src/trace/pages/SpanDetail.tsx b/src/renderer/src/trace/pages/SpanDetail.tsx index 1ac22138e4..336f30504f 100644 --- a/src/renderer/src/trace/pages/SpanDetail.tsx +++ b/src/renderer/src/trace/pages/SpanDetail.tsx @@ -1,6 +1,7 @@ import './Trace.css' import { DoubleLeftOutlined } from '@ant-design/icons' +import { loggerService } from '@logger' // import TraceModal from '@renderer/trace/TraceModal' import { TraceModal } from '@renderer/trace/pages/TraceModel' import { FC, useCallback, useEffect, useState } from 'react' @@ -10,6 +11,8 @@ import ReactJson from 'react-json-view' import { Box, Button, Text } from './Component' import { convertTime } from './TraceTree' +const logger = loggerService.withContext('SpanDetail') + interface SpanDetailProps { node: TraceModal clickShowModal: (input: boolean) => void @@ -41,7 +44,7 @@ const SpanDetail: FC = ({ node, clickShowModal }) => { setIsJson(true) return } catch { - console.error('failed to parse json data:', data) + logger.error('failed to parse json data:', data) } } else if (typeof data === 'object' || Array.isArray(data)) { setJsonData(data) @@ -82,16 +85,17 @@ const SpanDetail: FC = ({ node, clickShowModal }) => { return ( - + { e.preventDefault() clickShowModal(true) }} href={'#'} - style={{ marginRight: 8, fontSize: '14px' }}> - -  {t('trace.backList')} + style={{ color: '#1677ff' }} + className="back-button"> + + {t('trace.backList')} {t('trace.spanDetail')} @@ -130,7 +134,7 @@ const SpanDetail: FC = ({ node, clickShowModal }) => { {t('trace.parentId')}: {node.parentId} */} - + @@ -168,4 +172,5 @@ const SpanDetail: FC = ({ node, clickShowModal }) => { ) } + export default SpanDetail diff --git a/src/renderer/src/windows/selection/action/components/__tests__/ActionUtils.test.ts b/src/renderer/src/windows/selection/action/components/__tests__/ActionUtils.test.ts index d6b34021b8..3431dd9440 100644 --- a/src/renderer/src/windows/selection/action/components/__tests__/ActionUtils.test.ts +++ b/src/renderer/src/windows/selection/action/components/__tests__/ActionUtils.test.ts @@ -1,6 +1,7 @@ import type { Assistant, Topic } from '@renderer/types' import { ChunkType } from '@renderer/types/chunk' import { AssistantMessageStatus, MessageBlockStatus } from '@renderer/types/newMessage' +import OpenAI from 'openai' import { afterEach, beforeEach, describe, expect, it, type Mock, vi } from 'vitest' import { processMessages } from '../ActionUtils' @@ -164,6 +165,28 @@ describe('processMessages', () => { for (const chunk of mockChunks) { await onChunkReceived(chunk) } + const rawOutput: OpenAI.ChatCompletion = { + id: 'test-id', + model: 'test-model', + object: 'chat.completion', + created: Date.now(), + choices: [ + { + index: 0, + message: { + role: 'assistant', + content: 'Here is my answer to your question.', + refusal: '' + }, + finish_reason: 'stop', + logprobs: null + } + ] + } + return { + rawOutput, + getText: () => 'Here is my answer to your question.' + } }) await processMessages( @@ -272,6 +295,28 @@ describe('processMessages', () => { for (const chunk of mockChunks) { await onChunkReceived(chunk) } + const rawOutput: OpenAI.ChatCompletion = { + id: 'test-id', + model: 'test-model', + object: 'chat.completion', + created: Date.now(), + choices: [ + { + index: 0, + message: { + role: 'assistant', + content: 'Partial response', + refusal: '' + }, + finish_reason: 'stop', + logprobs: null + } + ] + } + return { + rawOutput, + getText: () => 'Partial response' + } }) await processMessages( @@ -360,6 +405,28 @@ describe('processMessages', () => { for (const chunk of mockChunks) { await onChunkReceived(chunk) } + const rawOutput: OpenAI.ChatCompletion = { + id: 'test-id', + model: 'test-model', + object: 'chat.completion', + created: Date.now(), + choices: [ + { + index: 0, + message: { + role: 'assistant', + content: 'Partial', + refusal: '' + }, + finish_reason: 'stop', + logprobs: null + } + ] + } + return { + rawOutput, + getText: () => 'Partial' + } }) await processMessages( @@ -500,6 +567,28 @@ describe('processMessages', () => { for (const chunk of mockChunks) { await onChunkReceived(chunk) } + const rawOutput: OpenAI.ChatCompletion = { + id: 'test-id', + model: 'test-model', + object: 'chat.completion', + created: Date.now(), + choices: [ + { + index: 0, + message: { + role: 'assistant', + content: 'Second text', + refusal: '' + }, + finish_reason: 'stop', + logprobs: null + } + ] + } + return { + rawOutput, + getText: () => 'Second text' + } }) await processMessages( diff --git a/yarn.lock b/yarn.lock index 6ee60a0f7b..70b23a7145 100644 --- a/yarn.lock +++ b/yarn.lock @@ -872,7 +872,7 @@ __metadata: languageName: node linkType: hard -"@babel/code-frame@npm:^7.26.2": +"@babel/code-frame@npm:^7.10.4, @babel/code-frame@npm:^7.26.2": version: 7.27.1 resolution: "@babel/code-frame@npm:7.27.1" dependencies: @@ -969,7 +969,7 @@ __metadata: languageName: node linkType: hard -"@babel/helper-string-parser@npm:^7.27.1": +"@babel/helper-string-parser@npm:^7.25.9, @babel/helper-string-parser@npm:^7.27.1": version: 7.27.1 resolution: "@babel/helper-string-parser@npm:7.27.1" checksum: 10c0/8bda3448e07b5583727c103560bcf9c4c24b3c1051a4c516d4050ef69df37bb9a4734a585fe12725b8c2763de0a265aa1e909b485a4e3270b7cfd3e4dbe4b602 @@ -1022,7 +1022,7 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.10.1, @babel/runtime@npm:^7.10.4, @babel/runtime@npm:^7.11.1, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.16.7, @babel/runtime@npm:^7.18.0, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.18.6, @babel/runtime@npm:^7.20.0, @babel/runtime@npm:^7.20.7, @babel/runtime@npm:^7.21.0, @babel/runtime@npm:^7.22.5, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.23.6, @babel/runtime@npm:^7.23.9, @babel/runtime@npm:^7.24.1, @babel/runtime@npm:^7.24.4, @babel/runtime@npm:^7.24.7, @babel/runtime@npm:^7.24.8, @babel/runtime@npm:^7.25.7, @babel/runtime@npm:^7.26.0, @babel/runtime@npm:^7.9.2": +"@babel/runtime@npm:^7.0.0, @babel/runtime@npm:^7.10.1, @babel/runtime@npm:^7.10.4, @babel/runtime@npm:^7.11.1, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.1, @babel/runtime@npm:^7.16.7, @babel/runtime@npm:^7.18.0, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.18.6, @babel/runtime@npm:^7.20.0, @babel/runtime@npm:^7.20.7, @babel/runtime@npm:^7.21.0, @babel/runtime@npm:^7.22.5, @babel/runtime@npm:^7.23.2, @babel/runtime@npm:^7.23.6, @babel/runtime@npm:^7.23.9, @babel/runtime@npm:^7.24.1, @babel/runtime@npm:^7.24.4, @babel/runtime@npm:^7.24.7, @babel/runtime@npm:^7.24.8, @babel/runtime@npm:^7.25.7, @babel/runtime@npm:^7.26.0, @babel/runtime@npm:^7.9.2": version: 7.27.1 resolution: "@babel/runtime@npm:7.27.1" checksum: 10c0/530a7332f86ac5a7442250456823a930906911d895c0b743bf1852efc88a20a016ed4cd26d442d0ca40ae6d5448111e02a08dd638a4f1064b47d080e2875dc05 @@ -1062,7 +1062,7 @@ __metadata: languageName: node linkType: hard -"@babel/types@npm:^7.25.4, @babel/types@npm:^7.27.1": +"@babel/types@npm:^7.25.4": version: 7.27.1 resolution: "@babel/types@npm:7.27.1" dependencies: @@ -11579,13 +11579,6 @@ __metadata: languageName: node linkType: hard -"fn.name@npm:1.x.x": - version: 1.1.0 - resolution: "fn.name@npm:1.1.0" - checksum: 10c0/8ad62aa2d4f0b2a76d09dba36cfec61c540c13a0fd72e5d94164e430f987a7ce6a743112bbeb14877c810ef500d1f73d7f56e76d029d2e3413f20d79e3460a9a - languageName: node - linkType: hard - "flux@npm:^4.0.1": version: 4.0.4 resolution: "flux@npm:4.0.4" @@ -11598,6 +11591,13 @@ __metadata: languageName: node linkType: hard +"fn.name@npm:1.x.x": + version: 1.1.0 + resolution: "fn.name@npm:1.1.0" + checksum: 10c0/8ad62aa2d4f0b2a76d09dba36cfec61c540c13a0fd72e5d94164e430f987a7ce6a743112bbeb14877c810ef500d1f73d7f56e76d029d2e3413f20d79e3460a9a + languageName: node + linkType: hard + "follow-redirects@npm:^1.15.6": version: 1.15.9 resolution: "follow-redirects@npm:1.15.9"