diff --git a/agents/agent-koog/README.md b/agents/agent-koog/README.md index e59acc37..a6bffd3f 100644 --- a/agents/agent-koog/README.md +++ b/agents/agent-koog/README.md @@ -1,50 +1,72 @@ # agent-koog -This is a Kotlin Multiplatform project targeting Android. +これは Android をターゲットとした Kotlin Multiplatform プロジェクトです。 +Kotlin Multiplatform による Amazon Bedrock を使った Koog によるチャットボットエージェントライブラリと、そのフロントエンドとなる Android アプリです。 -- [/composeApp](./composeApp/src) is for code that will be shared across your Compose Multiplatform applications. - It contains several subfolders: +- [/app](./app/src)は、フロントエンドとなる Android アプリです。 +- [/share](./share/src) は Compose Multiplatformアプリケーション間で共有されるコード用です。 - - [commonMain](./composeApp/src/commonMain/kotlin) is for code that’s common for all targets. - - Other folders are for Kotlin code that will be compiled for only the platform indicated in the folder name. - For example, if you want to use Apple’s CoreCrypto for the iOS part of your Kotlin app, - the [iosMain](./composeApp/src/iosMain/kotlin) folder would be the right place for such calls. - Similarly, if you want to edit the Desktop (JVM) specific part, the [jvmMain](./composeApp/src/jvmMain/kotlin) - folder is the appropriate location. +## 環境変数 -## Build and Run Android Application +サンプルアプリのため、AWSの一時的なクレデンシャルや OpenTelemetry Observer の設定はビルド時に環境変数から解決しています。 +Android Studio を使用する場合は、Android Studio の起動時に設定されている必要があります。 -To build and run the development version of the Android app, use the run configuration from the run widget -in your IDE’s toolbar or build it directly from the terminal: +| 環境変数名 | 必須か? | 設定する値の説明 | デフォルト値 | +| -------- | ---- | -------------- | ---------- | +| AWS_ACCESS_KEY_ID | 必須 | AWS のアクセスキーID (AWS IAM Identity Center の一時的なクレデンシャルの値で可) | (なし) | +| AWS_SECRET_ACCESS_KEY | 必須 | AWS のシークレットキー (AWS IAM Identity Center の一時的なクレデンシャルの値で可) | (なし) | +| AWS_SESSION_TOKEN | 条件次第 | AWS のセッショントークン (AWS IAM Identity Center の一時的なクレデンシャルの場合は必須) | (なし) | +| AWS_REGION | 任意 | AWS のリージョン | us-west-2 | +| OTLP_BACKEND | 条件次第 | 使用する Observer を指定。指定されない場合や不正値の場合は OpenTelemetry のトレーシングを送信しません。指定可能なObserver は現状、 jaeger または langfuse。※ langfuse は動作未確認 | (なし) | +| OTLP_ENDPOINT | 任意 | OpenTelemetry Observer のエンドポイント URL。OTLP_BACKEND に jaeger または langfuse が指定されている場合にのみ使われます。 | | +| OTLP_HEADERS | 任意 | Observer にテレメトリーを送信する際に付与する HTTP リクエストヘッダーを `ヘッダー名:値` 形式で指定できます。複数指定する場合は `,` 区切りとする想定ですが、現状は 1 つのみの指定しか対応していません。 | (なし) | -- on macOS/Linux +### macOS で環境変数を設定して Android Studio を起動する方法 + +```shell +# (環境変数を export で設定) +export AWS_ACCESS_KEY_ID=XXXX +# (環境変数の設定の記述を割愛) + +# Android Studio を起動 +open -a "Android Studio" +``` + +## Androidアプリケーションのビルドと実行 + +Androidアプリの開発バージョンをビルドして実行するには、IDEのツールバーにある実行ウィジェットから実行構成を使用するか、ターミナルから直接ビルドしてください: + +- macOS/Linuxの場合 ```shell ./gradlew :composeApp:assembleDebug ``` -- on Windows +- Windowsの場合 ```shell .\gradlew.bat :composeApp:assembleDebug ``` --- +[Kotlin Multiplatform](https://www.jetbrains.com/help/kotlin-multiplatform-dev/get-started.html)について詳しく学ぶ… + +## 可観測性(Observability) -Learn more about [Kotlin Multiplatform](https://www.jetbrains.com/help/kotlin-multiplatform-dev/get-started.html)… +Langfuse などの SaaS を使用する前に、ローカルで Jaeger を使用して確認して、OpenTelemetry のトレースがきちんと登録できることを確認した上で SaaS を使うことで、アプリ側の計装や Android のネットワーク設定の問題か SaaS との連携固有の問題なのかを切り分けできます。 -## Observability +### Jaeger の起動方法 `$download_url` は からダウンロードURLを調べて取得したURLに置き換えてください。 ```shell -# Jaegerを起動 -curl -sSL "$download_url" -o jaeger-2.13.0.tar.gz -tar -xzf jaeger-2.13.0.tar.gz -cd jaeger-2.13.0 +# Jaeger を起動 +curl -sSL "$download_url" -o jaeger.tar.gz +tar -xzf jaeger.tar.gz +cd jaeger COLLECTOR_OTLP_ENABLED=true ./jaeger \ --set=receivers.otlp.protocols.grpc.endpoint=0.0.0.0:4317 \ --set=receivers.otlp.protocols.http.endpoint=0.0.0.0:4318 ``` -Access to + にアクセスしてください diff --git a/agents/agent-koog/app/build.gradle.kts b/agents/agent-koog/app/build.gradle.kts new file mode 100644 index 00000000..973b197a --- /dev/null +++ b/agents/agent-koog/app/build.gradle.kts @@ -0,0 +1,81 @@ +plugins { + alias(libs.plugins.androidApplication) + alias(libs.plugins.kotlinAndroid) + alias(libs.plugins.composeCompiler) + alias(libs.plugins.composeMultiplatform) + + // Add Kotlin serialization plugin for Koog API support + kotlin("plugin.serialization") version "2.3.0" +} + +android { + namespace = "com.example.koog_example" + compileSdkVersion(libs.versions.android.compileSdk.get().toInt()) + + defaultConfig { + applicationId = "com.example.koog_example" + minSdk = libs.versions.android.minSdk.get().toInt() + targetSdk = libs.versions.android.targetSdk.get().toInt() + versionCode = 1 + versionName = "1.0" + } + + buildFeatures { + buildConfig = true + } + + packaging { + resources { + excludes += "/META-INF/{AL2.0,LGPL2.1}" + } + dex { + useLegacyPackaging = false + } + } + + buildTypes { + getByName("release") { + isMinifyEnabled = true + // 検証コードなのでproguard設定はしない + } + getByName("debug") { + applicationIdSuffix = ".debug" + isDebuggable = true + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_21 + targetCompatibility = JavaVersion.VERSION_21 + } + + buildFeatures { + buildConfig = true + } +} + +dependencies { + debugImplementation(compose.uiTooling) + // KMP 共有ライブラリへの依存 + implementation(project(":shared")) + + implementation(compose.preview) + implementation(libs.androidx.activity.compose) + implementation(compose.runtime) + implementation(compose.foundation) + implementation(compose.material3) + implementation(compose.ui) + implementation(libs.androidx.material.icons.core) + implementation(compose.components.resources) + implementation(compose.components.uiToolingPreview) + implementation(libs.androidx.lifecycle.viewmodelCompose) + implementation(libs.androidx.lifecycle.runtimeCompose) + testImplementation(libs.kotlin.test) + androidTestImplementation(libs.androidx.espresso.core) +} + +configurations.all { + // FIXME exclude netty from Koog dependencies? + exclude(group = "io.netty", module = "*") + exclude(group = "org.apache.httpcomponents.httpclient5", module = "*") + exclude(group = "org.apache.httpcomponents.core5", module = "*") +} diff --git a/agents/agent-koog/composeApp/src/debug/res/xml/network_security_config.xml b/agents/agent-koog/app/src/debug/res/xml/network_security_config.xml similarity index 100% rename from agents/agent-koog/composeApp/src/debug/res/xml/network_security_config.xml rename to agents/agent-koog/app/src/debug/res/xml/network_security_config.xml diff --git a/agents/agent-koog/composeApp/src/androidMain/AndroidManifest.xml b/agents/agent-koog/app/src/main/AndroidManifest.xml similarity index 100% rename from agents/agent-koog/composeApp/src/androidMain/AndroidManifest.xml rename to agents/agent-koog/app/src/main/AndroidManifest.xml diff --git a/agents/agent-koog/composeApp/src/androidMain/composeResources/drawable/compose-multiplatform.xml b/agents/agent-koog/app/src/main/composeResources/drawable/compose-multiplatform.xml similarity index 100% rename from agents/agent-koog/composeApp/src/androidMain/composeResources/drawable/compose-multiplatform.xml rename to agents/agent-koog/app/src/main/composeResources/drawable/compose-multiplatform.xml diff --git a/agents/agent-koog/composeApp/src/androidMain/kotlin/com/example/koog_example/ChatActivity.kt b/agents/agent-koog/app/src/main/kotlin/com/example/koog_example/App.kt similarity index 99% rename from agents/agent-koog/composeApp/src/androidMain/kotlin/com/example/koog_example/ChatActivity.kt rename to agents/agent-koog/app/src/main/kotlin/com/example/koog_example/App.kt index 46361398..d4cf7f9c 100644 --- a/agents/agent-koog/composeApp/src/androidMain/kotlin/com/example/koog_example/ChatActivity.kt +++ b/agents/agent-koog/app/src/main/kotlin/com/example/koog_example/App.kt @@ -52,6 +52,7 @@ import androidx.compose.ui.unit.sp import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewmodel.compose.viewModel +import com.example.shared.Agent import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow @@ -284,5 +285,5 @@ class ChatActivity : ComponentActivity() { @Preview @Composable fun AppAndroidPreview() { - App() + ChatActivity() } diff --git a/agents/agent-koog/composeApp/src/androidMain/kotlin/com/example/koog_example/Platform.kt b/agents/agent-koog/app/src/main/kotlin/com/example/koog_example/Platform.kt similarity index 100% rename from agents/agent-koog/composeApp/src/androidMain/kotlin/com/example/koog_example/Platform.kt rename to agents/agent-koog/app/src/main/kotlin/com/example/koog_example/Platform.kt diff --git a/agents/agent-koog/composeApp/src/androidMain/res/drawable-v24/ic_launcher_foreground.xml b/agents/agent-koog/app/src/main/res/drawable-v24/ic_launcher_foreground.xml similarity index 100% rename from agents/agent-koog/composeApp/src/androidMain/res/drawable-v24/ic_launcher_foreground.xml rename to agents/agent-koog/app/src/main/res/drawable-v24/ic_launcher_foreground.xml diff --git a/agents/agent-koog/composeApp/src/androidMain/res/drawable/ic_launcher_background.xml b/agents/agent-koog/app/src/main/res/drawable/ic_launcher_background.xml similarity index 100% rename from agents/agent-koog/composeApp/src/androidMain/res/drawable/ic_launcher_background.xml rename to agents/agent-koog/app/src/main/res/drawable/ic_launcher_background.xml diff --git a/agents/agent-koog/composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher.xml b/agents/agent-koog/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml similarity index 100% rename from agents/agent-koog/composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher.xml rename to agents/agent-koog/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml diff --git a/agents/agent-koog/composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher_round.xml b/agents/agent-koog/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml similarity index 100% rename from agents/agent-koog/composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher_round.xml rename to agents/agent-koog/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml diff --git a/agents/agent-koog/composeApp/src/androidMain/res/mipmap-hdpi/ic_launcher.png b/agents/agent-koog/app/src/main/res/mipmap-hdpi/ic_launcher.png similarity index 100% rename from agents/agent-koog/composeApp/src/androidMain/res/mipmap-hdpi/ic_launcher.png rename to agents/agent-koog/app/src/main/res/mipmap-hdpi/ic_launcher.png diff --git a/agents/agent-koog/composeApp/src/androidMain/res/mipmap-hdpi/ic_launcher_round.png b/agents/agent-koog/app/src/main/res/mipmap-hdpi/ic_launcher_round.png similarity index 100% rename from agents/agent-koog/composeApp/src/androidMain/res/mipmap-hdpi/ic_launcher_round.png rename to agents/agent-koog/app/src/main/res/mipmap-hdpi/ic_launcher_round.png diff --git a/agents/agent-koog/composeApp/src/androidMain/res/mipmap-mdpi/ic_launcher.png b/agents/agent-koog/app/src/main/res/mipmap-mdpi/ic_launcher.png similarity index 100% rename from agents/agent-koog/composeApp/src/androidMain/res/mipmap-mdpi/ic_launcher.png rename to agents/agent-koog/app/src/main/res/mipmap-mdpi/ic_launcher.png diff --git a/agents/agent-koog/composeApp/src/androidMain/res/mipmap-mdpi/ic_launcher_round.png b/agents/agent-koog/app/src/main/res/mipmap-mdpi/ic_launcher_round.png similarity index 100% rename from agents/agent-koog/composeApp/src/androidMain/res/mipmap-mdpi/ic_launcher_round.png rename to agents/agent-koog/app/src/main/res/mipmap-mdpi/ic_launcher_round.png diff --git a/agents/agent-koog/composeApp/src/androidMain/res/mipmap-xhdpi/ic_launcher.png b/agents/agent-koog/app/src/main/res/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from agents/agent-koog/composeApp/src/androidMain/res/mipmap-xhdpi/ic_launcher.png rename to agents/agent-koog/app/src/main/res/mipmap-xhdpi/ic_launcher.png diff --git a/agents/agent-koog/composeApp/src/androidMain/res/mipmap-xhdpi/ic_launcher_round.png b/agents/agent-koog/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png similarity index 100% rename from agents/agent-koog/composeApp/src/androidMain/res/mipmap-xhdpi/ic_launcher_round.png rename to agents/agent-koog/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png diff --git a/agents/agent-koog/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher.png b/agents/agent-koog/app/src/main/res/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from agents/agent-koog/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher.png rename to agents/agent-koog/app/src/main/res/mipmap-xxhdpi/ic_launcher.png diff --git a/agents/agent-koog/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher_round.png b/agents/agent-koog/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png similarity index 100% rename from agents/agent-koog/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher_round.png rename to agents/agent-koog/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png diff --git a/agents/agent-koog/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher.png b/agents/agent-koog/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png similarity index 100% rename from agents/agent-koog/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher.png rename to agents/agent-koog/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/agents/agent-koog/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_round.png b/agents/agent-koog/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png similarity index 100% rename from agents/agent-koog/composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_round.png rename to agents/agent-koog/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png diff --git a/agents/agent-koog/composeApp/src/androidMain/res/values/strings.xml b/agents/agent-koog/app/src/main/res/values/strings.xml similarity index 100% rename from agents/agent-koog/composeApp/src/androidMain/res/values/strings.xml rename to agents/agent-koog/app/src/main/res/values/strings.xml diff --git a/agents/agent-koog/composeApp/src/release/res/xml/network_security_config.xml b/agents/agent-koog/app/src/release/res/xml/network_security_config.xml similarity index 100% rename from agents/agent-koog/composeApp/src/release/res/xml/network_security_config.xml rename to agents/agent-koog/app/src/release/res/xml/network_security_config.xml diff --git a/agents/agent-koog/composeApp/src/androidUnitTest/kotlin/com/example/koog_example/ComposeAppAndroidUnitTest.kt b/agents/agent-koog/app/src/test/kotlin/com/example/koog_example/ComposeAppAndroidUnitTest.kt similarity index 100% rename from agents/agent-koog/composeApp/src/androidUnitTest/kotlin/com/example/koog_example/ComposeAppAndroidUnitTest.kt rename to agents/agent-koog/app/src/test/kotlin/com/example/koog_example/ComposeAppAndroidUnitTest.kt diff --git a/agents/agent-koog/build.gradle.kts b/agents/agent-koog/build.gradle.kts index f251368c..c94c7282 100644 --- a/agents/agent-koog/build.gradle.kts +++ b/agents/agent-koog/build.gradle.kts @@ -3,17 +3,31 @@ plugins { // in each subproject's classloader alias(libs.plugins.androidApplication) apply false alias(libs.plugins.androidLibrary) apply false + alias(libs.plugins.kotlinAndroid) apply false alias(libs.plugins.composeMultiplatform) apply false alias(libs.plugins.composeCompiler) apply false alias(libs.plugins.kotlinMultiplatform) apply false + alias(libs.plugins.androidKotlinMultiplatformLibrary) apply false + alias(libs.plugins.buildkonfig) apply false + alias(libs.plugins.androidLint) apply false +} + +buildscript { + dependencies { + // For KGP + classpath(libs.kotlin.gradle.plugin) + + // For KSP + classpath(libs.symbol.processing.gradle.plugin) + } } repositories { - google() - // Use Maven Central for resolving dependencies. - mavenCentral() - // Add JetBrains repository for Koog framework - maven { - url = uri("https://packages.jetbrains.team/maven/p/grazi/grazie-platform-public") - } + google() + // Use Maven Central for resolving dependencies. + mavenCentral() + // Add JetBrains repository for Koog framework + maven { + url = uri("https://packages.jetbrains.team/maven/p/grazi/grazie-platform-public") + } } diff --git a/agents/agent-koog/composeApp/build.gradle.kts b/agents/agent-koog/composeApp/build.gradle.kts deleted file mode 100644 index c75dbfcd..00000000 --- a/agents/agent-koog/composeApp/build.gradle.kts +++ /dev/null @@ -1,106 +0,0 @@ -import org.jetbrains.kotlin.gradle.dsl.JvmTarget - -plugins { - alias(libs.plugins.kotlinMultiplatform) - alias(libs.plugins.androidApplication) - alias(libs.plugins.composeMultiplatform) - alias(libs.plugins.composeCompiler) - - // Add Kotlin serialization plugin for Koog API support - kotlin("plugin.serialization") version "2.3.0" -} - -kotlin { - androidTarget { - compilerOptions { - jvmTarget.set(JvmTarget.JVM_21) - } - } - - sourceSets { - androidMain.dependencies { - implementation(compose.preview) - implementation(libs.androidx.activity.compose) - } - commonMain.dependencies { - implementation(compose.runtime) - implementation(compose.foundation) - implementation(compose.material3) - implementation(compose.ui) - implementation(libs.androidx.material.icons.core) - implementation(compose.components.resources) - implementation(compose.components.uiToolingPreview) - implementation(libs.androidx.lifecycle.viewmodelCompose) - implementation(libs.androidx.lifecycle.runtimeCompose) - implementation(libs.opentelemetry.logback.appender.x.x) - implementation(libs.slf4j.api) - implementation(libs.logback.android) - } - commonTest.dependencies { - implementation(libs.kotlin.test) - } - } -} - -android { - namespace = "com.example.koog_example" - compileSdk = libs.versions.android.compileSdk.get().toInt() - - defaultConfig { - applicationId = "com.example.koog_example" - minSdk = libs.versions.android.minSdk.get().toInt() - targetSdk = libs.versions.android.targetSdk.get().toInt() - versionCode = 1 - versionName = "1.0" - - // 環境変数から読み込んでBuildConfigに埋め込む - buildConfigField("String", "AWS_ACCESS_KEY_ID", "\"${System.getenv("AWS_ACCESS_KEY_ID") ?: ""}\"") - buildConfigField("String", "AWS_SECRET_ACCESS_KEY", "\"${System.getenv("AWS_SECRET_ACCESS_KEY") ?: ""}\"") - buildConfigField("String", "AWS_SESSION_TOKEN", "\"${System.getenv("AWS_SESSION_TOKEN") ?: ""}\"") - buildConfigField("String", "AWS_REGION", "\"${System.getenv("AWS_REGION") ?: "us-west-2"}\"") - - buildConfigField("String", "OTLP_BACKEND", "\"${System.getenv("OTLP_BACKEND") ?: ""}\"") - buildConfigField("String", "OTLP_ENDPOINT", "\"${System.getenv("OTLP_ENDPOINT") ?: "http://10.0.2.2:4318/v1/traces"}\"") - buildConfigField("String", "OTLP_HEADERS", "\"${System.getenv("LANGFUSE_API_KEY") ?: ""}\"") - } - packaging { - resources { - excludes += "/META-INF/{AL2.0,LGPL2.1}" - } - dex { - useLegacyPackaging = false - } - } - buildTypes { - getByName("release") { - isMinifyEnabled = true - // 検証コードなのでproguard設定はしない - } - getByName("debug") { - applicationIdSuffix = ".debug" - isDebuggable = true - } - } - compileOptions { - sourceCompatibility = JavaVersion.VERSION_21 - targetCompatibility = JavaVersion.VERSION_21 - } - - buildFeatures { - buildConfig = true - } -} - -dependencies { - debugImplementation(compose.uiTooling) - implementation(libs.koog.agents) - implementation(libs.opentelemetry.exporter.otlp) - implementation(libs.opentelemetry.exporter.logging) -} - -configurations.all { - // FIXME exclude netty from Koog dependencies? - exclude(group = "io.netty", module = "*") - exclude(group = "org.apache.httpcomponents.httpclient5", module = "*") - exclude(group = "org.apache.httpcomponents.core5", module = "*") -} diff --git a/agents/agent-koog/composeApp/src/androidMain/kotlin/com/example/koog_example/Agent.kt b/agents/agent-koog/composeApp/src/androidMain/kotlin/com/example/koog_example/Agent.kt deleted file mode 100644 index e2cc7cea..00000000 --- a/agents/agent-koog/composeApp/src/androidMain/kotlin/com/example/koog_example/Agent.kt +++ /dev/null @@ -1,88 +0,0 @@ -package com.example.koog_example - -import ai.koog.agents.core.agent.AIAgent -import ai.koog.agents.core.agent.AIAgentService -import ai.koog.agents.features.opentelemetry.feature.OpenTelemetry -import ai.koog.prompt.executor.clients.bedrock.BedrockModels -import ai.koog.prompt.executor.llms.all.simpleBedrockExecutor -import ai.koog.utils.io.use -import io.opentelemetry.api.common.AttributeKey -import io.opentelemetry.exporter.logging.LoggingSpanExporter -import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext -import java.util.concurrent.TimeUnit - -class Agent { - - private val accessKey = BuildConfig.AWS_ACCESS_KEY_ID - private val secretKey = BuildConfig.AWS_SECRET_ACCESS_KEY - private val sessionToken = BuildConfig.AWS_SESSION_TOKEN - - private val observabilityBackend = BuildConfig.OTLP_BACKEND // "jaeger", "langfuse", "langsmith" - - private val observabilityEndpoint = BuildConfig.OTLP_ENDPOINT - private val observabilityHeaders = - BuildConfig.OTLP_HEADERS // カンマ区切りの "key1:value1,key2:value2" 形式 - - private val agent = AIAgentService( - promptExecutor = simpleBedrockExecutor(accessKey, secretKey, sessionToken), - llmModel = BedrockModels.AmazonNovaMicro, - installFeatures = { - install(OpenTelemetry) { - setServiceInfo("koog-chat-app", "1.0.0") - // Add a console logger for local debugging - addSpanExporter(LoggingSpanExporter.create()) - - // バックエンドに応じてエクスポーターを設定 - when (observabilityBackend) { - "langfuse" -> { - val builder = OtlpHttpSpanExporter.builder() - .setTimeout(30, TimeUnit.SECONDS) - .setEndpoint(observabilityEndpoint) // http://10.0.2.2:4318/v1/traces - // ヘッダー文字列を安全に分割 - val headerParts = observabilityHeaders.split(":", limit = 2) - if (headerParts.size == 2) { - builder.addHeader(headerParts[0], headerParts[1]) - } - addSpanExporter( - builder - .build() - ) - } - - else -> { - // Jaeger (HTTP) - addSpanExporter( - OtlpHttpSpanExporter.builder() - .setTimeout(30, TimeUnit.SECONDS) - .setEndpoint(observabilityEndpoint) // http://10.0.2.2:4318/v1/traces - .build() - ) - } - } - - // 詳細ログを環境に応じて制御 - setVerbose(BuildConfig.DEBUG) - - // リソース属性を追加 - addResourceAttributes( - mapOf( - AttributeKey.stringKey("deployment.environment") to "android", - AttributeKey.stringKey("device.platform") to "Android", - ) - ) - } - } - ) - - suspend fun execute(query: String): String { - return withContext(Dispatchers.IO) { - try { - agent.createAgentAndRun(query) - } catch (e: Exception) { - "エラーが発生しました: ${e.message}" - } - } - } -} diff --git a/agents/agent-koog/composeApp/src/androidMain/kotlin/com/example/koog_example/App.kt b/agents/agent-koog/composeApp/src/androidMain/kotlin/com/example/koog_example/App.kt deleted file mode 100644 index 0e8ff916..00000000 --- a/agents/agent-koog/composeApp/src/androidMain/kotlin/com/example/koog_example/App.kt +++ /dev/null @@ -1,47 +0,0 @@ -package com.example.koog_example - -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.foundation.Image -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.safeContentPadding -import androidx.compose.material3.Button -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.runtime.* -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import org.jetbrains.compose.resources.painterResource -import org.jetbrains.compose.ui.tooling.preview.Preview - -import koogexample.composeapp.generated.resources.Res -import koogexample.composeapp.generated.resources.compose_multiplatform - -@Composable -@Preview -fun App() { - MaterialTheme { - var showContent by remember { mutableStateOf(false) } - Column( - modifier = Modifier - .background(MaterialTheme.colorScheme.primaryContainer) - .safeContentPadding() - .fillMaxSize(), - horizontalAlignment = Alignment.CenterHorizontally, - ) { - Button(onClick = { showContent = !showContent }) { - Text("Click me!") - } - AnimatedVisibility(showContent) { - Column( - modifier = Modifier.fillMaxWidth(), - horizontalAlignment = Alignment.CenterHorizontally, - ) { - Image(painterResource(Res.drawable.compose_multiplatform), null) - } - } - } - } -} diff --git a/agents/agent-koog/gradle/libs.versions.toml b/agents/agent-koog/gradle/libs.versions.toml index 97393e2b..89cb67a9 100644 --- a/agents/agent-koog/gradle/libs.versions.toml +++ b/agents/agent-koog/gradle/libs.versions.toml @@ -1,58 +1,61 @@ [versions] agp = "8.13.2" +androidGradlePlugin = "8.13.2" android-compileSdk = "36" android-minSdk = "24" android-targetSdk = "36" androidx-activity = "1.12.2" androidx-appcompat = "1.7.1" -androidx-core = "1.17.0" androidx-espresso = "3.7.0" androidx-lifecycle = "2.9.6" androidx-testExt = "1.3.0" composeMultiplatform = "1.9.3" -junit = "4.13.2" koogAgents = "0.5.4" kotlin = "2.3.0" +kotlinGradlePlugin = "2.3.0" +kotlinxCoroutinesCore = "1.10.2" logbackAndroid = "3.0.0" materialIconsCore = "1.7.8" opentelemetryExporterOtlp = "1.57.0" -lifecycleViewmodelKtx = "2.10.0" -opentelemetryExporterOtlpHttpTrace = "1.14.0" opentelemetryLogbackAppenderXX = "2.23.0-alpha" slf4jApi = "2.0.17" -ui = "1.10.0" -runtime = "1.10.0" -foundationLayout = "1.10.0" -material3 = "1.4.0" +symbolProcessingGradlePlugin = "2.3.4" +buildkonfig = "0.17.1" +kotlinStdlib = "2.3.0" +runner = "1.7.0" +core = "1.7.0" +kermit = "2.0.4" [libraries] androidx-material-icons-core = { module = "androidx.compose.material:material-icons-core", version.ref = "materialIconsCore" } koog-agents = { module = "ai.koog:koog-agents", version.ref = "koogAgents" } +kotlin-gradle-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlinGradlePlugin" } kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" } -kotlin-testJunit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlin" } -junit = { module = "junit:junit", version.ref = "junit" } -androidx-core-ktx = { module = "androidx.core:core-ktx", version.ref = "androidx-core" } androidx-testExt-junit = { module = "androidx.test.ext:junit", version.ref = "androidx-testExt" } androidx-espresso-core = { module = "androidx.test.espresso:espresso-core", version.ref = "androidx-espresso" } androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "androidx-appcompat" } androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activity" } androidx-lifecycle-viewmodelCompose = { module = "org.jetbrains.androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "androidx-lifecycle" } androidx-lifecycle-runtimeCompose = { module = "org.jetbrains.androidx.lifecycle:lifecycle-runtime-compose", version.ref = "androidx-lifecycle" } +kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinxCoroutinesCore" } logback-android = { module = "com.github.tony19:logback-android", version.ref = "logbackAndroid" } opentelemetry-exporter-logging = { module = "io.opentelemetry:opentelemetry-exporter-logging", version.ref = "opentelemetryExporterOtlp" } opentelemetry-exporter-otlp = { module = "io.opentelemetry:opentelemetry-exporter-otlp", version.ref = "opentelemetryExporterOtlp" } -androidx-lifecycle-viewmodel-ktx = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "lifecycleViewmodelKtx" } -androidx-ui = { module = "androidx.compose.ui:ui", version.ref = "ui" } -androidx-runtime = { module = "androidx.compose.runtime:runtime", version.ref = "runtime" } -androidx-foundation-layout = { module = "androidx.compose.foundation:foundation-layout", version.ref = "foundationLayout" } -androidx-material3 = { module = "androidx.compose.material3:material3", version.ref = "material3" } -opentelemetry-exporter-otlp-http-trace = { module = "io.opentelemetry:opentelemetry-exporter-otlp-http-trace", version.ref = "opentelemetryExporterOtlpHttpTrace" } opentelemetry-logback-appender-x-x = { module = "io.opentelemetry.instrumentation:opentelemetry-logback-appender-1.0", version.ref = "opentelemetryLogbackAppenderXX" } slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4jApi" } +symbol-processing-gradle-plugin = { module = "com.google.devtools.ksp:symbol-processing-gradle-plugin", version.ref = "symbolProcessingGradlePlugin" } +kotlin-stdlib = { group = "org.jetbrains.kotlin", name = "kotlin-stdlib", version.ref = "kotlinStdlib" } +androidx-runner = { group = "androidx.test", name = "runner", version.ref = "runner" } +androidx-core = { group = "androidx.test", name = "core", version.ref = "core" } +kermit = { module = "co.touchlab:kermit", version.ref = "kermit" } [plugins] androidApplication = { id = "com.android.application", version.ref = "agp" } +kotlinAndroid = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } androidLibrary = { id = "com.android.library", version.ref = "agp" } composeMultiplatform = { id = "org.jetbrains.compose", version.ref = "composeMultiplatform" } composeCompiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } +androidKotlinMultiplatformLibrary = { id = "com.android.kotlin.multiplatform.library", version.ref = "androidGradlePlugin" } +buildkonfig = { id = "com.codingfeline.buildkonfig", version.ref = "buildkonfig" } +androidLint = { id = "com.android.lint", version.ref = "agp" } diff --git a/agents/agent-koog/settings.gradle.kts b/agents/agent-koog/settings.gradle.kts index fdd7dcba..79644a8a 100644 --- a/agents/agent-koog/settings.gradle.kts +++ b/agents/agent-koog/settings.gradle.kts @@ -28,4 +28,5 @@ dependencyResolutionManagement { } } -include(":composeApp") \ No newline at end of file +include(":app") +include(":shared") diff --git a/agents/agent-koog/shared/.gitignore b/agents/agent-koog/shared/.gitignore new file mode 100644 index 00000000..42afabfd --- /dev/null +++ b/agents/agent-koog/shared/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/agents/agent-koog/shared/build.gradle.kts b/agents/agent-koog/shared/build.gradle.kts new file mode 100644 index 00000000..3ca5a8da --- /dev/null +++ b/agents/agent-koog/shared/build.gradle.kts @@ -0,0 +1,131 @@ +import com.codingfeline.buildkonfig.compiler.FieldSpec.Type + +plugins { + alias(libs.plugins.kotlinMultiplatform) + alias(libs.plugins.composeMultiplatform) + alias(libs.plugins.composeCompiler) + alias(libs.plugins.androidKotlinMultiplatformLibrary) + alias(libs.plugins.androidLint) + alias(libs.plugins.buildkonfig) + + // Add Kotlin serialization plugin for Koog API support + kotlin("plugin.serialization") version "2.3.0" +} + +kotlin { + + // Target declarations - add or remove as needed below. These define + // which platforms this KMP module supports. + // See: https://kotlinlang.org/docs/multiplatform-discover-project.html#targets + androidLibrary { + namespace = "com.example.shared" + compileSdk = libs.versions.android.compileSdk.get().toInt() + minSdk = libs.versions.android.minSdk.get().toInt() + + withJava() // enable java compilation support + compilerOptions { + jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_21) + } + + withHostTestBuilder { + } + + withDeviceTestBuilder { + sourceSetTreeName = "test" + }.configure { + instrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } + + packaging { + resources { + excludes += "/META-INF/{AL2.0,LGPL2.1}" + } + dex { + useLegacyPackaging = false + } + } + } + + sourceSets { + androidMain { + dependencies { + implementation(compose.preview) + implementation(libs.androidx.activity.compose) + implementation(libs.kotlinx.coroutines.core) + } + } + commonMain { + // BuildKonfig の出力ディレクトリをソースに追加 + kotlin.srcDir("build/generated/buildkonfig") + + dependencies { + implementation(libs.kotlin.stdlib) + implementation(compose.runtime) + implementation(compose.foundation) + implementation(compose.material3) + implementation(compose.ui) + implementation(libs.androidx.material.icons.core) + implementation(compose.components.resources) + implementation(compose.components.uiToolingPreview) + implementation(libs.androidx.lifecycle.viewmodelCompose) + implementation(libs.androidx.lifecycle.runtimeCompose) + implementation(libs.androidx.appcompat) + implementation(libs.opentelemetry.logback.appender.x.x) + implementation(libs.slf4j.api) + implementation(libs.logback.android) + implementation(libs.koog.agents) + implementation(libs.opentelemetry.exporter.otlp) + implementation(libs.opentelemetry.exporter.logging) + implementation(libs.kermit) + } + } + commonTest { + dependencies { + implementation(libs.kotlin.test) + } + } + + getByName("androidDeviceTest") { + dependencies { + implementation(libs.androidx.runner) + implementation(libs.androidx.core) + implementation(libs.androidx.testExt.junit) + } + } + } +} + +buildkonfig { + packageName = "com.example.koog_example" + defaultConfigs { + // 環境変数から読み込んでBuildConfigに埋め込む + buildConfigField(Type.STRING, "AWS_ACCESS_KEY_ID", System.getenv("AWS_ACCESS_KEY_ID") ?: "") + buildConfigField( + Type.STRING, + "AWS_SECRET_ACCESS_KEY", + System.getenv("AWS_SECRET_ACCESS_KEY") ?: "" + ) + buildConfigField(Type.STRING, "AWS_SESSION_TOKEN", System.getenv("AWS_SESSION_TOKEN") ?: "") + buildConfigField(Type.STRING, "AWS_REGION", System.getenv("AWS_REGION") ?: "us-west-2") + + buildConfigField(Type.STRING, "OTLP_BACKEND", System.getenv("OTLP_BACKEND") ?: "") + buildConfigField( + Type.STRING, + "OTLP_ENDPOINT", + System.getenv("OTLP_ENDPOINT") ?: "http://10.0.2.2:4318/v1/traces" + ) + buildConfigField(Type.STRING, "OTLP_HEADERS", System.getenv("LANGFUSE_API_KEY") ?: "") + } +} + +// すべての Kotlin コンパイルタスクが BuildKonfig に依存するように設定 +tasks.withType().configureEach { + dependsOn("generateBuildKonfig") +} + +configurations.all { + // FIXME exclude netty from Koog dependencies? + exclude(group = "io.netty", module = "*") + exclude(group = "org.apache.httpcomponents.httpclient5", module = "*") + exclude(group = "org.apache.httpcomponents.core5", module = "*") +} diff --git a/agents/agent-koog/shared/src/androidDeviceTest/kotlin/com/example/shared/ExampleInstrumentedTest.kt b/agents/agent-koog/shared/src/androidDeviceTest/kotlin/com/example/shared/ExampleInstrumentedTest.kt new file mode 100644 index 00000000..1e409b35 --- /dev/null +++ b/agents/agent-koog/shared/src/androidDeviceTest/kotlin/com/example/shared/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.example.shared + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.example.shared.test", appContext.packageName) + } +} \ No newline at end of file diff --git a/agents/agent-koog/shared/src/androidHostTest/kotlin/com/example/shared/ExampleUnitTest.kt b/agents/agent-koog/shared/src/androidHostTest/kotlin/com/example/shared/ExampleUnitTest.kt new file mode 100644 index 00000000..2603c1bf --- /dev/null +++ b/agents/agent-koog/shared/src/androidHostTest/kotlin/com/example/shared/ExampleUnitTest.kt @@ -0,0 +1,16 @@ +package com.example.shared + +import kotlin.test.Test +import kotlin.test.assertEquals + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/agents/agent-koog/shared/src/androidMain/AndroidManifest.xml b/agents/agent-koog/shared/src/androidMain/AndroidManifest.xml new file mode 100644 index 00000000..0b8a3533 --- /dev/null +++ b/agents/agent-koog/shared/src/androidMain/AndroidManifest.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/agents/agent-koog/shared/src/androidMain/kotlin/com/example/shared/Platform.android.kt b/agents/agent-koog/shared/src/androidMain/kotlin/com/example/shared/Platform.android.kt new file mode 100644 index 00000000..94195656 --- /dev/null +++ b/agents/agent-koog/shared/src/androidMain/kotlin/com/example/shared/Platform.android.kt @@ -0,0 +1,3 @@ +package com.example.shared + +actual fun platform() = "Android" \ No newline at end of file diff --git a/agents/agent-koog/shared/src/commonMain/kotlin/com/example/shared/Agent.kt b/agents/agent-koog/shared/src/commonMain/kotlin/com/example/shared/Agent.kt new file mode 100644 index 00000000..92b0190c --- /dev/null +++ b/agents/agent-koog/shared/src/commonMain/kotlin/com/example/shared/Agent.kt @@ -0,0 +1,106 @@ +package com.example.shared + +import ai.koog.agents.core.agent.AIAgentService +import ai.koog.agents.features.opentelemetry.feature.OpenTelemetry +import ai.koog.prompt.executor.clients.bedrock.BedrockClientSettings +import ai.koog.prompt.executor.clients.bedrock.BedrockModels +import ai.koog.prompt.executor.llms.all.simpleBedrockExecutor +import com.example.koog_example.BuildKonfig +import io.opentelemetry.api.common.AttributeKey +import io.opentelemetry.exporter.logging.LoggingSpanExporter +import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext +import co.touchlab.kermit.Logger +import java.util.concurrent.TimeUnit + +class Agent { + + private val accessKey = BuildKonfig.AWS_ACCESS_KEY_ID + private val secretKey = BuildKonfig.AWS_SECRET_ACCESS_KEY + private val sessionToken = BuildKonfig.AWS_SESSION_TOKEN + private val region = BuildKonfig.AWS_REGION + + private val observabilityBackend = BuildKonfig.OTLP_BACKEND // "jaeger", "langfuse", "langsmith" + + private val observabilityEndpoint = BuildKonfig.OTLP_ENDPOINT + private val observabilityHeaders = BuildKonfig.OTLP_HEADERS // カンマ区切りの "key1:value1,key2:value2" 形式 + + private val agent = AIAgentService.Companion( + promptExecutor = simpleBedrockExecutor( + accessKey, + secretKey, + sessionToken, + BedrockClientSettings(region) + ), + llmModel = BedrockModels.AmazonNovaMicro, + installFeatures = { + // バックエンドに応じてエクスポーターを設定 + when (observabilityBackend) { + "langfuse" -> { + install(OpenTelemetry.Feature) { + setServiceInfo("koog-chat-app", "1.0.0") + // Add a console logger for local debugging + addSpanExporter(LoggingSpanExporter.create()) + + val builder = OtlpHttpSpanExporter.builder() + .setTimeout(30, TimeUnit.SECONDS) + .setEndpoint(observabilityEndpoint) // http://10.0.2.2:4318/v1/traces + // ヘッダー文字列を安全に分割 + val headerParts = observabilityHeaders.split(":", limit = 2) + if (headerParts.size == 2) { + builder.addHeader(headerParts[0], headerParts[1]) + } + addSpanExporter( + builder + .build() + ) + + // リソース属性を追加 + addResourceAttributes( + mapOf( + AttributeKey.stringKey("deployment.environment") to "android", + AttributeKey.stringKey("device.platform") to "Android", + ) + ) + } + + } + "jaeger" -> { + install(OpenTelemetry.Feature) { + setServiceInfo("koog-chat-app", "1.0.0") + // Add a console logger for local debugging + addSpanExporter(LoggingSpanExporter.create()) + // Jaeger (HTTP) + addSpanExporter( + OtlpHttpSpanExporter.builder() + .setTimeout(30, TimeUnit.SECONDS) + .setEndpoint(observabilityEndpoint) // http://10.0.2.2:4318/v1/traces + .build() + ) + // リソース属性を追加 + addResourceAttributes( + mapOf( + AttributeKey.stringKey("deployment.environment") to "android", + AttributeKey.stringKey("device.platform") to "Android", + ) + ) + } + } + } + } + ) + + private val logger = Logger.withTag("Agent") + + suspend fun execute(query: String): String { + return withContext(Dispatchers.IO) { + try { + agent.createAgentAndRun(query) + } catch (e: Exception) { + logger.e("エラー", e) + "エラーが発生しました: ${e.message}" + } + } + } +} \ No newline at end of file diff --git a/agents/agent-koog/shared/src/commonMain/kotlin/com/example/shared/Platform.kt b/agents/agent-koog/shared/src/commonMain/kotlin/com/example/shared/Platform.kt new file mode 100644 index 00000000..8108e799 --- /dev/null +++ b/agents/agent-koog/shared/src/commonMain/kotlin/com/example/shared/Platform.kt @@ -0,0 +1,3 @@ +package com.example.shared + +expect fun platform(): String \ No newline at end of file