Skip to content

Commit 11c0fef

Browse files
author
Robert Winkler
committed
Added ArcEventListener
1 parent 2c7f088 commit 11c0fef

File tree

13 files changed

+204
-111
lines changed

13 files changed

+204
-111
lines changed

kotlin-wot-integration-tests/build.gradle.kts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,6 @@ plugins {
66
id("io.spring.dependency-management") version "1.1.3"
77
}
88

9-
tasks.named<Test>("test") {
10-
enabled = false
11-
}
129

1310
dependencies {
1411
api(project(":kotlin-wot-binding-http"))

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

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import ai.ancf.lmos.wot.thing.schema.WoTConsumedThing
1010
import kotlinx.coroutines.runBlocking
1111
import org.slf4j.Logger
1212
import org.slf4j.LoggerFactory
13+
import org.springframework.context.ApplicationEventPublisher
1314
import org.springframework.context.annotation.Bean
1415
import org.springframework.context.annotation.Configuration
1516

@@ -78,14 +79,11 @@ class AgentConfiguration {
7879
tools = AllTools
7980
}
8081

82+
@Bean
83+
fun agentEventListener(applicationEventPublisher: ApplicationEventPublisher) = ArcEventListener(applicationEventPublisher)
8184

8285
@Bean
8386
fun discoverTools(functions: Functions, wot: Wot) : List<LLMFunction> = runBlocking {
84-
//discoverTool(wot, functions, "http://localhost:8081/scraper")
85-
/*
86-
discoverTool(wot, functions, "https://plugfest.webthings.io/things/virtual-things-2",
87-
BearerSecurityScheme())
88-
*/
8987
ThingToFunctionsMapper.exploreToolDirectory(wot, functions, "https://plugfest.webthings.io/.well-known/wot",
9088
BearerSecurityScheme())
9189
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package ai.ancf.lmos.wot.integration
2+
3+
import ai.ancf.lmos.arc.agents.events.Event
4+
import ai.ancf.lmos.arc.agents.events.EventHandler
5+
import ai.ancf.lmos.wot.JsonMapper
6+
import org.springframework.context.ApplicationEvent
7+
import org.springframework.context.ApplicationEventPublisher
8+
9+
class ArcEventListener(private val applicationEventPublisher: ApplicationEventPublisher) : EventHandler<Event> {
10+
11+
override fun onEvent(event: Event) {
12+
applicationEventPublisher.publishEvent(AgentEvent(JsonMapper.instance.writeValueAsString(event)))
13+
}
14+
}
15+
16+
data class AgentEvent(val message: String) : ApplicationEvent(message)

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

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,17 @@ import ai.ancf.lmos.arc.agents.conversation.latest
88
import ai.ancf.lmos.arc.agents.conversation.toConversation
99
import ai.ancf.lmos.arc.agents.getAgentByName
1010
import ai.ancf.lmos.arc.core.getOrThrow
11+
import ai.ancf.lmos.wot.JsonMapper
12+
import ai.ancf.lmos.wot.protocol.ConversationalAgent
1113
import ai.ancf.lmos.wot.protocol.LMOSContext
1214
import ai.ancf.lmos.wot.protocol.LMOSThingType
1315
import ai.ancf.lmos.wot.reflection.annotations.*
16+
import kotlinx.coroutines.CoroutineScope
17+
import kotlinx.coroutines.Dispatchers
1418
import kotlinx.coroutines.flow.Flow
1519
import kotlinx.coroutines.flow.MutableSharedFlow
16-
import kotlinx.coroutines.flow.MutableStateFlow
20+
import kotlinx.coroutines.launch
21+
import org.springframework.context.ApplicationListener
1722
import org.springframework.stereotype.Component
1823

1924

@@ -23,30 +28,34 @@ import org.springframework.stereotype.Component
2328
@VersionInfo(instance = "1.0.0")
2429
@Component
2530
class ChatAgent(agentProvider: AgentProvider, @Property(readOnly = true)
26-
val modelConfiguration: ModelConfiguration = ModelConfiguration(0.5, 50)) {
31+
val modelConfiguration: ModelConfiguration = ModelConfiguration(0.5, 50))
32+
: ConversationalAgent<String, String>, ApplicationListener<AgentEvent> {
2733

28-
private val messageFlow = MutableSharedFlow<String>(replay = 1) // Replay last emitted value
34+
private val agentEventFlow = MutableSharedFlow<String>(replay = 1) // Replay last emitted value
2935

3036
val agent = agentProvider.getAgentByName("ChatAgent") as ChatAgent
3137

32-
@Property(title = "Observable Property", readOnly = true)
33-
val observableProperty : MutableStateFlow<String> = MutableStateFlow("Hello World")
34-
35-
@Action(title = "Ask", description = "Ask the agent a question.")
36-
suspend fun ask(chat : Chat) : String {
37-
val assistantMessage = agent.execute(chat.message.toConversation(User("myId"))).getOrThrow().latest<AssistantMessage>() ?:
38+
@Action(title = "Chat", description = "Ask the agent a question.")
39+
@ActionInput(title = "The question", description = "A question")
40+
@ActionOutput(title = "The question", description = "A question")
41+
override suspend fun chat(message: String) : String {
42+
val assistantMessage = agent.execute(message.toConversation(User("myId"))).getOrThrow().latest<AssistantMessage>() ?:
3843
throw RuntimeException("No Assistant response")
39-
messageFlow.emit(assistantMessage.content)
4044
return assistantMessage.content
4145
}
4246

43-
@Event(title = "Generated message")
44-
fun messageGenerated() : Flow<String> {
45-
return messageFlow
47+
@Event(title = "Agent Event", description = "An event from the agent.")
48+
fun agentEvent() : Flow<String> {
49+
return agentEventFlow
50+
}
51+
52+
override fun onApplicationEvent(event: AgentEvent) {
53+
CoroutineScope(Dispatchers.IO).launch {
54+
agentEventFlow.emit(JsonMapper.instance.writeValueAsString(event))
55+
}
4656
}
4757
}
4858

4959
data class ModelConfiguration(val modelTemperature: Double, val maxTokens: Int)
5060

51-
data class Chat(val message: String)
5261

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

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,39 +5,40 @@ import ai.ancf.lmos.wot.Wot
55
import ai.ancf.lmos.wot.binding.http.HttpProtocolClientFactory
66
import ai.ancf.lmos.wot.binding.http.HttpsProtocolClientFactory
77
import ai.ancf.lmos.wot.binding.websocket.WebSocketProtocolClientFactory
8-
import ai.ancf.lmos.wot.integration.Chat
8+
import ai.ancf.lmos.wot.protocol.ConsumedConversationalAgent
9+
import ai.ancf.lmos.wot.protocol.ConversationalAgent
10+
import ai.ancf.lmos.wot.protocol.EventListener
911
import ai.ancf.lmos.wot.thing.ConsumedThing
10-
import ai.ancf.lmos.wot.thing.schema.InteractionListener
1112
import org.slf4j.Logger
1213
import org.slf4j.LoggerFactory
1314

1415

15-
class ConversationalAgent private constructor(private val thing : ConsumedThing) {
16+
class WotConversationalAgent private constructor(private val thing : ConsumedThing) :
17+
ConsumedConversationalAgent<String, String, String> {
1618

1719
private val log : Logger = LoggerFactory.getLogger(ConversationalAgent::class.java)
1820

1921
companion object {
20-
suspend fun create(wot: Wot, url: String): ConversationalAgent {
21-
return ConversationalAgent(wot.consume(wot.requestThingDescription(url)) as ConsumedThing)
22+
suspend fun create(wot: Wot, url: String): ConsumedConversationalAgent<String, String, String> {
23+
return WotConversationalAgent(wot.consume(wot.requestThingDescription(url)) as ConsumedThing)
2224
}
2325

24-
suspend fun create(url: String): ConversationalAgent {
26+
suspend fun create(url: String): ConsumedConversationalAgent<String, String, String> {
2527
val wot = Wot.create(Servient(clientFactories = listOf(HttpProtocolClientFactory(), HttpsProtocolClientFactory(), WebSocketProtocolClientFactory())))
2628
return create(wot, url)
2729
}
2830
}
2931

30-
suspend fun chat(inputMessage: String): String {
31-
val chat = Chat(inputMessage)
32+
override suspend fun chat(message: String): String {
3233
return try {
33-
thing.invokeAction(actionName = "ask", input = chat)
34+
thing.invokeAction(actionName = "chat", input = message)
3435
} catch (e: Exception) {
3536
log.error("Failed to receive an answer", e)
3637
"Failed to receive an answer"
3738
}
3839
}
3940

40-
suspend fun consumeEvent(s: String, listener: InteractionListener) {
41-
thing.subscribeEvent("contentRetrieved",listener)
41+
override suspend fun consumeEvent(eventName: String, listener: EventListener<String>) {
42+
thing.subscribeEvent(eventName, { listener.handleEvent(it.value().asText()) })
4243
}
4344
}

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

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import ai.ancf.lmos.arc.agents.conversation.latest
88
import ai.ancf.lmos.arc.agents.conversation.toConversation
99
import ai.ancf.lmos.arc.agents.getAgentByName
1010
import ai.ancf.lmos.arc.core.getOrThrow
11+
import ai.ancf.lmos.wot.protocol.ConversationalAgent
1112
import ai.ancf.lmos.wot.protocol.LMOSContext
1213
import ai.ancf.lmos.wot.protocol.LMOSThingType
1314
import ai.ancf.lmos.wot.reflection.annotations.Action
@@ -23,15 +24,15 @@ import org.springframework.stereotype.Component
2324
@Context(prefix = LMOSContext.prefix, url = LMOSContext.url)
2425
@VersionInfo(instance = "1.0.0")
2526
@Component
26-
class ResearcherAgent(agentProvider: AgentProvider, ) {
27+
class ResearcherAgent(agentProvider: AgentProvider) : ConversationalAgent<String, String> {
2728

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

3031
val agent = agentProvider.getAgentByName("ResearcherAgent") as ChatAgent
3132

32-
@Action(title = "Ask", description = "Ask the agent a question.")
33-
suspend fun ask(chat : Chat) : String {
34-
val assistantMessage = agent.execute(chat.message.toConversation(User("myId"))).getOrThrow().latest<AssistantMessage>() ?:
33+
@Action(title = "Chat", description = "Ask the agent a question.")
34+
override suspend fun chat(message : String ) : String {
35+
val assistantMessage = agent.execute(message.toConversation(User("myId"))).getOrThrow().latest<AssistantMessage>() ?:
3536
throw RuntimeException("No Assistant response")
3637
return assistantMessage.content
3738
}

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

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import ai.ancf.lmos.arc.agents.conversation.latest
88
import ai.ancf.lmos.arc.agents.conversation.toConversation
99
import ai.ancf.lmos.arc.agents.getAgentByName
1010
import ai.ancf.lmos.arc.core.getOrThrow
11+
import ai.ancf.lmos.wot.protocol.ConversationalAgent
1112
import ai.ancf.lmos.wot.protocol.LMOSContext
1213
import ai.ancf.lmos.wot.protocol.LMOSThingType
1314
import ai.ancf.lmos.wot.reflection.annotations.*
@@ -21,22 +22,23 @@ import org.springframework.stereotype.Component
2122
@Context(prefix = LMOSContext.prefix, url = LMOSContext.url)
2223
@VersionInfo(instance = "1.0.0")
2324
@Component
24-
class ScraperAgent(agentProvider: AgentProvider, ) {
25+
class ScraperAgent(agentProvider: AgentProvider) : ConversationalAgent<String, Unit> {
2526

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

2829
val agent = agentProvider.getAgentByName("ScraperAgent") as ChatAgent
2930

30-
@Action(title = "Ask", description = "Ask the agent a question.")
31-
suspend fun ask(chat : Chat) {
32-
val assistantMessage = agent.execute(chat.message.toConversation(User("myId"))).getOrThrow().latest<AssistantMessage>() ?:
33-
throw RuntimeException("No Assistant response")
34-
messageFlow.emit(assistantMessage.content)
35-
}
3631

3732
@Event(description = "HTML Content of the scraped web site")
3833
fun contentRetrieved() : Flow<String> {
3934
return messageFlow
4035
}
36+
37+
@Action(title = "chat", description = "Ask the agent a question.")
38+
override suspend fun chat(message: String) {
39+
val assistantMessage = agent.execute(message.toConversation(User("myId"))).getOrThrow().latest<AssistantMessage>() ?:
40+
throw RuntimeException("No Assistant response")
41+
messageFlow.emit(assistantMessage.content)
42+
}
4143
}
4244

0 commit comments

Comments
 (0)