Skip to content

Commit 4b70811

Browse files
author
Robert Winkler
committed
Added a Spring Boot Application example
1 parent 26ce7d2 commit 4b70811

File tree

10 files changed

+163
-46
lines changed

10 files changed

+163
-46
lines changed

build.gradle.kts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ plugins {
22
kotlin("jvm") version "2.0.20"
33
id("org.jlleitschuh.gradle.ktlint") version "12.1.0"
44
id("org.jetbrains.kotlinx.kover") version "0.8.3"
5-
id("org.cadixdev.licenser") version "0.6.1"
65
}
76

87

@@ -11,18 +10,10 @@ subprojects {
1110
apply(plugin = "org.jetbrains.kotlin.jvm")
1211
apply(plugin = "org.jlleitschuh.gradle.ktlint")
1312
apply(plugin = "org.jetbrains.kotlinx.kover")
14-
apply(plugin = "org.cadixdev.licenser")
1513

1614
group = "ai.ancf.lmos"
1715
version = "1.0-SNAPSHOT"
1816

19-
license {
20-
include("**/*.java")
21-
include("**/*.kt")
22-
include("**/*.yaml")
23-
exclude("**/*.properties")
24-
}
25-
2617
dependencies {
2718
testImplementation(kotlin("test"))
2819
testImplementation ("org.jetbrains.kotlinx:kotlinx-coroutines-test")
Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,24 @@
1+
plugins {
2+
kotlin("plugin.spring") version "1.9.10"
3+
id("org.springframework.boot") version "3.1.5" // Use the latest compatible version
4+
id("io.spring.dependency-management") version "1.1.3"
5+
}
6+
17
dependencies {
28
api(project(":kotlin-wot"))
39
api(project(":kotlin-wot-binding-http"))
410
api(project(":kotlin-wot-binding-mqtt"))
511
api(project(":kotlin-wot-reflection"))
6-
api("ch.qos.logback:logback-classic:1.5.12")
7-
implementation("ai.ancf.lmos:arc-agents:0.98.0")
8-
implementation("ai.ancf.lmos:arc-langchain4j-client:0.98.0")
12+
implementation("ai.ancf.lmos:arc-agents:0.111.0")
13+
//implementation("ai.ancf.lmos:arc-langchain4j-client:0.111.0")
14+
implementation("org.springframework.boot:spring-boot-starter")
15+
implementation("org.springframework.boot:spring-boot-starter-logging")
16+
17+
implementation("ai.ancf.lmos:arc-azure-client:0.111.0")
18+
implementation("ai.ancf.lmos:arc-spring-boot-starter:0.111.0")
919

10-
implementation("dev.langchain4j:langchain4j-azure-open-ai:0.35.0")
11-
implementation("dev.langchain4j:langchain4j:0.35.0")
20+
//implementation("dev.langchain4j:langchain4j-azure-open-ai:0.35.0")
21+
//implementation("dev.langchain4j:langchain4j:0.35.0")
1222
testImplementation("com.hivemq:hivemq-mqtt-client:1.3.3")
1323
implementation("org.testcontainers:testcontainers:1.20.3")
1424
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package ai.ancf.lmos.wot.integration
2+
3+
import ai.ancf.lmos.arc.spring.Agents
4+
import ai.ancf.lmos.wot.Wot
5+
import org.springframework.context.annotation.Bean
6+
import org.springframework.context.annotation.Configuration
7+
8+
9+
@Configuration
10+
class AgentConfiguration {
11+
12+
@Bean
13+
fun myAgent(agent: Agents) = agent {
14+
name = "My Agent"
15+
prompt { "you are a helpful weather agent." }
16+
model = { "GPT-4o" }
17+
}
18+
19+
@Bean
20+
fun servient() = createServient("HTTP")
21+
22+
@Bean
23+
fun wot() = Wot.create(servient())
24+
25+
26+
}

kotlin-wot-integration-tests/src/main/kotlin/integration/HttpAgent.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,12 @@ package ai.ancf.lmos.wot.integration
22

33
import ai.ancf.lmos.wot.Wot
44
import ai.ancf.lmos.wot.reflection.ExposedThingBuilder
5-
import ai.ancf.lmos.wot.reflection.annotations.ThingAgent
65
import ai.ancf.lmos.wot.thing.schema.WoTExposedThing
76
import kotlinx.coroutines.Job
87
import kotlinx.coroutines.launch
98
import kotlinx.coroutines.runBlocking
109

11-
10+
/*
1211
fun main(): Unit = runBlocking {
1312
1413
val agent = ThingAgent()
@@ -34,4 +33,4 @@ fun main(): Unit = runBlocking {
3433
println("Exposed Agent on HTTP Server")
3534
// Keep the application running until process is stopped
3635
Job().join()
37-
}
36+
}*/

kotlin-wot-integration-tests/src/main/kotlin/integration/MqttAgent.kt

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,6 @@
11
package ai.ancf.lmos.wot.integration
22

3-
import ai.ancf.lmos.wot.Wot
4-
import ai.ancf.lmos.wot.reflection.ExposedThingBuilder
5-
import ai.ancf.lmos.wot.reflection.annotations.ThingAgent
6-
import ai.ancf.lmos.wot.thing.schema.WoTExposedThing
7-
import kotlinx.coroutines.Job
8-
import kotlinx.coroutines.launch
9-
import kotlinx.coroutines.runBlocking
10-
3+
/*
114
125
fun main(): Unit = runBlocking {
136
val agent = ThingAgent()
@@ -32,4 +25,5 @@ fun main(): Unit = runBlocking {
3225
println("Exposed Thing:${exposedThing.toJson()}")
3326
// Keep the application running until process is stopped
3427
Job().join()
35-
}
28+
}
29+
*/
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package ai.ancf.lmos.wot.integration
2+
3+
import ai.ancf.lmos.wot.Servient
4+
import ai.ancf.lmos.wot.Wot
5+
import ai.ancf.lmos.wot.reflection.ExposedThingBuilder
6+
import ai.ancf.lmos.wot.thing.schema.WoTExposedThing
7+
import jakarta.annotation.PreDestroy
8+
import kotlinx.coroutines.Job
9+
import kotlinx.coroutines.runBlocking
10+
import org.slf4j.Logger
11+
import org.slf4j.LoggerFactory
12+
import org.springframework.beans.factory.annotation.Autowired
13+
import org.springframework.boot.CommandLineRunner
14+
import org.springframework.boot.autoconfigure.SpringBootApplication
15+
import org.springframework.boot.runApplication
16+
17+
18+
fun main(args: Array<String>) {
19+
runApplication<ThingAgentApplication>(*args)
20+
}
21+
22+
@SpringBootApplication
23+
class ThingAgentApplication : CommandLineRunner {
24+
25+
@Autowired
26+
private lateinit var servient: Servient
27+
28+
@Autowired
29+
private lateinit var wot: Wot
30+
31+
@Autowired
32+
private lateinit var agent: ThingAgent
33+
34+
companion object {
35+
private val log: Logger = LoggerFactory.getLogger(ThingAgentApplication::class.java)
36+
}
37+
38+
@PreDestroy
39+
fun onExit() {
40+
log.info("###STOPing###")
41+
log.info("###STOP FROM THE LIFECYCLE###")
42+
}
43+
44+
override fun run(vararg args: String?) = runBlocking {
45+
46+
// Protocol can be "HTTP" or "MQTT"
47+
val servient = createServient("HTTP")
48+
49+
// Register a shutdown hook
50+
Runtime.getRuntime().addShutdownHook(Thread {
51+
println("Application is shutting down. Performing cleanup...")
52+
runBlocking { servient.shutdown() }
53+
})
54+
55+
val wot = Wot.create(servient)
56+
val exposedThing = ExposedThingBuilder.createExposedThing(wot, agent, ThingAgent::class)
57+
58+
// Start `servient` in a separate coroutine
59+
servient.start()
60+
// Add and expose the thing after `start()` has had time to begin
61+
servient.addThing(exposedThing as WoTExposedThing)
62+
servient.expose("agent")
63+
64+
println("Exposed Agent on HTTP Server")
65+
Job().join()
66+
}
67+
}
68+

kotlin-wot-integration-tests/src/main/kotlin/integration/ThingAgent.kt

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,50 @@
1-
package ai.ancf.lmos.wot.reflection.annotations
2-
3-
import dev.langchain4j.model.azure.AzureOpenAiChatModel
1+
package ai.ancf.lmos.wot.integration
2+
3+
import ai.ancf.lmos.arc.agents.AgentProvider
4+
import ai.ancf.lmos.arc.agents.ChatAgent
5+
import ai.ancf.lmos.arc.agents.User
6+
import ai.ancf.lmos.arc.agents.conversation.AssistantMessage
7+
import ai.ancf.lmos.arc.agents.conversation.latest
8+
import ai.ancf.lmos.arc.agents.conversation.toConversation
9+
import ai.ancf.lmos.arc.agents.getAgentByName
10+
import ai.ancf.lmos.arc.core.getOrThrow
11+
import ai.ancf.lmos.wot.reflection.annotations.*
412
import kotlinx.coroutines.flow.Flow
513
import kotlinx.coroutines.flow.MutableSharedFlow
614
import kotlinx.coroutines.flow.MutableStateFlow
15+
import org.springframework.stereotype.Component
716

817

918
@Thing(id= "agent", title="Agent",
1019
description= "A simple agent.")
1120
@VersionInfo(instance = "1.0.0")
12-
class ThingAgent(@Property(name = "modelTemperature", readOnly = true)
21+
@Component
22+
class ThingAgent(val agentProvider: AgentProvider, @Property(name = "modelTemperature", readOnly = true)
1323
val modelConfiguration: ModelConfiguration = ModelConfiguration(0.5, 50)) {
1424

1525
private val messageFlow = MutableSharedFlow<String>(replay = 1) // Replay last emitted value
1626

27+
28+
/*
1729
private val model: AzureOpenAiChatModel = AzureOpenAiChatModel.builder()
1830
.apiKey("af12dab9c046453e82dcf4b24af90bca")
1931
.deploymentName("GPT35T-1106")
2032
.endpoint("https://gpt4-uk.openai.azure.com/")
2133
.temperature(modelConfiguration.modelTemperature)
2234
.build();
35+
*/
2336

2437
@Property(name = "observableProperty", title = "Observable Property", readOnly = true)
2538
val observableProperty : MutableStateFlow<String> = MutableStateFlow("Hello World")
2639

2740
@Action(name = "ask", title = "Ask", description = "Ask the agent a question.")
28-
suspend fun ask(message : String) : String {
29-
val response = model.generate(message)
30-
messageFlow.emit(response)
31-
return model.generate(response)
41+
suspend fun ask(message : String) : String {
42+
val agent = agentProvider.getAgentByName("My Agent") as ChatAgent? ?: error("Agent not found!")
43+
val conversation = message.toConversation(User("anonymous"))
44+
val assistantMessage = agent.execute(conversation).getOrThrow().latest<AssistantMessage>() ?:
45+
throw RuntimeException("No Assistant response")
46+
messageFlow.emit(assistantMessage.content)
47+
return assistantMessage.content
3248
}
3349

3450
@Event(name = "messageGenerated", title = "Generated message")

kotlin-wot-integration-tests/src/main/kotlin/integration/Util.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import ai.ancf.lmos.wot.binding.http.HttpProtocolClientFactory
66
import ai.ancf.lmos.wot.binding.http.HttpProtocolServer
77
import ai.ancf.lmos.wot.binding.mqtt.MqttClientConfig
88
import ai.ancf.lmos.wot.binding.mqtt.MqttProtocolServer
9-
import ai.ancf.lmos.wot.reflection.annotations.ThingAgent
109
import ai.ancf.lmos.wot.thing.ExposedThing
1110
import ai.ancf.lmos.wot.thing.Type
1211
import ai.ancf.lmos.wot.thing.schema.*
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
arc:
2+
ai:
3+
clients:
4+
- id: GPT-4o
5+
model-name: GPT35T-1106
6+
api-key: af12dab9c046453e82dcf4b24af90bca
7+
client: azure
8+
url: https://gpt4-uk.openai.azure.com

kotlin-wot-reflection/src/main/kotlin/reflection/ExposedThingBuilder.kt

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,26 @@
11
package ai.ancf.lmos.wot.reflection
22

33
import ai.ancf.lmos.wot.Wot
4-
import ai.ancf.lmos.wot.reflection.annotations.Action
5-
import ai.ancf.lmos.wot.reflection.annotations.Event
6-
import ai.ancf.lmos.wot.reflection.annotations.Property
7-
import ai.ancf.lmos.wot.reflection.annotations.Thing
4+
import ai.ancf.lmos.wot.reflection.annotations.*
85
import ai.ancf.lmos.wot.reflection.annotations.VersionInfo
96
import ai.ancf.lmos.wot.thing.ExposedThing
107
import ai.ancf.lmos.wot.thing.schema.*
8+
import ai.ancf.lmos.wot.thing.schema.ArraySchema
9+
import ai.ancf.lmos.wot.thing.schema.IntegerSchema
10+
import ai.ancf.lmos.wot.thing.schema.NumberSchema
11+
import ai.ancf.lmos.wot.thing.schema.StringSchema
1112
import ai.ancf.lmos.wot.thing.thingDescription
1213
import kotlinx.coroutines.CoroutineExceptionHandler
1314
import kotlinx.coroutines.CoroutineScope
1415
import kotlinx.coroutines.Dispatchers
1516
import kotlinx.coroutines.flow.Flow
1617
import kotlinx.coroutines.flow.MutableStateFlow
1718
import kotlinx.coroutines.flow.StateFlow
18-
import kotlinx.coroutines.flow.combine
1919
import kotlinx.coroutines.launch
2020
import org.slf4j.Logger
2121
import org.slf4j.LoggerFactory
2222
import kotlin.reflect.*
23-
import kotlin.reflect.full.declaredMemberFunctions
24-
import kotlin.reflect.full.findAnnotation
25-
import kotlin.reflect.full.memberProperties
26-
import kotlin.reflect.full.primaryConstructor
23+
import kotlin.reflect.full.*
2724

2825
/**
2926
* This utility class helps in constructing a [ExposedThing] for a given Kotlin class
@@ -393,9 +390,18 @@ object ExposedThingBuilder {
393390
val args = function.parameters.associateWith { param ->
394391
if (param.kind == KParameter.Kind.INSTANCE) instance
395392
else toKotlinObject(input.value(), param.type)
393+
}.toMutableMap()
394+
// Check if the function is a suspending function
395+
if (function.isSuspend) {
396+
// Call the suspending function with the continuation
397+
val result = function.callSuspendBy(args)
398+
// Return the result from the continuation
399+
InteractionInput.Value(DataSchemaValue.toDataSchemaValue(result))
400+
} else {
401+
// Regular function call
402+
val result = function.callBy(args)
403+
InteractionInput.Value(DataSchemaValue.toDataSchemaValue(result))
396404
}
397-
val result = function.callBy(args)
398-
InteractionInput.Value(DataSchemaValue.toDataSchemaValue(result))
399405
}
400406
}
401407
}

0 commit comments

Comments
 (0)