From 93d3fd2f6fb59f78dcfda1564fef58abc26339f2 Mon Sep 17 00:00:00 2001 From: "karol.rinc" Date: Thu, 17 Jul 2025 14:56:19 +0200 Subject: [PATCH 1/2] Cleanup --- src/main/kotlin/pl/brightinventions/koog/{ => agent}/JiraApi.kt | 2 +- src/main/kotlin/pl/brightinventions/koog/{ => agent}/Main.kt | 2 +- .../kotlin/pl/brightinventions/koog/{ => agent}/SlackApi.kt | 2 +- .../pl/brightinventions/koog/{ => agent}/TicketUpdateToolSet.kt | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) rename src/main/kotlin/pl/brightinventions/koog/{ => agent}/JiraApi.kt (97%) rename src/main/kotlin/pl/brightinventions/koog/{ => agent}/Main.kt (98%) rename src/main/kotlin/pl/brightinventions/koog/{ => agent}/SlackApi.kt (97%) rename src/main/kotlin/pl/brightinventions/koog/{ => agent}/TicketUpdateToolSet.kt (96%) diff --git a/src/main/kotlin/pl/brightinventions/koog/JiraApi.kt b/src/main/kotlin/pl/brightinventions/koog/agent/JiraApi.kt similarity index 97% rename from src/main/kotlin/pl/brightinventions/koog/JiraApi.kt rename to src/main/kotlin/pl/brightinventions/koog/agent/JiraApi.kt index a054049..20e2c8c 100644 --- a/src/main/kotlin/pl/brightinventions/koog/JiraApi.kt +++ b/src/main/kotlin/pl/brightinventions/koog/agent/JiraApi.kt @@ -1,4 +1,4 @@ -package pl.brightinventions.koog +package pl.brightinventions.koog.agent import io.ktor.client.HttpClient import io.ktor.client.call.body diff --git a/src/main/kotlin/pl/brightinventions/koog/Main.kt b/src/main/kotlin/pl/brightinventions/koog/agent/Main.kt similarity index 98% rename from src/main/kotlin/pl/brightinventions/koog/Main.kt rename to src/main/kotlin/pl/brightinventions/koog/agent/Main.kt index f9adb65..f78f1b4 100644 --- a/src/main/kotlin/pl/brightinventions/koog/Main.kt +++ b/src/main/kotlin/pl/brightinventions/koog/agent/Main.kt @@ -1,4 +1,4 @@ -package pl.brightinventions.koog +package pl.brightinventions.koog.agent import ai.koog.agents.core.agent.AIAgent import ai.koog.agents.core.tools.ToolRegistry diff --git a/src/main/kotlin/pl/brightinventions/koog/SlackApi.kt b/src/main/kotlin/pl/brightinventions/koog/agent/SlackApi.kt similarity index 97% rename from src/main/kotlin/pl/brightinventions/koog/SlackApi.kt rename to src/main/kotlin/pl/brightinventions/koog/agent/SlackApi.kt index c9f6227..5a5fafc 100644 --- a/src/main/kotlin/pl/brightinventions/koog/SlackApi.kt +++ b/src/main/kotlin/pl/brightinventions/koog/agent/SlackApi.kt @@ -1,4 +1,4 @@ -package pl.brightinventions.koog +package pl.brightinventions.koog.agent import io.ktor.client.HttpClient import io.ktor.client.request.header diff --git a/src/main/kotlin/pl/brightinventions/koog/TicketUpdateToolSet.kt b/src/main/kotlin/pl/brightinventions/koog/agent/TicketUpdateToolSet.kt similarity index 96% rename from src/main/kotlin/pl/brightinventions/koog/TicketUpdateToolSet.kt rename to src/main/kotlin/pl/brightinventions/koog/agent/TicketUpdateToolSet.kt index d1846eb..32c001f 100644 --- a/src/main/kotlin/pl/brightinventions/koog/TicketUpdateToolSet.kt +++ b/src/main/kotlin/pl/brightinventions/koog/agent/TicketUpdateToolSet.kt @@ -1,4 +1,4 @@ -package pl.brightinventions.koog +package pl.brightinventions.koog.agent import ai.koog.agents.core.tools.annotations.LLMDescription import ai.koog.agents.core.tools.annotations.Tool From f76900e968d218a77028fdf2711f32ed68be1481 Mon Sep 17 00:00:00 2001 From: "karol.rinc" Date: Thu, 17 Jul 2025 16:04:04 +0200 Subject: [PATCH 2/2] WIP --- .../koog/financial/news/monitor/Article.kt | 18 +++++++ .../financial/news/monitor/ArticleTool.kt | 21 ++++++++ .../koog/financial/news/monitor/Main.kt | 52 ++++++++++++++++++ .../monitor/markdownNewsArticleDefinition.kt | 28 ++++++++++ .../monitor/parseMarkdownStreamToArticles.kt | 54 +++++++++++++++++++ 5 files changed, 173 insertions(+) create mode 100644 src/main/kotlin/pl/brightinventions/koog/financial/news/monitor/Article.kt create mode 100644 src/main/kotlin/pl/brightinventions/koog/financial/news/monitor/ArticleTool.kt create mode 100644 src/main/kotlin/pl/brightinventions/koog/financial/news/monitor/Main.kt create mode 100644 src/main/kotlin/pl/brightinventions/koog/financial/news/monitor/markdownNewsArticleDefinition.kt create mode 100644 src/main/kotlin/pl/brightinventions/koog/financial/news/monitor/parseMarkdownStreamToArticles.kt diff --git a/src/main/kotlin/pl/brightinventions/koog/financial/news/monitor/Article.kt b/src/main/kotlin/pl/brightinventions/koog/financial/news/monitor/Article.kt new file mode 100644 index 0000000..9e789e2 --- /dev/null +++ b/src/main/kotlin/pl/brightinventions/koog/financial/news/monitor/Article.kt @@ -0,0 +1,18 @@ +package pl.brightinventions.koog.financial.news.monitor + +import ai.koog.agents.core.tools.Tool.Args +import kotlinx.serialization.Serializable + +@Serializable +data class Article( + val title: String, + val summary: String, + val author: String, + val source: String, + val url: String, +) : Args { + + override fun toString(): String { + return "$title by $author, source: $source, URL: $url\nSummary: $summary" + } +} diff --git a/src/main/kotlin/pl/brightinventions/koog/financial/news/monitor/ArticleTool.kt b/src/main/kotlin/pl/brightinventions/koog/financial/news/monitor/ArticleTool.kt new file mode 100644 index 0000000..9cf4c6f --- /dev/null +++ b/src/main/kotlin/pl/brightinventions/koog/financial/news/monitor/ArticleTool.kt @@ -0,0 +1,21 @@ +package pl.brightinventions.koog.financial.news.monitor + +import ai.koog.agents.core.tools.SimpleTool +import ai.koog.agents.core.tools.ToolDescriptor +import kotlinx.serialization.KSerializer + +class ArticleTool : SimpleTool
() { + + override suspend fun doExecute(args: Article): String { + return args.toString() + } + + override val argsSerializer: KSerializer
= Article.serializer() + + override val descriptor: ToolDescriptor = ToolDescriptor( + name = "articleTool", + description = "A tool to parse book information from markdown", + requiredParameters = listOf(), + optionalParameters = listOf() + ) +} \ No newline at end of file diff --git a/src/main/kotlin/pl/brightinventions/koog/financial/news/monitor/Main.kt b/src/main/kotlin/pl/brightinventions/koog/financial/news/monitor/Main.kt new file mode 100644 index 0000000..51a7786 --- /dev/null +++ b/src/main/kotlin/pl/brightinventions/koog/financial/news/monitor/Main.kt @@ -0,0 +1,52 @@ +package pl.brightinventions.koog.financial.news.monitor + +import ai.koog.agents.core.agent.AIAgent +import ai.koog.agents.core.agent.config.AIAgentConfig +import ai.koog.agents.core.dsl.builder.forwardTo +import ai.koog.agents.core.dsl.builder.strategy +import ai.koog.agents.core.tools.ToolRegistry +import ai.koog.prompt.executor.llms.all.simpleOpenAIExecutor +import kotlinx.coroutines.runBlocking + +fun main() { + val agentStrategy = strategy("articles-assistant") { + val getMdOutput by node { input -> + val mdDefinition = markdownArticleDefinition() + + llm.writeSession { + updatePrompt { user(input) } + val markdownStream = requestLLMStreaming(mdDefinition) + + parseMarkdownStreamToArticles(markdownStream) + .collect { article -> + callTool(article) + } + } + + "" + } + + edge(nodeStart forwardTo getMdOutput) + edge(getMdOutput forwardTo nodeFinish) + } + + + val agent = AIAgent( + promptExecutor = simpleOpenAIExecutor(System.getenv("OPENAI_API_KEY")), + strategy = agentStrategy, + agentConfig = AIAgentConfig.withSystemPrompt( + prompt = """ + You're AI financial news assistant. Please provide users with comprehensive and structured information about the financial news of the world. + """.trimIndent() + ), + toolRegistry = ToolRegistry { + tool(ArticleTool()) + }, + ) + + val command = """ + Provide me latest financial news. + """.trimIndent() + + runBlocking { agent.run(command) } +} \ No newline at end of file diff --git a/src/main/kotlin/pl/brightinventions/koog/financial/news/monitor/markdownNewsArticleDefinition.kt b/src/main/kotlin/pl/brightinventions/koog/financial/news/monitor/markdownNewsArticleDefinition.kt new file mode 100644 index 0000000..f3fd519 --- /dev/null +++ b/src/main/kotlin/pl/brightinventions/koog/financial/news/monitor/markdownNewsArticleDefinition.kt @@ -0,0 +1,28 @@ +package pl.brightinventions.koog.financial.news.monitor + +import ai.koog.prompt.markdown.markdown +import ai.koog.prompt.structure.markdown.MarkdownStructuredDataDefinition + +fun markdownArticleDefinition(): MarkdownStructuredDataDefinition { + return MarkdownStructuredDataDefinition("articlesList", schema = { + markdown { + header(1, "articleTitle") + bulleted { + item("summary") + item("author") + item("source") + item("url") + } + } + }, examples = { + markdown { + header(1, "CocaCola stock is reaching all time high!") + bulleted { + item("CocaCola's stock has reached an all-time high, driven by strong quarterly earnings and increased consumer demand.") + item("John Doe") + item("Financial News Daily") + item("https://financialnewsdaily.com/coca-cola-stock-high") + } + } + }) +} \ No newline at end of file diff --git a/src/main/kotlin/pl/brightinventions/koog/financial/news/monitor/parseMarkdownStreamToArticles.kt b/src/main/kotlin/pl/brightinventions/koog/financial/news/monitor/parseMarkdownStreamToArticles.kt new file mode 100644 index 0000000..d247856 --- /dev/null +++ b/src/main/kotlin/pl/brightinventions/koog/financial/news/monitor/parseMarkdownStreamToArticles.kt @@ -0,0 +1,54 @@ +package pl.brightinventions.koog.financial.news.monitor + +import ai.koog.prompt.structure.markdown.markdownStreamingParser +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.FlowCollector +import kotlinx.coroutines.flow.flow + +fun parseMarkdownStreamToArticles(markdownStream: Flow): Flow
{ + return flow { + markdownStreamingParser { + var currentArticleTitle = "" + val bulletPoints = mutableListOf() + + onHeader(1) { headerText -> + if (currentArticleTitle.isNotEmpty() && bulletPoints.isNotEmpty()) { + mapAndEmit(bulletPoints, currentArticleTitle) + } + + currentArticleTitle = headerText + bulletPoints.clear() + } + + onBullet { bulletText -> + bulletPoints.add(bulletText) + } + + onFinishStream { + if (currentArticleTitle.isNotEmpty() && bulletPoints.isNotEmpty()) { + mapAndEmit(bulletPoints, currentArticleTitle) + } + } + }.parseStream(markdownStream) + } +} + +private suspend fun FlowCollector
.mapAndEmit( + bulletPoints: List, + currentArticleTitle: String +) { + val summary = bulletPoints.getOrNull(0) ?: "" + val author = bulletPoints.getOrNull(1) ?: "" + val source = bulletPoints.getOrNull(2) ?: "" + val url = bulletPoints.getOrNull(3) ?: "" + + emit( + Article( + title = currentArticleTitle, + summary = summary, + author = author, + source = source, + url = url + ) + ) +} \ No newline at end of file