@@ -6,26 +6,34 @@ import ai.ancf.lmos.arc.agents.functions.ParameterSchema
66import ai.ancf.lmos.arc.agents.functions.ParameterType
77import ai.ancf.lmos.arc.spring.Agents
88import ai.ancf.lmos.arc.spring.Functions
9- import ai.ancf.lmos.wot.JsonMapper
109import ai.ancf.lmos.wot.Wot
1110import ai.ancf.lmos.wot.security.BearerSecurityScheme
1211import ai.ancf.lmos.wot.security.SecurityScheme
1312import ai.ancf.lmos.wot.thing.schema.*
1413import com.fasterxml.jackson.databind.JsonNode
14+ import com.fasterxml.jackson.databind.node.BooleanNode
15+ import com.fasterxml.jackson.databind.node.DecimalNode
16+ import com.fasterxml.jackson.databind.node.IntNode
1517import com.fasterxml.jackson.databind.node.TextNode
16- import com.fasterxml.jackson.module.kotlin.convertValue
1718import kotlinx.coroutines.runBlocking
19+ import org.slf4j.Logger
20+ import org.slf4j.LoggerFactory
1821import org.springframework.context.annotation.Bean
1922import org.springframework.context.annotation.Configuration
23+ import java.math.BigDecimal
2024
2125
2226@Configuration
2327class AgentConfiguration {
2428
29+ private lateinit var thingDescriptionsMap : Map <String , WoTConsumedThing >
30+
31+ private val log : Logger = LoggerFactory .getLogger(AgentConfiguration ::class .java)
32+
2533 @Bean
2634 fun chatArcAgent (agent : Agents ) = agent {
2735 name = " ChatAgent"
28- prompt { " You are a helpful smart home agent that can control devices." }
36+ prompt { " You are a helpful smart home agent that can control devices. But never print thingIds to the customer " }
2937 model = { " GPT-4o" }
3038 tools = AllTools
3139 }
@@ -49,8 +57,40 @@ class AgentConfiguration {
4957 @Bean
5058 fun discoverTools (functions : Functions , wot : Wot ) : List <LLMFunction > = runBlocking {
5159 // discoverTool(wot, functions, "http://localhost:8081/scraper")
60+ /*
5261 discoverTool(wot, functions, "https://plugfest.webthings.io/things/virtual-things-2",
5362 BearerSecurityScheme())
63+ */
64+ exploreToolDirectory(wot, functions, " https://plugfest.webthings.io/.well-known/wot" ,
65+ BearerSecurityScheme ())
66+ }
67+
68+ private suspend fun exploreToolDirectory (wot : Wot , functions : Functions , url : String ,
69+ securityScheme : SecurityScheme ) : List <LLMFunction > {
70+ val thingDescriptions = wot.exploreDirectory(url, securityScheme)
71+
72+ val retrieveAllFunction = functions(
73+ " retrieveAllThings" ,
74+ " Retrieves the metadata information of all things/devices available. " +
75+ " Can be used to understand which device types are available and retrieve the " +
76+ " thingIds to control multiple devices. The types tell you the capabilities of a device." ,
77+ " all_things"
78+ ) {
79+ summarizeThingDescriptions(thingDescriptions)
80+ }
81+
82+ val consumedThings = thingDescriptions.map { wot.consume(it) }
83+ thingDescriptionsMap = consumedThings.associateBy { it.getThingDescription().id }
84+
85+ return retrieveAllFunction + consumedThings
86+ .flatMap { mapThingDescriptionToFunctions(functions, it) }
87+ }
88+
89+ fun summarizeThingDescriptions (things : Set <WoTThingDescription >): String {
90+ return things.joinToString(separator = " \n " ) { thing ->
91+ val types = thing.objectType?.types?.joinToString(" , " ) ? : " N/A"
92+ " thingId: ${thing.id} , Title: ${thing.title ? : " N/A" } , Types: $types "
93+ }
5494 }
5595
5696 private suspend fun discoverTool (wot : Wot , functions : Functions , url : String ,
@@ -60,28 +100,43 @@ class AgentConfiguration {
60100
61101 val thing = wot.consume(thingDescription)
62102
63- return mapThingDescriptionToFunctions(thingDescription, functions, thing)
103+ return mapThingDescriptionToFunctions(functions, thing)
64104 }
65105
66106 private suspend fun mapThingDescriptionToFunctions (
67- thingDescription : WoTThingDescription ,
68107 functions : Functions ,
69108 thing : WoTConsumedThing
70109 ): List <LLMFunction > {
110+ val thingDescription = thing.getThingDescription()
111+
112+ val defaultParams = listOf (Pair (ParameterSchema (
113+ name = " thingId" ,
114+ description = " The unique identifier of the thing" ,
115+ type = ParameterType (" string" ),
116+ enum = emptyList()
117+ ), true ))
118+
71119 val actionFunctions = thingDescription.actions.flatMap { (actionName, action) ->
72120
73- val params = action.input?.let { input ->
121+ val actionParams = action.input?.let { input ->
74122 listOf (Pair (mapDataSchemaToParam(input), true ))
75- }
123+ } ? : emptyList()
124+
125+ val params = defaultParams + actionParams
76126
77127 functions(
78128 actionName,
79129 action.description ? : " No Description available" ,
80130 thingDescription.title,
81- params ? : emptyList() ,
131+ params,
82132 ) {
83- (url) ->
84- thing.invokeAction(actionName, TextNode (url)).asText()
133+ (thingId, input) ->
134+ try {
135+ thingDescriptionsMap[thingId]?.invokeAction(actionName, TextNode (input))?.asText()? : " Function call failed"
136+ }catch (e: Exception ) {
137+ log.error(" Error invoking action $actionName " , e)
138+ " Function call failed"
139+ }
85140 }
86141 }
87142 val propertiesFunctions = functions(
@@ -101,38 +156,68 @@ class AgentConfiguration {
101156 property.description ? : " Can be used to read the $propertyName property" ,
102157 thingDescription.title
103158 ) {
104- thing.readProperty(propertyName).value().asText()
159+ (thingId) ->
160+ try {
161+ thingDescriptionsMap[thingId]?.readProperty(propertyName)?.value()?.asText() ? : " Function call failed"
162+ } catch (e: Exception ) {
163+ log.error(" Error reading property $propertyName " , e)
164+ " Function call failed"
165+ }
105166 }
106167 } else if (property.writeOnly) {
168+ val params = defaultParams + listOf (Pair (mapDataSchemaToParam(property), true ))
169+
107170 functions(
108171 " set$propertyName " ,
109172 property.description ? : " Can be used to set the $propertyName property" ,
110173 thingDescription.title,
111- listOf ( Pair (mapDataSchemaToParam(property), true ))
174+ params
112175 ) {
113- (propertyValue) ->
114- thing.writeProperty(propertyName, TextNode (propertyValue))
115- " Property $propertyName set to $propertyValue "
176+ (thingId, propertyValue) ->
177+ if (propertyValue != null ){
178+ try {
179+ val propertyAffordance = thing.getThingDescription().properties[propertyName]!!
180+ thingDescriptionsMap[thingId]?.writeProperty(propertyName, mapSchemaToJsonNode(propertyAffordance, propertyValue)) ? : " Function failed"
181+ " Property $propertyName set to $propertyValue "
182+ } catch (e: Exception ) {
183+ log.error(" Error writing property $propertyName " , e)
184+ " Function call failed"
185+ }
186+ }else {
187+ " Function call failed"
188+ }
116189 }
117190 } else {
118191 functions(
119192 " read$propertyName " ,
120193 property.description ? : " Can be used to read the $propertyName property" ,
121194 thingDescription.title
122195 ) {
123- thing.readProperty(propertyName).value().asText()
196+ (thingId) ->
197+ try {
198+ thingDescriptionsMap[thingId]?.readProperty(propertyName)?.value()?.asText() ? : " Function failed"
199+ } catch (e: Exception ) {
200+ log.error(" Error reading property $propertyName " , e)
201+ " Function call failed"
202+ }
124203 }
204+ val params = defaultParams + listOf (Pair (mapDataSchemaToParam(property), true ))
125205 functions(
126206 " set$propertyName " ,
127207 property.description ? : " Can be used to set the $propertyName property" ,
128208 thingDescription.title,
129- listOf ( Pair (mapDataSchemaToParam(property), true ))
209+ params
130210 ) {
131- ( propertyValue) ->
211+ (thingId, propertyValue) ->
132212 if (propertyValue != null ){
133- val input : JsonNode = JsonMapper .instance.convertValue(propertyValue)
134- thing.writeProperty(propertyName, input)
135- " Property $propertyName set to $propertyValue "
213+ try {
214+ val propertyAffordance = thing.getThingDescription().properties[propertyName]!!
215+ thingDescriptionsMap[thingId]?.writeProperty(propertyName, mapSchemaToJsonNode(propertyAffordance, propertyValue)) ? : " Function failed"
216+ " Property $propertyName set to $propertyValue "
217+ } catch (e: Exception ) {
218+ log.error(" Error writing property $propertyName " , e)
219+ " Function call failed"
220+ }
136221 }else {
137222 " Function call failed"
138223 }
@@ -142,7 +227,24 @@ class AgentConfiguration {
142227 return actionFunctions + propertyFunctions + propertiesFunctions
143228 }
144229
145-
230+ fun mapSchemaToJsonNode (schema : DataSchema <* >, value : String ): JsonNode {
231+ return when (schema) {
232+ is StringSchema -> TextNode (value)
233+ is IntegerSchema -> {
234+ val intValue = value.toIntOrNull() ? : 0
235+ IntNode (intValue)
236+ }
237+ is NumberSchema -> {
238+ val numberValue = value.toBigDecimalOrNull() ? : BigDecimal .ZERO
239+ DecimalNode (numberValue)
240+ }
241+ is BooleanSchema -> {
242+ val boolValue = value?.toBooleanStrictOrNull() ? : false
243+ BooleanNode .valueOf(boolValue)
244+ }
245+ else -> throw IllegalArgumentException (" Unsupported schema type: ${schema::class .simpleName} " )
246+ }
247+ }
146248
147249 /*
148250
0 commit comments