mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2025-12-30 07:39:06 +08:00
feat: enhance cache service with casual methods and update related components
- Introduced casual methods (`getCasual`, `setCasual`, `deleteCasual`, etc.) in `CacheService` for dynamic key handling. - Updated various components to utilize the new casual methods for improved flexibility in cache management. - Added new cache schema keys for file and resource paths in `cacheSchemas.ts`. - Refactored `UserPopup`, `useAppInit`, and other components to align with the new caching strategy, ensuring consistent access to cached data.
This commit is contained in:
parent
361aa87838
commit
1139d8d57a
6
packages/shared/data/cache/cacheSchemas.ts
vendored
6
packages/shared/data/cache/cacheSchemas.ts
vendored
@ -31,6 +31,9 @@ export type UseCacheSchema = {
|
||||
'app.dist.update_state': CacheValueTypes.CacheAppUpdateState
|
||||
'app.user.avatar': string
|
||||
|
||||
'app.path.files': string
|
||||
'app.path.resources': string
|
||||
|
||||
// Chat context
|
||||
'chat.multi_select_mode': boolean
|
||||
'chat.selected_message_ids': string[]
|
||||
@ -62,7 +65,8 @@ export const DefaultUseCache: UseCacheSchema = {
|
||||
ignore: false
|
||||
},
|
||||
'app.user.avatar': '',
|
||||
|
||||
'app.path.files': '',
|
||||
'app.path.resources': '',
|
||||
// Chat context
|
||||
'chat.multi_select_mode': false,
|
||||
'chat.selected_message_ids': [],
|
||||
|
||||
@ -175,16 +175,16 @@ export abstract class BaseApiClient<
|
||||
return keys[0]
|
||||
}
|
||||
|
||||
const lastUsedKey = cacheService.getShared(keyName) as string | undefined
|
||||
const lastUsedKey = cacheService.getSharedCasual<string>(keyName)
|
||||
if (lastUsedKey === undefined) {
|
||||
cacheService.setShared(keyName, keys[0])
|
||||
cacheService.setSharedCasual(keyName, keys[0])
|
||||
return keys[0]
|
||||
}
|
||||
|
||||
const currentIndex = keys.indexOf(lastUsedKey)
|
||||
const nextIndex = (currentIndex + 1) % keys.length
|
||||
const nextKey = keys[nextIndex]
|
||||
cacheService.setShared(keyName, nextKey)
|
||||
cacheService.setSharedCasual(keyName, nextKey)
|
||||
|
||||
return nextKey
|
||||
}
|
||||
@ -343,7 +343,7 @@ export abstract class BaseApiClient<
|
||||
}
|
||||
|
||||
private getMemoryReferencesFromCache(message: Message) {
|
||||
const memories = cacheService.get(`memory-search-${message.id}`) as MemoryItem[] | undefined
|
||||
const memories = cacheService.getCasual<MemoryItem[]>(`memory-search-${message.id}`)
|
||||
if (memories) {
|
||||
const memoryReferences: KnowledgeReference[] = memories.map((mem, index) => ({
|
||||
id: index + 1,
|
||||
@ -361,10 +361,10 @@ export abstract class BaseApiClient<
|
||||
if (isEmpty(content)) {
|
||||
return []
|
||||
}
|
||||
const webSearch: WebSearchResponse | undefined = cacheService.get(`web-search-${message.id}`)
|
||||
const webSearch = cacheService.getCasual<WebSearchResponse>(`web-search-${message.id}`)
|
||||
|
||||
if (webSearch) {
|
||||
cacheService.delete(`web-search-${message.id}`)
|
||||
cacheService.deleteCasual(`web-search-${message.id}`)
|
||||
return (webSearch.results as WebSearchProviderResponse).results.map(
|
||||
(result, index) =>
|
||||
({
|
||||
@ -387,10 +387,10 @@ export abstract class BaseApiClient<
|
||||
if (isEmpty(content)) {
|
||||
return []
|
||||
}
|
||||
const knowledgeReferences: KnowledgeReference[] | undefined = cacheService.get(`knowledge-search-${message.id}`)
|
||||
const knowledgeReferences = cacheService.getCasual<KnowledgeReference[]>(`knowledge-search-${message.id}`)
|
||||
|
||||
if (knowledgeReferences && !isEmpty(knowledgeReferences)) {
|
||||
cacheService.delete(`knowledge-search-${message.id}`)
|
||||
cacheService.deleteCasual(`knowledge-search-${message.id}`)
|
||||
logger.debug(`Found ${knowledgeReferences.length} knowledge base references in cache for ID: ${message.id}`)
|
||||
return knowledgeReferences
|
||||
}
|
||||
|
||||
@ -44,7 +44,7 @@ const PopupContainer: React.FC<Props> = ({ resolve }) => {
|
||||
// set emoji string
|
||||
await ImageStorage.set('avatar', emoji)
|
||||
// update avatar display
|
||||
cacheService.set('avatar', emoji)
|
||||
cacheService.set('app.user.avatar', emoji)
|
||||
setEmojiPickerOpen(false)
|
||||
} catch (error: any) {
|
||||
window.toast.error(error.message)
|
||||
@ -53,7 +53,7 @@ const PopupContainer: React.FC<Props> = ({ resolve }) => {
|
||||
const handleReset = async () => {
|
||||
try {
|
||||
await ImageStorage.set('avatar', DefaultAvatar)
|
||||
cacheService.set('avatar', DefaultAvatar)
|
||||
cacheService.set('app.user.avatar', DefaultAvatar)
|
||||
setDropdownOpen(false)
|
||||
} catch (error: any) {
|
||||
window.toast.error(error.message)
|
||||
@ -78,7 +78,7 @@ const PopupContainer: React.FC<Props> = ({ resolve }) => {
|
||||
const compressedFile = await compressImage(_file)
|
||||
await ImageStorage.set('avatar', compressedFile)
|
||||
}
|
||||
cacheService.set('avatar', await ImageStorage.get('avatar'))
|
||||
cacheService.set('app.user.avatar', await ImageStorage.get('avatar'))
|
||||
setDropdownOpen(false)
|
||||
} catch (error: any) {
|
||||
window.toast.error(error.message)
|
||||
|
||||
@ -93,13 +93,27 @@ export class CacheService {
|
||||
// ============ Memory Cache (Cross-component) ============
|
||||
|
||||
/**
|
||||
* Get value from memory cache with TTL validation
|
||||
* @param key - Cache key to retrieve
|
||||
* Get value from memory cache with TTL validation (type-safe)
|
||||
* @param key - Schema-defined cache key
|
||||
* @returns Cached value or undefined if not found or expired
|
||||
*/
|
||||
get<K extends UseCacheKey>(key: K): UseCacheSchema[K]
|
||||
get<T>(key: Exclude<string, UseCacheKey>): T | undefined
|
||||
get(key: string): any {
|
||||
get<K extends UseCacheKey>(key: K): UseCacheSchema[K] {
|
||||
return this.getInternal(key)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get value from memory cache with TTL validation (casual, dynamic key)
|
||||
* @param key - Dynamic cache key (e.g., `topic:${id}`)
|
||||
* @returns Cached value or undefined if not found or expired
|
||||
*/
|
||||
getCasual<T>(key: Exclude<string, UseCacheKey>): T | undefined {
|
||||
return this.getInternal(key)
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal implementation for memory cache get
|
||||
*/
|
||||
private getInternal(key: string): any {
|
||||
const entry = this.memoryCache.get(key)
|
||||
if (entry === undefined) {
|
||||
return undefined
|
||||
@ -115,14 +129,29 @@ export class CacheService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set value in memory cache with optional TTL
|
||||
* @param key - Cache key to store
|
||||
* Set value in memory cache with optional TTL (type-safe)
|
||||
* @param key - Schema-defined cache key
|
||||
* @param value - Value to cache (type inferred from schema)
|
||||
* @param ttl - Time to live in milliseconds (optional)
|
||||
*/
|
||||
set<K extends UseCacheKey>(key: K, value: UseCacheSchema[K], ttl?: number): void {
|
||||
this.setInternal(key, value, ttl)
|
||||
}
|
||||
|
||||
/**
|
||||
* Set value in memory cache with optional TTL (casual, dynamic key)
|
||||
* @param key - Dynamic cache key (e.g., `topic:${id}`)
|
||||
* @param value - Value to cache
|
||||
* @param ttl - Time to live in milliseconds (optional)
|
||||
*/
|
||||
set<K extends UseCacheKey>(key: K, value: UseCacheSchema[K]): void
|
||||
set<T>(key: Exclude<string, UseCacheKey>, value: T, ttl?: number): void
|
||||
set(key: string, value: any, ttl?: number): void {
|
||||
setCasual<T>(key: Exclude<string, UseCacheKey>, value: T, ttl?: number): void {
|
||||
this.setInternal(key, value, ttl)
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal implementation for memory cache set
|
||||
*/
|
||||
private setInternal(key: string, value: any, ttl?: number): void {
|
||||
const existingEntry = this.memoryCache.get(key)
|
||||
|
||||
// Value comparison optimization
|
||||
@ -149,14 +178,27 @@ export class CacheService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if key exists in memory cache and is not expired
|
||||
* @param key - Cache key to check
|
||||
* Check if key exists in memory cache and is not expired (type-safe)
|
||||
* @param key - Schema-defined cache key
|
||||
* @returns True if key exists and is valid, false otherwise
|
||||
*/
|
||||
has<K extends UseCacheKey>(key: K): boolean {
|
||||
return this.hasInternal(key)
|
||||
}
|
||||
|
||||
has<K extends UseCacheKey>(key: K): boolean
|
||||
has(key: Exclude<string, UseCacheKey>): boolean
|
||||
has(key: string): boolean {
|
||||
/**
|
||||
* Check if key exists in memory cache and is not expired (casual, dynamic key)
|
||||
* @param key - Dynamic cache key
|
||||
* @returns True if key exists and is valid, false otherwise
|
||||
*/
|
||||
hasCasual(key: Exclude<string, UseCacheKey>): boolean {
|
||||
return this.hasInternal(key)
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal implementation for memory cache has
|
||||
*/
|
||||
private hasInternal(key: string): boolean {
|
||||
const entry = this.memoryCache.get(key)
|
||||
if (entry === undefined) {
|
||||
return false
|
||||
@ -173,13 +215,27 @@ export class CacheService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete from memory cache with hook protection
|
||||
* @param key - Cache key to delete
|
||||
* Delete from memory cache with hook protection (type-safe)
|
||||
* @param key - Schema-defined cache key
|
||||
* @returns True if deletion succeeded, false if key is protected by active hooks
|
||||
*/
|
||||
delete<K extends UseCacheKey>(key: K): boolean
|
||||
delete(key: Exclude<string, UseCacheKey>): boolean
|
||||
delete(key: string): boolean {
|
||||
delete<K extends UseCacheKey>(key: K): boolean {
|
||||
return this.deleteInternal(key)
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete from memory cache with hook protection (casual, dynamic key)
|
||||
* @param key - Dynamic cache key
|
||||
* @returns True if deletion succeeded, false if key is protected by active hooks
|
||||
*/
|
||||
deleteCasual(key: Exclude<string, UseCacheKey>): boolean {
|
||||
return this.deleteInternal(key)
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal implementation for memory cache delete
|
||||
*/
|
||||
private deleteInternal(key: string): boolean {
|
||||
// Check if key is being used by hooks
|
||||
if (this.activeHooks.has(key)) {
|
||||
logger.error(`Cannot delete key "${key}" as it's being used by useCache hook`)
|
||||
@ -199,25 +255,41 @@ export class CacheService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a key has TTL set in memory cache
|
||||
* @param key - Cache key to check
|
||||
* Check if a key has TTL set in memory cache (type-safe)
|
||||
* @param key - Schema-defined cache key
|
||||
* @returns True if key has TTL configured
|
||||
*/
|
||||
hasTTL<K extends UseCacheKey>(key: K): boolean
|
||||
hasTTL(key: Exclude<string, UseCacheKey>): boolean
|
||||
hasTTL(key: string): boolean {
|
||||
hasTTL<K extends UseCacheKey>(key: K): boolean {
|
||||
const entry = this.memoryCache.get(key)
|
||||
return entry?.expireAt !== undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a shared cache key has TTL set
|
||||
* @param key - Shared cache key to check
|
||||
* Check if a key has TTL set in memory cache (casual, dynamic key)
|
||||
* @param key - Dynamic cache key
|
||||
* @returns True if key has TTL configured
|
||||
*/
|
||||
hasSharedTTL<K extends UseSharedCacheKey>(key: K): boolean
|
||||
hasSharedTTL(key: Exclude<string, UseSharedCacheKey>): boolean
|
||||
hasSharedTTL(key: string): boolean {
|
||||
hasTTLCasual(key: Exclude<string, UseCacheKey>): boolean {
|
||||
const entry = this.memoryCache.get(key)
|
||||
return entry?.expireAt !== undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a shared cache key has TTL set (type-safe)
|
||||
* @param key - Schema-defined shared cache key
|
||||
* @returns True if key has TTL configured
|
||||
*/
|
||||
hasSharedTTL<K extends UseSharedCacheKey>(key: K): boolean {
|
||||
const entry = this.sharedCache.get(key)
|
||||
return entry?.expireAt !== undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a shared cache key has TTL set (casual, dynamic key)
|
||||
* @param key - Dynamic shared cache key
|
||||
* @returns True if key has TTL configured
|
||||
*/
|
||||
hasSharedTTLCasual(key: Exclude<string, UseSharedCacheKey>): boolean {
|
||||
const entry = this.sharedCache.get(key)
|
||||
return entry?.expireAt !== undefined
|
||||
}
|
||||
@ -225,13 +297,27 @@ export class CacheService {
|
||||
// ============ Shared Cache (Cross-window) ============
|
||||
|
||||
/**
|
||||
* Get value from shared cache with TTL validation
|
||||
* @param key - Shared cache key to retrieve
|
||||
* Get value from shared cache with TTL validation (type-safe)
|
||||
* @param key - Schema-defined shared cache key
|
||||
* @returns Cached value or undefined if not found or expired
|
||||
*/
|
||||
getShared<K extends UseSharedCacheKey>(key: K): UseSharedCacheSchema[K]
|
||||
getShared<T>(key: Exclude<string, UseSharedCacheKey>): T | undefined
|
||||
getShared(key: string): any {
|
||||
getShared<K extends UseSharedCacheKey>(key: K): UseSharedCacheSchema[K] | undefined {
|
||||
return this.getSharedInternal(key)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get value from shared cache with TTL validation (casual, dynamic key)
|
||||
* @param key - Dynamic shared cache key (e.g., `window:${id}`)
|
||||
* @returns Cached value or undefined if not found or expired
|
||||
*/
|
||||
getSharedCasual<T>(key: Exclude<string, UseSharedCacheKey>): T | undefined {
|
||||
return this.getSharedInternal(key)
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal implementation for shared cache get
|
||||
*/
|
||||
private getSharedInternal(key: string): any {
|
||||
const entry = this.sharedCache.get(key)
|
||||
if (!entry) return undefined
|
||||
|
||||
@ -246,14 +332,29 @@ export class CacheService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set value in shared cache with cross-window synchronization
|
||||
* @param key - Shared cache key to store
|
||||
* Set value in shared cache with cross-window synchronization (type-safe)
|
||||
* @param key - Schema-defined shared cache key
|
||||
* @param value - Value to cache (type inferred from schema)
|
||||
* @param ttl - Time to live in milliseconds (optional)
|
||||
*/
|
||||
setShared<K extends UseSharedCacheKey>(key: K, value: UseSharedCacheSchema[K], ttl?: number): void {
|
||||
this.setSharedInternal(key, value, ttl)
|
||||
}
|
||||
|
||||
/**
|
||||
* Set value in shared cache with cross-window synchronization (casual, dynamic key)
|
||||
* @param key - Dynamic shared cache key (e.g., `window:${id}`)
|
||||
* @param value - Value to cache
|
||||
* @param ttl - Time to live in milliseconds (optional)
|
||||
*/
|
||||
setShared<K extends UseSharedCacheKey>(key: K, value: UseSharedCacheSchema[K]): void
|
||||
setShared<T>(key: Exclude<string, UseSharedCacheKey>, value: T, ttl?: number): void
|
||||
setShared(key: string, value: any, ttl?: number): void {
|
||||
setSharedCasual<T>(key: Exclude<string, UseSharedCacheKey>, value: T, ttl?: number): void {
|
||||
this.setSharedInternal(key, value, ttl)
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal implementation for shared cache set
|
||||
*/
|
||||
private setSharedInternal(key: string, value: any, ttl?: number): void {
|
||||
const existingEntry = this.sharedCache.get(key)
|
||||
|
||||
// Value comparison optimization
|
||||
@ -296,13 +397,27 @@ export class CacheService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if key exists in shared cache and is not expired
|
||||
* @param key - Shared cache key to check
|
||||
* Check if key exists in shared cache and is not expired (type-safe)
|
||||
* @param key - Schema-defined shared cache key
|
||||
* @returns True if key exists and is valid, false otherwise
|
||||
*/
|
||||
hasShared<K extends UseSharedCacheKey>(key: K): boolean
|
||||
hasShared(key: Exclude<string, UseSharedCacheKey>): boolean
|
||||
hasShared(key: string): boolean {
|
||||
hasShared<K extends UseSharedCacheKey>(key: K): boolean {
|
||||
return this.hasSharedInternal(key)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if key exists in shared cache and is not expired (casual, dynamic key)
|
||||
* @param key - Dynamic shared cache key
|
||||
* @returns True if key exists and is valid, false otherwise
|
||||
*/
|
||||
hasSharedCasual(key: Exclude<string, UseSharedCacheKey>): boolean {
|
||||
return this.hasSharedInternal(key)
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal implementation for shared cache has
|
||||
*/
|
||||
private hasSharedInternal(key: string): boolean {
|
||||
const entry = this.sharedCache.get(key)
|
||||
if (!entry) return false
|
||||
|
||||
@ -317,13 +432,27 @@ export class CacheService {
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete from shared cache with cross-window synchronization and hook protection
|
||||
* @param key - Shared cache key to delete
|
||||
* Delete from shared cache with cross-window synchronization and hook protection (type-safe)
|
||||
* @param key - Schema-defined shared cache key
|
||||
* @returns True if deletion succeeded, false if key is protected by active hooks
|
||||
*/
|
||||
deleteShared<K extends UseSharedCacheKey>(key: K): boolean
|
||||
deleteShared(key: Exclude<string, UseSharedCacheKey>): boolean
|
||||
deleteShared(key: string): boolean {
|
||||
deleteShared<K extends UseSharedCacheKey>(key: K): boolean {
|
||||
return this.deleteSharedInternal(key)
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete from shared cache with cross-window synchronization and hook protection (casual, dynamic key)
|
||||
* @param key - Dynamic shared cache key
|
||||
* @returns True if deletion succeeded, false if key is protected by active hooks
|
||||
*/
|
||||
deleteSharedCasual(key: Exclude<string, UseSharedCacheKey>): boolean {
|
||||
return this.deleteSharedInternal(key)
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal implementation for shared cache delete
|
||||
*/
|
||||
private deleteSharedInternal(key: string): boolean {
|
||||
// Check if key is being used by hooks
|
||||
if (this.activeHooks.has(key)) {
|
||||
logger.error(`Cannot delete key "${key}" as it's being used by useSharedCache hook`)
|
||||
|
||||
@ -156,19 +156,32 @@ await preferenceService.setMultiple({
|
||||
```typescript
|
||||
import { cacheService } from '@data/CacheService'
|
||||
|
||||
// Memory cache (component-level)
|
||||
// Memory cache - Type-safe (schema key, with auto-completion)
|
||||
cacheService.set('temp.calculation', result, 30000) // 30s TTL
|
||||
const result = cacheService.get('temp.calculation')
|
||||
|
||||
// Shared cache (cross-window)
|
||||
// Memory cache - Casual (dynamic key, requires manual type)
|
||||
cacheService.setCasual<TopicCache>(`topic:${id}`, topicData)
|
||||
const topic = cacheService.getCasual<TopicCache>(`topic:${id}`)
|
||||
|
||||
// Shared cache - Type-safe (schema key)
|
||||
cacheService.setShared('window.layout', layoutConfig)
|
||||
const layout = cacheService.getShared('window.layout')
|
||||
|
||||
// Persist cache (survives restarts)
|
||||
// Shared cache - Casual (dynamic key)
|
||||
cacheService.setSharedCasual<WindowState>(`window:${windowId}`, state)
|
||||
const state = cacheService.getSharedCasual<WindowState>(`window:${windowId}`)
|
||||
|
||||
// Persist cache (survives restarts, schema keys only)
|
||||
cacheService.setPersist('app.recent_files', recentFiles)
|
||||
const files = cacheService.getPersist('app.recent_files')
|
||||
```
|
||||
|
||||
**When to Use Type-safe vs Casual Methods**:
|
||||
- **Type-safe** (`get`, `set`, `getShared`, `setShared`): Use when the key is predefined in the cache schema. Provides auto-completion and type inference.
|
||||
- **Casual** (`getCasual`, `setCasual`, `getSharedCasual`, `setSharedCasual`): Use when the key is dynamically constructed (e.g., `topic:${id}`). Requires manual type specification via generics.
|
||||
- **Persist Cache**: Only supports schema keys (no Casual methods) to ensure data integrity.
|
||||
|
||||
## React Hooks
|
||||
|
||||
### useDataApi
|
||||
|
||||
@ -44,8 +44,8 @@ export function useCache<K extends UseCacheKey>(
|
||||
*/
|
||||
const value = useSyncExternalStore(
|
||||
useCallback((callback) => cacheService.subscribe(key, callback), [key]),
|
||||
useCallback(() => cacheService.get<UseCacheSchema[K]>(key), [key]),
|
||||
useCallback(() => cacheService.get<UseCacheSchema[K]>(key), [key]) // SSR snapshot
|
||||
useCallback(() => cacheService.get(key), [key]),
|
||||
useCallback(() => cacheService.get(key), [key]) // SSR snapshot
|
||||
)
|
||||
|
||||
/**
|
||||
@ -131,8 +131,8 @@ export function useSharedCache<K extends UseSharedCacheKey>(
|
||||
*/
|
||||
const value = useSyncExternalStore(
|
||||
useCallback((callback) => cacheService.subscribe(key, callback), [key]),
|
||||
useCallback(() => cacheService.getShared<UseSharedCacheSchema[K]>(key), [key]),
|
||||
useCallback(() => cacheService.getShared<UseSharedCacheSchema[K]>(key), [key]) // SSR snapshot
|
||||
useCallback(() => cacheService.getShared(key), [key]),
|
||||
useCallback(() => cacheService.getShared(key), [key]) // SSR snapshot
|
||||
)
|
||||
|
||||
/**
|
||||
|
||||
@ -79,7 +79,7 @@ export function useAppInit() {
|
||||
useFullScreenNotice()
|
||||
|
||||
useEffect(() => {
|
||||
savedAvatar?.value && cacheService.set('avatar', savedAvatar.value)
|
||||
savedAvatar?.value && cacheService.set('app.user.avatar', savedAvatar.value)
|
||||
}, [savedAvatar])
|
||||
|
||||
useEffect(() => {
|
||||
@ -149,8 +149,8 @@ export function useAppInit() {
|
||||
useEffect(() => {
|
||||
// set files path
|
||||
window.api.getAppInfo().then((info) => {
|
||||
cacheService.set('filesPath', info.filesPath)
|
||||
cacheService.set('resourcesPath', info.resourcesPath)
|
||||
cacheService.set('app.path.files', info.filesPath)
|
||||
cacheService.set('app.path.resources', info.resourcesPath)
|
||||
})
|
||||
}, [])
|
||||
|
||||
|
||||
@ -29,7 +29,7 @@ export function getModel(id?: string, providerId?: string) {
|
||||
}
|
||||
|
||||
export function modelGenerating() {
|
||||
const generating = cacheService.get<boolean>('generating') ?? false
|
||||
const generating = cacheService.get('chat.generating') ?? false
|
||||
|
||||
if (generating) {
|
||||
window.toast.warning(i18n.t('message.switch.disabled'))
|
||||
|
||||
@ -24,12 +24,12 @@ export default function useScrollPosition(key: string, throttleWait?: number) {
|
||||
const handleScroll = throttle(() => {
|
||||
const position = containerRef.current?.scrollTop ?? 0
|
||||
window.requestAnimationFrame(() => {
|
||||
cacheService.set(scrollKeyRef.current, position)
|
||||
cacheService.setCasual(scrollKeyRef.current, position)
|
||||
})
|
||||
}, throttleWait ?? 100)
|
||||
|
||||
useEffect(() => {
|
||||
const scroll = () => containerRef.current?.scrollTo({ top: cacheService.get(scrollKey) || 0 })
|
||||
const scroll = () => containerRef.current?.scrollTo({ top: cacheService.getCasual<number>(scrollKey) || 0 })
|
||||
scroll()
|
||||
setTimeoutTimer('scrollEffect', scroll, 50)
|
||||
}, [scrollKey, setTimeoutTimer])
|
||||
|
||||
@ -82,9 +82,9 @@ export async function getTopicById(topicId: string) {
|
||||
* 开始重命名指定话题
|
||||
*/
|
||||
export const startTopicRenaming = (topicId: string) => {
|
||||
const currentIds = cacheService.get<string[]>('renamingTopics') ?? []
|
||||
const currentIds = cacheService.get('topic.renaming') ?? []
|
||||
if (!currentIds.includes(topicId)) {
|
||||
cacheService.set('renamingTopics', [...currentIds, topicId])
|
||||
cacheService.set('topic.renaming', [...currentIds, topicId])
|
||||
}
|
||||
}
|
||||
|
||||
@ -93,23 +93,23 @@ export const startTopicRenaming = (topicId: string) => {
|
||||
*/
|
||||
export const finishTopicRenaming = (topicId: string) => {
|
||||
// 1. 立即从 renamingTopics 移除
|
||||
const renamingTopics = cacheService.get<string[]>('renamingTopics')
|
||||
const renamingTopics = cacheService.get('topic.renaming')
|
||||
if (renamingTopics && renamingTopics.includes(topicId)) {
|
||||
cacheService.set(
|
||||
'renamingTopics',
|
||||
'topic.renaming',
|
||||
renamingTopics.filter((id) => id !== topicId)
|
||||
)
|
||||
}
|
||||
|
||||
// 2. 立即添加到 newlyRenamedTopics
|
||||
const currentNewlyRenamed = cacheService.get<string[]>('newlyRenamedTopics') ?? []
|
||||
cacheService.set('newlyRenamedTopics', [...currentNewlyRenamed, topicId])
|
||||
const currentNewlyRenamed = cacheService.get('topic.newly_renamed') ?? []
|
||||
cacheService.set('topic.newly_renamed', [...currentNewlyRenamed, topicId])
|
||||
|
||||
// 3. 延迟从 newlyRenamedTopics 移除
|
||||
setTimeout(() => {
|
||||
const current = cacheService.get<string[]>('newlyRenamedTopics') ?? []
|
||||
const current = cacheService.get('topic.newly_renamed') ?? []
|
||||
cacheService.set(
|
||||
'newlyRenamedTopics',
|
||||
'topic.newly_renamed',
|
||||
current.filter((id) => id !== topicId)
|
||||
)
|
||||
}, 700)
|
||||
|
||||
@ -169,8 +169,8 @@ const AgentSessionInputbarInner: FC<InnerProps> = ({ assistant, agentId, session
|
||||
setText,
|
||||
isEmpty: inputEmpty
|
||||
} = useInputText({
|
||||
initialValue: cacheService.get<string>(draftCacheKey) ?? '',
|
||||
onChange: (value) => cacheService.set(draftCacheKey, value, DRAFT_CACHE_TTL)
|
||||
initialValue: cacheService.getCasual<string>(draftCacheKey) ?? '',
|
||||
onChange: (value) => cacheService.setCasual(draftCacheKey, value, DRAFT_CACHE_TTL)
|
||||
})
|
||||
const {
|
||||
textareaRef,
|
||||
|
||||
@ -57,7 +57,7 @@ const DRAFT_CACHE_TTL = 24 * 60 * 60 * 1000 // 24 hours
|
||||
const getMentionedModelsCacheKey = (assistantId: string) => `inputbar-mentioned-models-${assistantId}`
|
||||
|
||||
const getValidatedCachedModels = (assistantId: string): Model[] => {
|
||||
const cached = cacheService.get<Model[]>(getMentionedModelsCacheKey(assistantId))
|
||||
const cached = cacheService.getCasual<Model[]>(getMentionedModelsCacheKey(assistantId))
|
||||
if (!Array.isArray(cached)) return []
|
||||
return cached.filter((model) => model?.id && model?.name)
|
||||
}
|
||||
@ -135,8 +135,8 @@ const InputbarInner: FC<InputbarInnerProps> = ({ assistant: initialAssistant, se
|
||||
const { setCouldAddImageFile } = useInputbarToolsInternalDispatch()
|
||||
|
||||
const { text, setText } = useInputText({
|
||||
initialValue: cacheService.get<string>(INPUTBAR_DRAFT_CACHE_KEY) ?? '',
|
||||
onChange: (value) => cacheService.set(INPUTBAR_DRAFT_CACHE_KEY, value, DRAFT_CACHE_TTL)
|
||||
initialValue: cacheService.getCasual<string>(INPUTBAR_DRAFT_CACHE_KEY) ?? '',
|
||||
onChange: (value) => cacheService.setCasual(INPUTBAR_DRAFT_CACHE_KEY, value, DRAFT_CACHE_TTL)
|
||||
})
|
||||
const {
|
||||
textareaRef,
|
||||
@ -210,7 +210,7 @@ const InputbarInner: FC<InputbarInnerProps> = ({ assistant: initialAssistant, se
|
||||
}, [canAddImageFile, setCouldAddImageFile])
|
||||
|
||||
const onUnmount = useEffectEvent((id: string) => {
|
||||
cacheService.set(getMentionedModelsCacheKey(id), mentionedModels, DRAFT_CACHE_TTL)
|
||||
cacheService.setCasual(getMentionedModelsCacheKey(id), mentionedModels, DRAFT_CACHE_TTL)
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@ -30,7 +30,7 @@ function CitationBlock({ block }: { block: CitationMessageBlock }) {
|
||||
}, [formattedCitations, block.knowledge, block.memories, hasGeminiBlock])
|
||||
|
||||
const getWebSearchStatusText = (requestId: string) => {
|
||||
const status = cacheService.get('activeSearches')?.[requestId] ?? { phase: 'default' }
|
||||
const status = cacheService.getCasual('activeSearches')?.[requestId] ?? { phase: 'default' }
|
||||
|
||||
switch (status.phase) {
|
||||
case 'fetch_complete':
|
||||
|
||||
@ -61,7 +61,7 @@ export class TokenFluxService {
|
||||
const cacheKey = `tokenflux_models_${this.apiHost}`
|
||||
|
||||
// Check cache first
|
||||
const cachedModels = cacheService.get<TokenFluxModel[]>(cacheKey)
|
||||
const cachedModels = cacheService.getCasual<TokenFluxModel[]>(cacheKey)
|
||||
if (cachedModels) {
|
||||
return cachedModels
|
||||
}
|
||||
@ -79,7 +79,7 @@ export class TokenFluxService {
|
||||
}
|
||||
|
||||
// Cache for 60 minutes (3,600,000 milliseconds)
|
||||
cacheService.set(cacheKey, data.data, 60 * 60 * 1000)
|
||||
cacheService.setCasual(cacheKey, data.data, 60 * 60 * 1000)
|
||||
|
||||
return data.data
|
||||
}
|
||||
|
||||
@ -475,8 +475,8 @@ const MemorySettings = () => {
|
||||
const handleSettingsSubmit = async () => {
|
||||
setSettingsModalVisible(false)
|
||||
await memoryService.updateConfig()
|
||||
if (cacheService.get('memory.wait.settings')) {
|
||||
cacheService.delete('memory.wait.settings')
|
||||
if (cacheService.getCasual<boolean>('memory.wait.settings')) {
|
||||
cacheService.deleteCasual('memory.wait.settings')
|
||||
setGlobalMemoryEnabled(true)
|
||||
}
|
||||
}
|
||||
@ -484,7 +484,7 @@ const MemorySettings = () => {
|
||||
const handleSettingsCancel = () => {
|
||||
setSettingsModalVisible(false)
|
||||
form.resetFields()
|
||||
cacheService.delete('memory.wait.settings')
|
||||
cacheService.deleteCasual('memory.wait.settings')
|
||||
}
|
||||
|
||||
const handleResetMemories = async (userId: string) => {
|
||||
@ -550,7 +550,7 @@ const MemorySettings = () => {
|
||||
|
||||
const handleGlobalMemoryToggle = async (enabled: boolean) => {
|
||||
if (enabled && !embedderModel) {
|
||||
cacheService.set('memory.wait.settings', true)
|
||||
cacheService.setCasual('memory.wait.settings', true)
|
||||
return setSettingsModalVisible(true)
|
||||
}
|
||||
|
||||
|
||||
@ -28,7 +28,7 @@ export const getAgentsFromSystemAgents = (systemAgents: any) => {
|
||||
export function useSystemAssistantPresets() {
|
||||
const { defaultAgent: defaultPreset } = useSettings()
|
||||
const [presets, setPresets] = useState<AssistantPreset[]>([])
|
||||
const resourcesPath = cacheService.get('resourcesPath') ?? ''
|
||||
const resourcesPath = cacheService.get('app.path.resources') ?? ''
|
||||
const { agentssubscribeUrl } = store.getState().settings
|
||||
const { i18n } = useTranslation()
|
||||
const currentLanguage = i18n.language
|
||||
|
||||
@ -39,16 +39,16 @@ export default abstract class BaseWebSearchProvider {
|
||||
return keys[0]
|
||||
}
|
||||
|
||||
const lastUsedKey = cacheService.getShared(keyName) as string | undefined
|
||||
const lastUsedKey = cacheService.getSharedCasual<string>(keyName)
|
||||
if (lastUsedKey === undefined) {
|
||||
cacheService.setShared(keyName, keys[0])
|
||||
cacheService.setSharedCasual(keyName, keys[0])
|
||||
return keys[0]
|
||||
}
|
||||
|
||||
const currentIndex = keys.indexOf(lastUsedKey)
|
||||
const nextIndex = (currentIndex + 1) % keys.length
|
||||
const nextKey = keys[nextIndex]
|
||||
cacheService.setShared(keyName, nextKey)
|
||||
cacheService.setSharedCasual(keyName, nextKey)
|
||||
|
||||
return nextKey
|
||||
}
|
||||
|
||||
@ -468,9 +468,9 @@ function getRotatedApiKey(provider: Provider): string {
|
||||
return keys[0]
|
||||
}
|
||||
|
||||
const lastUsedKey = cacheService.get(keyName) as string
|
||||
const lastUsedKey = cacheService.getCasual<string>(keyName)
|
||||
if (!lastUsedKey) {
|
||||
cacheService.set(keyName, keys[0])
|
||||
cacheService.setCasual(keyName, keys[0])
|
||||
return keys[0]
|
||||
}
|
||||
|
||||
@ -486,7 +486,7 @@ function getRotatedApiKey(provider: Provider): string {
|
||||
|
||||
const nextIndex = (currentIndex + 1) % keys.length
|
||||
const nextKey = keys[nextIndex]
|
||||
cacheService.set(keyName, nextKey)
|
||||
cacheService.setCasual(keyName, nextKey)
|
||||
|
||||
return nextKey
|
||||
}
|
||||
|
||||
@ -81,7 +81,7 @@ class FileManager {
|
||||
const file = await db.files.get(id)
|
||||
|
||||
if (file) {
|
||||
const filesPath = cacheService.get('filesPath') ?? ''
|
||||
const filesPath = cacheService.get('app.path.files') ?? ''
|
||||
file.path = filesPath + '/' + file.id + file.ext
|
||||
}
|
||||
|
||||
@ -89,7 +89,7 @@ class FileManager {
|
||||
}
|
||||
|
||||
static getFilePath(file: FileMetadata) {
|
||||
const filesPath = cacheService.get('filesPath') ?? ''
|
||||
const filesPath = cacheService.get('app.path.files') ?? ''
|
||||
return filesPath + '/' + file.id + file.ext
|
||||
}
|
||||
|
||||
@ -137,7 +137,7 @@ class FileManager {
|
||||
}
|
||||
|
||||
static getFileUrl(file: FileMetadata) {
|
||||
const filesPath = cacheService.get('filesPath') ?? ''
|
||||
const filesPath = cacheService.get('app.path.files') ?? ''
|
||||
return 'file://' + filesPath + '/' + file.name
|
||||
}
|
||||
|
||||
|
||||
@ -104,7 +104,7 @@ export class MemoryProcessor {
|
||||
if (!memoryConfig.llmApiClient) {
|
||||
throw new Error('No LLM model configured for memory processing')
|
||||
}
|
||||
const existingMemoriesResult = (cacheService.get(`memory-search-${lastMessageId}`) as MemoryItem[]) || []
|
||||
const existingMemoriesResult = cacheService.getCasual<MemoryItem[]>(`memory-search-${lastMessageId}`) || []
|
||||
|
||||
const existingMemories = existingMemoriesResult.map((memory) => ({
|
||||
id: memory.id,
|
||||
|
||||
@ -27,16 +27,16 @@ export abstract class OcrBaseApiClient {
|
||||
return keys[0]
|
||||
}
|
||||
|
||||
const lastUsedKey = cacheService.getShared(keyName) as string | undefined
|
||||
const lastUsedKey = cacheService.getSharedCasual<string>(keyName)
|
||||
if (lastUsedKey === undefined) {
|
||||
cacheService.setShared(keyName, keys[0])
|
||||
cacheService.setSharedCasual(keyName, keys[0])
|
||||
return keys[0]
|
||||
}
|
||||
|
||||
const currentIndex = keys.indexOf(lastUsedKey)
|
||||
const nextIndex = (currentIndex + 1) % keys.length
|
||||
const nextKey = keys[nextIndex]
|
||||
cacheService.setShared(keyName, nextKey)
|
||||
cacheService.setSharedCasual(keyName, nextKey)
|
||||
|
||||
return nextKey
|
||||
}
|
||||
|
||||
@ -1097,8 +1097,8 @@ export const resendMessageThunk =
|
||||
// Clear cached search results for the user message being resent
|
||||
// This ensures that the regenerated responses will not use stale search results
|
||||
try {
|
||||
cacheService.delete(`web-search-${userMessageToResend.id}`)
|
||||
cacheService.delete(`knowledge-search-${userMessageToResend.id}`)
|
||||
cacheService.deleteCasual(`web-search-${userMessageToResend.id}`)
|
||||
cacheService.deleteCasual(`knowledge-search-${userMessageToResend.id}`)
|
||||
} catch (error) {
|
||||
logger.warn(`Failed to clear keyv cache for message ${userMessageToResend.id}:`, error as Error)
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user