Compare commits
4 Commits
feat/proxy
...
feature/pe
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
630c8e4d38 | ||
|
|
3fd53572dd | ||
|
|
3aec08d650 | ||
|
|
7328664edf |
@@ -998,6 +998,7 @@
|
|||||||
},
|
},
|
||||||
"memory": {
|
"memory": {
|
||||||
"actions": "Actions",
|
"actions": "Actions",
|
||||||
|
"active_memory_user": "Active Memory User",
|
||||||
"add_failed": "Failed to add memory",
|
"add_failed": "Failed to add memory",
|
||||||
"add_first_memory": "Add Your First Memory",
|
"add_first_memory": "Add Your First Memory",
|
||||||
"add_memory": "Add Memory",
|
"add_memory": "Add Memory",
|
||||||
|
|||||||
@@ -998,6 +998,7 @@
|
|||||||
},
|
},
|
||||||
"memory": {
|
"memory": {
|
||||||
"actions": "アクション",
|
"actions": "アクション",
|
||||||
|
"active_memory_user": "アクティブメモリユーザー",
|
||||||
"add_failed": "メモリーの追加に失敗しました",
|
"add_failed": "メモリーの追加に失敗しました",
|
||||||
"add_first_memory": "最初のメモリを追加",
|
"add_first_memory": "最初のメモリを追加",
|
||||||
"add_memory": "メモリーを追加",
|
"add_memory": "メモリーを追加",
|
||||||
|
|||||||
@@ -998,6 +998,7 @@
|
|||||||
},
|
},
|
||||||
"memory": {
|
"memory": {
|
||||||
"actions": "Действия",
|
"actions": "Действия",
|
||||||
|
"active_memory_user": "Активный пользователь памяти",
|
||||||
"add_failed": "Не удалось добавить память",
|
"add_failed": "Не удалось добавить память",
|
||||||
"add_first_memory": "Добавить первое воспоминание",
|
"add_first_memory": "Добавить первое воспоминание",
|
||||||
"add_memory": "Добавить память",
|
"add_memory": "Добавить память",
|
||||||
|
|||||||
@@ -998,6 +998,7 @@
|
|||||||
},
|
},
|
||||||
"memory": {
|
"memory": {
|
||||||
"actions": "操作",
|
"actions": "操作",
|
||||||
|
"active_memory_user": "活跃记忆用户",
|
||||||
"add_failed": "添加记忆失败",
|
"add_failed": "添加记忆失败",
|
||||||
"add_first_memory": "添加您的第一条记忆",
|
"add_first_memory": "添加您的第一条记忆",
|
||||||
"add_memory": "添加记忆",
|
"add_memory": "添加记忆",
|
||||||
|
|||||||
@@ -998,6 +998,7 @@
|
|||||||
},
|
},
|
||||||
"memory": {
|
"memory": {
|
||||||
"actions": "操作",
|
"actions": "操作",
|
||||||
|
"active_memory_user": "活躍記憶使用者",
|
||||||
"add_failed": "新增記憶失敗",
|
"add_failed": "新增記憶失敗",
|
||||||
"add_first_memory": "新增您的第一個記憶",
|
"add_first_memory": "新增您的第一個記憶",
|
||||||
"add_memory": "新增記憶",
|
"add_memory": "新增記憶",
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ import { loggerService } from '@logger'
|
|||||||
import { Box } from '@renderer/components/Layout'
|
import { Box } from '@renderer/components/Layout'
|
||||||
import MemoriesSettingsModal from '@renderer/pages/memory/settings-modal'
|
import MemoriesSettingsModal from '@renderer/pages/memory/settings-modal'
|
||||||
import MemoryService from '@renderer/services/MemoryService'
|
import MemoryService from '@renderer/services/MemoryService'
|
||||||
import { selectGlobalMemoryEnabled, selectMemoryConfig } from '@renderer/store/memory'
|
import { selectCurrentUserId, selectGlobalMemoryEnabled, selectMemoryConfig } from '@renderer/store/memory'
|
||||||
import { Assistant, AssistantSettings } from '@renderer/types'
|
import { Assistant, AssistantSettings } from '@renderer/types'
|
||||||
import { Alert, Button, Card, Space, Switch, Tooltip, Typography } from 'antd'
|
import { Alert, Button, Card, Select, Space, Switch, Tooltip, Typography } from 'antd'
|
||||||
import { useForm } from 'antd/es/form/Form'
|
import { useForm } from 'antd/es/form/Form'
|
||||||
import { Settings2 } from 'lucide-react'
|
import { Settings2 } from 'lucide-react'
|
||||||
import { useCallback, useEffect, useState } from 'react'
|
import { useCallback, useEffect, useState } from 'react'
|
||||||
@@ -28,19 +28,36 @@ const AssistantMemorySettings: React.FC<Props> = ({ assistant, updateAssistant,
|
|||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const memoryConfig = useSelector(selectMemoryConfig)
|
const memoryConfig = useSelector(selectMemoryConfig)
|
||||||
const globalMemoryEnabled = useSelector(selectGlobalMemoryEnabled)
|
const globalMemoryEnabled = useSelector(selectGlobalMemoryEnabled)
|
||||||
|
const currentUserId = useSelector(selectCurrentUserId)
|
||||||
const [memoryStats, setMemoryStats] = useState<{ count: number; loading: boolean }>({
|
const [memoryStats, setMemoryStats] = useState<{ count: number; loading: boolean }>({
|
||||||
count: 0,
|
count: 0,
|
||||||
loading: true
|
loading: true
|
||||||
})
|
})
|
||||||
|
const [availableUsers, setAvailableUsers] = useState<
|
||||||
|
{ userId: string; memoryCount: number; lastMemoryDate: string }[]
|
||||||
|
>([])
|
||||||
const [settingsModalVisible, setSettingsModalVisible] = useState(false)
|
const [settingsModalVisible, setSettingsModalVisible] = useState(false)
|
||||||
const memoryService = MemoryService.getInstance()
|
const memoryService = MemoryService.getInstance()
|
||||||
const form = useForm()
|
const form = useForm()
|
||||||
|
|
||||||
|
// Load available memory users
|
||||||
|
const loadUsers = useCallback(async () => {
|
||||||
|
try {
|
||||||
|
const users = await memoryService.getUsersList()
|
||||||
|
setAvailableUsers(users)
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Failed to load memory users:', error as Error)
|
||||||
|
setAvailableUsers([])
|
||||||
|
}
|
||||||
|
}, [memoryService])
|
||||||
|
|
||||||
// Load memory statistics for this assistant
|
// Load memory statistics for this assistant
|
||||||
const loadMemoryStats = useCallback(async () => {
|
const loadMemoryStats = useCallback(async () => {
|
||||||
setMemoryStats((prev) => ({ ...prev, loading: true }))
|
setMemoryStats((prev) => ({ ...prev, loading: true }))
|
||||||
try {
|
try {
|
||||||
|
const effectiveUserId = memoryService.getEffectiveUserId(assistant, currentUserId)
|
||||||
const result = await memoryService.list({
|
const result = await memoryService.list({
|
||||||
|
userId: effectiveUserId,
|
||||||
agentId: assistant.id,
|
agentId: assistant.id,
|
||||||
limit: 1000
|
limit: 1000
|
||||||
})
|
})
|
||||||
@@ -49,16 +66,25 @@ const AssistantMemorySettings: React.FC<Props> = ({ assistant, updateAssistant,
|
|||||||
logger.error('Failed to load memory stats:', error as Error)
|
logger.error('Failed to load memory stats:', error as Error)
|
||||||
setMemoryStats({ count: 0, loading: false })
|
setMemoryStats({ count: 0, loading: false })
|
||||||
}
|
}
|
||||||
}, [assistant.id, memoryService])
|
}, [assistant, currentUserId, memoryService])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
loadUsers()
|
||||||
loadMemoryStats()
|
loadMemoryStats()
|
||||||
}, [loadMemoryStats])
|
}, [loadUsers, loadMemoryStats])
|
||||||
|
|
||||||
const handleMemoryToggle = (enabled: boolean) => {
|
const handleMemoryToggle = (enabled: boolean) => {
|
||||||
updateAssistant({ ...assistant, enableMemory: enabled })
|
updateAssistant({ ...assistant, enableMemory: enabled })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleMemoryUserChange = (value: string) => {
|
||||||
|
// 'global' means use global default (undefined)
|
||||||
|
const memoryUserId = value === 'global' ? undefined : value
|
||||||
|
updateAssistant({ ...assistant, memoryUserId })
|
||||||
|
// Reload stats after changing user
|
||||||
|
setTimeout(() => loadMemoryStats(), 100)
|
||||||
|
}
|
||||||
|
|
||||||
const handleNavigateToMemory = () => {
|
const handleNavigateToMemory = () => {
|
||||||
// Close current modal/page first
|
// Close current modal/page first
|
||||||
if (onClose) {
|
if (onClose) {
|
||||||
@@ -70,6 +96,8 @@ const AssistantMemorySettings: React.FC<Props> = ({ assistant, updateAssistant,
|
|||||||
|
|
||||||
const isMemoryConfigured = memoryConfig.embedderApiClient && memoryConfig.llmApiClient
|
const isMemoryConfigured = memoryConfig.embedderApiClient && memoryConfig.llmApiClient
|
||||||
const isMemoryEnabled = globalMemoryEnabled && isMemoryConfigured
|
const isMemoryEnabled = globalMemoryEnabled && isMemoryConfigured
|
||||||
|
const effectiveUserId = memoryService.getEffectiveUserId(assistant, currentUserId)
|
||||||
|
const currentMemoryUser = assistant.memoryUserId || 'global'
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
@@ -124,12 +152,41 @@ const AssistantMemorySettings: React.FC<Props> = ({ assistant, updateAssistant,
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Memory User Selection */}
|
||||||
|
{assistant.enableMemory && isMemoryEnabled && (
|
||||||
|
<Card size="small" style={{ marginBottom: 16 }}>
|
||||||
|
<Space direction="vertical" style={{ width: '100%' }}>
|
||||||
|
<div>
|
||||||
|
<Text strong>{t('memory.active_memory_user')}: </Text>
|
||||||
|
<Select
|
||||||
|
value={currentMemoryUser}
|
||||||
|
onChange={handleMemoryUserChange}
|
||||||
|
style={{ width: 200, marginLeft: 8 }}
|
||||||
|
disabled={!assistant.enableMemory}>
|
||||||
|
<Select.Option value="global">
|
||||||
|
{t('memory.default_user')} ({currentUserId})
|
||||||
|
</Select.Option>
|
||||||
|
{availableUsers.map((user) => (
|
||||||
|
<Select.Option key={user.userId} value={user.userId}>
|
||||||
|
{user.userId} ({user.memoryCount} memories)
|
||||||
|
</Select.Option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
</Space>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
|
|
||||||
<Card size="small" style={{ marginBottom: 16 }}>
|
<Card size="small" style={{ marginBottom: 16 }}>
|
||||||
<Space direction="vertical" style={{ width: '100%' }}>
|
<Space direction="vertical" style={{ width: '100%' }}>
|
||||||
<div>
|
<div>
|
||||||
<Text strong>{t('memory.stored_memories')}: </Text>
|
<Text strong>{t('memory.stored_memories')}: </Text>
|
||||||
<Text>{memoryStats.loading ? t('common.loading') : memoryStats.count}</Text>
|
<Text>{memoryStats.loading ? t('common.loading') : memoryStats.count}</Text>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<Text strong>{t('memory.active_memory_user')}: </Text>
|
||||||
|
<Text code>{effectiveUserId}</Text>
|
||||||
|
</div>
|
||||||
{memoryConfig.embedderApiClient && (
|
{memoryConfig.embedderApiClient && (
|
||||||
<div>
|
<div>
|
||||||
<Text strong>{t('memory.embedding_model')}: </Text>
|
<Text strong>{t('memory.embedding_model')}: </Text>
|
||||||
|
|||||||
@@ -458,7 +458,7 @@ const MemorySettings = () => {
|
|||||||
try {
|
try {
|
||||||
// Create the user by adding an initial memory with the userId
|
// Create the user by adding an initial memory with the userId
|
||||||
// This implicitly creates the user in the system
|
// This implicitly creates the user in the system
|
||||||
await memoryService.setCurrentUser(userId)
|
memoryService.setCurrentUser(userId)
|
||||||
await memoryService.add(t('memory.initial_memory_content'), { userId })
|
await memoryService.add(t('memory.initial_memory_content'), { userId })
|
||||||
|
|
||||||
// Refresh the users list from the database to persist the new user
|
// Refresh the users list from the database to persist the new user
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
|
import CustomTag from '@renderer/components/CustomTag'
|
||||||
import { HStack } from '@renderer/components/Layout'
|
import { HStack } from '@renderer/components/Layout'
|
||||||
|
import { useAssistants } from '@renderer/hooks/useAssistant'
|
||||||
import { Avatar, Button, Select, Space, Tooltip } from 'antd'
|
import { Avatar, Button, Select, Space, Tooltip } from 'antd'
|
||||||
import { UserRoundPlus } from 'lucide-react'
|
import { UserRoundPlus } from 'lucide-react'
|
||||||
import { useCallback, useMemo } from 'react'
|
import { useCallback, useMemo } from 'react'
|
||||||
@@ -15,23 +17,50 @@ interface UserSelectorProps {
|
|||||||
|
|
||||||
const UserSelector: React.FC<UserSelectorProps> = ({ currentUser, uniqueUsers, onUserSwitch, onAddUser }) => {
|
const UserSelector: React.FC<UserSelectorProps> = ({ currentUser, uniqueUsers, onUserSwitch, onAddUser }) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
const { assistants } = useAssistants()
|
||||||
|
|
||||||
const getUserAvatar = useCallback((user: string) => {
|
const getUserAvatar = useCallback((user: string) => {
|
||||||
return user === DEFAULT_USER_ID ? user.slice(0, 1).toUpperCase() : user.slice(0, 2).toUpperCase()
|
return user === DEFAULT_USER_ID ? user.slice(0, 1).toUpperCase() : user.slice(0, 2).toUpperCase()
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
// Get assistants linked to a specific memory user
|
||||||
|
const getAssistantsForUser = useCallback(
|
||||||
|
(userId: string) => {
|
||||||
|
return assistants.filter(
|
||||||
|
(assistant) =>
|
||||||
|
// Assistant uses this user if either:
|
||||||
|
// 1. memoryUserId explicitly matches
|
||||||
|
// 2. memoryUserId is undefined and this is the current global user
|
||||||
|
assistant.memoryUserId === userId || (!assistant.memoryUserId && userId === currentUser)
|
||||||
|
)
|
||||||
|
},
|
||||||
|
[assistants, currentUser]
|
||||||
|
)
|
||||||
|
|
||||||
const renderLabel = useCallback(
|
const renderLabel = useCallback(
|
||||||
(userId: string, userName: string) => {
|
(userId: string, userName: string) => {
|
||||||
|
const linkedAssistants = getAssistantsForUser(userId)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<HStack alignItems="center" gap={10}>
|
<HStack alignItems="center" justifyContent="space-between" style={{ width: '100%' }}>
|
||||||
<Avatar size={20} style={{ background: 'var(--color-primary)' }}>
|
<HStack alignItems="center" gap={8} style={{ minWidth: 0 }}>
|
||||||
{getUserAvatar(userId)}
|
<Avatar size={20} style={{ background: 'var(--color-primary)' }}>
|
||||||
</Avatar>
|
{getUserAvatar(userId)}
|
||||||
<span>{userName}</span>
|
</Avatar>
|
||||||
|
<span style={{ whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden' }}>{userName}</span>
|
||||||
|
</HStack>
|
||||||
|
{linkedAssistants.length > 0 && (
|
||||||
|
<CustomTag
|
||||||
|
color="#8c8c8c"
|
||||||
|
size={10}
|
||||||
|
tooltip={`Linked Assistants: ${linkedAssistants.map((a) => a.name).join(', ')}`}>
|
||||||
|
{linkedAssistants.length}
|
||||||
|
</CustomTag>
|
||||||
|
)}
|
||||||
</HStack>
|
</HStack>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
[getUserAvatar]
|
[getUserAvatar, getAssistantsForUser]
|
||||||
)
|
)
|
||||||
|
|
||||||
const options = useMemo(() => {
|
const options = useMemo(() => {
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ import {
|
|||||||
} from './AssistantService'
|
} from './AssistantService'
|
||||||
import { processKnowledgeSearch } from './KnowledgeService'
|
import { processKnowledgeSearch } from './KnowledgeService'
|
||||||
import { MemoryProcessor } from './MemoryProcessor'
|
import { MemoryProcessor } from './MemoryProcessor'
|
||||||
|
import MemoryService from './MemoryService'
|
||||||
import {
|
import {
|
||||||
filterContextMessages,
|
filterContextMessages,
|
||||||
filterEmptyMessages,
|
filterEmptyMessages,
|
||||||
@@ -232,10 +233,12 @@ async function fetchExternalTool(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (memoryConfig.llmApiClient && memoryConfig.embedderApiClient) {
|
if (memoryConfig.llmApiClient && memoryConfig.embedderApiClient) {
|
||||||
const currentUserId = selectCurrentUserId(store.getState())
|
const globalUserId = selectCurrentUserId(store.getState())
|
||||||
// Search for relevant memories
|
const memoryService = MemoryService.getInstance()
|
||||||
const processorConfig = MemoryProcessor.getProcessorConfig(memoryConfig, assistant.id, currentUserId)
|
const effectiveUserId = memoryService.getEffectiveUserId(assistant, globalUserId)
|
||||||
logger.info(`Searching for relevant memories with content: ${content}`)
|
// Search for relevant memories using effective user ID
|
||||||
|
const processorConfig = MemoryProcessor.getProcessorConfig(memoryConfig, assistant.id, effectiveUserId)
|
||||||
|
logger.info(`Searching for relevant memories with content: ${content} for effective user: ${effectiveUserId}`)
|
||||||
const memoryProcessor = new MemoryProcessor()
|
const memoryProcessor = new MemoryProcessor()
|
||||||
const relevantMemories = await memoryProcessor.searchRelevantMemories(
|
const relevantMemories = await memoryProcessor.searchRelevantMemories(
|
||||||
content,
|
content,
|
||||||
@@ -557,7 +560,9 @@ async function processConversationMemory(messages: Message[], assistant: Assista
|
|||||||
// return
|
// return
|
||||||
// }
|
// }
|
||||||
|
|
||||||
const currentUserId = selectCurrentUserId(store.getState())
|
const globalUserId = selectCurrentUserId(store.getState())
|
||||||
|
const memoryService = MemoryService.getInstance()
|
||||||
|
const effectiveUserId = memoryService.getEffectiveUserId(assistant, globalUserId)
|
||||||
|
|
||||||
// Create updated memory config with resolved models
|
// Create updated memory config with resolved models
|
||||||
const updatedMemoryConfig = {
|
const updatedMemoryConfig = {
|
||||||
@@ -582,7 +587,7 @@ async function processConversationMemory(messages: Message[], assistant: Assista
|
|||||||
const processorConfig = MemoryProcessor.getProcessorConfig(
|
const processorConfig = MemoryProcessor.getProcessorConfig(
|
||||||
updatedMemoryConfig,
|
updatedMemoryConfig,
|
||||||
assistant.id,
|
assistant.id,
|
||||||
currentUserId,
|
effectiveUserId,
|
||||||
lastUserMessage?.id
|
lastUserMessage?.id
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -238,12 +238,10 @@ export class MemoryProcessor {
|
|||||||
limit
|
limit
|
||||||
})
|
})
|
||||||
|
|
||||||
logger.debug(
|
logger.debug('Searching memories successful', { query, userId, assistantId, result })
|
||||||
`Searching memories with query: ${query} for user: ${userId} and assistant: ${assistantId} result: ${result}`
|
|
||||||
)
|
|
||||||
return result.results
|
return result.results
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Error searching memories:', error as Error)
|
logger.error('Searching memories error:', { error })
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
import { loggerService } from '@logger'
|
import { loggerService } from '@logger'
|
||||||
|
import type { RootState } from '@renderer/store'
|
||||||
import store from '@renderer/store'
|
import store from '@renderer/store'
|
||||||
import { selectMemoryConfig } from '@renderer/store/memory'
|
import { selectCurrentUserId, selectMemoryConfig } from '@renderer/store/memory'
|
||||||
import {
|
import {
|
||||||
AddMemoryOptions,
|
AddMemoryOptions,
|
||||||
|
Assistant,
|
||||||
AssistantMessage,
|
AssistantMessage,
|
||||||
MemoryHistoryItem,
|
MemoryHistoryItem,
|
||||||
MemoryListOptions,
|
MemoryListOptions,
|
||||||
@@ -10,6 +12,8 @@ import {
|
|||||||
MemorySearchResult
|
MemorySearchResult
|
||||||
} from '@types'
|
} from '@types'
|
||||||
|
|
||||||
|
import { getAssistantById } from './AssistantService'
|
||||||
|
|
||||||
const logger = loggerService.withContext('MemoryService')
|
const logger = loggerService.withContext('MemoryService')
|
||||||
|
|
||||||
// Main process SearchResult type (matches what the IPC actually returns)
|
// Main process SearchResult type (matches what the IPC actually returns)
|
||||||
@@ -26,8 +30,10 @@ interface SearchResult {
|
|||||||
class MemoryService {
|
class MemoryService {
|
||||||
private static instance: MemoryService | null = null
|
private static instance: MemoryService | null = null
|
||||||
private currentUserId: string = 'default-user'
|
private currentUserId: string = 'default-user'
|
||||||
|
private getStateFunction: () => RootState
|
||||||
|
|
||||||
constructor() {
|
constructor(getStateFunction: () => RootState = () => store.getState()) {
|
||||||
|
this.getStateFunction = getStateFunction
|
||||||
this.init()
|
this.init()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,6 +74,37 @@ class MemoryService {
|
|||||||
return this.currentUserId
|
return this.currentUserId
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the effective memory user ID for an assistant using dependency injection
|
||||||
|
* Falls back to global currentUserId when assistant has no specific memoryUserId
|
||||||
|
* @param assistant - The assistant object containing memoryUserId
|
||||||
|
* @param globalUserId - The global user ID to fall back to
|
||||||
|
* @returns The effective user ID to use for memory operations
|
||||||
|
*/
|
||||||
|
public getEffectiveUserId(assistant: Assistant, globalUserId: string): string {
|
||||||
|
return assistant.memoryUserId || globalUserId
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Private helper to resolve user ID for context operations
|
||||||
|
* @param assistant - Optional assistant object to determine effective user ID
|
||||||
|
* @returns The resolved user ID to use for memory operations
|
||||||
|
*/
|
||||||
|
private resolveUserId(assistant?: Assistant): string {
|
||||||
|
let globalUserId = this.currentUserId
|
||||||
|
|
||||||
|
if (this.getStateFunction) {
|
||||||
|
try {
|
||||||
|
globalUserId = selectCurrentUserId(this.getStateFunction())
|
||||||
|
} catch (error) {
|
||||||
|
logger.warn('Failed to get state, falling back to internal currentUserId:', error as Error)
|
||||||
|
globalUserId = this.currentUserId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return assistant ? this.getEffectiveUserId(assistant, globalUserId) : globalUserId
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Lists all stored memories
|
* Lists all stored memories
|
||||||
* @param config - Optional configuration for filtering memories
|
* @param config - Optional configuration for filtering memories
|
||||||
@@ -110,12 +147,32 @@ class MemoryService {
|
|||||||
* @returns Promise resolving to search results of added memories
|
* @returns Promise resolving to search results of added memories
|
||||||
*/
|
*/
|
||||||
public async add(messages: string | AssistantMessage[], options: AddMemoryOptions): Promise<MemorySearchResult> {
|
public async add(messages: string | AssistantMessage[], options: AddMemoryOptions): Promise<MemorySearchResult> {
|
||||||
options.userId = this.currentUserId
|
const optionsWithUser = {
|
||||||
const result: SearchResult = await window.api.memory.add(messages, options)
|
...options,
|
||||||
// Convert SearchResult to MemorySearchResult for consistency
|
userId: this.currentUserId
|
||||||
return {
|
}
|
||||||
results: result.memories,
|
|
||||||
relations: []
|
try {
|
||||||
|
const result: SearchResult = await window.api.memory.add(messages, optionsWithUser)
|
||||||
|
|
||||||
|
// Handle error responses from main process
|
||||||
|
if (result.error) {
|
||||||
|
logger.error(`Memory service error: ${result.error}`)
|
||||||
|
throw new Error(result.error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert SearchResult to MemorySearchResult for consistency
|
||||||
|
return {
|
||||||
|
results: result.memories || [],
|
||||||
|
relations: []
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Failed to add memories:', error as Error)
|
||||||
|
// Return empty result on error to prevent UI crashes
|
||||||
|
return {
|
||||||
|
results: [],
|
||||||
|
relations: []
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,12 +183,42 @@ class MemoryService {
|
|||||||
* @returns Promise resolving to search results matching the query
|
* @returns Promise resolving to search results matching the query
|
||||||
*/
|
*/
|
||||||
public async search(query: string, options: MemorySearchOptions): Promise<MemorySearchResult> {
|
public async search(query: string, options: MemorySearchOptions): Promise<MemorySearchResult> {
|
||||||
options.userId = this.currentUserId
|
const optionsWithUser = {
|
||||||
const result: SearchResult = await window.api.memory.search(query, options)
|
...options,
|
||||||
// Convert SearchResult to MemorySearchResult for consistency
|
userId: this.currentUserId
|
||||||
return {
|
}
|
||||||
results: result.memories,
|
|
||||||
relations: []
|
// If agentId is provided, resolve userId from assistant's memoryUserId
|
||||||
|
if (optionsWithUser.agentId) {
|
||||||
|
const assistant = getAssistantById(optionsWithUser.agentId)
|
||||||
|
if (assistant) {
|
||||||
|
optionsWithUser.userId = assistant.memoryUserId || this.currentUserId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug('Searching memories start with options', { query: query, options: optionsWithUser })
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result: SearchResult = await window.api.memory.search(query, optionsWithUser)
|
||||||
|
|
||||||
|
// Handle error responses from main process
|
||||||
|
if (result.error) {
|
||||||
|
logger.error(`Memory service error: ${result.error}`)
|
||||||
|
throw new Error(result.error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert SearchResult to MemorySearchResult for consistency
|
||||||
|
return {
|
||||||
|
results: result.memories || [],
|
||||||
|
relations: []
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Failed to search memories:', error as Error)
|
||||||
|
// Return empty result on error to prevent UI crashes
|
||||||
|
return {
|
||||||
|
results: [],
|
||||||
|
relations: []
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,12 +284,13 @@ class MemoryService {
|
|||||||
*/
|
*/
|
||||||
public async updateConfig(): Promise<void> {
|
public async updateConfig(): Promise<void> {
|
||||||
try {
|
try {
|
||||||
if (!store || !store.getState) {
|
if (!this.getStateFunction) {
|
||||||
logger.warn('Store not available, skipping memory config update')
|
logger.warn('State function not available, skipping memory config update')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const memoryConfig = selectMemoryConfig(store.getState())
|
const state = this.getStateFunction()
|
||||||
|
const memoryConfig = selectMemoryConfig(state)
|
||||||
const embedderApiClient = memoryConfig.embedderApiClient
|
const embedderApiClient = memoryConfig.embedderApiClient
|
||||||
const llmApiClient = memoryConfig.llmApiClient
|
const llmApiClient = memoryConfig.llmApiClient
|
||||||
|
|
||||||
@@ -218,6 +306,138 @@ class MemoryService {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Enhanced methods with assistant context support
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lists stored memories with assistant context support
|
||||||
|
* Automatically resolves the effective user ID based on assistant's memoryUserId
|
||||||
|
* @param config - Configuration for filtering memories
|
||||||
|
* @param assistant - Optional assistant object to determine effective user ID
|
||||||
|
* @returns Promise resolving to search results containing filtered memories
|
||||||
|
*/
|
||||||
|
public async listWithContext(
|
||||||
|
config?: Omit<MemoryListOptions, 'userId'>,
|
||||||
|
assistant?: Assistant
|
||||||
|
): Promise<MemorySearchResult> {
|
||||||
|
const effectiveUserId = this.resolveUserId(assistant)
|
||||||
|
|
||||||
|
const configWithUser = {
|
||||||
|
...config,
|
||||||
|
userId: effectiveUserId
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result: SearchResult = await window.api.memory.list(configWithUser)
|
||||||
|
|
||||||
|
// Handle error responses from main process
|
||||||
|
if (result.error) {
|
||||||
|
logger.error(`Memory service error: ${result.error}`)
|
||||||
|
throw new Error(result.error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert SearchResult to MemorySearchResult for consistency
|
||||||
|
return {
|
||||||
|
results: result.memories || [],
|
||||||
|
relations: []
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Failed to list memories with context:', error as Error)
|
||||||
|
// Return empty result on error to prevent UI crashes
|
||||||
|
return {
|
||||||
|
results: [],
|
||||||
|
relations: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds new memory entries with assistant context support
|
||||||
|
* Automatically resolves the effective user ID based on assistant's memoryUserId
|
||||||
|
* @param messages - String content or array of assistant messages to store as memory
|
||||||
|
* @param options - Configuration options for adding memory (without userId)
|
||||||
|
* @param assistant - Optional assistant object to determine effective user ID
|
||||||
|
* @returns Promise resolving to search results of added memories
|
||||||
|
*/
|
||||||
|
public async addWithContext(
|
||||||
|
messages: string | AssistantMessage[],
|
||||||
|
options: Omit<AddMemoryOptions, 'userId'>,
|
||||||
|
assistant?: Assistant
|
||||||
|
): Promise<MemorySearchResult> {
|
||||||
|
const effectiveUserId = this.resolveUserId(assistant)
|
||||||
|
|
||||||
|
const optionsWithUser = {
|
||||||
|
...options,
|
||||||
|
userId: effectiveUserId
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result: SearchResult = await window.api.memory.add(messages, optionsWithUser)
|
||||||
|
|
||||||
|
// Handle error responses from main process
|
||||||
|
if (result.error) {
|
||||||
|
logger.error(`Memory service error: ${result.error}`)
|
||||||
|
throw new Error(result.error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert SearchResult to MemorySearchResult for consistency
|
||||||
|
return {
|
||||||
|
results: result.memories || [],
|
||||||
|
relations: []
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Failed to add memories with context:', error as Error)
|
||||||
|
// Return empty result on error to prevent UI crashes
|
||||||
|
return {
|
||||||
|
results: [],
|
||||||
|
relations: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Searches stored memories with assistant context support
|
||||||
|
* Automatically resolves the effective user ID based on assistant's memoryUserId
|
||||||
|
* @param query - Search query string to find relevant memories
|
||||||
|
* @param options - Configuration options for memory search (without userId)
|
||||||
|
* @param assistant - Optional assistant object to determine effective user ID
|
||||||
|
* @returns Promise resolving to search results matching the query
|
||||||
|
*/
|
||||||
|
public async searchWithContext(
|
||||||
|
query: string,
|
||||||
|
options: Omit<MemorySearchOptions, 'userId'>,
|
||||||
|
assistant?: Assistant
|
||||||
|
): Promise<MemorySearchResult> {
|
||||||
|
const effectiveUserId = this.resolveUserId(assistant)
|
||||||
|
|
||||||
|
const optionsWithUser = {
|
||||||
|
...options,
|
||||||
|
userId: effectiveUserId
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result: SearchResult = await window.api.memory.search(query, optionsWithUser)
|
||||||
|
|
||||||
|
// Handle error responses from main process
|
||||||
|
if (result.error) {
|
||||||
|
logger.error(`Memory service error: ${result.error}`)
|
||||||
|
throw new Error(result.error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert SearchResult to MemorySearchResult for consistency
|
||||||
|
return {
|
||||||
|
results: result.memories || [],
|
||||||
|
relations: []
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
logger.error('Failed to search memories with context:', error as Error)
|
||||||
|
// Return empty result on error to prevent UI crashes
|
||||||
|
return {
|
||||||
|
results: [],
|
||||||
|
relations: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default MemoryService
|
export default MemoryService
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ export function createStreamProcessor(callbacks: StreamProcessorCallbacks = {})
|
|||||||
return (chunk: Chunk) => {
|
return (chunk: Chunk) => {
|
||||||
try {
|
try {
|
||||||
const data = chunk
|
const data = chunk
|
||||||
logger.debug('data: ', data)
|
logger.silly('data: ', data)
|
||||||
switch (data.type) {
|
switch (data.type) {
|
||||||
case ChunkType.BLOCK_COMPLETE: {
|
case ChunkType.BLOCK_COMPLETE: {
|
||||||
if (callbacks.onComplete) callbacks.onComplete(AssistantMessageStatus.SUCCESS, data?.response)
|
if (callbacks.onComplete) callbacks.onComplete(AssistantMessageStatus.SUCCESS, data?.response)
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ export type Assistant = {
|
|||||||
regularPhrases?: QuickPhrase[] // Added for regular phrase
|
regularPhrases?: QuickPhrase[] // Added for regular phrase
|
||||||
tags?: string[] // 助手标签
|
tags?: string[] // 助手标签
|
||||||
enableMemory?: boolean
|
enableMemory?: boolean
|
||||||
|
memoryUserId?: string // 绑定的记忆用户ID,当未指定时使用全局记忆用户
|
||||||
}
|
}
|
||||||
|
|
||||||
export type TranslateAssistant = Assistant & {
|
export type TranslateAssistant = Assistant & {
|
||||||
|
|||||||
Reference in New Issue
Block a user