1dc5552f98
Добавлен SystemPromptToolsetSection — рендер markdown-секции для system prompt. Контракт: - toolsets пустой → null (секция не добавляется, агент не знает о механике) - иначе → краткое описание концепции + список 'name — description' для активных и неактивных (одинаковый формат per design contract) - auto-activation НЕ упоминается в промпте (только в dispatch) Интеграция в ChatAgent: - Добавлен параметр toolsets: List<ToolsetContribution> = emptyList() - При пустом списке — enable_toolset/disable_toolset НЕ регистрируются, секция в system prompt НЕ появляется (полная невидимость per A1-α) - При непустом — тулы регистрируются, секция добавляется - ToolsetRegistry + ToolsetDispatchPolicy создаются per-agent (один реестр на все диалоги — состояние 'активные тулсеты' общее) Интеграция в ChatConversation: - Новый параметр toolsetDispatch: ToolsetDispatchPolicy? = null - runToolAndPersist: если задан — вызов идёт через policy (auto-activate неактивных тулсетов, fallback в base dispatcher для плоских тулов) - Иначе — старое поведение через toolsByName Тесты: - 7 новых в :agent-toolsets (SystemPromptToolsetSection): пустые списки, только активные, только неактивные, оба, проверка отсутствия auto-activation упоминания, registry-based рендер, пустой реестр - 5 новых в :standalone (ChatAgentToolsetsTest): default (пустой) — нет тулов и секции; non-empty — тулы и секция есть; enable_toolset активирует; вызов тула из неактивного тулсета — auto-activate; disable_toolset снимает из active set (но auto-activate на следующем вызове — by design) Tests: 340/340 green (335 ранее + 5 новых ChatAgent integration)
149 lines
6.6 KiB
Kotlin
149 lines
6.6 KiB
Kotlin
@file:OptIn(org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi::class)
|
||
|
||
import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi
|
||
import org.jetbrains.kotlin.gradle.targets.jvm.KotlinJvmTarget
|
||
|
||
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
|
||
import org.gradle.api.artifacts.ConfigurationContainer
|
||
|
||
plugins {
|
||
alias(libs.plugins.kotlin.multiplatform)
|
||
alias(libs.plugins.kotlin.serialization)
|
||
alias(libs.plugins.shadow)
|
||
}
|
||
|
||
kotlin {
|
||
jvmToolchain(21)
|
||
|
||
jvm {
|
||
binaries {
|
||
executable {
|
||
mainClass.set("pw.binom.agentik.standalone.MainKt")
|
||
}
|
||
}
|
||
}
|
||
|
||
sourceSets {
|
||
commonMain.dependencies {
|
||
implementation(project(":proto"))
|
||
implementation(project(":server"))
|
||
|
||
// Commons
|
||
implementation(libs.kotlinx.coroutines.core)
|
||
implementation(libs.kotlinx.serialization.json)
|
||
|
||
// litert-kmp: контракт (commonMain)
|
||
api(libs.litert.api)
|
||
|
||
// litert-openai: JVM-реализация
|
||
implementation(libs.litert.openai)
|
||
}
|
||
|
||
jvmMain.dependencies {
|
||
// Парсер и загрузчик скилов (YAML frontmatter + markdown body).
|
||
implementation(project(":skills"))
|
||
|
||
// Долговременная память (Hermes-style MD-бэкенд) + хранилище истории.
|
||
implementation(project(":memory-api"))
|
||
implementation(project(":memory-md"))
|
||
implementation(project(":memory-vector"))
|
||
implementation(project(":storage-core"))
|
||
implementation(project(":storage-sqlite"))
|
||
implementation(project(":agent-toolsets"))
|
||
|
||
// litert-google: встроенный LiteRT-LM движок, нужен только на runtime
|
||
runtimeOnly(libs.litert.google)
|
||
|
||
// Ktor server (для :server facade + a2aServer)
|
||
// Используем CIO вместо Netty — он KMP (jvm + linuxX64/ios/...), нам нужен
|
||
// для будущей native-сборки; на JVM работает идентично для нашего сценария.
|
||
implementation(libs.ktor.server.core)
|
||
implementation(libs.ktor.server.sse)
|
||
implementation(libs.ktor.server.cio)
|
||
implementation(libs.ktor.server.content.negotiation)
|
||
implementation(libs.ktor.serialization.kotlinx.json)
|
||
|
||
// Транспортные фасады (A2A остаётся заготовкой; на v1 не подключается в Main.kt —
|
||
// см. docs/ARCHITECTURE.md §3).
|
||
implementation(libs.a2a.server)
|
||
|
||
// MCP (Model Context Protocol) клиент — подключение внешних/внутренних MCP-серверов
|
||
implementation(libs.mcp.sdk.client)
|
||
implementation(libs.ktor.client.core)
|
||
implementation(libs.ktor.client.cio)
|
||
implementation(libs.ktor.client.content.negotiation)
|
||
implementation(libs.ktor.serialization.kotlinx.json)
|
||
|
||
// Логирование: kotlin-logging (тонкая обёртка slf4j-api) + logback-classic
|
||
// (binding для JVM; без него slf4j-api NOP-логирует и не падает).
|
||
implementation(libs.kotlin.logging)
|
||
implementation(libs.logback.classic)
|
||
}
|
||
|
||
commonTest.dependencies {
|
||
implementation(libs.kotlinx.coroutines.core)
|
||
implementation(libs.kotlin.test)
|
||
}
|
||
|
||
jvmTest.dependencies {
|
||
implementation(libs.kotlinx.coroutines.core)
|
||
implementation(libs.kotlinx.serialization.json)
|
||
implementation(libs.kotlin.test)
|
||
// Ktor test engine для smoke-тестов HTTP
|
||
implementation(libs.ktor.server.test.host)
|
||
}
|
||
}
|
||
}
|
||
|
||
// --- Fatjar (uberjar) ---
|
||
//
|
||
// :standalone — KMP jvm-only бэкенд. Стандартный KMP `jvmJar` содержит только
|
||
// наши классы и требует подтянуть все зависимости на runtime через classpath.
|
||
// Для дистрибутива нужен self-contained `*-all.jar`, который содержит и наш
|
||
// код, и runtime classpath. Делается это Shadow-плагином:
|
||
//
|
||
// 1. shadowJar расширяет `jvmJar` (через from(tasks.named("jvmJar")))
|
||
// 2. Добавляет все артефакты из jvmRuntimeClasspath как `zipTree`
|
||
// 3. Корректно сливает META-INF/services (через ServiceFileTransformer)
|
||
// 4. Прописывает Main-Class в манифесте
|
||
//
|
||
// Результат: `build/libs/standalone-*-all.jar` запускается как
|
||
// java -jar standalone-0.1.0-all.jar
|
||
//
|
||
// KMP-сборка под ios/lin/macos не затрагивается — задача живёт только в jvm.
|
||
// Shadow 8.x не авторегистрирует shadowJar в KMP-проектах (нет стандартного `jar`),
|
||
// поэтому регистрируем явно через tasks.register.
|
||
//
|
||
// В Gradle 9 KGP подменяет `configurations` на сгенерированный dependency-accessor
|
||
// (он возвращает List), поэтому используем прямой доступ к Project API.
|
||
val shadowJarTask = tasks.register<ShadowJar>("shadowJar") {
|
||
archiveBaseName.set("standalone")
|
||
archiveClassifier.set("all")
|
||
description = "Self-contained fatjar with all runtime dependencies bundled."
|
||
group = "build"
|
||
|
||
from(tasks.named("jvmJar"))
|
||
// javaClass-каст через Any? чтобы вытащить реальный ConfigurationContainer
|
||
// из любого типа, который сейчас прикидывается `configurations`.
|
||
val cc = try {
|
||
@Suppress("UNCHECKED_CAST")
|
||
configurations as org.gradle.api.artifacts.ConfigurationContainer
|
||
} catch (_: ClassCastException) {
|
||
// KGP-generated dependency accessor; обходим через raw project.
|
||
@Suppress("UNCHECKED_CAST")
|
||
(project as org.gradle.api.Project).configurations as org.gradle.api.artifacts.ConfigurationContainer
|
||
}
|
||
from(cc.getByName("jvmRuntimeClasspath"))
|
||
|
||
mergeServiceFiles()
|
||
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||
|
||
manifest {
|
||
attributes["Main-Class"] = "pw.binom.agentik.standalone.MainKt"
|
||
attributes["Implementation-Title"] = "agentik-standalone"
|
||
attributes["Implementation-Version"] = project.version.toString()
|
||
}
|
||
|
||
includeEmptyDirs = false
|
||
}
|