Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
61c2663
Draft Ktor integration proposal in examples
Ololoshechkin Jul 11, 2025
216bb59
Create separate module with Ktor plugin and support YAML properties f…
Ololoshechkin Jul 13, 2025
bfca0d1
Add tests for environment config loading and model id parsing
Ololoshechkin Jul 13, 2025
9121004
- Rewrite Koog plugin to class-based plugin which allows for easier r…
nomisRev Jul 20, 2025
b8d3ebc
Adopt version catalog for Ktor dependencies and refactor build script…
nomisRev Jul 20, 2025
2282684
Rename `koog-ktor-plugin` module to `koog-ktor` and refactor related …
nomisRev Jul 27, 2025
5ce70c2
Adopt Ktor version catalog in `settings.gradle.kts`, remove explicit …
nomisRev Jul 27, 2025
1150765
Remove `suspend` from `mcp` configuration, and create a temporary sco…
nomisRev Jul 27, 2025
51e83c2
- Remove `suspend` from `mcp` configuration, and create a temporary s…
nomisRev Jul 27, 2025
ec5dd3d
Revert "Adopt version catalog for Ktor dependencies and refactor buil…
nomisRev Jul 29, 2025
81bd62f
Remove `yarn.lock` from the `kotlin-js-store` directory.
nomisRev Jul 29, 2025
bc1821f
Rename `MCPToolsConfig` to `McpToolsConfig` and remove unused `Global…
nomisRev Jul 29, 2025
7c727ab
Make client configuration properties nullable and set default values …
nomisRev Jul 29, 2025
8928514
Refactor AI agent functions to include `model` parameter and improve …
nomisRev Jul 29, 2025
ff1cd62
Introduce `CoroutineScope` for improved configuration flexibility, re…
nomisRev Jul 29, 2025
edd7a29
Simplify YAML configuration structure, remove redundant timeout setti…
nomisRev Jul 29, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ build
env.properties
.qodana/code-coverage
local.properties
/kotlin-js-store/yarn.lock
1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ dependencies {
dokka(project(":prompt:prompt-tokenizer"))
dokka(project(":prompt:prompt-xml"))
dokka(project(":koog-spring-boot-starter"))
dokka(project(":koog-ktor"))
dokka(project(":rag:rag-base"))
dokka(project(":rag:vector-storage"))
}
Expand Down
2 changes: 2 additions & 0 deletions examples/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,14 @@ dependencies {
api(project(":prompt:prompt-executor:prompt-executor-clients:prompt-executor-bedrock-client"))
api(project(":prompt:prompt-executor:prompt-executor-llms"))
api(project(":prompt:prompt-executor:prompt-executor-llms-all"))
api(project(":koog-ktor"))

api(libs.kotlinx.datetime)

implementation(libs.logback.classic)
implementation(libs.opentelemetry.exporter.logging)
implementation(libs.opentelemetry.exporter.otlp)
implementation(libs.ktor.server.cio)
implementation(project.dependencies.platform(libs.opentelemetry.bom))

testImplementation(kotlin("test"))
Expand Down
3 changes: 3 additions & 0 deletions examples/demo-android-app/gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,11 @@ androidx-datastore-preferences = { group = "androidx.datastore", name = "datasto
# Koog
koog-agents = { module = "ai.koog:koog-agents", version.ref = "koog" }


# Ktor
ktor-client-core = { group = "io.ktor", name = "ktor-client-core", version.ref = "ktor" }


ktor-client-android = { group = "io.ktor", name = "ktor-client-android", version.ref = "ktor" }
ktor-client-content-negotiation = { group = "io.ktor", name = "ktor-client-content-negotiation", version.ref = "ktor" }
ktor-serialization-kotlinx-json = { group = "io.ktor", name = "ktor-serialization-kotlinx-json", version.ref = "ktor" }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
package ai.koog.agents.example.ktor

import ai.koog.agents.core.tools.annotations.LLMDescription
import ai.koog.agents.core.tools.annotations.Tool
import ai.koog.agents.core.tools.reflect.tool
import ai.koog.agents.ext.agent.reActStrategy
import ai.koog.agents.features.opentelemetry.feature.OpenTelemetry
import ai.koog.ktor.*
import ai.koog.prompt.dsl.prompt
import ai.koog.prompt.executor.clients.openai.OpenAIModels
import ai.koog.prompt.executor.model.PromptExecutorExt.execute
import ai.koog.prompt.llm.OllamaModels
import io.ktor.http.*
import io.ktor.server.application.*
import io.ktor.server.cio.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import io.opentelemetry.exporter.logging.LoggingSpanExporter

@Tool
@LLMDescription("Searches in Google and returns the most relevant page URLs")
fun searchInGoogle(request: String, numberOfPages: String): List<String> {
return emptyList()
}

@Tool
@LLMDescription("Executes bash command")
fun executeBash(command: String): String {
return "bash not supported"
}


@Tool
@LLMDescription("Secret function -- call it and you'll see the output")
fun doSomethingElse(input: String): String {
return "Surprise! I do nothing. Never call me again -_-"
}


fun main(args: Array<String>) = EngineMain.main(args)

fun Application.main() {
configureKoog()

defineRoutes()
}

fun Application.configureKoog() {
// Install the Koog plugin
// LLM configurations will be loaded from application.yaml
// You can still provide additional configuration or override settings from the YAML file
install(Koog) {
// The following configurations are optional and will override any settings from application.yaml
llm {
// Example of overriding a configuration from application.yaml
// This will take precedence over the configuration in the YAML file
openAI(apiKey = System.getenv("OPENAI_API_KEY") ?: "override-from-code") {
// Override baseUrl only if needed
// baseUrl = "custom-override-url"
}

fallback { }
}

agentConfig {
mcp {
sse("put some url here...")
}

registerTools {
tool(::searchInGoogle)
tool(::executeBash)
tool(::doSomethingElse)
}

prompt {
system("You are professional joke based on user's request")
}

install(OpenTelemetry) {
addSpanExporter(LoggingSpanExporter())
}
}
}
}

fun Application.defineRoutes() {
routing {
route("api/v1") {
normalRoutes()
}

route("agents/v1") {
agenticRoutes()
}
}
}

private fun Route.agenticRoutes() {
get("user") {
val userRequest = call.receive<String>()

val isHarmful = llm().moderate(prompt("id") {
user(userRequest)
}, OpenAIModels.Moderation.Omni).isHarmful

if (isHarmful) {
call.respond(HttpStatusCode.BadRequest, "Harmful content detected")
return@get
}

val updatedRequest = llm().execute(prompt("id") {
system(
"You are a helpful assistant that can correct user answers. " +
"You will get a user's question and your task is to make it more clear for the further processing."
)
user(userRequest)
}, OllamaModels.Meta.LLAMA_3_2)

val output = singleRunAgent(updatedRequest.content, OpenAIModels.Chat.GPT4_1)
call.respond(HttpStatusCode.OK, output)
}
get("organization") {
val orgName = call.parameters["name"]!!
val output = aiAgent(reActStrategy(), OpenAIModels.Chat.GPT4_1, "What's new in $orgName organization")
call.respond(HttpStatusCode.OK, output)
}
}

private fun Route.normalRoutes() {
get("user") {
call.respondText { "Hello, user!" }
}
get("organization") {
call.respondText { "Hello, organization!" }
}
}
11 changes: 11 additions & 0 deletions examples/src/main/resources/application.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
ktor:
application:
modules:
- ai.koog.agents.example.ktor.KtorIntegrationExampleKt.main

koog:
openai.apikey: "your-openai-api-key"
anthropic.apikey: "your-anthropic-api-key"
google.apikey: "your-google-api-key"
openrouter.apikey: "your-openrouter-api-key"
ollama.enable: true
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should require a url for Ollama?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It follows the same behavior as the Ollama Koog integration which is the default URL, or ollama.baseUrl = "...", or ollama { baseUrl = "..." } in code.

3 changes: 2 additions & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ kotlinx-coroutines = "1.10.2"
kotlinx-datetime = "0.6.2"
kotlinx-io = "0.7.0"
kotlinx-serialization = "1.8.1" # check with IJ
ktor3 = "3.1.3"
ktor3 = "3.2.2"
lettuce = "6.5.5.RELEASE"
logback = "1.5.13"
oshai-logging = "7.0.7"
Expand Down Expand Up @@ -41,6 +41,7 @@ ktor-client-content-negotiation = { module = "io.ktor:ktor-client-content-negoti
ktor-client-logging = { module = "io.ktor:ktor-client-logging", version.ref = "ktor3" }
ktor-client-sse = { module = "io.ktor:ktor-server-sse", version.ref = "ktor3" }
ktor-serialization-kotlinx-json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor3" }
ktor-server-core = { module = "io.ktor:ktor-server-core", version.ref = "ktor3" }
ktor-server-cio = { module = "io.ktor:ktor-server-cio", version.ref = "ktor3" }
ktor-server-sse = { module = "io.ktor:ktor-server-sse", version.ref = "ktor3" }
lettuce-core = { module = "io.lettuce:lettuce-core", version.ref = "lettuce" }
Expand Down
1 change: 1 addition & 0 deletions koog-agents/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ val excluded = setOf(
":examples",
":integration-tests",
":koog-spring-boot-starter",
":koog-ktor",
project.path, // the current project should not depend on itself
)

Expand Down
Loading
Loading