Skip to content

feat: Koog → Aigentic Platform run-publishing proof-of-concept#146

Open
nsmnds wants to merge 5 commits into
mainfrom
claude/focused-fermi-w6rkp
Open

feat: Koog → Aigentic Platform run-publishing proof-of-concept#146
nsmnds wants to merge 5 commits into
mainfrom
claude/focused-fermi-w6rkp

Conversation

@nsmnds

@nsmnds nsmnds commented Jun 2, 2026

Copy link
Copy Markdown
Collaborator

What & why

Investigates whether JetBrains Koog can be made to publish its agent runs to the Aigentic Platform using the same contract Aigentic itself uses, and provides a working proof-of-concept.

Answer: yes — and no fork/patch of Koog is needed. This was verified against the real Koog 1.0.0 source, not guessed.

How Aigentic publishes a run (the contract we target)

After every run, publishRun (src/core/.../agent/AgentExecutor.kt) makes a single authenticated request when a platform { … } is configured:

POST RunDto  ->  /gateway/runs        (HTTP Basic Auth; 201 / 401 / 400 / 500)

RunDto (src/platform/wirespec/gateway.ws) carries startedAt/finishedAt, config (task, model identifier, system prompt, tools, temperature…), result (Finished/Stuck/Fatal), messages[], and modelRequests[] (per-LLM-call token usage). Publishing is not runtime-specific — it is just "build a RunDto and POST it".

How the PoC mirrors it from Koog

Koog's agents-features-event-handler exposes the full lifecycle via handleEvents { … }. A small aigenticPlatform(name, secret, …) extension installs it, collects the run, and POSTs a RunDto on completion — the Koog-side equivalent of publishRun:

val agent = AIAgent(promptExecutor = executor, llmModel = model, systemPrompt = "") {
    aigenticPlatform(name = "", secret = "")   // publishes the run on completion
}
agent.run("")

Hooks used: onAgentStarting, onLLMCallStarting/onLLMCallCompleted (full Prompt, LLModel, tool descriptors, the Message.Assistant response + its token counts → modelRequests), and onAgentCompleted/onAgentExecutionFailed (→ Finished/Fatal result + POST).

What's included

  • PlatformRunDto.kt — DTOs mirroring gateway.ws.
  • KoogRunMapper.kt — maps Koog Message/MessagePart/ToolDescriptor → Aigentic DTOs.
  • AigenticPlatformExporter.kt — the aigenticPlatform(...) EventHandler exporter.
  • AigenticPlatformExporterTest.kt — runs a real Koog agent (LLM mocked via getMockExecutor, platform mocked via Ktor MockEngine) and asserts exactly one RunDto POSTed to /gateway/runs containing the system prompt, the assistant response, a modelRequests entry, and a FinishedResultDto. ✅ passing.
./gradlew -p poc/koog-platform test

Important: why this is an isolated build

Koog 1.0.0 is built with Kotlin 2.3.10; Aigentic is on Kotlin 2.1.10. Kotlin metadata is not forward-compatible, so the main (2.1.10) build cannot compile against Koog's artifacts. The PoC is therefore a standalone Gradle build under poc/koog-platform/ (its own settings.gradle.kts, Kotlin 2.3.10), is not wired into the root build, and does not affect ./gradlew build. PlatformRunDto.kt reproduces the contract locally for the same reason.

A production integration would reuse the Wirespec-generated RunDto/Gateway client from the published community.flock.aigentic:platform artifact (readable by a Kotlin ≥ 2.3 consumer) or align the two projects' Kotlin versions.

See poc/koog-platform/README.md for full details.


Generated by Claude Code

Demonstrates that JetBrains Koog can publish its agent runs to the Aigentic
Platform using the same `POST RunDto -> /gateway/runs` contract Aigentic uses.

It installs Koog's public EventHandler hooks (`handleEvents`) to collect a run
and POST a RunDto on completion, mirroring Aigentic's automatic `publishRun`.
A test runs a real Koog agent (LLM mocked via `getMockExecutor`, platform
mocked via a Ktor `MockEngine`) and asserts the RunDto posted to /gateway/runs.

This is an isolated Gradle build (Kotlin 2.3.10) because Koog 1.0.0 is built
with Kotlin 2.3.10 while the main project is on 2.1.10 and Kotlin metadata is
not forward-compatible; it is not wired into the root build and does not affect
`./gradlew build`.
claude added 4 commits June 2, 2026 08:44
Extends the proof-of-concept to cover the full Aigentic MessageDto union:
UrlMessageDto and Base64MessageDto are mapped from Koog's MessagePart.Attachment
(AttachmentContent.URL / Binary via asBase64()), and MimeTypeDto /
StructuredOutputMessageDto are added to the contract DTOs.

Documents the two compatibility caveats: Aigentic's MimeTypeDto is a fixed subset
(5 image types + PDF) so unsupported Koog media (video/audio/other) is skipped, and
structured output has no distinct Koog message type. Adds KoogRunMapperTest covering
every supported type and the unsupported-mime skip.
Adds a structured request/response example (PDF invoice -> typed Invoice ->
finished) using Koog's nodeLLMRequestStructured<T>, and shows the typed result is
published to the Aigentic Platform as the run result (FinishedResultDto.response)
plus the assistant JSON message.

The exporter now uses the model's actual response text for the finished result, so
structured output is captured faithfully. Documents Koog's first-class structured
output API and how a PDF attachment feeds into a structured request.
Mirrors how an Aigentic structured-output agent publishes: the exporter detects a
structured run from the agent's typed result (result !is String) and emits the final
assistant message as StructuredOutputMessageDto (Model sender), sets
FinishedResultDto.response to the structured JSON, and populates
config.responseJsonSchema from the prompt schema when available.

Wires a real PDF MessagePart.Attachment through a structured request so it is
published as a Base64MessageDto (APPLICATION_PDF). The structured-output test now
runs the full PDF-invoice -> typed Invoice -> finished flow and asserts the published
RunDto matches the shape an equivalent Aigentic run would produce.
…ion)

Mirrors Aigentic's MessageCategory assignment (seedInitialMessages): the exporter
records the base config prompt size at agent start, then categorizes messages as
SYSTEM_PROMPT / CONFIG_CONTEXT (config prompt) / RUN_CONTEXT (content before the first
model turn) / EXECUTION (from the first assistant message on). An explicit
metaInfo.metadata "aigentic.category" tag overrides the positional default, mirroring
Aigentic choosing the category from the API used.

Adds MessageCategoryTest for both signals and extends the structured PDF test to attach
a reference image in the config prompt (CONFIG_CONTEXT) and the invoice PDF in the run
(RUN_CONTEXT), asserting the published RunDto distinguishes them.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants