Skip to content

Commit 5dabdad

Browse files
authored
Graphql (#84)
* return flow<AssistantMessage> * Replace Map with List<KeyValuePair> for context parameters * LmosInfoController * GraphQL * GraphQL * e-grpahql-running-harcoded agent url, quick events * todo: helm * helm * license * helm * review comments
1 parent c996ce0 commit 5dabdad

File tree

38 files changed

+1085
-89
lines changed

38 files changed

+1085
-89
lines changed

lmos-runtime-core/src/main/kotlin/org/eclipse/lmos/runtime/core/inbound/ConversationHandler.kt

Lines changed: 25 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66

77
package org.eclipse.lmos.runtime.core.inbound
88

9+
import kotlinx.coroutines.coroutineScope
10+
import kotlinx.coroutines.flow.Flow
11+
import kotlinx.coroutines.flow.onEach
912
import org.eclipse.lmos.runtime.core.LmosRuntimeConfig
1013
import org.eclipse.lmos.runtime.core.cache.LmosRuntimeTenantAwareCache
1114
import org.eclipse.lmos.runtime.core.constants.LmosRuntimeConstants.Cache.ROUTES
@@ -24,7 +27,7 @@ interface ConversationHandler {
2427
conversationId: String,
2528
tenantId: String,
2629
turnId: String,
27-
): AssistantMessage
30+
): Flow<AssistantMessage>
2831
}
2932

3033
class DefaultConversationHandler(
@@ -41,31 +44,32 @@ class DefaultConversationHandler(
4144
conversationId: String,
4245
tenantId: String,
4346
turnId: String,
44-
): AssistantMessage {
45-
log.debug("Request Received, conversationId: $conversationId, turnId: $turnId")
46-
val routingInformation =
47-
lmosRuntimeTenantAwareCache.get(tenantId, ROUTES, conversationId)
48-
?: agentRegistryService.getRoutingInformation(tenantId, conversation.systemContext.channelId)
49-
.also { result ->
50-
log.debug("Caching routing information: {}", result)
51-
lmosRuntimeTenantAwareCache.save(
52-
tenantId, ROUTES, conversationId,
53-
result, lmosRuntimeConfig.cache.ttl,
54-
)
55-
}
56-
log.info("routingInformation: $routingInformation")
57-
val agent: Agent = agentRoutingService.resolveAgentForConversation(conversation, routingInformation.agentList)
58-
log.info("Resolved agent: $agent")
59-
val agentResponse =
47+
): Flow<AssistantMessage> =
48+
coroutineScope {
49+
log.debug("Request Received, conversationId: $conversationId, turnId: $turnId")
50+
val routingInformation =
51+
lmosRuntimeTenantAwareCache.get(tenantId, ROUTES, conversationId)
52+
?: agentRegistryService.getRoutingInformation(tenantId, conversation.systemContext.channelId)
53+
.also { result ->
54+
log.debug("Caching routing information: {}", result)
55+
lmosRuntimeTenantAwareCache.save(
56+
tenantId, ROUTES, conversationId,
57+
result, lmosRuntimeConfig.cache.ttl,
58+
)
59+
}
60+
log.info("routingInformation: $routingInformation")
61+
val agent: Agent = agentRoutingService.resolveAgentForConversation(conversation, routingInformation.agentList)
62+
log.info("Resolved agent: $agent")
63+
6064
agentClientService.askAgent(
6165
conversation,
6266
conversationId,
6367
turnId,
6468
agent.name,
6569
agent.addresses.random(),
6670
routingInformation.subset,
67-
)
68-
log.info("Agent Response: $agentResponse")
69-
return agentResponse
70-
}
71+
).onEach {
72+
log.info("Agent Response: $it")
73+
}
74+
}
7175
}

lmos-runtime-core/src/main/kotlin/org/eclipse/lmos/runtime/core/model/Agent.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ data class Agent(
1111
val version: String,
1212
val description: String,
1313
val capabilities: List<AgentCapability>,
14-
val addresses: Set<Address>,
14+
val addresses: List<Address>,
1515
)
1616

1717
data class Address(
@@ -31,7 +31,7 @@ class AgentBuilder {
3131
private var version: String = ""
3232
private var description: String = ""
3333
private var capabilities: MutableList<AgentCapability> = mutableListOf()
34-
private var addresses: MutableSet<Address> = mutableSetOf()
34+
private var addresses: MutableList<Address> = mutableListOf()
3535

3636
fun name(name: String) = apply { this.name = name }
3737

@@ -45,7 +45,7 @@ class AgentBuilder {
4545

4646
fun addAddress(address: Address) = apply { this.addresses.add(address) }
4747

48-
fun addresses(addresses: Set<Address>) = apply { this.addresses.addAll(addresses) }
48+
fun addresses(addresses: List<Address>) = apply { this.addresses.addAll(addresses) }
4949

5050
fun build(): Agent {
5151
return Agent(

lmos-runtime-core/src/main/kotlin/org/eclipse/lmos/runtime/core/model/Conversation.kt

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,15 @@ data class InputContext(
2727
@SerialName("systemContext")
2828
data class SystemContext(
2929
val channelId: String,
30-
val contextParams: Map<String, String> = mapOf(),
30+
val contextParams: List<KeyValuePair> = emptyList(),
3131
)
3232

3333
@Serializable
3434
@SerialName("userContext")
3535
data class UserContext(
3636
val userId: String,
3737
val userToken: String?,
38-
val contextParams: Map<String, String> = mapOf(),
38+
val contextParams: List<KeyValuePair> = emptyList(),
3939
)
4040

4141
sealed class ChatMessage {
@@ -46,3 +46,9 @@ data class AssistantMessage(
4646
override val content: String,
4747
val anonymizationEntities: List<AnonymizationEntity>? = emptyList(),
4848
) : ChatMessage()
49+
50+
@Serializable
51+
data class KeyValuePair(
52+
val key: String,
53+
val value: String,
54+
)

lmos-runtime-core/src/main/kotlin/org/eclipse/lmos/runtime/core/service/outbound/AgentClientService.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
package org.eclipse.lmos.runtime.core.service.outbound
88

9+
import kotlinx.coroutines.flow.Flow
910
import org.eclipse.lmos.runtime.core.model.Address
1011
import org.eclipse.lmos.runtime.core.model.AssistantMessage
1112
import org.eclipse.lmos.runtime.core.model.Conversation
@@ -18,5 +19,5 @@ interface AgentClientService {
1819
agentName: String,
1920
agentAddress: Address,
2021
subset: String?,
21-
): AssistantMessage
22+
): Flow<AssistantMessage>
2223
}

lmos-runtime-core/src/main/kotlin/org/eclipse/lmos/runtime/outbound/ArcAgentClientService.kt

Lines changed: 46 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,12 @@
66

77
package org.eclipse.lmos.runtime.outbound
88

9-
import kotlinx.coroutines.flow.toCollection
9+
import kotlinx.coroutines.Dispatchers
10+
import kotlinx.coroutines.flow.Flow
11+
import kotlinx.coroutines.flow.flow
12+
import kotlinx.coroutines.flow.flowOn
1013
import org.eclipse.lmos.arc.agent.client.graphql.GraphQlAgentClient
11-
import org.eclipse.lmos.arc.api.AgentRequest
12-
import org.eclipse.lmos.arc.api.ConversationContext
13-
import org.eclipse.lmos.arc.api.ProfileEntry
14-
import org.eclipse.lmos.arc.api.SystemContextEntry
15-
import org.eclipse.lmos.arc.api.UserContext
14+
import org.eclipse.lmos.arc.api.*
1615
import org.eclipse.lmos.runtime.core.constants.LmosRuntimeConstants
1716
import org.eclipse.lmos.runtime.core.exception.AgentClientException
1817
import org.eclipse.lmos.runtime.core.model.Address
@@ -31,51 +30,59 @@ class ArcAgentClientService : AgentClientService {
3130
agentName: String,
3231
agentAddress: Address,
3332
subset: String?,
34-
): AssistantMessage {
35-
return createGraphQlAgentClient(agentAddress).use { graphQlAgentClient ->
33+
): Flow<AssistantMessage> =
34+
flow {
35+
createGraphQlAgentClient(agentAddress).use { graphQlAgentClient ->
36+
37+
val subsetHeader = subset?.let { mapOf(LmosRuntimeConstants.SUBSET to subset) } ?: emptyMap()
38+
39+
val agentRequest =
40+
AgentRequest(
41+
conversationContext =
42+
ConversationContext(
43+
conversationId = conversationId,
44+
anonymizationEntities = conversation.inputContext.anonymizationEntities,
45+
),
46+
systemContext =
47+
conversation.systemContext.contextParams.map { (key, value) ->
48+
SystemContextEntry(key, value)
49+
}.toList(),
50+
userContext =
51+
UserContext(
52+
userId = conversation.userContext.userId,
53+
userToken = conversation.userContext.userToken,
54+
profile =
55+
conversation.userContext.contextParams.map { (key, value) ->
56+
ProfileEntry(key, value)
57+
}.toList(),
58+
),
59+
messages = conversation.inputContext.messages,
60+
)
3661

37-
val subsetHeader = subset?.let { mapOf(LmosRuntimeConstants.SUBSET to subset) } ?: emptyMap()
38-
val agentResponse =
3962
try {
4063
graphQlAgentClient.callAgent(
41-
AgentRequest(
42-
conversationContext =
43-
ConversationContext(
44-
conversationId = conversationId,
45-
anonymizationEntities = conversation.inputContext.anonymizationEntities,
46-
),
47-
systemContext =
48-
conversation.systemContext.contextParams.map { (key, value) ->
49-
SystemContextEntry(key, value)
50-
}.toList(),
51-
userContext =
52-
UserContext(
53-
userId = conversation.userContext.userId,
54-
userToken = conversation.userContext.userToken,
55-
profile =
56-
conversation.userContext.contextParams.map { (key, value) ->
57-
ProfileEntry(key, value)
58-
}.toList(),
59-
),
60-
messages = conversation.inputContext.messages,
61-
),
64+
agentRequest,
6265
requestHeaders = subsetHeader,
63-
).toCollection(mutableListOf())
66+
).collect { response ->
67+
log.info("Agent Response: $response")
68+
emit(
69+
AssistantMessage(
70+
response.messages[0].content,
71+
response.anonymizationEntities,
72+
),
73+
)
74+
}
6475
} catch (e: Exception) {
6576
log.error("Error response from ArcAgentClient", e)
6677
throw AgentClientException(e.message)
6778
}
68-
69-
AssistantMessage(
70-
agentResponse.first().messages[0].content,
71-
agentResponse.first().anonymizationEntities,
72-
)
73-
}
74-
}
79+
}
80+
}.flowOn(Dispatchers.IO)
7581

7682
fun createGraphQlAgentClient(agentAddress: Address): GraphQlAgentClient {
7783
// TODO - remove hardcoded parts of agent url
7884
val agentUrl = "ws://${agentAddress.uri}:8080/subscriptions"
85+
7986
log.info("Creating GraphQlAgentClient with url $agentUrl")
8087
val graphQlAgentClient = GraphQlAgentClient(agentUrl)
8188
return graphQlAgentClient

lmos-runtime-core/src/main/kotlin/org/eclipse/lmos/runtime/outbound/LmosAgentRoutingService.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ class LmosAgentRoutingService(private val lmosRuntimeConfig: LmosRuntimeConfig)
3737
return agentRoutingSpec?.toAgent() ?: throw AgentRoutingSpecResolverException("No agent resolved for user query")
3838
}
3939

40-
fun resolveAgent(
40+
private fun resolveAgent(
4141
agentRoutingSpecResolver: AgentRoutingSpecsResolver,
4242
context: Context,
4343
input: UserMessage,
@@ -122,7 +122,7 @@ fun AgentRoutingSpec.toAgent(): Agent =
122122
.name(name)
123123
.description(description)
124124
.version(version)
125-
.addresses(addresses.map { address -> org.eclipse.lmos.runtime.core.model.Address(address.protocol, address.uri) }.toSet())
125+
.addresses(addresses.map { address -> org.eclipse.lmos.runtime.core.model.Address(address.protocol, address.uri) }.toList())
126126
.apply {
127127
capabilities(
128128
capabilities.map { capability ->

lmos-runtime-core/src/test/kotlin/org/eclipse/lmos/runtime/core/inbound/ConversationHandlerIntegrationTest.kt

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
package org.eclipse.lmos.runtime.core.inbound
88

99
import io.mockk.*
10+
import kotlinx.coroutines.flow.first
1011
import kotlinx.coroutines.flow.flow
1112
import kotlinx.coroutines.runBlocking
1213
import org.eclipse.lmos.arc.agent.client.graphql.GraphQlAgentClient
@@ -68,7 +69,7 @@ class ConversationHandlerIntegrationTest : BaseWireMockTest() {
6869
}
6970

7071
val assistantMessage =
71-
conversationHandler.handleConversation(conversation, conversationId, tenantId, turnId)
72+
conversationHandler.handleConversation(conversation, conversationId, tenantId, turnId).first()
7273

7374
assertEquals("Dummy response from Agent", assistantMessage.content)
7475
coVerify(exactly = 1) { mockGraphQlAgentClient.callAgent(any(), any(), any()) }
@@ -91,7 +92,7 @@ class ConversationHandlerIntegrationTest : BaseWireMockTest() {
9192
conversationId,
9293
tenantId,
9394
turnId,
94-
)
95+
).first()
9596
}
9697
coVerify(exactly = 0) { mockGraphQlAgentClient.callAgent(any(), any(), any()) }
9798
}
@@ -107,7 +108,14 @@ class ConversationHandlerIntegrationTest : BaseWireMockTest() {
107108

108109
val mockGraphQlAgentClient = mockk<GraphQlAgentClient>()
109110

110-
assertThrows<AgentNotFoundException> { conversationHandler.handleConversation(conversation, conversationId, tenantId, turnId) }
111+
assertThrows<AgentNotFoundException> {
112+
conversationHandler.handleConversation(
113+
conversation,
114+
conversationId,
115+
tenantId,
116+
turnId,
117+
).first()
118+
}
111119
coVerify(exactly = 0) { mockGraphQlAgentClient.callAgent(any(), any(), any()) }
112120
}
113121

@@ -124,7 +132,14 @@ class ConversationHandlerIntegrationTest : BaseWireMockTest() {
124132
coEvery { agentClientService.createGraphQlAgentClient(any()) } returns mockGraphQlAgentClient
125133
coEvery { mockGraphQlAgentClient.callAgent(any(), any(), any()) } throws RuntimeException("Something went wrong")
126134

127-
assertThrows<AgentClientException> { conversationHandler.handleConversation(conversation, conversationId, tenantId, turnId) }
135+
assertThrows<AgentClientException> {
136+
conversationHandler.handleConversation(
137+
conversation,
138+
conversationId,
139+
tenantId,
140+
turnId,
141+
).first()
142+
}
128143
coVerify(exactly = 1) { mockGraphQlAgentClient.callAgent(any(), any(), any()) }
129144
}
130145

0 commit comments

Comments
 (0)