generated from dellevin/template
去掉记忆功能
This commit is contained in:
@@ -1,9 +0,0 @@
|
||||
package fuck.andes.agent.memory
|
||||
|
||||
data class MemoryContext(
|
||||
val memories: List<String> = emptyList(),
|
||||
) {
|
||||
companion object {
|
||||
val EMPTY = MemoryContext()
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
package fuck.andes.agent.model
|
||||
|
||||
import fuck.andes.agent.memory.MemoryContext
|
||||
import fuck.andes.agent.skill.SkillContext
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
@@ -103,25 +102,4 @@ internal object AgentPromptBuilder {
|
||||
JSONObject()
|
||||
.put("role", "system")
|
||||
.put("content", content)
|
||||
|
||||
private const val MEMORY_CHAR_BUDGET = 1500
|
||||
|
||||
fun buildMemoryAppendText(memoryContext: MemoryContext): String? {
|
||||
val budgeted = budgetedMemories(memoryContext.memories, MEMORY_CHAR_BUDGET)
|
||||
if (budgeted.isEmpty()) return null
|
||||
return "\u3010\u8bb0\u5fc6\u3011\n" + budgeted.joinToString("\n") { "- $it" } +
|
||||
"\n\n\u4ee5\u4e0a\u662f\u5173\u4e8e\u7528\u6237\u7684\u5df2\u77e5\u4fe1\u606f\uff0c\u5728\u76f8\u5173\u65f6\u53c2\u8003\uff0c\u4f46\u4e0d\u8981\u4e3b\u52a8\u63d0\u53ca\u300c\u8bb0\u5fc6\u300d\u3002"
|
||||
}
|
||||
|
||||
private fun budgetedMemories(memories: List<String>, budget: Int): List<String> {
|
||||
val result = mutableListOf<String>()
|
||||
var remaining = budget
|
||||
for (memory in memories) {
|
||||
val line = "- $memory\n"
|
||||
if (line.length > remaining) break
|
||||
result.add(memory)
|
||||
remaining -= line.length
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,9 +18,8 @@ import androidx.room.migration.Migration
|
||||
RuntimeArchiveRunEntity::class,
|
||||
RuntimeArchiveEventEntity::class,
|
||||
SkillRegistryEntity::class,
|
||||
MemoryEntity::class,
|
||||
],
|
||||
version = 13,
|
||||
version = 14,
|
||||
exportSchema = false,
|
||||
)
|
||||
internal abstract class FuckAndesDatabase : RoomDatabase() {
|
||||
@@ -28,7 +27,6 @@ internal abstract class FuckAndesDatabase : RoomDatabase() {
|
||||
abstract fun providerDao(): ProviderDao
|
||||
abstract fun runtimeRunDao(): RuntimeRunDao
|
||||
abstract fun skillDao(): SkillDao
|
||||
abstract fun memoryDao(): MemoryDao
|
||||
|
||||
companion object {
|
||||
@Volatile
|
||||
@@ -41,7 +39,7 @@ internal abstract class FuckAndesDatabase : RoomDatabase() {
|
||||
FuckAndesDatabase::class.java,
|
||||
"fuck_andes.db",
|
||||
)
|
||||
.addMigrations(MIGRATION_6_7, MIGRATION_7_8, MIGRATION_8_9, MIGRATION_9_10, MIGRATION_10_11, MIGRATION_11_12, MIGRATION_12_13)
|
||||
.addMigrations(MIGRATION_6_7, MIGRATION_7_8, MIGRATION_8_9, MIGRATION_9_10, MIGRATION_10_11, MIGRATION_11_12, MIGRATION_12_13, MIGRATION_13_14)
|
||||
.fallbackToDestructiveMigration(dropAllTables = true)
|
||||
.build()
|
||||
.also { instance = it }
|
||||
@@ -163,5 +161,9 @@ internal abstract class FuckAndesDatabase : RoomDatabase() {
|
||||
internal val MIGRATION_12_13 = Migration(12, 13) { database ->
|
||||
database.execSQL("ALTER TABLE memories ADD COLUMN priority INTEGER NOT NULL DEFAULT 0")
|
||||
}
|
||||
|
||||
internal val MIGRATION_13_14 = Migration(13, 14) { database ->
|
||||
database.execSQL("DROP TABLE IF EXISTS memories")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
package fuck.andes.data.db
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
|
||||
@Dao
|
||||
internal interface MemoryDao {
|
||||
@Query("SELECT * FROM memories WHERE scope = 'global' AND enabled = 1 ORDER BY priority ASC, updated_at DESC")
|
||||
suspend fun enabledGlobalMemories(): List<MemoryEntity>
|
||||
|
||||
@Query("SELECT * FROM memories WHERE scope = :scope ORDER BY priority ASC, updated_at DESC")
|
||||
suspend fun memoriesByScope(scope: String): List<MemoryEntity>
|
||||
|
||||
@Query("SELECT * FROM memories ORDER BY priority ASC, updated_at DESC")
|
||||
suspend fun allMemories(): List<MemoryEntity>
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun upsert(memory: MemoryEntity)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun upsertAll(memories: List<MemoryEntity>)
|
||||
|
||||
@Query("UPDATE memories SET enabled = :enabled WHERE id = :id")
|
||||
suspend fun setEnabled(id: String, enabled: Boolean)
|
||||
|
||||
@Query("DELETE FROM memories WHERE id = :id")
|
||||
suspend fun delete(id: String)
|
||||
|
||||
@Query("SELECT COUNT(*) FROM memories WHERE scope = 'global' AND enabled = 1")
|
||||
suspend fun enabledGlobalCount(): Int
|
||||
|
||||
@Query("UPDATE memories SET priority = :priority WHERE id = :id")
|
||||
suspend fun setPriority(id: String, priority: Int)
|
||||
|
||||
@Query("SELECT MAX(priority) FROM memories WHERE scope = :scope")
|
||||
suspend fun maxPriority(scope: String): Int?
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package fuck.andes.data.db
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.Index
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity(
|
||||
tableName = "memories",
|
||||
indices = [
|
||||
Index("scope"),
|
||||
],
|
||||
)
|
||||
internal data class MemoryEntity(
|
||||
@PrimaryKey val id: String,
|
||||
val content: String,
|
||||
val scope: String,
|
||||
val enabled: Boolean = true,
|
||||
@ColumnInfo(name = "priority") val priority: Int = 0,
|
||||
@ColumnInfo(name = "created_at") val createdAt: Long,
|
||||
@ColumnInfo(name = "updated_at") val updatedAt: Long,
|
||||
)
|
||||
@@ -1,51 +0,0 @@
|
||||
package fuck.andes.data.repository
|
||||
|
||||
import android.content.Context
|
||||
import fuck.andes.data.db.FuckAndesDatabase
|
||||
import fuck.andes.data.db.MemoryEntity
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
|
||||
internal object MemoryRepository {
|
||||
private val globalCache = AtomicReference<List<MemoryEntity>>(null)
|
||||
|
||||
suspend fun loadGlobalMemories(context: Context): List<MemoryEntity> {
|
||||
globalCache.get()?.let { return it }
|
||||
val memories = FuckAndesDatabase.get(context).memoryDao().enabledGlobalMemories()
|
||||
globalCache.set(memories)
|
||||
return memories
|
||||
}
|
||||
|
||||
fun invalidateGlobalCache() { globalCache.set(null) }
|
||||
|
||||
suspend fun upsert(context: Context, memory: MemoryEntity) {
|
||||
FuckAndesDatabase.get(context).memoryDao().upsert(memory)
|
||||
invalidateGlobalCache()
|
||||
}
|
||||
|
||||
suspend fun delete(context: Context, id: String) {
|
||||
FuckAndesDatabase.get(context).memoryDao().delete(id)
|
||||
invalidateGlobalCache()
|
||||
}
|
||||
|
||||
suspend fun setEnabled(context: Context, id: String, enabled: Boolean) {
|
||||
FuckAndesDatabase.get(context).memoryDao().setEnabled(id, enabled)
|
||||
invalidateGlobalCache()
|
||||
}
|
||||
|
||||
suspend fun allMemoriesForManagement(context: Context, scope: String): List<MemoryEntity> =
|
||||
FuckAndesDatabase.get(context).memoryDao().memoriesByScope(scope)
|
||||
|
||||
suspend fun enabledGlobalCount(context: Context): Int =
|
||||
FuckAndesDatabase.get(context).memoryDao().enabledGlobalCount()
|
||||
|
||||
suspend fun setPriority(context: Context, id: String, priority: Int) {
|
||||
FuckAndesDatabase.get(context).memoryDao().setPriority(id, priority)
|
||||
invalidateGlobalCache()
|
||||
}
|
||||
|
||||
suspend fun maxPriority(context: Context, scope: String): Int? =
|
||||
FuckAndesDatabase.get(context).memoryDao().maxPriority(scope)
|
||||
|
||||
suspend fun totalEnabledCharCount(context: Context): Int =
|
||||
loadGlobalMemories(context).sumOf { it.content.length }
|
||||
}
|
||||
@@ -33,7 +33,6 @@ import androidx.lifecycle.compose.LocalLifecycleOwner
|
||||
import fuck.andes.FuckAndesApp
|
||||
import fuck.andes.agent.accessibility.AgentAccessibilityService
|
||||
import fuck.andes.config.Prefs
|
||||
import fuck.andes.data.repository.MemoryRepository
|
||||
import fuck.andes.data.repository.ProviderRepository
|
||||
import fuck.andes.data.repository.RuntimeConfigRepository
|
||||
import fuck.andes.ui.components.MiuixBackButton
|
||||
@@ -237,18 +236,6 @@ internal fun SettingsScreen(
|
||||
onClick = { onNavigate(AppRoute.Skills) },
|
||||
)
|
||||
PrefDivider()
|
||||
ArrowPreference(
|
||||
title = "记忆管理",
|
||||
summary = rememberMemorySummary(context),
|
||||
startAction = {
|
||||
TintedIcon(
|
||||
icon = LucideR.drawable.lucide_ic_brain,
|
||||
tint = ColorOSRoyalBlue,
|
||||
)
|
||||
},
|
||||
onClick = { onNavigate(AppRoute.Memory) },
|
||||
)
|
||||
PrefDivider()
|
||||
ArrowPreference(
|
||||
title = "权限健康",
|
||||
startAction = {
|
||||
@@ -498,19 +485,3 @@ private fun isAgentAccessibilityEnabled(context: Context): Boolean {
|
||||
).orEmpty()
|
||||
return enabledServices.split(':').any { it.equals(expected, ignoreCase = true) }
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun rememberMemorySummary(context: Context): String {
|
||||
var summary by remember { mutableStateOf("AI 跨会话记忆用户偏好与事实") }
|
||||
LaunchedEffect(Unit) {
|
||||
val result = withContext(Dispatchers.IO) {
|
||||
runCatching {
|
||||
val count = MemoryRepository.enabledGlobalCount(context)
|
||||
val chars = MemoryRepository.totalEnabledCharCount(context)
|
||||
"$count 条记忆 / $chars / 1500 字预算"
|
||||
}.getOrElse { "AI 跨会话记忆用户偏好与事实" }
|
||||
}
|
||||
summary = result
|
||||
}
|
||||
return summary
|
||||
}
|
||||
|
||||
@@ -60,8 +60,6 @@ import fuck.andes.ui.model.AgentSkillsAction
|
||||
import fuck.andes.ui.model.AgentSystemEnhanceAction
|
||||
import fuck.andes.ui.model.AgentToolsAction
|
||||
import fuck.andes.ui.model.ConversationSearchAction
|
||||
import fuck.andes.ui.model.MemoryDetailAction
|
||||
import fuck.andes.ui.model.MemoryManagementAction
|
||||
import fuck.andes.ui.model.PermissionHealthAction
|
||||
import fuck.andes.ui.model.RunReplayAction
|
||||
import fuck.andes.ui.navigation.AgentNavigator
|
||||
@@ -72,8 +70,6 @@ import fuck.andes.ui.screens.enhance.SystemEnhanceScreen
|
||||
import fuck.andes.ui.screens.home.AgentHomeScreen
|
||||
import fuck.andes.ui.screens.permissions.PermissionHealthScreen
|
||||
import fuck.andes.ui.screens.search.ConversationSearchScreen
|
||||
import fuck.andes.ui.screens.memory.MemoryDetailScreen
|
||||
import fuck.andes.ui.screens.memory.MemoryManagementScreen
|
||||
import fuck.andes.ui.screens.replay.RunReplayScreen
|
||||
import fuck.andes.ui.screens.skills.AgentSkillsScreen
|
||||
import fuck.andes.ui.screens.terminal.LinuxEnvironmentScreen
|
||||
@@ -383,58 +379,6 @@ fun AgentAppRoot() {
|
||||
},
|
||||
)
|
||||
}
|
||||
entry<AppRoute.Memory> {
|
||||
LaunchedEffect(Unit) {
|
||||
agentState.refreshMemories()
|
||||
}
|
||||
MemoryManagementScreen(
|
||||
state = agentState.memoryManagementState,
|
||||
onAction = { action ->
|
||||
when (action) {
|
||||
MemoryManagementAction.NavigateBack -> popRoute()
|
||||
is MemoryManagementAction.ToggleMemory -> agentState.toggleMemory(action.memoryId, action.enabled)
|
||||
is MemoryManagementAction.DeleteMemory -> agentState.deleteMemory(action.memoryId)
|
||||
is MemoryManagementAction.EditMemory -> agentState.editMemory(action.memoryId, action.content)
|
||||
is MemoryManagementAction.AddMemory -> agentState.addMemory(action.content)
|
||||
is MemoryManagementAction.OpenMemoryDetail -> pushRoute(AppRoute.MemoryDetail(action.memoryId))
|
||||
MemoryManagementAction.OpenMemoryNew -> pushRoute(AppRoute.MemoryDetail(null))
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
entry<AppRoute.MemoryDetail> { route ->
|
||||
val memory = if (route.memoryId != null) {
|
||||
agentState.memoryManagementState.memories.find { it.id == route.memoryId }
|
||||
} else {
|
||||
agentState.draftNewMemory
|
||||
}
|
||||
if (memory != null) {
|
||||
MemoryDetailScreen(
|
||||
memory = memory,
|
||||
isNew = route.memoryId == null,
|
||||
onAction = { action ->
|
||||
when (action) {
|
||||
MemoryDetailAction.NavigateBack -> popRoute()
|
||||
is MemoryDetailAction.Save -> {
|
||||
agentState.editMemory(route.memoryId!!, action.content)
|
||||
}
|
||||
is MemoryDetailAction.SaveNew -> {
|
||||
agentState.addMemory(action.content)
|
||||
}
|
||||
MemoryDetailAction.Delete -> {
|
||||
agentState.deleteMemory(route.memoryId!!)
|
||||
}
|
||||
MemoryDetailAction.MoveUp -> {
|
||||
agentState.moveMemoryUp(route.memoryId!!)
|
||||
}
|
||||
MemoryDetailAction.MoveDown -> {
|
||||
agentState.moveMemoryDown(route.memoryId!!)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
entry<AppRoute.ConversationSearch> { route ->
|
||||
ConversationSearchScreen(
|
||||
state = agentState.conversationSearchState,
|
||||
|
||||
@@ -146,10 +146,8 @@ private fun titleForRoute(route: AppRoute?): String = when (route) {
|
||||
is AppRoute.Settings -> "设置"
|
||||
is AppRoute.LinuxEnvironment -> "Linux 工具环境"
|
||||
is AppRoute.ModelProviders -> "模型提供商"
|
||||
is AppRoute.Memory -> "记忆管理"
|
||||
is AppRoute.ConversationSearch -> "搜索对话"
|
||||
is AppRoute.RunReplay -> "执行回放"
|
||||
is AppRoute.MemoryDetail -> if (route.memoryId == null) "添加记忆" else "编辑记忆"
|
||||
is AppRoute.ModelProviderDetail -> route.providerId.let { "Provider 详情" }
|
||||
is AppRoute.ModelProviderNew -> "新建提供商"
|
||||
null -> "Eta"
|
||||
|
||||
@@ -15,7 +15,6 @@ import fuck.andes.FuckAndesApp
|
||||
import fuck.andes.agent.accessibility.AgentAccessibilityService
|
||||
import fuck.andes.agent.device.DeviceLocationProvider
|
||||
import fuck.andes.agent.media.AgentImageCodec
|
||||
import fuck.andes.agent.memory.MemoryContext
|
||||
import fuck.andes.agent.runtime.AgentEvent
|
||||
import fuck.andes.agent.runtime.AgentExternalArchivePayload
|
||||
import fuck.andes.agent.runtime.AgentRunArchiveStore
|
||||
@@ -30,8 +29,6 @@ import fuck.andes.config.Prefs
|
||||
import fuck.andes.core.AndroidAgentLogger
|
||||
import fuck.andes.core.safeLogType
|
||||
import fuck.andes.data.db.FuckAndesDatabase
|
||||
import fuck.andes.data.db.MemoryEntity
|
||||
import fuck.andes.data.repository.MemoryRepository
|
||||
import fuck.andes.data.repository.RuntimeConfigRepository
|
||||
import fuck.andes.ui.model.AgentChatHomeUiState
|
||||
import fuck.andes.ui.model.AgentChatMessageUi
|
||||
@@ -50,8 +47,6 @@ import fuck.andes.ui.model.ConversationSearchMatchUi
|
||||
import fuck.andes.ui.model.ConversationSearchUiState
|
||||
import fuck.andes.ui.model.ConversationPaneUiState
|
||||
import fuck.andes.ui.model.ConversationSummaryUi
|
||||
import fuck.andes.ui.model.MemoryItemUi
|
||||
import fuck.andes.ui.model.MemoryManagementUiState
|
||||
import fuck.andes.ui.model.PermissionHealthItemUi
|
||||
import fuck.andes.ui.model.PermissionHealthUiState
|
||||
import fuck.andes.ui.model.PermissionStatusUi
|
||||
@@ -140,20 +135,6 @@ internal class AgentAppState(
|
||||
var systemEnhanceState by mutableStateOf(buildSystemEnhanceState())
|
||||
private set
|
||||
|
||||
var memoryManagementState by mutableStateOf(MemoryManagementUiState(isLoading = true))
|
||||
private set
|
||||
|
||||
val draftNewMemory = MemoryItemUi(
|
||||
id = "new",
|
||||
content = "",
|
||||
enabled = true,
|
||||
createdAt = 0L,
|
||||
priority = 0,
|
||||
charCount = 0,
|
||||
)
|
||||
|
||||
private var cachedGlobalMemoryContents: List<String> = emptyList()
|
||||
|
||||
init {
|
||||
refreshConversationSummaries()
|
||||
runtimeRecoveryInProgress.set(true)
|
||||
@@ -404,15 +385,7 @@ internal class AgentAppState(
|
||||
terminalTools = remoteBooleanForUi(Prefs.Keys.AGENT_TERMINAL_TOOLS),
|
||||
browserTools = remoteBooleanForUi(Prefs.Keys.AGENT_BROWSER_TOOLS),
|
||||
thinkingEnabled = thinkingEnabled,
|
||||
)?.let { cfg ->
|
||||
val memoryContext = MemoryContext(memories = cachedGlobalMemoryContents)
|
||||
val memoryAppendText = AgentPromptBuilder.buildMemoryAppendText(memoryContext)
|
||||
if (memoryAppendText != null) {
|
||||
cfg.copy(systemPrompt = cfg.systemPrompt + "\n\n" + memoryAppendText)
|
||||
} else {
|
||||
cfg
|
||||
}
|
||||
}
|
||||
)
|
||||
if (config == null) {
|
||||
withContext(Dispatchers.Main) {
|
||||
applyRunResult(
|
||||
@@ -516,101 +489,6 @@ internal class AgentAppState(
|
||||
permissionHealthState = buildPermissionHealthState(appContext, mediaProjectionGranted)
|
||||
}
|
||||
|
||||
fun refreshMemories() {
|
||||
scope.launch(Dispatchers.IO) {
|
||||
val memories = runCatching {
|
||||
MemoryRepository.allMemoriesForManagement(appContext, "global")
|
||||
}.getOrDefault(emptyList())
|
||||
val totalEnabledChars = memories.filter { it.enabled }.sumOf { it.content.length }
|
||||
memoryManagementState = MemoryManagementUiState(
|
||||
memories = memories.map { it.toItemUi() },
|
||||
isLoading = false,
|
||||
totalEnabledChars = totalEnabledChars,
|
||||
)
|
||||
refreshMemoryCache()
|
||||
}
|
||||
}
|
||||
|
||||
fun toggleMemory(memoryId: String, enabled: Boolean) {
|
||||
scope.launch(Dispatchers.IO) {
|
||||
val item = memoryManagementState.memories.find { it.id == memoryId } ?: return@launch
|
||||
MemoryRepository.setEnabled(appContext, memoryId, enabled)
|
||||
refreshMemories()
|
||||
}
|
||||
}
|
||||
|
||||
fun deleteMemory(memoryId: String) {
|
||||
scope.launch(Dispatchers.IO) {
|
||||
MemoryRepository.delete(appContext, memoryId)
|
||||
refreshMemories()
|
||||
}
|
||||
}
|
||||
|
||||
fun editMemory(memoryId: String, content: String) {
|
||||
scope.launch(Dispatchers.IO) {
|
||||
val item = memoryManagementState.memories.find { it.id == memoryId } ?: return@launch
|
||||
val now = System.currentTimeMillis()
|
||||
MemoryRepository.upsert(appContext, MemoryEntity(
|
||||
id = memoryId,
|
||||
content = content,
|
||||
scope = "global",
|
||||
enabled = item.enabled,
|
||||
priority = item.priority,
|
||||
createdAt = item.createdAt,
|
||||
updatedAt = now,
|
||||
))
|
||||
refreshMemories()
|
||||
}
|
||||
}
|
||||
|
||||
fun addMemory(content: String) {
|
||||
scope.launch(Dispatchers.IO) {
|
||||
val now = System.currentTimeMillis()
|
||||
val maxP = MemoryRepository.maxPriority(appContext, "global") ?: -1
|
||||
MemoryRepository.upsert(appContext, MemoryEntity(
|
||||
id = java.util.UUID.randomUUID().toString(),
|
||||
content = content,
|
||||
scope = "global",
|
||||
enabled = true,
|
||||
priority = maxP + 1,
|
||||
createdAt = now,
|
||||
updatedAt = now,
|
||||
))
|
||||
refreshMemories()
|
||||
}
|
||||
}
|
||||
|
||||
fun moveMemoryUp(memoryId: String) {
|
||||
val memories = memoryManagementState.memories
|
||||
val index = memories.indexOfFirst { it.id == memoryId }
|
||||
if (index <= 0) return
|
||||
val current = memories[index]
|
||||
val above = memories[index - 1]
|
||||
scope.launch(Dispatchers.IO) {
|
||||
MemoryRepository.setPriority(appContext, current.id, above.priority)
|
||||
MemoryRepository.setPriority(appContext, above.id, current.priority)
|
||||
refreshMemories()
|
||||
}
|
||||
}
|
||||
|
||||
fun moveMemoryDown(memoryId: String) {
|
||||
val memories = memoryManagementState.memories
|
||||
val index = memories.indexOfFirst { it.id == memoryId }
|
||||
if (index < 0 || index >= memories.lastIndex) return
|
||||
val current = memories[index]
|
||||
val below = memories[index + 1]
|
||||
scope.launch(Dispatchers.IO) {
|
||||
MemoryRepository.setPriority(appContext, current.id, below.priority)
|
||||
MemoryRepository.setPriority(appContext, below.id, current.priority)
|
||||
refreshMemories()
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun refreshMemoryCache() {
|
||||
val globalContents = MemoryRepository.loadGlobalMemories(appContext).map { it.content }
|
||||
cachedGlobalMemoryContents = globalContents
|
||||
}
|
||||
|
||||
var pendingShareIntent by mutableStateOf<Intent?>(null)
|
||||
private set
|
||||
|
||||
@@ -1803,12 +1681,6 @@ private fun buildSystemEnhanceState(): AgentSystemEnhanceUiState =
|
||||
id = "future",
|
||||
title = "后续能力",
|
||||
items = listOf(
|
||||
SystemEnhanceItemUi(
|
||||
id = "memory",
|
||||
title = "记忆系统",
|
||||
summary = "长期记忆和定时触发器后续接入",
|
||||
status = SystemEnhanceStatusUi.Inactive,
|
||||
),
|
||||
SystemEnhanceItemUi(
|
||||
id = "hook",
|
||||
title = "Hook 二级能力",
|
||||
@@ -1847,15 +1719,6 @@ private fun isRootAvailable(): Boolean {
|
||||
}
|
||||
}
|
||||
|
||||
private fun MemoryEntity.toItemUi() = MemoryItemUi(
|
||||
id = id,
|
||||
content = content,
|
||||
enabled = enabled,
|
||||
createdAt = createdAt,
|
||||
priority = priority,
|
||||
charCount = content.length,
|
||||
)
|
||||
|
||||
private fun hasAppListAccess(context: Context): Boolean {
|
||||
return try {
|
||||
val pm = context.packageManager
|
||||
|
||||
@@ -53,25 +53,6 @@ sealed interface AgentSystemEnhanceAction {
|
||||
data class ToggleItem(val itemId: String) : AgentSystemEnhanceAction
|
||||
}
|
||||
|
||||
sealed interface MemoryManagementAction {
|
||||
data object NavigateBack : MemoryManagementAction
|
||||
data class ToggleMemory(val memoryId: String, val enabled: Boolean) : MemoryManagementAction
|
||||
data class DeleteMemory(val memoryId: String) : MemoryManagementAction
|
||||
data class EditMemory(val memoryId: String, val content: String) : MemoryManagementAction
|
||||
data class AddMemory(val content: String) : MemoryManagementAction
|
||||
data class OpenMemoryDetail(val memoryId: String) : MemoryManagementAction
|
||||
data object OpenMemoryNew : MemoryManagementAction
|
||||
}
|
||||
|
||||
sealed interface MemoryDetailAction {
|
||||
data object NavigateBack : MemoryDetailAction
|
||||
data class Save(val content: String) : MemoryDetailAction
|
||||
data class SaveNew(val content: String) : MemoryDetailAction
|
||||
data object Delete : MemoryDetailAction
|
||||
data object MoveUp : MemoryDetailAction
|
||||
data object MoveDown : MemoryDetailAction
|
||||
}
|
||||
|
||||
sealed interface ConversationSearchAction {
|
||||
data object NavigateBack : ConversationSearchAction
|
||||
data class QueryChanged(val query: String) : ConversationSearchAction
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
package fuck.andes.ui.model
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
|
||||
@Immutable
|
||||
data class MemoryManagementUiState(
|
||||
val memories: List<MemoryItemUi> = emptyList(),
|
||||
val isLoading: Boolean = false,
|
||||
val totalEnabledChars: Int = 0,
|
||||
val charBudget: Int = 1500,
|
||||
)
|
||||
|
||||
@Immutable
|
||||
data class MemoryItemUi(
|
||||
val id: String,
|
||||
val content: String,
|
||||
val enabled: Boolean,
|
||||
val createdAt: Long,
|
||||
val priority: Int = 0,
|
||||
val charCount: Int = 0,
|
||||
)
|
||||
@@ -13,8 +13,6 @@ sealed interface AppRoute : NavKey {
|
||||
data object Settings : AppRoute
|
||||
data object LinuxEnvironment : AppRoute
|
||||
data object ModelProviders : AppRoute
|
||||
data object Memory : AppRoute
|
||||
data class MemoryDetail(val memoryId: String?) : AppRoute
|
||||
data class ConversationSearch(val initialQuery: String = "") : AppRoute
|
||||
data class RunReplay(val runId: String) : AppRoute
|
||||
data class ModelProviderDetail(val providerId: String) : AppRoute
|
||||
|
||||
@@ -1,140 +0,0 @@
|
||||
package fuck.andes.ui.screens.memory
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import fuck.andes.ui.components.MiuixScaffoldPage
|
||||
import fuck.andes.ui.components.PrefDivider
|
||||
import fuck.andes.ui.model.MemoryDetailAction
|
||||
import fuck.andes.ui.model.MemoryItemUi
|
||||
import top.yukonga.miuix.kmp.basic.BasicComponent
|
||||
import top.yukonga.miuix.kmp.basic.Button
|
||||
import top.yukonga.miuix.kmp.basic.ButtonDefaults
|
||||
import top.yukonga.miuix.kmp.basic.Card
|
||||
import top.yukonga.miuix.kmp.basic.SmallTitle
|
||||
import top.yukonga.miuix.kmp.basic.Text
|
||||
import top.yukonga.miuix.kmp.basic.TextButton
|
||||
import top.yukonga.miuix.kmp.basic.TextField
|
||||
import top.yukonga.miuix.kmp.theme.MiuixTheme
|
||||
import top.yukonga.miuix.kmp.window.WindowDialog
|
||||
|
||||
@Composable
|
||||
fun MemoryDetailScreen(
|
||||
memory: MemoryItemUi,
|
||||
isNew: Boolean,
|
||||
onAction: (MemoryDetailAction) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
var content by remember(memory.id) { mutableStateOf(memory.content) }
|
||||
var showDeleteConfirm by remember { mutableStateOf(false) }
|
||||
val hasChanged = content.trim() != memory.content
|
||||
|
||||
MiuixScaffoldPage(
|
||||
title = if (isNew) "添加记忆" else "编辑记忆",
|
||||
onBack = { onAction(MemoryDetailAction.NavigateBack) },
|
||||
modifier = modifier,
|
||||
) {
|
||||
item(key = "section_content") {
|
||||
SmallTitle("内容")
|
||||
}
|
||||
item(key = "card_content") {
|
||||
Card(modifier = Modifier.padding(horizontal = 12.dp)) {
|
||||
TextField(
|
||||
value = content,
|
||||
onValueChange = { content = it },
|
||||
maxLines = 15,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp, vertical = 12.dp),
|
||||
)
|
||||
Text(
|
||||
text = "${content.length} 字",
|
||||
fontSize = MiuixTheme.textStyles.body2.fontSize,
|
||||
color = if (content.length > 1500) MiuixTheme.colorScheme.error
|
||||
else MiuixTheme.colorScheme.onSurfaceVariantSummary,
|
||||
modifier = Modifier.padding(horizontal = 16.dp, vertical = 4.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
item(key = "section_actions") {
|
||||
SmallTitle("操作")
|
||||
}
|
||||
item(key = "card_actions") {
|
||||
Card(modifier = Modifier.padding(horizontal = 12.dp)) {
|
||||
if (hasChanged || isNew) {
|
||||
BasicComponent(
|
||||
title = if (isNew) "添加" else "保存",
|
||||
insideMargin = PaddingValues(16.dp),
|
||||
onClick = {
|
||||
if (isNew) {
|
||||
onAction(MemoryDetailAction.SaveNew(content.trim()))
|
||||
} else {
|
||||
onAction(MemoryDetailAction.Save(content.trim()))
|
||||
}
|
||||
onAction(MemoryDetailAction.NavigateBack)
|
||||
},
|
||||
)
|
||||
PrefDivider()
|
||||
}
|
||||
if (!isNew) {
|
||||
BasicComponent(
|
||||
title = "上移",
|
||||
insideMargin = PaddingValues(16.dp),
|
||||
onClick = { onAction(MemoryDetailAction.MoveUp) },
|
||||
)
|
||||
PrefDivider()
|
||||
BasicComponent(
|
||||
title = "下移",
|
||||
insideMargin = PaddingValues(16.dp),
|
||||
onClick = { onAction(MemoryDetailAction.MoveDown) },
|
||||
)
|
||||
PrefDivider()
|
||||
BasicComponent(
|
||||
title = "删除",
|
||||
insideMargin = PaddingValues(16.dp),
|
||||
onClick = { showDeleteConfirm = true },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (showDeleteConfirm) {
|
||||
WindowDialog(
|
||||
show = true,
|
||||
title = "删除后,该记忆将不可恢复",
|
||||
onDismissRequest = { showDeleteConfirm = false },
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Button(
|
||||
onClick = {
|
||||
onAction(MemoryDetailAction.Delete)
|
||||
onAction(MemoryDetailAction.NavigateBack)
|
||||
},
|
||||
colors = ButtonDefaults.buttonColorsPrimary(),
|
||||
) {
|
||||
Text("删除该记忆")
|
||||
}
|
||||
TextButton(
|
||||
text = "取消",
|
||||
onClick = { showDeleteConfirm = false },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
package fuck.andes.ui.screens.memory
|
||||
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.composables.icons.lucide.R as LucideR
|
||||
import fuck.andes.ui.components.MiuixScaffoldPage
|
||||
import fuck.andes.ui.components.PrefDivider
|
||||
import fuck.andes.ui.model.MemoryItemUi
|
||||
import fuck.andes.ui.model.MemoryManagementAction
|
||||
import fuck.andes.ui.model.MemoryManagementUiState
|
||||
import top.yukonga.miuix.kmp.basic.BasicComponent
|
||||
import top.yukonga.miuix.kmp.basic.Card
|
||||
import top.yukonga.miuix.kmp.basic.Icon
|
||||
import top.yukonga.miuix.kmp.basic.SmallTitle
|
||||
import top.yukonga.miuix.kmp.theme.MiuixTheme
|
||||
|
||||
@Composable
|
||||
fun MemoryManagementScreen(
|
||||
state: MemoryManagementUiState,
|
||||
onAction: (MemoryManagementAction) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
MiuixScaffoldPage(
|
||||
title = "记忆管理",
|
||||
onBack = { onAction(MemoryManagementAction.NavigateBack) },
|
||||
modifier = modifier,
|
||||
) {
|
||||
item(key = "section_title") {
|
||||
SmallTitle("记忆")
|
||||
}
|
||||
item(key = "budget_summary") {
|
||||
SmallTitle("${state.totalEnabledChars} / ${state.charBudget} 字")
|
||||
}
|
||||
item(key = "card_memories") {
|
||||
Card(modifier = Modifier.padding(horizontal = 12.dp)) {
|
||||
if (state.memories.isEmpty()) {
|
||||
BasicComponent(
|
||||
title = "暂无记忆",
|
||||
summary = "手动添加记忆,AI 对话时会参考",
|
||||
insideMargin = PaddingValues(16.dp),
|
||||
)
|
||||
} else {
|
||||
state.memories.forEachIndexed { index, memory ->
|
||||
MemoryItemRow(
|
||||
memory = memory,
|
||||
onToggle = { onAction(MemoryManagementAction.ToggleMemory(memory.id, it)) },
|
||||
onClick = { onAction(MemoryManagementAction.OpenMemoryDetail(memory.id)) },
|
||||
onDelete = { onAction(MemoryManagementAction.DeleteMemory(memory.id)) },
|
||||
)
|
||||
if (index < state.memories.lastIndex) {
|
||||
PrefDivider()
|
||||
}
|
||||
}
|
||||
}
|
||||
PrefDivider()
|
||||
BasicComponent(
|
||||
title = "添加记忆",
|
||||
insideMargin = PaddingValues(16.dp),
|
||||
onClick = { onAction(MemoryManagementAction.OpenMemoryNew) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MemoryItemRow(
|
||||
memory: MemoryItemUi,
|
||||
onToggle: (Boolean) -> Unit,
|
||||
onClick: () -> Unit,
|
||||
onDelete: () -> Unit,
|
||||
) {
|
||||
val firstLine = memory.content.lineSequence().firstOrNull().orEmpty()
|
||||
|
||||
BasicComponent(
|
||||
title = firstLine,
|
||||
summary = buildString {
|
||||
append("${memory.charCount} 字")
|
||||
val secondLine = if (memory.content.lines().size > 1 || memory.content.length > firstLine.length) {
|
||||
memory.content.drop(firstLine.length).trimStart().lineSequence().firstOrNull()?.take(40)?.plus("…")?.ifBlank { null }
|
||||
} else null
|
||||
if (secondLine != null) {
|
||||
append(" · ")
|
||||
append(secondLine)
|
||||
}
|
||||
},
|
||||
insideMargin = PaddingValues(16.dp),
|
||||
endActions = {
|
||||
Icon(
|
||||
painter = painterResource(LucideR.drawable.lucide_ic_chevron_right),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(16.dp),
|
||||
tint = MiuixTheme.colorScheme.onSurfaceVariantSummary,
|
||||
)
|
||||
},
|
||||
onClick = onClick,
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user