Коммит заменяет SQLDelight на ksqlite и добавляет journal/context/outbox
ci / JVM build + tests (push) Failing after 1m24s
ci / JVM build + tests (push) Failing after 1m24s
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
plugins {
|
||||
alias(libs.plugins.kotlin.multiplatform)
|
||||
alias(libs.plugins.kotlin.serialization)
|
||||
}
|
||||
|
||||
// Public API для mutable working-memory — runtime context агента (compaction,
|
||||
// order_idx, summary entries). Зависит от :message-log-api для Ids (wm- префикс).
|
||||
//
|
||||
// НЕ нужен тонким клиентам — только серверному рантайму (`:standalone`, `:agentik-cli`,
|
||||
// будущий `:android-agent` core).
|
||||
|
||||
kotlin {
|
||||
jvmToolchain(21)
|
||||
|
||||
jvm()
|
||||
macosX64()
|
||||
macosArm64()
|
||||
iosX64()
|
||||
iosArm64()
|
||||
iosSimulatorArm64()
|
||||
linuxX64()
|
||||
linuxArm64()
|
||||
mingwX64()
|
||||
|
||||
sourceSets {
|
||||
commonMain.dependencies {
|
||||
api(project(":message-log-api"))
|
||||
api(libs.kotlinx.coroutines.core)
|
||||
api(libs.kotlinx.serialization.core)
|
||||
api(libs.kotlinx.serialization.json)
|
||||
}
|
||||
commonTest.dependencies {
|
||||
implementation(kotlin("test"))
|
||||
implementation(libs.kotlinx.coroutines.test)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package pw.binom.agentik.context
|
||||
|
||||
import kotlin.time.Instant
|
||||
|
||||
/**
|
||||
* Одна строка `working_memory` таблицы (внутреннее представление store).
|
||||
*
|
||||
* Используется для тестов и для перестроения [WorkingMemoryEntry] из row.
|
||||
* Агент не должен с этим типом работать напрямую — он работает с
|
||||
* [WorkingMemoryEntry] через [ContextStore].
|
||||
*/
|
||||
data class WorkingMemoryRow(
|
||||
val id: String,
|
||||
val conversationId: String,
|
||||
val orderIdx: Long,
|
||||
val sourceMessageId: String?,
|
||||
val entry: WorkingMemoryEntry,
|
||||
val createdAt: Instant,
|
||||
)
|
||||
|
||||
/**
|
||||
* Мутируемое представление LLM-контекста диалога (`working_memory` table).
|
||||
*
|
||||
* Аудит-лог — [MessageStore], неизменный; здесь — ровно то, что агент сейчас
|
||||
* «видит»: системный промпт + реплики + (опционально) суммаризации.
|
||||
* Строки упорядочены по `order_idx` ASC; `source_message_id` NULL указывает
|
||||
* на синтетические строки (System, а в v2 — Summary).
|
||||
*
|
||||
* Суммаризация / чистка — один атомарный вызов [compact].
|
||||
*/
|
||||
interface ContextStore : 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
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package pw.binom.agentik.context
|
||||
|
||||
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).
|
||||
*
|
||||
* Sealed-иерархия: `User`/`Assistant` (реплики с ссылкой на audit log
|
||||
* через [sourceMessageId]), `ToolExchange` (синтетическая запись об одном
|
||||
* tool-вызове + его результате — для replay в LiteMessage(TOOL, ToolResult)
|
||||
* при пересоздании LiteConv), `Summary` (суммаризация при compaction).
|
||||
*/
|
||||
@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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user