Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions .github/workflows/demo-compose-app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,12 @@ jobs:
gradle-tasks: [
"ktlintCheck lint :androidApp:assembleDebug :androidApp:assembleRelease",
":desktopApp:jar",
":webApp:composeCompatibilityBrowserDistribution"
":webApp:jsBrowserDevelopmentWebpack"
]
os: [ ubuntu-latest ]
include:
- gradle-tasks: ":commonApp:iosSimulatorArm64Binaries"
os: macos-latest-xlarge
os: macos-latest

steps:
- name: Configure Git
Expand Down
2 changes: 1 addition & 1 deletion examples/demo-compose-app/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ To run the application on iPhone device/simulator:
- Or use [Kotlin Multiplatform Mobile plugin](https://plugins.jetbrains.com/plugin/14936-kotlin-multiplatform-mobile) for Android Studio

### Web Distribution
Build web distribution: `./gradlew :webApp:composeCompatibilityBrowserDistribution`
Build web pack: `./gradlew :webApp:jsBrowserDevelopmentWebpack`
Deploy a dir `webApp/build/dist/composeWebCompatibility/productionExecutable` to a web server

### JS Browser
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ internal class CalculatorAgentProvider(private val provideLLMClient: suspend ()
) {
handleEvents {
onToolCallStarting { ctx ->
onToolCallEvent("Tool ${ctx.tool.name}, args ${ctx.toolArgs}")
onToolCallEvent("Tool ${ctx.toolName}, args ${ctx.toolArgs}")
}

onAgentExecutionFailed { ctx ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,14 @@ import ai.koog.agents.core.tools.annotations.LLMDescription
import kotlinx.serialization.Serializable

sealed class CalculatorTool(
override val name: String,
override val description: String,
) : Tool<CalculatorTool.Args, CalculatorTool.Result>() {
name: String,
description: String,
) : Tool<CalculatorTool.Args, CalculatorTool.Result>(
argsSerializer = Args.serializer(),
resultSerializer = Result.serializer(),
name = name,
description = description
) {
@Serializable
data class Args(
@property:LLMDescription("First number")
Expand All @@ -19,9 +24,6 @@ sealed class CalculatorTool(
@Serializable
class Result(val result: Float)

final override val argsSerializer = Args.serializer()
final override val resultSerializer = Result.serializer()

/**
* 2. Implement the tool (tools).
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@ import ai.koog.agents.core.tools.SimpleTool
import ai.koog.agents.core.tools.annotations.LLMDescription
import kotlinx.serialization.Serializable

object ExitTool : SimpleTool<ExitTool.Args>() {
object ExitTool : SimpleTool<ExitTool.Args>(
argsSerializer = Args.serializer(),
name = "exit",
description = "Exit the agent session with the specified result. Call this tool to finish the conversation with the user."
) {
@Serializable
data class Args(
@property:LLMDescription("The result of the agent session. Default is empty, if there's no particular result.")
val result: String = ""
)

override val argsSerializer = Args.serializer()
override val description: String =
"Exit the agent session with the specified result. Call this tool to finish the conversation with the user."

override suspend fun doExecute(args: Args): String = args.result
override suspend fun execute(args: Args): String = args.result
}
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ internal class WeatherAgentProvider(private val provideLLMClient: suspend () ->
) {
handleEvents {
onToolCallStarting { ctx ->
onToolCallEvent("Tool ${ctx.tool.name}, args ${ctx.toolArgs}")
onToolCallEvent("Tool ${ctx.toolName}, args ${ctx.toolArgs}")
}

onAgentExecutionFailed { ctx ->
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package com.jetbrains.example.koog.compose.agents.weather

import ai.koog.agents.core.tools.Tool
import ai.koog.agents.core.tools.ToolResult
import ai.koog.agents.core.tools.ToolResultUtils
import ai.koog.agents.core.tools.annotations.LLMDescription
import kotlinx.datetime.DateTimePeriod
import kotlinx.datetime.LocalDate
Expand All @@ -12,7 +10,6 @@ import kotlinx.datetime.offsetAt
import kotlinx.datetime.plus
import kotlinx.datetime.toInstant
import kotlinx.datetime.toLocalDateTime
import kotlinx.serialization.KSerializer
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlin.time.Clock
Expand Down Expand Up @@ -46,7 +43,12 @@ sealed interface WeatherTools {
class CurrentDatetimeTool(
val defaultTimeZone: TimeZone = UTC_ZONE,
val clock: Clock = CLOCK,
) : Tool<CurrentDatetimeTool.Args, CurrentDatetimeTool.Result>() {
) : Tool<CurrentDatetimeTool.Args, CurrentDatetimeTool.Result>(
argsSerializer = Args.serializer(),
resultSerializer = Result.serializer(),
name = "current_datetime",
description = "Get the current date and time in the specified timezone"
) {
@Serializable
data class Args(
@property:LLMDescription("The timezone to get the current date and time in (e.g., 'UTC', 'America/New_York', 'Europe/London'). Defaults to UTC.")
Expand All @@ -59,17 +61,7 @@ sealed interface WeatherTools {
val date: String,
val time: String,
val timezone: String
) : ToolResult.TextSerializable() {
override fun textForLLM(): String {
return "Current datetime: $datetime, Date: $date, Time: $time, Timezone: $timezone"
}
}

override val argsSerializer = Args.serializer()
override val resultSerializer: KSerializer<Result> = ToolResultUtils.toTextSerializer<Result>()

override val name = "current_datetime"
override val description = "Get the current date and time in the specified timezone"
)

override suspend fun execute(args: Args): Result {
val zoneId = try {
Expand All @@ -94,6 +86,10 @@ sealed interface WeatherTools {
timezone = zoneId.id
)
}

override fun encodeResultToString(result: Result): String {
return "Current datetime: ${result.datetime}, Date: ${result.date}, Time: ${result.time}, Timezone: ${result.timezone}"
}
}

/**
Expand All @@ -102,7 +98,12 @@ sealed interface WeatherTools {
class AddDatetimeTool(
val defaultTimeZone: TimeZone = UTC_ZONE,
val clock: Clock = CLOCK,
) : Tool<AddDatetimeTool.Args, AddDatetimeTool.Result>() {
) : Tool<AddDatetimeTool.Args, AddDatetimeTool.Result>(
argsSerializer = Args.serializer(),
resultSerializer = Result.serializer(),
name = "add_datetime",
description = "Add a duration to a date. Use this tool when you need to calculate offsets, such as tomorrow, in two days, etc."
) {
@Serializable
data class Args(
@property:LLMDescription("The date to add to in ISO format (e.g., '2023-05-20')")
Expand All @@ -122,43 +123,7 @@ sealed interface WeatherTools {
val daysAdded: Int,
val hoursAdded: Int,
val minutesAdded: Int
) : ToolResult.TextSerializable() {
override fun textForLLM(): String {
return buildString {
append("Date: $date")
if (originalDate.isBlank()) {
append(" (starting from today)")
} else {
append(" (starting from $originalDate)")
}

if (daysAdded != 0 || hoursAdded != 0 || minutesAdded != 0) {
append(" after adding")

if (daysAdded != 0) {
append(" $daysAdded days")
}

if (hoursAdded != 0) {
if (daysAdded != 0) append(",")
append(" $hoursAdded hours")
}

if (minutesAdded != 0) {
if (daysAdded != 0 || hoursAdded != 0) append(",")
append(" $minutesAdded minutes")
}
}
}
}
}

override val argsSerializer = Args.serializer()
override val resultSerializer = ToolResultUtils.toTextSerializer<Result>()

override val name = "add_datetime"
override val description =
"Add a duration to a date. Use this tool when you need to calculate offsets, such as tomorrow, in two days, etc."
)

override suspend fun execute(args: Args): Result {
val baseDate = if (args.date.isNotBlank()) {
Expand Down Expand Up @@ -193,6 +158,35 @@ sealed interface WeatherTools {
minutesAdded = args.minutes
)
}

override fun encodeResultToString(result: Result): String {
return buildString {
append("Date: ${result.date}")
if (result.originalDate.isBlank()) {
append(" (starting from today)")
} else {
append(" (starting from ${result.originalDate})")
}

if (result.daysAdded != 0 || result.hoursAdded != 0 || result.minutesAdded != 0) {
append(" after adding")

if (result.daysAdded != 0) {
append(" ${result.daysAdded} days")
}

if (result.hoursAdded != 0) {
if (result.daysAdded != 0) append(",")
append(" ${result.hoursAdded} hours")
}

if (result.minutesAdded != 0) {
if (result.daysAdded != 0 || result.hoursAdded != 0) append(",")
append(" ${result.minutesAdded} minutes")
}
}
}
}
}

/**
Expand All @@ -201,7 +195,12 @@ sealed interface WeatherTools {
class WeatherForecastTool(
private val openMeteoClient: OpenMeteoClient = OpenMeteoClient(),
val defaultTimeZone: TimeZone = UTC_ZONE
) : Tool<WeatherForecastTool.Args, WeatherForecastTool.Result>() {
) : Tool<WeatherForecastTool.Args, WeatherForecastTool.Result>(
argsSerializer = Args.serializer(),
resultSerializer = Result.serializer(),
name = "weather_forecast",
description = "Get a weather forecast for a location with specified granularity (daily or hourly)"
) {
@Serializable
data class Args(
@property:LLMDescription("The location to get the weather forecast for (e.g., 'New York', 'London', 'Paris')")
Expand All @@ -221,28 +220,7 @@ sealed interface WeatherTools {
val forecast: String,
val date: String,
val granularity: Granularity
) : ToolResult.TextSerializable() {
override fun textForLLM(): String {
val granularityText = when (granularity) {
Granularity.DAILY -> "daily"
Granularity.HOURLY -> "hourly"
}
val dateInfo = if (date.isBlank()) "starting from today" else "for $date"
val formattedLocation = if (locationCountry.isNullOrBlank()) {
locationName
} else {
"$locationName, $locationCountry"
}.trim().trimEnd(',')

return "Weather forecast for $formattedLocation ($granularityText, $dateInfo):\n$forecast"
}
}

override val argsSerializer = Args.serializer()
override val resultSerializer = ToolResultUtils.toTextSerializer<Result>()

override val name = "weather_forecast"
override val description = "Get a weather forecast for a location with specified granularity (daily or hourly)"
)

override suspend fun execute(args: Args): Result {
// Search for the location
Expand Down Expand Up @@ -371,5 +349,20 @@ sealed interface WeatherTools {
else -> "Unknown"
}
}

override fun encodeResultToString(result: Result): String {
val granularityText = when (result.granularity) {
Granularity.DAILY -> "daily"
Granularity.HOURLY -> "hourly"
}
val dateInfo = if (result.date.isBlank()) "starting from today" else "for ${result.date}"
val formattedLocation = if (result.locationCountry.isNullOrBlank()) {
result.locationName
} else {
"${result.locationName}, ${result.locationCountry}"
}.trim().trimEnd(',')

return "Weather forecast for $formattedLocation ($granularityText, $dateInfo):\n${result.forecast}"
}
}
}
2 changes: 1 addition & 1 deletion examples/demo-compose-app/gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ javaVersion = "17"
jetbrains-lifecycle = "2.9.6"
jetbrains-navigation = "2.9.1"
koin-bom = "4.1.1"
koog = "0.5.2"
koog = "0.6.0"
kotlin = "2.2.21"
kotlinx-coroutines = "1.10.2"
kotlinx-datetime = "0.7.1-0.6.x-compat"
Expand Down