diff --git a/README.md b/README.md index c779bae..62aa045 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # LLM Agent Builder +[![build](https://github.com/LLMAgentBuilder/llm-agent-builder/actions/workflows/build.yaml/badge.svg)](https://github.com/LLMAgentBuilder/llm-agent-builder/actions/workflows/build.yaml) + **LLM Agent Powered by Java / Spring AI** ## Documentation diff --git a/bootstrap/pom.xml b/bootstrap/pom.xml index 2a65804..cb733a3 100644 --- a/bootstrap/pom.xml +++ b/bootstrap/pom.xml @@ -48,6 +48,11 @@ open-telemetry ${project.version} + + io.github.llmagentbuilder + chat-agent-ui + 0.3.0 + io.github.llmagentbuilder llm-openai diff --git a/bootstrap/src/main/kotlin/io/github/llmagentbuilder/bootstrap/AgentBootstrap.kt b/bootstrap/src/main/kotlin/io/github/llmagentbuilder/bootstrap/AgentBootstrap.kt index 406e402..706782e 100644 --- a/bootstrap/src/main/kotlin/io/github/llmagentbuilder/bootstrap/AgentBootstrap.kt +++ b/bootstrap/src/main/kotlin/io/github/llmagentbuilder/bootstrap/AgentBootstrap.kt @@ -122,7 +122,7 @@ object AgentBootstrap { UUID.randomUUID().toString(), observationRegistry, ) - KtorLauncher.launch(chatAgent) + KtorLauncher.launch(chatAgent, agentConfig.launch) } private fun profileAdvisor(agentConfig: AgentConfig): Advisor? { diff --git a/core/src/main/kotlin/io/github/llmagentbuilder/core/AgentConfig.kt b/core/src/main/kotlin/io/github/llmagentbuilder/core/AgentConfig.kt index 691f118..99e1c5d 100644 --- a/core/src/main/kotlin/io/github/llmagentbuilder/core/AgentConfig.kt +++ b/core/src/main/kotlin/io/github/llmagentbuilder/core/AgentConfig.kt @@ -63,6 +63,21 @@ class ObservationConfig { var metrics: MetricsConfig? = null } +class LaunchConfig { + var server: ServerConfig? = null + val feature: FeatureConfig? = null +} + +class ServerConfig { + var host: String? = "localhost" + var port: Int? = 8080 +} + +class FeatureConfig { + var devUiEnabled: Boolean? = true +} + + class AgentConfig { var metadata: AgentMetadata? = null var profile: ProfileConfig? = null @@ -71,6 +86,7 @@ class AgentConfig { var planner: Map? = null var tools: List? = null var observation: ObservationConfig? = null + var launch: LaunchConfig? = null } object AgentConfigLoader { diff --git a/launchers/ktor/pom.xml b/launchers/ktor/pom.xml index ca5dc04..e27eae0 100644 --- a/launchers/ktor/pom.xml +++ b/launchers/ktor/pom.xml @@ -65,6 +65,11 @@ ktor-serialization-jackson-jvm ${ktor.version} + + io.ktor + ktor-server-webjars-jvm + ${ktor.version} + \ No newline at end of file diff --git a/launchers/ktor/src/main/kotlin/io/github/llmagentbuilder/launcher/ktor/server/AppMain.kt b/launchers/ktor/src/main/kotlin/io/github/llmagentbuilder/launcher/ktor/server/AppMain.kt index cf52d24..4aee183 100644 --- a/launchers/ktor/src/main/kotlin/io/github/llmagentbuilder/launcher/ktor/server/AppMain.kt +++ b/launchers/ktor/src/main/kotlin/io/github/llmagentbuilder/launcher/ktor/server/AppMain.kt @@ -1,7 +1,10 @@ package io.github.llmagentbuilder.launcher.ktor.server import io.github.llmagentbuilder.core.ChatAgent +import io.github.llmagentbuilder.core.FeatureConfig +import io.github.llmagentbuilder.core.LaunchConfig import io.github.llmagentbuilder.launcher.ktor.server.apis.AgentApi +import io.github.llmagentbuilder.launcher.ktor.server.apis.devUI import io.ktor.serialization.jackson.* import io.ktor.server.application.* import io.ktor.server.cio.* @@ -11,13 +14,20 @@ import io.ktor.server.plugins.contentnegotiation.* import io.ktor.server.plugins.defaultheaders.* import io.ktor.server.resources.* import io.ktor.server.routing.* +import io.ktor.server.webjars.* +import org.slf4j.LoggerFactory object KtorLauncher { - fun launch(chatAgent: ChatAgent) { + private val logger = LoggerFactory.getLogger(KtorLauncher::class.java) + + fun launch(chatAgent: ChatAgent, launchConfig: LaunchConfig? = null) { + val host = launchConfig?.server?.host ?: "localhost" + val port = launchConfig?.server?.port ?: 8080 + logger.info("Starting agent server: $host:$port") embeddedServer( CIO, - port = 8080, - host = "0.0.0.0", + port, + host, module = { module(chatAgent) } @@ -25,7 +35,13 @@ object KtorLauncher { } } -fun Application.module(chatAgent: ChatAgent) { +fun Application.module( + chatAgent: ChatAgent, + featureConfig: FeatureConfig? = null +) { + install(Webjars) { + path = "/webjars" + } install(DefaultHeaders) install(ContentNegotiation) { jackson() @@ -37,5 +53,8 @@ fun Application.module(chatAgent: ChatAgent) { install(Resources) install(Routing) { AgentApi(chatAgent) + if (featureConfig?.devUiEnabled != false) { + devUI() + } } } diff --git a/launchers/ktor/src/main/kotlin/io/github/llmagentbuilder/launcher/ktor/server/apis/DevUI.kt b/launchers/ktor/src/main/kotlin/io/github/llmagentbuilder/launcher/ktor/server/apis/DevUI.kt new file mode 100644 index 0000000..48b6e71 --- /dev/null +++ b/launchers/ktor/src/main/kotlin/io/github/llmagentbuilder/launcher/ktor/server/apis/DevUI.kt @@ -0,0 +1,15 @@ +package io.github.llmagentbuilder.launcher.ktor.server.apis + +import io.ktor.server.application.* +import io.ktor.server.response.* +import io.ktor.server.routing.* + +fun Route.devUI() { + get("/") { + call.respondRedirect("/webjars/chat-agent-ui/index.html") + } + get("/_next/{path...}") { + val redirectPath = call.parameters.getAll("path")?.joinToString("/") + call.respondRedirect("/webjars/chat-agent-ui/${redirectPath}") + } +}