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:
@@ -25,6 +25,7 @@ kotlin {
|
||||
sourceSets {
|
||||
commonMain.dependencies {
|
||||
api(project(":message-log-api"))
|
||||
api(project(":context-api"))
|
||||
api(libs.kotlinx.coroutines.core)
|
||||
api(libs.kotlinx.serialization.core)
|
||||
api(libs.kotlinx.serialization.json)
|
||||
|
||||
+11
-81
@@ -1,86 +1,16 @@
|
||||
package pw.binom.agentik.workingMemory
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import pw.binom.agentik.messageLog.Content
|
||||
import pw.binom.agentik.messageLog.MessageContext
|
||||
|
||||
/**
|
||||
* Запись в working memory диалога: ровно то, что агент сейчас видит в
|
||||
* LLM-контексте. Упорядочено по `order_idx` (заполняется в store при append).
|
||||
* @Deprecated
|
||||
* Перенесено в `:context-api`. Используй `pw.binom.agentik.context.WorkingMemoryEntry`.
|
||||
*
|
||||
* Sealed-иерархия: `User`/`Assistant` (реплики с ссылкой на audit log
|
||||
* через [sourceMessageId]), `ToolExchange` (синтетическая запись об одном
|
||||
* tool-вызове + его результате — для replay в LiteMessage(TOOL, ToolResult)
|
||||
* при пересоздании LiteConv), `Summary` (суммаризация при compaction).
|
||||
* Backward-compat typealias. Все импорты `pw.binom.agentik.workingMemory.WorkingMemoryEntry`
|
||||
* продолжают работать как тип `pw.binom.agentik.context.WorkingMemoryEntry` (тот же тип, транспарентно).
|
||||
*
|
||||
* Удалить когда все импорты будут на `:context-api`.
|
||||
*/
|
||||
@Serializable
|
||||
sealed interface WorkingMemoryEntry {
|
||||
|
||||
/** Ссылка на исходное сообщение в audit log (`message.id`). `null` для синтетических строк. */
|
||||
val sourceMessageId: String?
|
||||
|
||||
/** Реплика пользователя. */
|
||||
@Serializable
|
||||
@SerialName("user")
|
||||
data class User(
|
||||
override val sourceMessageId: String,
|
||||
val content: List<Content>,
|
||||
/**
|
||||
* Контекст инициации хода. Применяется при сборке `LiteConversation`:
|
||||
* если `origin != USER`, текст префиксуется `[origin] description (sourceId=…)`,
|
||||
* чтобы модель видела, что её разбудил не пользователь.
|
||||
* `null` = обычное user-сообщение.
|
||||
*/
|
||||
val context: MessageContext? = null,
|
||||
) : WorkingMemoryEntry
|
||||
|
||||
/** Реплика ассистента. */
|
||||
@Serializable
|
||||
@SerialName("assistant")
|
||||
data class Assistant(
|
||||
override val sourceMessageId: String,
|
||||
val content: List<Content>,
|
||||
) : WorkingMemoryEntry
|
||||
|
||||
/**
|
||||
* Синтетический блок: один tool-вызов + его результат. Синтетический — потому
|
||||
* что в audit log это две отдельные записи (`MessageRecord.ToolCall` +
|
||||
* `MessageRecord.ToolResult`), а в working_memory мы храним одной строкой
|
||||
* для удобства replay'а.
|
||||
*
|
||||
* При создании новой LiteConv каждая такая запись превращается в
|
||||
* `LiteMessage(TOOL, [ToolResult(callId, name, response)])` — LiteRT-LM
|
||||
* матчит по `name`, `callId` берётся из [sourceMessageId] (= id исходного
|
||||
* [MessageRecord.ToolCall]). Если [wasCancelled] = true, [resultText]
|
||||
* содержит маркер `[cancelled by user]` — модель видит честную причину
|
||||
* отсутствия результата.
|
||||
*
|
||||
* [sourceMessageId] = id исходного [MessageRecord.ToolCall] (для трассировки
|
||||
* в audit log).
|
||||
*/
|
||||
@Serializable
|
||||
@SerialName("tool_exchange")
|
||||
data class ToolExchange(
|
||||
override val sourceMessageId: String,
|
||||
val toolName: String,
|
||||
val toolArgsJson: String,
|
||||
val resultText: String,
|
||||
val wasCancelled: Boolean = false,
|
||||
) : WorkingMemoryEntry
|
||||
|
||||
/**
|
||||
* Синтетический блок: суммаризация старых ходов, сгенерированная при
|
||||
* compaction'е working memory. Не имеет ссылки на конкретное сообщение
|
||||
* в audit log — это наша собственная интерпретация контекста.
|
||||
*/
|
||||
@Serializable
|
||||
@SerialName("summary")
|
||||
data class Summary(
|
||||
val text: String,
|
||||
/** Ходы, которые были свёрнуты в этот summary (диапазон order_idx в виде меты). */
|
||||
val coversUpToOrderIdx: Long? = null,
|
||||
) : WorkingMemoryEntry {
|
||||
override val sourceMessageId: String? = null
|
||||
}
|
||||
}
|
||||
@Deprecated(
|
||||
message = "Перенесено в :context-api. Используй pw.binom.agentik.context.WorkingMemoryEntry.",
|
||||
replaceWith = ReplaceWith("WorkingMemoryEntry", "pw.binom.agentik.context.WorkingMemoryEntry"),
|
||||
)
|
||||
typealias WorkingMemoryEntry = pw.binom.agentik.context.WorkingMemoryEntry
|
||||
|
||||
+12
-52
@@ -1,58 +1,18 @@
|
||||
package pw.binom.agentik.workingMemory
|
||||
|
||||
import kotlin.time.Instant
|
||||
|
||||
/**
|
||||
* Одна строка `working_memory` таблицы (внутреннее представление store).
|
||||
* @Deprecated
|
||||
* Перенесено в `:context-api`. Используй `pw.binom.agentik.context.ContextStore`.
|
||||
*
|
||||
* Используется для тестов и для перестроения [WorkingMemoryEntry] из row.
|
||||
* Агент не должен с этим типом работать напрямую — он работает с
|
||||
* [WorkingMemoryEntry] через [WorkingMemoryStore].
|
||||
* Этот typealias сохранён временно для backward-compat: существующие impl'ы
|
||||
* (`KsqliteStores.workingMemory` в `:storage-ksqlite`, тесты) импортируют
|
||||
* `pw.binom.agentik.workingMemory.WorkingMemoryStore`. Буквально тот же тип,
|
||||
* что и `pw.binom.agentik.context.ContextStore` — typealias транспарентен.
|
||||
*
|
||||
* Удалить когда все импорты будут на `:context-api`.
|
||||
*/
|
||||
data class WorkingMemoryRow(
|
||||
val id: String,
|
||||
val conversationId: String,
|
||||
val orderIdx: Long,
|
||||
val sourceMessageId: String?,
|
||||
val entry: WorkingMemoryEntry,
|
||||
val createdAt: Instant,
|
||||
@Deprecated(
|
||||
message = "Перенесено в :context-api. Используй pw.binom.agentik.context.ContextStore.",
|
||||
replaceWith = ReplaceWith("ContextStore", "pw.binom.agentik.context.ContextStore"),
|
||||
)
|
||||
|
||||
/**
|
||||
* Мутируемое представление LLM-контекста диалога (`working_memory` table).
|
||||
*
|
||||
* Аудит-лог — [MessageStore], неизменный; здесь — ровно то, что агент сейчас
|
||||
* «видит»: системный промпт + реплики + (опционально) суммаризации.
|
||||
* Строки упорядочены по `order_idx` ASC; `source_message_id` NULL указывает
|
||||
* на синтетические строки (System, а в v2 — Summary).
|
||||
*
|
||||
* Суммаризация / чистка — один атомарный вызов [compact].
|
||||
*/
|
||||
interface WorkingMemoryStore : AutoCloseable {
|
||||
|
||||
/** Добавить запись в конец working memory (новый максимальный `order_idx`). */
|
||||
suspend fun append(conversationId: String, entry: WorkingMemoryEntry, now: Instant)
|
||||
|
||||
/** Все строки working memory диалога в порядке отправки. */
|
||||
suspend fun list(conversationId: String): List<WorkingMemoryRow>
|
||||
|
||||
/** Очистить working memory диалога (используется при reset/rebuild). */
|
||||
suspend fun clear(conversationId: String)
|
||||
|
||||
/**
|
||||
* Атомарная суммаризация: удаляет все строки с `order_idx` в диапазоне
|
||||
* `[dropFromOrderIdx, +∞)`. Если [summaryText] непустое — вместо удалённых
|
||||
* строк вставляется одна синтетическая [WorkingMemoryEntry.Summary]
|
||||
* с этим текстом и `order_idx = max(old order_idx after delete) + 1`
|
||||
* (т.е. summary становится хвостом working memory).
|
||||
*
|
||||
* Если [summaryText] == null — работает как «отрезать хвост» (v1 поведение).
|
||||
*
|
||||
* Возвращает новый максимальный `order_idx` после операции.
|
||||
*/
|
||||
suspend fun compact(
|
||||
dropFromOrderIdx: Long,
|
||||
conversationId: String,
|
||||
summaryText: String? = null,
|
||||
): Long
|
||||
}
|
||||
typealias WorkingMemoryStore = pw.binom.agentik.context.ContextStore
|
||||
|
||||
Reference in New Issue
Block a user