refactor: preload模块化拆分

This commit is contained in:
digua
2026-02-01 12:50:05 +08:00
parent 889ec4341a
commit 55878256c3
5 changed files with 1646 additions and 1646 deletions
+739
View File
@@ -0,0 +1,739 @@
/**
* AI 相关 API - AI 对话、LLM 服务、Agent、Embedding
*/
import { ipcRenderer } from 'electron'
// ==================== 类型定义 ====================
// AI API 类型
export interface SearchMessageResult {
id: number
senderName: string
senderPlatformId: string
senderAliases: string[]
senderAvatar: string | null
content: string
timestamp: number
type: number
}
export interface AIConversation {
id: string
sessionId: string
title: string | null
createdAt: number
updatedAt: number
}
// 内容块类型(用于 AI 消息的混合渲染)
export type ContentBlock =
| { type: 'text'; text: string }
| {
type: 'tool'
tool: {
name: string
displayName: string
status: 'running' | 'done' | 'error'
params?: Record<string, unknown>
}
}
export interface AIMessage {
id: string
conversationId: string
role: 'user' | 'assistant'
content: string
timestamp: number
dataKeywords?: string[]
dataMessageCount?: number
contentBlocks?: ContentBlock[]
}
// LLM API 类型
export interface LLMProvider {
id: string
name: string
description: string
defaultBaseUrl: string
models: Array<{ id: string; name: string; description?: string }>
}
export interface ChatMessage {
role: 'system' | 'user' | 'assistant'
content: string
}
export interface ChatOptions {
temperature?: number
maxTokens?: number
}
export interface ChatStreamChunk {
content: string
isFinished: boolean
finishReason?: 'stop' | 'length' | 'error'
}
// Agent API 类型
export interface AgentStreamChunk {
type: 'content' | 'think' | 'tool_start' | 'tool_result' | 'done' | 'error'
content?: string
thinkTag?: string
thinkDurationMs?: number
toolName?: string
toolParams?: Record<string, unknown>
toolResult?: unknown
error?: string
isFinished?: boolean
}
export interface AgentResult {
content: string
toolsUsed: string[]
toolRounds: number
}
export interface ToolContext {
sessionId: string
timeFilter?: { startTs: number; endTs: number }
}
// AI 服务配置类型(前端用)
export interface AIServiceConfigDisplay {
id: string
name: string
provider: string
apiKey: string // 脱敏后的 API Key
apiKeySet: boolean
model?: string
baseUrl?: string
maxTokens?: number
disableThinking?: boolean
isReasoningModel?: boolean
createdAt: number
updatedAt: number
}
// Embedding 服务配置(多配置模式)
export interface EmbeddingServiceConfig {
id: string
name: string
apiSource: 'reuse_llm' | 'custom'
model: string
baseUrl?: string
apiKey?: string
createdAt: number
updatedAt: number
}
// Embedding 配置展示用(隐藏 apiKey
export interface EmbeddingServiceConfigDisplay {
id: string
name: string
apiSource: 'reuse_llm' | 'custom'
model: string
baseUrl?: string
apiKeySet: boolean
createdAt: number
updatedAt: number
}
// 用户自定义提示词配置
export interface PromptConfig {
roleDefinition: string
responseRules: string
}
// ==================== AI API ====================
export const aiApi = {
/**
* 搜索消息(关键词搜索)
* @param senderId 可选的发送者成员 ID,用于筛选特定成员的消息
*/
searchMessages: (
sessionId: string,
keywords: string[],
filter?: { startTs?: number; endTs?: number },
limit?: number,
offset?: number,
senderId?: number
): Promise<{ messages: SearchMessageResult[]; total: number }> => {
return ipcRenderer.invoke('ai:searchMessages', sessionId, keywords, filter, limit, offset, senderId)
},
/**
* 获取消息上下文
* @param messageIds 支持单个或批量消息 ID
*/
getMessageContext: (
sessionId: string,
messageIds: number | number[],
contextSize?: number
): Promise<SearchMessageResult[]> => {
return ipcRenderer.invoke('ai:getMessageContext', sessionId, messageIds, contextSize)
},
/**
* 获取最近消息(AI Agent 专用)
*/
getRecentMessages: (
sessionId: string,
filter?: { startTs?: number; endTs?: number },
limit?: number
): Promise<{ messages: SearchMessageResult[]; total: number }> => {
return ipcRenderer.invoke('ai:getRecentMessages', sessionId, filter, limit)
},
/**
* 获取所有最近消息(消息查看器专用)
*/
getAllRecentMessages: (
sessionId: string,
filter?: { startTs?: number; endTs?: number },
limit?: number
): Promise<{ messages: SearchMessageResult[]; total: number }> => {
return ipcRenderer.invoke('ai:getAllRecentMessages', sessionId, filter, limit)
},
/**
* 获取两人之间的对话
*/
getConversationBetween: (
sessionId: string,
memberId1: number,
memberId2: number,
filter?: { startTs?: number; endTs?: number },
limit?: number
): Promise<{ messages: SearchMessageResult[]; total: number; member1Name: string; member2Name: string }> => {
return ipcRenderer.invoke('ai:getConversationBetween', sessionId, memberId1, memberId2, filter, limit)
},
/**
* 获取指定消息之前的 N 条(用于向上无限滚动)
*/
getMessagesBefore: (
sessionId: string,
beforeId: number,
limit?: number,
filter?: { startTs?: number; endTs?: number },
senderId?: number,
keywords?: string[]
): Promise<{ messages: SearchMessageResult[]; hasMore: boolean }> => {
return ipcRenderer.invoke('ai:getMessagesBefore', sessionId, beforeId, limit, filter, senderId, keywords)
},
/**
* 获取指定消息之后的 N 条(用于向下无限滚动)
*/
getMessagesAfter: (
sessionId: string,
afterId: number,
limit?: number,
filter?: { startTs?: number; endTs?: number },
senderId?: number,
keywords?: string[]
): Promise<{ messages: SearchMessageResult[]; hasMore: boolean }> => {
return ipcRenderer.invoke('ai:getMessagesAfter', sessionId, afterId, limit, filter, senderId, keywords)
},
// ==================== 自定义筛选 ====================
/**
* 按条件筛选消息并扩充上下文
*/
filterMessagesWithContext: (
sessionId: string,
keywords?: string[],
timeFilter?: { startTs: number; endTs: number },
senderIds?: number[],
contextSize?: number
): Promise<{
blocks: Array<{
startTs: number
endTs: number
messages: Array<{
id: number
senderName: string
senderPlatformId: string
senderAliases: string[]
senderAvatar: string | null
content: string
timestamp: number
type: number
replyToMessageId: string | null
replyToContent: string | null
replyToSenderName: string | null
isHit: boolean
}>
hitCount: number
}>
stats: {
totalMessages: number
hitMessages: number
totalChars: number
}
}> => {
return ipcRenderer.invoke('ai:filterMessagesWithContext', sessionId, keywords, timeFilter, senderIds, contextSize)
},
/**
* 获取多个会话的完整消息
*/
getMultipleSessionsMessages: (
sessionId: string,
chatSessionIds: number[]
): Promise<{
blocks: Array<{
startTs: number
endTs: number
messages: Array<{
id: number
senderName: string
senderPlatformId: string
senderAliases: string[]
senderAvatar: string | null
content: string
timestamp: number
type: number
replyToMessageId: string | null
replyToContent: string | null
replyToSenderName: string | null
isHit: boolean
}>
hitCount: number
}>
stats: {
totalMessages: number
hitMessages: number
totalChars: number
}
}> => {
return ipcRenderer.invoke('ai:getMultipleSessionsMessages', sessionId, chatSessionIds)
},
/**
* 创建 AI 对话
*/
createConversation: (sessionId: string, title?: string): Promise<AIConversation> => {
return ipcRenderer.invoke('ai:createConversation', sessionId, title)
},
/**
* 获取会话的所有 AI 对话列表
*/
getConversations: (sessionId: string): Promise<AIConversation[]> => {
return ipcRenderer.invoke('ai:getConversations', sessionId)
},
/**
* 获取单个 AI 对话
*/
getConversation: (conversationId: string): Promise<AIConversation | null> => {
return ipcRenderer.invoke('ai:getConversation', conversationId)
},
/**
* 更新 AI 对话标题
*/
updateConversationTitle: (conversationId: string, title: string): Promise<boolean> => {
return ipcRenderer.invoke('ai:updateConversationTitle', conversationId, title)
},
/**
* 删除 AI 对话
*/
deleteConversation: (conversationId: string): Promise<boolean> => {
return ipcRenderer.invoke('ai:deleteConversation', conversationId)
},
/**
* 添加 AI 消息
*/
addMessage: (
conversationId: string,
role: 'user' | 'assistant',
content: string,
dataKeywords?: string[],
dataMessageCount?: number,
contentBlocks?: ContentBlock[]
): Promise<AIMessage> => {
return ipcRenderer.invoke(
'ai:addMessage',
conversationId,
role,
content,
dataKeywords,
dataMessageCount,
contentBlocks
)
},
/**
* 获取 AI 对话的所有消息
*/
getMessages: (conversationId: string): Promise<AIMessage[]> => {
return ipcRenderer.invoke('ai:getMessages', conversationId)
},
/**
* 删除 AI 消息
*/
deleteMessage: (messageId: string): Promise<boolean> => {
return ipcRenderer.invoke('ai:deleteMessage', messageId)
},
/**
* 打开当前 AI 日志文件并定位到文件
*/
showAiLogFile: (): Promise<{ success: boolean; path?: string; error?: string }> => {
return ipcRenderer.invoke('ai:showLogFile')
},
}
// ==================== LLM API ====================
export const llmApi = {
/**
* 获取所有支持的 LLM 提供商
*/
getProviders: (): Promise<LLMProvider[]> => {
return ipcRenderer.invoke('llm:getProviders')
},
// ==================== 多配置管理 API ====================
/**
* 获取所有配置列表
*/
getAllConfigs: (): Promise<AIServiceConfigDisplay[]> => {
return ipcRenderer.invoke('llm:getAllConfigs')
},
/**
* 获取当前激活的配置 ID
*/
getActiveConfigId: (): Promise<string | null> => {
return ipcRenderer.invoke('llm:getActiveConfigId')
},
/**
* 添加新配置
*/
addConfig: (config: {
name: string
provider: string
apiKey: string
model?: string
baseUrl?: string
maxTokens?: number
disableThinking?: boolean
isReasoningModel?: boolean
}): Promise<{ success: boolean; config?: AIServiceConfigDisplay; error?: string }> => {
return ipcRenderer.invoke('llm:addConfig', config)
},
/**
* 更新配置
*/
updateConfig: (
id: string,
updates: {
name?: string
provider?: string
apiKey?: string
model?: string
baseUrl?: string
maxTokens?: number
disableThinking?: boolean
isReasoningModel?: boolean
}
): Promise<{ success: boolean; error?: string }> => {
return ipcRenderer.invoke('llm:updateConfig', id, updates)
},
/**
* 删除配置
*/
deleteConfig: (id?: string): Promise<{ success: boolean; error?: string }> => {
return ipcRenderer.invoke('llm:deleteConfig', id)
},
/**
* 设置激活的配置
*/
setActiveConfig: (id: string): Promise<{ success: boolean; error?: string }> => {
return ipcRenderer.invoke('llm:setActiveConfig', id)
},
/**
* 验证 API Key(支持自定义 baseUrl 和 model
*/
validateApiKey: (provider: string, apiKey: string, baseUrl?: string, model?: string): Promise<boolean> => {
return ipcRenderer.invoke('llm:validateApiKey', provider, apiKey, baseUrl, model)
},
/**
* 检查是否已配置 LLM(是否有激活的配置)
*/
hasConfig: (): Promise<boolean> => {
return ipcRenderer.invoke('llm:hasConfig')
},
/**
* 发送 LLM 聊天请求(非流式)
*/
chat: (
messages: ChatMessage[],
options?: ChatOptions
): Promise<{ success: boolean; content?: string; error?: string }> => {
return ipcRenderer.invoke('llm:chat', messages, options)
},
/**
* 发送 LLM 聊天请求(流式)
* 返回一个 Promise,该 Promise 在流完成后才 resolve
*/
chatStream: (
messages: ChatMessage[],
options?: ChatOptions,
onChunk?: (chunk: ChatStreamChunk) => void
): Promise<{ success: boolean; error?: string }> => {
return new Promise((resolve) => {
const requestId = `llm_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`
console.log('[preload] chatStream 开始,requestId:', requestId)
const handler = (
_event: Electron.IpcRendererEvent,
data: { requestId: string; chunk: ChatStreamChunk; error?: string }
) => {
if (data.requestId === requestId) {
if (data.error) {
console.log('[preload] chatStream 收到错误:', data.error)
if (onChunk) {
onChunk({ content: '', isFinished: true, finishReason: 'error' })
}
ipcRenderer.removeListener('llm:streamChunk', handler)
resolve({ success: false, error: data.error })
} else {
if (onChunk) {
onChunk(data.chunk)
}
// 如果已完成,移除监听器并 resolve
if (data.chunk.isFinished) {
console.log('[preload] chatStream 完成,requestId:', requestId)
ipcRenderer.removeListener('llm:streamChunk', handler)
resolve({ success: true })
}
}
}
}
ipcRenderer.on('llm:streamChunk', handler)
// 发起请求
ipcRenderer
.invoke('llm:chatStream', requestId, messages, options)
.then((result) => {
console.log('[preload] chatStream invoke 返回:', result)
if (!result.success) {
ipcRenderer.removeListener('llm:streamChunk', handler)
resolve(result)
}
// 如果 success,等待流完成(由 handler 处理 resolve
})
.catch((error) => {
console.error('[preload] chatStream invoke 错误:', error)
ipcRenderer.removeListener('llm:streamChunk', handler)
resolve({ success: false, error: String(error) })
})
})
},
}
// ==================== Agent API ====================
export const agentApi = {
/**
* 执行 Agent 对话(流式)
* Agent 会自动调用工具获取数据并生成回答
* @param historyMessages 对话历史(可选,用于上下文关联)
* @param chatType 聊天类型('group' | 'private'
* @param promptConfig 用户自定义提示词配置(可选)
* @param locale 语言设置(可选,默认 'zh-CN'
* @returns 返回 { requestId, promise }requestId 可用于中止请求
*/
runStream: (
userMessage: string,
context: ToolContext,
onChunk?: (chunk: AgentStreamChunk) => void,
historyMessages?: Array<{ role: 'user' | 'assistant'; content: string }>,
chatType?: 'group' | 'private',
promptConfig?: PromptConfig,
locale?: string
): { requestId: string; promise: Promise<{ success: boolean; result?: AgentResult; error?: string }> } => {
const requestId = `agent_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`
console.log(
'[preload] Agent runStream 开始,requestId:',
requestId,
'historyLength:',
historyMessages?.length ?? 0,
'chatType:',
chatType ?? 'group',
'hasPromptConfig:',
!!promptConfig
)
const promise = new Promise<{ success: boolean; result?: AgentResult; error?: string }>((resolve) => {
// 监听流式 chunks
const chunkHandler = (
_event: Electron.IpcRendererEvent,
data: { requestId: string; chunk: AgentStreamChunk }
) => {
if (data.requestId === requestId) {
if (onChunk) {
onChunk(data.chunk)
}
}
}
// 监听完成事件
const completeHandler = (
_event: Electron.IpcRendererEvent,
data: { requestId: string; result: AgentResult & { error?: string } }
) => {
if (data.requestId === requestId) {
console.log('[preload] Agent 完成,requestId:', requestId, 'hasError:', !!data.result?.error)
ipcRenderer.removeListener('agent:streamChunk', chunkHandler)
ipcRenderer.removeListener('agent:complete', completeHandler)
// 如果 result 中包含 error,返回失败状态
if (data.result?.error) {
resolve({ success: false, error: data.result.error })
} else {
resolve({ success: true, result: data.result })
}
}
}
ipcRenderer.on('agent:streamChunk', chunkHandler)
ipcRenderer.on('agent:complete', completeHandler)
// 发起请求(传递历史消息、聊天类型、提示词配置和语言设置)
ipcRenderer
.invoke('agent:runStream', requestId, userMessage, context, historyMessages, chatType, promptConfig, locale)
.then((result) => {
console.log('[preload] Agent invoke 返回:', result)
if (!result.success) {
ipcRenderer.removeListener('agent:streamChunk', chunkHandler)
ipcRenderer.removeListener('agent:complete', completeHandler)
resolve(result)
}
// 如果 success,等待完成(由 completeHandler 处理 resolve
})
.catch((error) => {
console.error('[preload] Agent invoke 错误:', error)
ipcRenderer.removeListener('agent:streamChunk', chunkHandler)
ipcRenderer.removeListener('agent:complete', completeHandler)
resolve({ success: false, error: String(error) })
})
})
return { requestId, promise }
},
/**
* 中止 Agent 请求
* @param requestId 请求 ID
*/
abort: (requestId: string): Promise<{ success: boolean; error?: string }> => {
console.log('[preload] Agent abort 请求,requestId:', requestId)
return ipcRenderer.invoke('agent:abort', requestId)
},
}
// ==================== Embedding API ====================
export const embeddingApi = {
/**
* 获取所有 Embedding 配置(展示用)
*/
getAllConfigs: (): Promise<EmbeddingServiceConfigDisplay[]> => {
return ipcRenderer.invoke('embedding:getAllConfigs')
},
/**
* 获取单个 Embedding 配置(用于编辑)
*/
getConfig: (id: string): Promise<EmbeddingServiceConfig | null> => {
return ipcRenderer.invoke('embedding:getConfig', id)
},
/**
* 获取激活的配置 ID
*/
getActiveConfigId: (): Promise<string | null> => {
return ipcRenderer.invoke('embedding:getActiveConfigId')
},
/**
* 检查语义搜索是否启用
*/
isEnabled: (): Promise<boolean> => {
return ipcRenderer.invoke('embedding:isEnabled')
},
/**
* 添加 Embedding 配置
*/
addConfig: (
config: Omit<EmbeddingServiceConfig, 'id' | 'createdAt' | 'updatedAt'>
): Promise<{ success: boolean; config?: EmbeddingServiceConfig; error?: string }> => {
return ipcRenderer.invoke('embedding:addConfig', config)
},
/**
* 更新 Embedding 配置
*/
updateConfig: (
id: string,
updates: Partial<Omit<EmbeddingServiceConfig, 'id' | 'createdAt' | 'updatedAt'>>
): Promise<{ success: boolean; error?: string }> => {
return ipcRenderer.invoke('embedding:updateConfig', id, updates)
},
/**
* 删除 Embedding 配置
*/
deleteConfig: (id: string): Promise<{ success: boolean; error?: string }> => {
return ipcRenderer.invoke('embedding:deleteConfig', id)
},
/**
* 设置激活的配置
*/
setActiveConfig: (id: string): Promise<{ success: boolean; error?: string }> => {
return ipcRenderer.invoke('embedding:setActiveConfig', id)
},
/**
* 验证 Embedding 配置
*/
validateConfig: (config: EmbeddingServiceConfig): Promise<{ success: boolean; error?: string }> => {
return ipcRenderer.invoke('embedding:validateConfig', config)
},
/**
* 获取向量存储统计信息
*/
getVectorStoreStats: (): Promise<{ enabled: boolean; count?: number; sizeBytes?: number }> => {
return ipcRenderer.invoke('rag:getVectorStoreStats')
},
/**
* 清空向量存储
*/
clearVectorStore: (): Promise<{ success: boolean; error?: string }> => {
return ipcRenderer.invoke('rag:clearVectorStore')
},
}
+492
View File
@@ -0,0 +1,492 @@
/**
* 聊天记录 API - 导入、分析、管理聊天记录
*/
import { ipcRenderer } from 'electron'
import type { AnalysisSession, MessageType, ImportProgress } from '../../../src/types/base'
import type {
MemberActivity,
MemberNameHistory,
HourlyActivity,
DailyActivity,
WeekdayActivity,
MonthlyActivity,
RepeatAnalysis,
CatchphraseAnalysis,
NightOwlAnalysis,
DragonKingAnalysis,
DivingAnalysis,
MonologueAnalysis,
MentionAnalysis,
LaughAnalysis,
CheckInAnalysis,
MemeBattleAnalysis,
MemberWithStats,
} from '../../../src/types/analysis'
import type { FileParseInfo, ConflictCheckResult, MergeParams, MergeResult } from '../../../src/types/format'
// Chat Analysis API
export const chatApi = {
// ==================== 数据库迁移 ====================
/**
* 检查是否需要数据库迁移
*/
checkMigration: (): Promise<{
needsMigration: boolean
count: number
currentVersion: number
pendingMigrations: Array<{ version: number; userMessage: string }>
}> => {
return ipcRenderer.invoke('chat:checkMigration')
},
/**
* 执行数据库迁移
*/
runMigration: (): Promise<{ success: boolean; migratedCount: number; error?: string }> => {
return ipcRenderer.invoke('chat:runMigration')
},
// ==================== 聊天记录导入与分析 ====================
/**
* 选择聊天记录文件
*/
selectFile: (): Promise<{ filePath?: string; format?: string; error?: string } | null> => {
return ipcRenderer.invoke('chat:selectFile')
},
/**
* 导入聊天记录
*/
import: (filePath: string): Promise<{ success: boolean; sessionId?: string; error?: string }> => {
return ipcRenderer.invoke('chat:import', filePath)
},
/**
* 获取所有分析会话列表
*/
getSessions: (): Promise<AnalysisSession[]> => {
return ipcRenderer.invoke('chat:getSessions')
},
/**
* 获取单个会话信息
*/
getSession: (sessionId: string): Promise<AnalysisSession | null> => {
return ipcRenderer.invoke('chat:getSession', sessionId)
},
/**
* 删除会话
*/
deleteSession: (sessionId: string): Promise<boolean> => {
return ipcRenderer.invoke('chat:deleteSession', sessionId)
},
/**
* 重命名会话
*/
renameSession: (sessionId: string, newName: string): Promise<boolean> => {
return ipcRenderer.invoke('chat:renameSession', sessionId, newName)
},
/**
* 获取可用年份列表
*/
getAvailableYears: (sessionId: string): Promise<number[]> => {
return ipcRenderer.invoke('chat:getAvailableYears', sessionId)
},
/**
* 获取成员活跃度排行
*/
getMemberActivity: (sessionId: string, filter?: { startTs?: number; endTs?: number }): Promise<MemberActivity[]> => {
return ipcRenderer.invoke('chat:getMemberActivity', sessionId, filter)
},
/**
* 获取成员历史昵称
*/
getMemberNameHistory: (sessionId: string, memberId: number): Promise<MemberNameHistory[]> => {
return ipcRenderer.invoke('chat:getMemberNameHistory', sessionId, memberId)
},
/**
* 获取每小时活跃度分布
*/
getHourlyActivity: (sessionId: string, filter?: { startTs?: number; endTs?: number }): Promise<HourlyActivity[]> => {
return ipcRenderer.invoke('chat:getHourlyActivity', sessionId, filter)
},
/**
* 获取每日活跃度趋势
*/
getDailyActivity: (sessionId: string, filter?: { startTs?: number; endTs?: number }): Promise<DailyActivity[]> => {
return ipcRenderer.invoke('chat:getDailyActivity', sessionId, filter)
},
/**
* 获取星期活跃度分布
*/
getWeekdayActivity: (
sessionId: string,
filter?: { startTs?: number; endTs?: number }
): Promise<WeekdayActivity[]> => {
return ipcRenderer.invoke('chat:getWeekdayActivity', sessionId, filter)
},
/**
* 获取月份活跃度分布
*/
getMonthlyActivity: (
sessionId: string,
filter?: { startTs?: number; endTs?: number }
): Promise<MonthlyActivity[]> => {
return ipcRenderer.invoke('chat:getMonthlyActivity', sessionId, filter)
},
/**
* 获取年份活跃度分布
*/
getYearlyActivity: (
sessionId: string,
filter?: { startTs?: number; endTs?: number }
): Promise<Array<{ year: number; messageCount: number }>> => {
return ipcRenderer.invoke('chat:getYearlyActivity', sessionId, filter)
},
/**
* 获取消息长度分布
*/
getMessageLengthDistribution: (
sessionId: string,
filter?: { startTs?: number; endTs?: number }
): Promise<{
detail: Array<{ len: number; count: number }>
grouped: Array<{ range: string; count: number }>
}> => {
return ipcRenderer.invoke('chat:getMessageLengthDistribution', sessionId, filter)
},
/**
* 获取消息类型分布
*/
getMessageTypeDistribution: (
sessionId: string,
filter?: { startTs?: number; endTs?: number }
): Promise<Array<{ type: MessageType; count: number }>> => {
return ipcRenderer.invoke('chat:getMessageTypeDistribution', sessionId, filter)
},
/**
* 获取时间范围
*/
getTimeRange: (sessionId: string): Promise<{ start: number; end: number } | null> => {
return ipcRenderer.invoke('chat:getTimeRange', sessionId)
},
/**
* 获取数据库存储目录
*/
getDbDirectory: (): Promise<string | null> => {
return ipcRenderer.invoke('chat:getDbDirectory')
},
/**
* 获取支持的格式列表
*/
getSupportedFormats: (): Promise<Array<{ name: string; platform: string }>> => {
return ipcRenderer.invoke('chat:getSupportedFormats')
},
/**
* 监听导入进度
*/
onImportProgress: (callback: (progress: ImportProgress) => void) => {
const handler = (_event: Electron.IpcRendererEvent, progress: ImportProgress) => {
callback(progress)
}
ipcRenderer.on('chat:importProgress', handler)
return () => {
ipcRenderer.removeListener('chat:importProgress', handler)
}
},
/**
* 获取复读分析数据
*/
getRepeatAnalysis: (sessionId: string, filter?: { startTs?: number; endTs?: number }): Promise<RepeatAnalysis> => {
return ipcRenderer.invoke('chat:getRepeatAnalysis', sessionId, filter)
},
/**
* 获取口头禅分析数据
*/
getCatchphraseAnalysis: (
sessionId: string,
filter?: { startTs?: number; endTs?: number }
): Promise<CatchphraseAnalysis> => {
return ipcRenderer.invoke('chat:getCatchphraseAnalysis', sessionId, filter)
},
/**
* 获取夜猫分析数据
*/
getNightOwlAnalysis: (
sessionId: string,
filter?: { startTs?: number; endTs?: number }
): Promise<NightOwlAnalysis> => {
return ipcRenderer.invoke('chat:getNightOwlAnalysis', sessionId, filter)
},
/**
* 获取龙王分析数据
*/
getDragonKingAnalysis: (
sessionId: string,
filter?: { startTs?: number; endTs?: number }
): Promise<DragonKingAnalysis> => {
return ipcRenderer.invoke('chat:getDragonKingAnalysis', sessionId, filter)
},
/**
* 获取潜水分析数据
*/
getDivingAnalysis: (sessionId: string, filter?: { startTs?: number; endTs?: number }): Promise<DivingAnalysis> => {
return ipcRenderer.invoke('chat:getDivingAnalysis', sessionId, filter)
},
/**
* 获取自言自语分析数据
*/
getMonologueAnalysis: (
sessionId: string,
filter?: { startTs?: number; endTs?: number }
): Promise<MonologueAnalysis> => {
return ipcRenderer.invoke('chat:getMonologueAnalysis', sessionId, filter)
},
/**
* 获取 @ 互动分析数据
*/
getMentionAnalysis: (sessionId: string, filter?: { startTs?: number; endTs?: number }): Promise<MentionAnalysis> => {
return ipcRenderer.invoke('chat:getMentionAnalysis', sessionId, filter)
},
/**
* 获取 @ 互动关系图数据
*/
getMentionGraph: (
sessionId: string,
filter?: { startTs?: number; endTs?: number }
): Promise<{
nodes: Array<{ id: number; name: string; value: number; symbolSize: number }>
links: Array<{ source: string; target: string; value: number }>
maxLinkValue: number
}> => {
return ipcRenderer.invoke('chat:getMentionGraph', sessionId, filter)
},
/**
* 获取含笑量分析数据
*/
getLaughAnalysis: (
sessionId: string,
filter?: { startTs?: number; endTs?: number },
keywords?: string[]
): Promise<LaughAnalysis> => {
return ipcRenderer.invoke('chat:getLaughAnalysis', sessionId, filter, keywords)
},
/**
* 获取斗图分析数据
*/
getMemeBattleAnalysis: (
sessionId: string,
filter?: { startTs?: number; endTs?: number }
): Promise<MemeBattleAnalysis> => {
return ipcRenderer.invoke('chat:getMemeBattleAnalysis', sessionId, filter)
},
/**
* 获取打卡分析数据(火花榜 + 忠臣榜)
*/
getCheckInAnalysis: (sessionId: string, filter?: { startTs?: number; endTs?: number }): Promise<CheckInAnalysis> => {
return ipcRenderer.invoke('chat:getCheckInAnalysis', sessionId, filter)
},
// ==================== 成员管理 ====================
/**
* 获取所有成员列表(含消息数和别名)
*/
getMembers: (sessionId: string): Promise<MemberWithStats[]> => {
return ipcRenderer.invoke('chat:getMembers', sessionId)
},
/**
* 获取成员列表(分页版本)
*/
getMembersPaginated: (
sessionId: string,
params: { page: number; pageSize: number; search?: string; sortOrder?: 'asc' | 'desc' }
): Promise<{
members: MemberWithStats[]
total: number
page: number
pageSize: number
totalPages: number
}> => {
return ipcRenderer.invoke('chat:getMembersPaginated', sessionId, params)
},
/**
* 更新成员别名
*/
updateMemberAliases: (sessionId: string, memberId: number, aliases: string[]): Promise<boolean> => {
return ipcRenderer.invoke('chat:updateMemberAliases', sessionId, memberId, aliases)
},
/**
* 删除成员及其所有消息
*/
deleteMember: (sessionId: string, memberId: number): Promise<boolean> => {
return ipcRenderer.invoke('chat:deleteMember', sessionId, memberId)
},
/**
* 更新会话的所有者(ownerId)
* @param ownerId 成员的 platformId,设置为 null 则清除
*/
updateSessionOwnerId: (sessionId: string, ownerId: string | null): Promise<boolean> => {
return ipcRenderer.invoke('chat:updateSessionOwnerId', sessionId, ownerId)
},
// ==================== SQL 实验室 ====================
/**
* 执行用户 SQL 查询
*/
executeSQL: (
sessionId: string,
sql: string
): Promise<{
columns: string[]
rows: any[][]
rowCount: number
duration: number
limited: boolean
}> => {
return ipcRenderer.invoke('chat:executeSQL', sessionId, sql)
},
/**
* 获取数据库 Schema
*/
getSchema: (
sessionId: string
): Promise<
Array<{
name: string
columns: Array<{
name: string
type: string
notnull: boolean
pk: boolean
}>
}>
> => {
return ipcRenderer.invoke('chat:getSchema', sessionId)
},
// ==================== 增量导入 ====================
/**
* 分析增量导入(检测去重后能新增多少消息)
*/
analyzeIncrementalImport: (
sessionId: string,
filePath: string
): Promise<{
newMessageCount: number
duplicateCount: number
totalInFile: number
error?: string
diagnosis?: { suggestion?: string }
}> => {
return ipcRenderer.invoke('chat:analyzeIncrementalImport', sessionId, filePath)
},
/**
* 执行增量导入
*/
incrementalImport: (
sessionId: string,
filePath: string
): Promise<{
success: boolean
newMessageCount: number
error?: string
}> => {
return ipcRenderer.invoke('chat:incrementalImport', sessionId, filePath)
},
/**
* 导出多个会话为临时文件(用于批量管理中的合并)
*/
exportSessionsToTempFiles: (
sessionIds: string[]
): Promise<{
success: boolean
tempFiles: string[]
error?: string
}> => {
return ipcRenderer.invoke('chat:exportSessionsToTempFiles', sessionIds)
},
/**
* 清理临时导出文件
*/
cleanupTempExportFiles: (
filePaths: string[]
): Promise<{
success: boolean
error?: string
}> => {
return ipcRenderer.invoke('chat:cleanupTempExportFiles', filePaths)
},
}
// Merge API - 合并功能
export const mergeApi = {
/**
* 解析文件获取基本信息(用于合并预览)
* 解析后结果会被缓存,后续合并时无需再次读取原始文件
*/
parseFileInfo: (filePath: string): Promise<FileParseInfo> => {
return ipcRenderer.invoke('merge:parseFileInfo', filePath)
},
/**
* 检测合并冲突
*/
checkConflicts: (filePaths: string[]): Promise<ConflictCheckResult> => {
return ipcRenderer.invoke('merge:checkConflicts', filePaths)
},
/**
* 执行合并
*/
mergeFiles: (params: MergeParams): Promise<MergeResult> => {
return ipcRenderer.invoke('merge:mergeFiles', params)
},
/**
* 清理解析缓存
* @param filePath 可选,指定文件路径则清理该文件的缓存,否则清理所有缓存
*/
clearCache: (filePath?: string): Promise<boolean> => {
return ipcRenderer.invoke('merge:clearCache', filePath)
},
}
+100
View File
@@ -0,0 +1,100 @@
/**
* 核心 API - 基础 IPC 通信和系统功能
*/
import { ipcRenderer } from 'electron'
// Custom APIs for renderer
export const api = {
send: (channel: string, data?: unknown) => {
// whitelist channels
const validChannels = [
'show-message',
'check-update',
'simulate-update',
'get-gpu-acceleration',
'set-gpu-acceleration',
'save-gpu-acceleration',
'window-close', // 用户协议拒绝时退出应用
]
if (validChannels.includes(channel)) {
ipcRenderer.send(channel, data)
}
},
receive: (channel: string, func: (...args: unknown[]) => void) => {
const validChannels = [
'show-message',
'chat:importProgress',
'merge:parseProgress',
'llm:streamChunk',
'agent:streamChunk',
'agent:complete',
]
if (validChannels.includes(channel)) {
// Deliberately strip event as it includes `sender`
ipcRenderer.on(channel, (_event, ...args) => func(...args))
}
},
removeListener: (channel: string, func: (...args: unknown[]) => void) => {
ipcRenderer.removeListener(channel, func)
},
setThemeSource: (mode: 'system' | 'light' | 'dark') => {
ipcRenderer.send('window:setThemeSource', mode)
},
}
// 扩展 api,添加 dialog、clipboard 和应用功能
export const extendedApi = {
...api,
dialog: {
showOpenDialog: (options: Electron.OpenDialogOptions): Promise<Electron.OpenDialogReturnValue> => {
return ipcRenderer.invoke('dialog:showOpenDialog', options)
},
},
clipboard: {
/**
* 复制图片到系统剪贴板
* @param dataUrl 图片的 base64 data URL
*/
copyImage: (dataUrl: string): Promise<{ success: boolean; error?: string }> => {
return ipcRenderer.invoke('copyImage', dataUrl)
},
},
app: {
/**
* 获取应用版本号
*/
getVersion: (): Promise<string> => {
return ipcRenderer.invoke('app:getVersion')
},
/**
* 检查更新
*/
checkUpdate: (): void => {
ipcRenderer.send('check-update')
},
/**
* 模拟更新弹窗(仅开发模式)
*/
simulateUpdate: (): void => {
ipcRenderer.send('simulate-update')
},
/**
* 获取远程配置(通过主进程请求,绕过 CORS)
*/
fetchRemoteConfig: (url: string): Promise<{ success: boolean; data?: unknown; error?: string }> => {
return ipcRenderer.invoke('app:fetchRemoteConfig', url)
},
/**
* 获取匿名统计开关状态
*/
getAnalyticsEnabled: (): Promise<boolean> => {
return ipcRenderer.invoke('analytics:getEnabled')
},
/**
* 设置匿名统计开关状态
*/
setAnalyticsEnabled: (enabled: boolean): Promise<{ success: boolean }> => {
return ipcRenderer.invoke('analytics:setEnabled', enabled)
},
},
}
+305
View File
@@ -0,0 +1,305 @@
/**
* 工具类 API - NLP、网络、缓存、会话索引
*/
import { ipcRenderer } from 'electron'
// ==================== 类型定义 ====================
// NLP API 类型
export interface WordFrequencyItem {
word: string
count: number
percentage: number
}
export interface WordFrequencyResult {
words: WordFrequencyItem[]
totalWords: number
totalMessages: number
uniqueWords: number
}
export type PosFilterMode = 'all' | 'meaningful' | 'custom'
export interface WordFrequencyParams {
sessionId: string
locale: 'zh-CN' | 'en-US'
timeFilter?: { startTs?: number; endTs?: number }
memberId?: number
topN?: number
minWordLength?: number
minCount?: number
/** 词性过滤模式:all=全部, meaningful=只保留有意义的词, custom=自定义 */
posFilterMode?: PosFilterMode
/** 自定义词性过滤列表(posFilterMode='custom' 时使用) */
customPosTags?: string[]
/** 是否启用停用词过滤,默认 true */
enableStopwords?: boolean
}
export interface PosTagInfo {
tag: string
name: string
description: string
meaningful: boolean
}
// Network API 类型
export type ProxyMode = 'off' | 'system' | 'manual'
export interface ProxyConfig {
mode: ProxyMode // 代理模式:关闭、跟随系统、手动配置
url: string // 仅 manual 模式使用
}
// Cache API 类型
export interface CacheDirectoryInfo {
id: string
name: string
description: string
path: string
icon: string
canClear: boolean
size: number
fileCount: number
exists: boolean
}
export interface CacheInfo {
baseDir: string
directories: CacheDirectoryInfo[]
totalSize: number
}
// Session API 类型
export interface SessionStats {
sessionCount: number
hasIndex: boolean
gapThreshold: number
}
export interface ChatSessionItem {
id: number
startTs: number
endTs: number
messageCount: number
firstMessageId: number
}
// ==================== NLP API ====================
export const nlpApi = {
/**
* 获取词频统计(用于词云)
*/
getWordFrequency: (params: WordFrequencyParams): Promise<WordFrequencyResult> => {
return ipcRenderer.invoke('nlp:getWordFrequency', params)
},
/**
* 单文本分词
*/
segmentText: (text: string, locale: 'zh-CN' | 'en-US', minLength?: number): Promise<string[]> => {
return ipcRenderer.invoke('nlp:segmentText', text, locale, minLength)
},
/**
* 获取词性标签定义
*/
getPosTags: (): Promise<PosTagInfo[]> => {
return ipcRenderer.invoke('nlp:getPosTags')
},
}
// ==================== Network API ====================
export const networkApi = {
/**
* 获取代理配置
*/
getProxyConfig: (): Promise<ProxyConfig> => {
return ipcRenderer.invoke('network:getProxyConfig')
},
/**
* 保存代理配置
*/
saveProxyConfig: (config: ProxyConfig): Promise<{ success: boolean; error?: string }> => {
return ipcRenderer.invoke('network:saveProxyConfig', config)
},
/**
* 测试代理连接
*/
testProxyConnection: (proxyUrl: string): Promise<{ success: boolean; error?: string }> => {
return ipcRenderer.invoke('network:testProxyConnection', proxyUrl)
},
}
// ==================== Cache API ====================
export const cacheApi = {
/**
* 获取所有缓存目录信息
*/
getInfo: (): Promise<CacheInfo> => {
return ipcRenderer.invoke('cache:getInfo')
},
/**
* 清理指定缓存目录
*/
clear: (cacheId: string): Promise<{ success: boolean; error?: string; message?: string }> => {
return ipcRenderer.invoke('cache:clear', cacheId)
},
/**
* 在文件管理器中打开缓存目录
*/
openDir: (cacheId: string): Promise<{ success: boolean; error?: string }> => {
return ipcRenderer.invoke('cache:openDir', cacheId)
},
/**
* 保存文件到下载目录
*/
saveToDownloads: (
filename: string,
dataUrl: string
): Promise<{ success: boolean; filePath?: string; error?: string }> => {
return ipcRenderer.invoke('cache:saveToDownloads', filename, dataUrl)
},
/**
* 获取最新的导入日志文件路径
*/
getLatestImportLog: (): Promise<{ success: boolean; path?: string; name?: string; error?: string }> => {
return ipcRenderer.invoke('cache:getLatestImportLog')
},
/**
* 在文件管理器中显示并高亮文件
*/
showInFolder: (filePath: string): Promise<{ success: boolean; error?: string }> => {
return ipcRenderer.invoke('cache:showInFolder', filePath)
},
}
// ==================== Session API ====================
export const sessionApi = {
/**
* 生成会话索引
* @param sessionId 数据库会话ID
* @param gapThreshold 时间间隔阈值(秒)
* @returns 生成的会话数量
*/
generate: (sessionId: string, gapThreshold?: number): Promise<number> => {
return ipcRenderer.invoke('session:generate', sessionId, gapThreshold)
},
/**
* 检查是否已生成会话索引
*/
hasIndex: (sessionId: string): Promise<boolean> => {
return ipcRenderer.invoke('session:hasIndex', sessionId)
},
/**
* 获取会话索引统计信息
*/
getStats: (sessionId: string): Promise<SessionStats> => {
return ipcRenderer.invoke('session:getStats', sessionId)
},
/**
* 清空会话索引
*/
clear: (sessionId: string): Promise<boolean> => {
return ipcRenderer.invoke('session:clear', sessionId)
},
/**
* 更新会话切分阈值
*/
updateGapThreshold: (sessionId: string, gapThreshold: number | null): Promise<boolean> => {
return ipcRenderer.invoke('session:updateGapThreshold', sessionId, gapThreshold)
},
/**
* 获取会话列表(用于时间线导航)
*/
getSessions: (sessionId: string): Promise<ChatSessionItem[]> => {
return ipcRenderer.invoke('session:getSessions', sessionId)
},
/**
* 生成单个会话摘要
*/
generateSummary: (
dbSessionId: string,
chatSessionId: number,
locale?: string,
forceRegenerate?: boolean
): Promise<{ success: boolean; summary?: string; error?: string }> => {
return ipcRenderer.invoke('session:generateSummary', dbSessionId, chatSessionId, locale, forceRegenerate)
},
/**
* 批量生成会话摘要
*/
generateSummaries: (
dbSessionId: string,
chatSessionIds: number[],
locale?: string
): Promise<{ success: number; failed: number; skipped: number }> => {
return ipcRenderer.invoke('session:generateSummaries', dbSessionId, chatSessionIds, locale)
},
/**
* 批量检查会话是否可以生成摘要
*/
checkCanGenerateSummary: (
dbSessionId: string,
chatSessionIds: number[]
): Promise<Record<number, { canGenerate: boolean; reason?: string }>> => {
return ipcRenderer.invoke('session:checkCanGenerateSummary', dbSessionId, chatSessionIds)
},
/**
* 根据时间范围查询会话列表
*/
getByTimeRange: (
dbSessionId: string,
startTs: number,
endTs: number
): Promise<
Array<{
id: number
startTs: number
endTs: number
messageCount: number
summary: string | null
}>
> => {
return ipcRenderer.invoke('session:getByTimeRange', dbSessionId, startTs, endTs)
},
/**
* 获取最近 N 条会话
*/
getRecent: (
dbSessionId: string,
limit: number
): Promise<
Array<{
id: number
startTs: number
endTs: number
messageCount: number
summary: string | null
}>
> => {
return ipcRenderer.invoke('session:getRecent', dbSessionId, limit)
},
}
+10 -1646
View File
File diff suppressed because it is too large Load Diff