refactor: migrate EventStore and MessageStore to :outbox-api and :journal-api
ci / JVM build + tests (push) Failing after 1m9s
ci / JVM build + tests (push) Failing after 1m9s
- Replaced usages of `:message-store-api` and `:working-memory-api` with `:journal-api`, `:outbox-api`, and `:context-api`. - Deprecated legacy `EventStore` and `MessageStore` interfaces, added `typealias` for backward compatibility. - Updated imports across all modules with references to `:journal-api` and `:outbox-api`. - Introduced `journalRoutes` and `outboxRoutes` in `:server` for audit log and live event stream endpoints. - Adjusted `Agent` to expose read-only `journal` and `outbox` stores for improved modularity and clarity. - Removed legacy Event and AgentEvent definitions from `:proto`, migrated to `:outbox-api`. - Storage-related modules have been updated to support the new APIs consistently.
This commit is contained in:
@@ -42,8 +42,9 @@ kotlin {
|
||||
implementation(project(":proto"))
|
||||
implementation(project(":server"))
|
||||
implementation(project(":message-store-api"))
|
||||
implementation(project(":message-log-api"))
|
||||
implementation(project(":working-memory-api"))
|
||||
implementation(project(":journal-api"))
|
||||
implementation(project(":outbox-api"))
|
||||
implementation(project(":context-api"))
|
||||
|
||||
// Commons
|
||||
implementation(libs.kotlinx.coroutines.core)
|
||||
@@ -67,13 +68,14 @@ kotlin {
|
||||
implementation(project(":memory-vector"))
|
||||
}
|
||||
implementation(project(":message-store-api"))
|
||||
implementation(project(":working-memory-api"))
|
||||
implementation(project(":journal-api"))
|
||||
implementation(project(":outbox-api"))
|
||||
implementation(project(":context-api"))
|
||||
implementation(project(":storage-ksqlite"))
|
||||
|
||||
// Новый единый канал событий агента — заменил старые
|
||||
// `agentEvents: MutableSharedFlow<AgentEvent>` и per-conv `ConversationEvents._flow`.
|
||||
// Bounded tail + auto-TTL, generic CommonEvent envelope.
|
||||
implementation(project(":event-store"))
|
||||
implementation(project(":event-store-in-memory"))
|
||||
implementation(project(":agent-toolsets"))
|
||||
// Generic LLM-side tools (LlmReflector, SkillMiner, LlmMemoryReviewer,
|
||||
|
||||
@@ -10,10 +10,10 @@ import pw.binom.a2a.model.Message
|
||||
import pw.binom.a2a.model.Role
|
||||
import pw.binom.a2a.model.TextPart
|
||||
import pw.binom.a2a.server.AgentHandler
|
||||
import pw.binom.agentik.outbox.Event
|
||||
import pw.binom.agentik.proto.Agent
|
||||
import pw.binom.agentik.proto.Content
|
||||
import pw.binom.agentik.proto.Conversation
|
||||
import pw.binom.agentik.proto.Event
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
private val log = KotlinLogging.logger {}
|
||||
|
||||
@@ -16,7 +16,7 @@ import pw.binom.agentik.llm.tools.LlmReflector
|
||||
import pw.binom.agentik.llm.tools.SkillMiner
|
||||
import pw.binom.agentik.standalone.agent.memory.Curator
|
||||
import pw.binom.agentik.skills.SkillStore
|
||||
import pw.binom.agentik.workingMemory.WorkingMemoryStore
|
||||
import pw.binom.agentik.context.ContextStore
|
||||
|
||||
/**
|
||||
* Debug-эндпоинты для ручного триггерирования фоновых фич (без ожидания
|
||||
@@ -37,7 +37,7 @@ import pw.binom.agentik.workingMemory.WorkingMemoryStore
|
||||
*/
|
||||
internal fun Route.debugRoutes(
|
||||
agent: Agent,
|
||||
workingMemoryStore: WorkingMemoryStore,
|
||||
workingMemoryStore: ContextStore,
|
||||
reflectionStore: ReflectionStore,
|
||||
reflector: LlmReflector?,
|
||||
skillMiner: SkillMiner?,
|
||||
@@ -118,14 +118,14 @@ internal fun Route.debugRoutes(
|
||||
* (для debug-триггеров reflector/miner; та же логика, что у хуков
|
||||
* [ChatConversation]).
|
||||
*/
|
||||
internal suspend fun recentTurns(workingMemoryStore: WorkingMemoryStore, conversationId: String, maxTurns: Int): List<ConversationTurn> {
|
||||
internal suspend fun recentTurns(workingMemoryStore: ContextStore, conversationId: String, maxTurns: Int): List<ConversationTurn> {
|
||||
val rows = workingMemoryStore.list(conversationId)
|
||||
val pairs = mutableListOf<ConversationTurn>()
|
||||
var pendingUser: String? = null
|
||||
for (row in rows) {
|
||||
when (val e = row.entry) {
|
||||
is pw.binom.agentik.workingMemory.WorkingMemoryEntry.User -> pendingUser = e.content.text()
|
||||
is pw.binom.agentik.workingMemory.WorkingMemoryEntry.Assistant -> {
|
||||
is pw.binom.agentik.context.WorkingMemoryEntry.User -> pendingUser = e.content.text()
|
||||
is pw.binom.agentik.context.WorkingMemoryEntry.Assistant -> {
|
||||
val user = pendingUser ?: ""
|
||||
pendingUser = null
|
||||
pairs += ConversationTurn(userMessage = user, assistantMessage = e.content.text())
|
||||
@@ -137,5 +137,5 @@ internal suspend fun recentTurns(workingMemoryStore: WorkingMemoryStore, convers
|
||||
}
|
||||
|
||||
/** Текстовое содержимое записей working memory (Text-контент, без картинок). */
|
||||
internal fun List<pw.binom.agentik.messageLog.Content>.text(): String =
|
||||
filterIsInstance<pw.binom.agentik.messageLog.Content.Text>().joinToString("\n") { it.body }
|
||||
internal fun List<pw.binom.agentik.journal.Content>.text(): String =
|
||||
filterIsInstance<pw.binom.agentik.journal.Content.Text>().joinToString("\n") { it.body }
|
||||
|
||||
+4
-4
@@ -12,10 +12,10 @@ import pw.binom.agentik.memory.ConversationTurn
|
||||
import pw.binom.agentik.memory.MemoryReviewer
|
||||
import pw.binom.agentik.memory.MemoryStore
|
||||
import pw.binom.agentik.skills.SkillStore
|
||||
import pw.binom.agentik.messageLog.Content
|
||||
import pw.binom.agentik.journal.Content
|
||||
import pw.binom.agentik.messageStore.ReflectionStore
|
||||
import pw.binom.agentik.workingMemory.WorkingMemoryEntry
|
||||
import pw.binom.agentik.workingMemory.WorkingMemoryStore
|
||||
import pw.binom.agentik.context.WorkingMemoryEntry
|
||||
import pw.binom.agentik.context.ContextStore
|
||||
import pw.binom.agentik.llm.tools.LlmReflector
|
||||
import pw.binom.agentik.llm.tools.SkillMiner
|
||||
import java.util.concurrent.atomic.AtomicLong
|
||||
@@ -46,7 +46,7 @@ internal data class BackgroundConfig(
|
||||
|
||||
internal class BackgroundScheduler(
|
||||
private val state: ConversationState,
|
||||
private val workingMemory: WorkingMemoryStore,
|
||||
private val workingMemory: ContextStore,
|
||||
private val config: BackgroundConfig,
|
||||
private val backgroundEvents: BackgroundEventBus,
|
||||
) {
|
||||
|
||||
@@ -16,9 +16,11 @@ import pw.binom.agentik.memory.MemoryPrefetcher
|
||||
import pw.binom.agentik.memory.MemoryReviewer
|
||||
import pw.binom.agentik.memory.MemorySystemGuidance
|
||||
import pw.binom.agentik.proto.Agent as ProtoAgent
|
||||
import pw.binom.agentik.proto.AgentEvent
|
||||
import pw.binom.agentik.eventStore.MutableEventStore
|
||||
import pw.binom.agentik.proto.CommonEvent
|
||||
import pw.binom.agentik.outbox.AgentEvent
|
||||
import pw.binom.agentik.outbox.CommonEvent
|
||||
import pw.binom.agentik.outbox.MutableOutboxStore
|
||||
import pw.binom.agentik.journal.JournalStore
|
||||
import pw.binom.agentik.outbox.OutboxStore
|
||||
import pw.binom.agentik.proto.Conversation as ProtoConversation
|
||||
import pw.binom.agentik.proto.Event as ProtoEvent
|
||||
import pw.binom.agentik.skills.SkillCatalog
|
||||
@@ -30,8 +32,8 @@ import pw.binom.agentik.messageStore.ConversationStore
|
||||
import pw.binom.agentik.messageStore.Ids
|
||||
import pw.binom.agentik.messageStore.Reflection
|
||||
import pw.binom.agentik.messageStore.ReflectionStore
|
||||
import pw.binom.agentik.messageLog.MutableMessageStore
|
||||
import pw.binom.agentik.workingMemory.WorkingMemoryStore
|
||||
import pw.binom.agentik.journal.MutableJournalStore
|
||||
import pw.binom.agentik.context.ContextStore
|
||||
import pw.binom.agentik.toolsets.DisableToolsetTool
|
||||
import pw.binom.agentik.toolsets.EnableToolsetTool
|
||||
import pw.binom.agentik.toolsets.NamedTool
|
||||
@@ -64,8 +66,8 @@ import pw.binom.litert.LiteLlm
|
||||
class ChatAgent(
|
||||
override val id: String,
|
||||
private val conversationStore: ConversationStore,
|
||||
private val messageStore: MutableMessageStore,
|
||||
private val workingMemoryStore: WorkingMemoryStore,
|
||||
private val messageStore: MutableJournalStore,
|
||||
private val workingMemoryStore: ContextStore,
|
||||
private val reflectionStore: ReflectionStore,
|
||||
private val llm: LiteLlm,
|
||||
private val llmConfig: LlmConfig,
|
||||
@@ -205,42 +207,46 @@ class ChatAgent(
|
||||
* - `agentEvents: MutableSharedFlow<AgentEvent>` (agent lifecycle)
|
||||
* - per-conv `ConversationEvents._flow: MutableSharedFlow<ProtoEvent>`
|
||||
*
|
||||
* Теперь оба пишут сюда через [MutableEventStore.append], а consumer'ы
|
||||
* Теперь оба пишут сюда через [MutableOutboxStore.append], а consumer'ы
|
||||
* читают через [EventStore.events]/[conversationEvents]/[agentEvents].
|
||||
*
|
||||
* **Live tail + auto-TTL** — клиенты больше не должны заботиться о persistence
|
||||
* или подписке на два отдельных канала.
|
||||
*/
|
||||
private val eventStore: MutableEventStore = pw.binom.agentik.eventStore.inmemory.InMemoryEventStore(
|
||||
private val eventStore: MutableOutboxStore = pw.binom.agentik.eventStore.inmemory.InMemoryEventStore(
|
||||
maxMessages = null,
|
||||
ttl = null,
|
||||
)
|
||||
|
||||
/**
|
||||
* Read-only view of [messageStore] для HTTP-фасада в `:server`
|
||||
* (`Route.agentikAgent` → `/journal/...` endpoint'ы).
|
||||
*
|
||||
* [MutableJournalStore] == [MutableJournalStore] (typealias), поэтому
|
||||
* narrowing с мутабельного writer'а на read-only view тривиальна.
|
||||
* Используется [Agent.journal] override'ом из `:proto`.
|
||||
*/
|
||||
override val journal: JournalStore
|
||||
get() = messageStore
|
||||
|
||||
/**
|
||||
* Read-only view of [eventStore] для HTTP-фасада в `:server`
|
||||
* (`Route.agentikAgent` → `/outbox/...` endpoint'ы).
|
||||
*
|
||||
* [MutableOutboxStore] == [MutableOutboxStore] (typealias), narrowing тривиальна.
|
||||
* Используется [Agent.outbox] override'ом из `:proto`.
|
||||
*/
|
||||
override val outbox: OutboxStore
|
||||
get() = eventStore
|
||||
|
||||
/** Защищает карту живых диалогов. */
|
||||
private val liveLock = Mutex()
|
||||
private val live: MutableMap<String, ChatConversation> = HashMap()
|
||||
|
||||
/** Live-подписка на события уровня агента (создание/удаление/переименование). */
|
||||
override fun events(after: Instant): Flow<AgentEvent> =
|
||||
eventStore.agentEvents(after).map { it.event }
|
||||
|
||||
/**
|
||||
* Все события в одном потоке: agent lifecycle + events всех диалогов.
|
||||
*
|
||||
* Snapshot живых диалогов берётся на момент подписки. Новые Created-Event'ы
|
||||
* НЕ переподписывают — это ответственность caller'а (см. KDoc в :event-store
|
||||
* про reconnect pattern + gap detection через [eventStore.earliestEventDate]).
|
||||
*/
|
||||
override fun allEvents(after: Instant): Flow<CommonEvent> = flow {
|
||||
emitAll(eventStore.agentEvents(after))
|
||||
live.values
|
||||
.asSequence()
|
||||
.filterNot { it.isClosed }
|
||||
.forEach { conv: ProtoConversation ->
|
||||
val cid: String = conv.id
|
||||
emitAll(eventStore.conversationEvents(after, cid).map { it as CommonEvent.Conversation })
|
||||
}
|
||||
}
|
||||
// events()/allEvents() больше НЕ override'ятся — эти методы удалены
|
||||
// из :proto.Agent после миграции событий в outbox-сущность.
|
||||
// Live-подписки теперь идут через Agent.outbox.events() / .agentEvents()
|
||||
// (см. README :proto для контракта catchup+live).
|
||||
|
||||
override fun createConversation(temp: Boolean): ProtoConversation {
|
||||
val now = now()
|
||||
|
||||
+5
-5
@@ -6,10 +6,10 @@ import pw.binom.agentik.memory.ConversationTurn
|
||||
import pw.binom.agentik.memory.MemoryReviewer
|
||||
import pw.binom.agentik.memory.MemoryStore
|
||||
import pw.binom.agentik.standalone.agent.memory.materializeReviewNote
|
||||
import pw.binom.agentik.messageLog.Content
|
||||
import pw.binom.agentik.workingMemory.WorkingMemoryEntry
|
||||
import pw.binom.agentik.workingMemory.WorkingMemoryRow
|
||||
import pw.binom.agentik.workingMemory.WorkingMemoryStore
|
||||
import pw.binom.agentik.journal.Content
|
||||
import pw.binom.agentik.context.WorkingMemoryEntry
|
||||
import pw.binom.agentik.context.WorkingMemoryRow
|
||||
import pw.binom.agentik.context.ContextStore
|
||||
import pw.binom.litert.LiteContentPart
|
||||
import pw.binom.litert.LiteConversation
|
||||
import pw.binom.litert.LiteConversationConfig
|
||||
@@ -26,7 +26,7 @@ internal class CompactionCoordinator(
|
||||
private val contextCompactor: ContextCompactor?,
|
||||
private val memoryReviewer: MemoryReviewer?,
|
||||
private val memoryStoreForReview: MemoryStore?,
|
||||
private val workingMemory: WorkingMemoryStore,
|
||||
private val workingMemory: ContextStore,
|
||||
private val liteLlm: LiteLlm,
|
||||
private val systemPrompt: String,
|
||||
private val backgroundEvents: BackgroundEventBus,
|
||||
|
||||
@@ -3,8 +3,8 @@ package pw.binom.agentik.standalone.agent
|
||||
import mu.KotlinLogging
|
||||
import pw.binom.agentik.memory.MemoryPrefetcher
|
||||
import pw.binom.litert.LiteContentPart
|
||||
import pw.binom.agentik.messageLog.MessageContext
|
||||
import pw.binom.agentik.messageLog.MessageOrigin
|
||||
import pw.binom.agentik.journal.MessageContext
|
||||
import pw.binom.agentik.journal.MessageOrigin
|
||||
|
||||
internal class ContextBuilder(
|
||||
private val memoryPrefetcher: MemoryPrefetcher?,
|
||||
|
||||
+3
-3
@@ -3,12 +3,12 @@ package pw.binom.agentik.standalone.agent
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import pw.binom.agentik.eventStore.MutableEventStore
|
||||
import pw.binom.agentik.proto.CommonEvent
|
||||
import pw.binom.agentik.outbox.MutableOutboxStore
|
||||
import pw.binom.agentik.outbox.CommonEvent
|
||||
import pw.binom.agentik.proto.Event as ProtoEvent
|
||||
|
||||
internal class ConversationEvents(
|
||||
private val globalEventStore: MutableEventStore,
|
||||
private val globalEventStore: MutableOutboxStore,
|
||||
private val conversationId: String,
|
||||
) {
|
||||
fun tryEmit(event: ProtoEvent): Boolean {
|
||||
|
||||
+13
-13
@@ -24,21 +24,21 @@ import pw.binom.agentik.memory.MemoryReviewer
|
||||
import pw.binom.agentik.memory.MemoryStore
|
||||
import pw.binom.agentik.proto.Content as ProtoContent
|
||||
import pw.binom.agentik.proto.Conversation as ProtoConversation
|
||||
import pw.binom.agentik.proto.Event as ProtoEvent
|
||||
import pw.binom.agentik.outbox.Event as ProtoEvent
|
||||
import pw.binom.agentik.messageStore.ReflectionStore
|
||||
import pw.binom.agentik.proto.Message as ProtoMessage
|
||||
import pw.binom.agentik.proto.MessageContext as ProtoMessageContext
|
||||
import pw.binom.agentik.skills.SkillStore
|
||||
import pw.binom.agentik.messageLog.Content
|
||||
import pw.binom.agentik.journal.Content
|
||||
import pw.binom.agentik.messageStore.ConversationRecord
|
||||
import pw.binom.agentik.messageStore.ConversationStore
|
||||
import pw.binom.agentik.messageLog.MessageContext
|
||||
import pw.binom.agentik.messageLog.MessageOrigin
|
||||
import pw.binom.agentik.messageLog.MessageRecord
|
||||
import pw.binom.agentik.messageLog.MutableMessageStore
|
||||
import pw.binom.agentik.messageStore.ReflectionStore
|
||||
import pw.binom.agentik.messageLog.TurnTokens
|
||||
import pw.binom.agentik.workingMemory.WorkingMemoryEntry
|
||||
import pw.binom.agentik.workingMemory.WorkingMemoryStore
|
||||
import pw.binom.agentik.journal.MessageContext
|
||||
import pw.binom.agentik.journal.MessageOrigin
|
||||
import pw.binom.agentik.journal.MessageRecord
|
||||
import pw.binom.agentik.journal.MutableJournalStore as MutableJournalStore
|
||||
import pw.binom.agentik.journal.TurnTokens
|
||||
import pw.binom.agentik.context.WorkingMemoryEntry
|
||||
import pw.binom.agentik.context.ContextStore
|
||||
import pw.binom.agentik.toolsets.ToolsetDispatchPolicy
|
||||
import pw.binom.litert.LiteContentPart
|
||||
import pw.binom.litert.LiteConversation
|
||||
@@ -55,10 +55,10 @@ import pw.binom.agentik.toolsets.NamedTool
|
||||
class ConversationLoop(
|
||||
record: ConversationRecord,
|
||||
private val conversationStore: ConversationStore,
|
||||
private val messageStore: MutableMessageStore,
|
||||
private val workingMemoryStore: WorkingMemoryStore,
|
||||
private val messageStore: MutableJournalStore,
|
||||
private val workingMemoryStore: ContextStore,
|
||||
private val reflectionStore: ReflectionStore?,
|
||||
private val eventStore: pw.binom.agentik.eventStore.MutableEventStore,
|
||||
private val eventStore: pw.binom.agentik.outbox.MutableOutboxStore,
|
||||
private val llm: LiteLlm,
|
||||
private val systemPrompt: String,
|
||||
private val tools: List<NamedTool> = emptyList(),
|
||||
|
||||
@@ -4,10 +4,10 @@ import kotlinx.coroutines.CancellationException
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.async
|
||||
import mu.KotlinLogging
|
||||
import pw.binom.agentik.proto.Event as ProtoEvent
|
||||
import pw.binom.agentik.messageLog.MessageRecord
|
||||
import pw.binom.agentik.messageLog.MutableMessageStore
|
||||
import pw.binom.agentik.workingMemory.WorkingMemoryEntry
|
||||
import pw.binom.agentik.outbox.Event as ProtoEvent
|
||||
import pw.binom.agentik.journal.MessageRecord
|
||||
import pw.binom.agentik.journal.MutableJournalStore as MutableJournalStore
|
||||
import pw.binom.agentik.context.WorkingMemoryEntry
|
||||
import pw.binom.agentik.toolsets.ToolsetDispatchPolicy
|
||||
import pw.binom.litert.LiteToolCall
|
||||
import pw.binom.litert.LiteTool
|
||||
@@ -16,7 +16,7 @@ import pw.binom.agentik.toolsets.NamedTool
|
||||
|
||||
internal class ToolDispatcher(
|
||||
private val state: ConversationState,
|
||||
private val messageStore: MutableMessageStore,
|
||||
private val messageStore: MutableJournalStore,
|
||||
private val events: ConversationEvents,
|
||||
private val backgroundEvents: BackgroundEventBus,
|
||||
private val toolsByName: MutableMap<String, NamedTool>,
|
||||
|
||||
@@ -7,15 +7,15 @@ import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.toList
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import pw.binom.agentik.proto.AgentEvent
|
||||
import pw.binom.agentik.outbox.AgentEvent
|
||||
import pw.binom.agentik.proto.Content
|
||||
import pw.binom.agentik.proto.Event as ProtoEvent
|
||||
import pw.binom.agentik.outbox.Event as ProtoEvent
|
||||
import pw.binom.agentik.skills.SkillCatalog
|
||||
import pw.binom.agentik.skills.SkillFile
|
||||
import pw.binom.agentik.standalone.llm.LlmBackend
|
||||
import pw.binom.agentik.standalone.llm.LlmConfig
|
||||
import pw.binom.agentik.messageLog.MessageRecord
|
||||
import pw.binom.agentik.workingMemory.WorkingMemoryEntry
|
||||
import pw.binom.agentik.journal.MessageRecord
|
||||
import pw.binom.agentik.context.WorkingMemoryEntry
|
||||
import pw.binom.agentik.storage.ksqlite.KsqliteStores
|
||||
import pw.binom.litert.LiteContentPart
|
||||
import pw.binom.litert.LiteConversation
|
||||
@@ -163,10 +163,10 @@ class ChatAgentTest {
|
||||
|
||||
// добавим сообщение, чтобы потом убедиться, что каскад сработал
|
||||
sqliteStores.messages.append(
|
||||
pw.binom.agentik.messageLog.MessageRecord.UserMessage(
|
||||
pw.binom.agentik.journal.MessageRecord.UserMessage(
|
||||
id = "m1",
|
||||
conversationId = id,
|
||||
content = listOf(pw.binom.agentik.messageLog.Content.Text("hi")),
|
||||
content = listOf(pw.binom.agentik.journal.Content.Text("hi")),
|
||||
createdAt = Instant.fromEpochMilliseconds(1_700_000_000_000),
|
||||
),
|
||||
)
|
||||
@@ -193,11 +193,11 @@ class ChatAgentTest {
|
||||
// user message записан в audit + working memory
|
||||
val msgs = sqliteStores.messages.listFlow(conv.id, Instant.DISTANT_PAST).toList()
|
||||
assertEquals(2, msgs.size)
|
||||
assertEquals("hi", (msgs[0] as pw.binom.agentik.messageLog.MessageRecord.UserMessage).content.let {
|
||||
(it[0] as pw.binom.agentik.messageLog.Content.Text).body
|
||||
assertEquals("hi", (msgs[0] as pw.binom.agentik.journal.MessageRecord.UserMessage).content.let {
|
||||
(it[0] as pw.binom.agentik.journal.Content.Text).body
|
||||
})
|
||||
assertEquals("hello back", (msgs[1] as pw.binom.agentik.messageLog.MessageRecord.AssistantMessage).content.let {
|
||||
(it[0] as pw.binom.agentik.messageLog.Content.Text).body
|
||||
assertEquals("hello back", (msgs[1] as pw.binom.agentik.journal.MessageRecord.AssistantMessage).content.let {
|
||||
(it[0] as pw.binom.agentik.journal.Content.Text).body
|
||||
})
|
||||
}
|
||||
|
||||
@@ -330,8 +330,8 @@ class ChatAgentTest {
|
||||
|
||||
val msgs = sqliteStores.messages.listFlow(conv.id, Instant.DISTANT_PAST).toList()
|
||||
assertEquals(2, msgs.size)
|
||||
assertIs<pw.binom.agentik.messageLog.MessageRecord.UserMessage>(msgs[0])
|
||||
val err = assertIs<pw.binom.agentik.messageLog.MessageRecord.Error>(msgs[1])
|
||||
assertIs<pw.binom.agentik.journal.MessageRecord.UserMessage>(msgs[0])
|
||||
val err = assertIs<pw.binom.agentik.journal.MessageRecord.Error>(msgs[1])
|
||||
assertEquals("boom from llm", err.message)
|
||||
|
||||
// backfill через getMessages (polling/reconnect) тоже видит ошибку
|
||||
@@ -375,7 +375,7 @@ class ChatAgentTest {
|
||||
// audit: только user (assistant не успел сгенериться)
|
||||
val msgs = sqliteStores.messages.listFlow(conv.id, Instant.DISTANT_PAST).toList()
|
||||
assertEquals(1, msgs.size)
|
||||
assertIs<pw.binom.agentik.messageLog.MessageRecord.UserMessage>(msgs[0])
|
||||
assertIs<pw.binom.agentik.journal.MessageRecord.UserMessage>(msgs[0])
|
||||
|
||||
// working memory: только user (assistant skipped because пустой)
|
||||
val wm = sqliteStores.workingMemory.list(conv.id)
|
||||
@@ -431,7 +431,7 @@ class ChatAgentTest {
|
||||
|
||||
// audit: user + toolcall + toolresult (tool выполнился), assistant может быть
|
||||
val msgs = sqliteStores.messages.listFlow(conv.id, Instant.DISTANT_PAST).toList()
|
||||
val toolResult = msgs.filterIsInstance<pw.binom.agentik.messageLog.MessageRecord.ToolResult>().firstOrNull()
|
||||
val toolResult = msgs.filterIsInstance<pw.binom.agentik.journal.MessageRecord.ToolResult>().firstOrNull()
|
||||
assertNotNull(toolResult, "tool result должен быть в audit — tool выполнился нормально")
|
||||
val toolResultResult = toolResult!!.result!!
|
||||
assertTrue(toolResultResult.contains("echo"), "tool result содержит реальный ответ тулы: $toolResultResult")
|
||||
@@ -574,7 +574,10 @@ class ChatAgentTest {
|
||||
val agent = newAgent()
|
||||
val events = mutableListOf<AgentEvent>()
|
||||
val job = launch(start = kotlinx.coroutines.CoroutineStart.UNDISPATCHED) {
|
||||
agent.events(Instant.DISTANT_PAST).collect { events.add(it) }
|
||||
// Agent.events() удалён из :proto — события живут в
|
||||
// agent.outbox.agentEvents(): Flow<CommonEvent.Agent>;
|
||||
// распаковываем .event для получения AgentEvent.
|
||||
agent.outbox.agentEvents(Instant.DISTANT_PAST).collect { events.add(it.event) }
|
||||
}
|
||||
val conv = agent.createConversation(temp = false)
|
||||
agent.deleteConversation(conv.id)
|
||||
|
||||
@@ -92,7 +92,7 @@ class CompactionTest {
|
||||
}
|
||||
val wm = sqliteStores.workingMemory.list(conv.id)
|
||||
// Без compaction все ходы остаются в памяти (System + 10 user/assistant = 21 строк).
|
||||
val summaries = wm.filter { it.entry is pw.binom.agentik.workingMemory.WorkingMemoryEntry.Summary }
|
||||
val summaries = wm.filter { it.entry is pw.binom.agentik.context.WorkingMemoryEntry.Summary }
|
||||
assertEquals(0, summaries.size, "compaction must not run without contextWindow")
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ class CompactionTest {
|
||||
conv.send(listOf(ProtoContent.Text("first")))
|
||||
val wm = sqliteStores.workingMemory.list(conv.id)
|
||||
// System + User + Assistant = 3. Без compactor — никаких Summary.
|
||||
val summaries = wm.filter { it.entry is pw.binom.agentik.workingMemory.WorkingMemoryEntry.Summary }
|
||||
val summaries = wm.filter { it.entry is pw.binom.agentik.context.WorkingMemoryEntry.Summary }
|
||||
assertEquals(0, summaries.size, "no compaction runs without compactor")
|
||||
}
|
||||
|
||||
@@ -122,10 +122,10 @@ class CompactionTest {
|
||||
assertTrue(compactor.calls > 0, "compactor must be called at least once when above threshold")
|
||||
// В working memory должна появиться Summary.
|
||||
val wm = sqliteStores.workingMemory.list(conv.id)
|
||||
val summaries = wm.filter { it.entry is pw.binom.agentik.workingMemory.WorkingMemoryEntry.Summary }
|
||||
val summaries = wm.filter { it.entry is pw.binom.agentik.context.WorkingMemoryEntry.Summary }
|
||||
assertTrue(summaries.isNotEmpty(), "at least one Summary entry should be present after compaction")
|
||||
// Summary-текст — то, что вернул наш compactor.
|
||||
val summaryText = (summaries.first().entry as pw.binom.agentik.workingMemory.WorkingMemoryEntry.Summary).text
|
||||
val summaryText = (summaries.first().entry as pw.binom.agentik.context.WorkingMemoryEntry.Summary).text
|
||||
assertTrue(summaryText.startsWith("**Goal**"), "summary text should come from compactor: $summaryText")
|
||||
}
|
||||
|
||||
@@ -165,8 +165,8 @@ class CompactionTest {
|
||||
// Должны быть: System + хотя бы один Summary + последние KEEP_RECENT_TURNS ходов.
|
||||
// KEEP_RECENT_TURNS = 4 → user/assistant последних двух ходов (third + second) могут быть не тронуты.
|
||||
val userAssistantCount = wm.count {
|
||||
it.entry is pw.binom.agentik.workingMemory.WorkingMemoryEntry.User ||
|
||||
it.entry is pw.binom.agentik.workingMemory.WorkingMemoryEntry.Assistant
|
||||
it.entry is pw.binom.agentik.context.WorkingMemoryEntry.User ||
|
||||
it.entry is pw.binom.agentik.context.WorkingMemoryEntry.Assistant
|
||||
}
|
||||
// Минимум 1 ход остаётся (KEEP_RECENT_TURNS).
|
||||
assertTrue(userAssistantCount >= 1, "at least one recent turn must be preserved")
|
||||
|
||||
+4
-4
@@ -1,9 +1,9 @@
|
||||
package pw.binom.agentik.standalone.agent
|
||||
|
||||
import pw.binom.agentik.messageLog.MessageContext
|
||||
import pw.binom.agentik.messageLog.MessageOrigin.EVENT
|
||||
import pw.binom.agentik.messageLog.MessageOrigin.SYSTEM
|
||||
import pw.binom.agentik.messageLog.MessageOrigin.USER
|
||||
import pw.binom.agentik.journal.MessageContext
|
||||
import pw.binom.agentik.journal.MessageOrigin.EVENT
|
||||
import pw.binom.agentik.journal.MessageOrigin.SYSTEM
|
||||
import pw.binom.agentik.journal.MessageOrigin.USER
|
||||
import pw.binom.litert.LiteContentPart
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
+5
-5
@@ -1,10 +1,10 @@
|
||||
package pw.binom.agentik.standalone.persistence
|
||||
import pw.binom.agentik.messageLog.MessageContext
|
||||
import pw.binom.agentik.messageLog.MessageOrigin
|
||||
import pw.binom.agentik.journal.MessageContext
|
||||
import pw.binom.agentik.journal.MessageOrigin
|
||||
import pw.binom.agentik.messageStore.ConversationRecord
|
||||
import pw.binom.agentik.messageLog.MessageRecord
|
||||
import pw.binom.agentik.messageLog.Content
|
||||
import pw.binom.agentik.workingMemory.WorkingMemoryEntry
|
||||
import pw.binom.agentik.journal.MessageRecord
|
||||
import pw.binom.agentik.journal.Content
|
||||
import pw.binom.agentik.context.WorkingMemoryEntry
|
||||
|
||||
import kotlinx.coroutines.flow.toList
|
||||
import kotlinx.coroutines.test.runTest
|
||||
|
||||
Reference in New Issue
Block a user