11package ai.koog.agents.features.opentelemetry.attribute
22
3+ import ai.koog.agents.core.tools.ToolDescriptor
34import ai.koog.agents.utils.HiddenString
5+ import ai.koog.prompt.llm.LLMProvider
46import ai.koog.prompt.llm.LLModel
7+ import ai.koog.prompt.message.ContentPart
8+ import ai.koog.prompt.message.Message
9+ import kotlinx.serialization.json.JsonArray
10+ import kotlinx.serialization.json.JsonArrayBuilder
11+ import kotlinx.serialization.json.JsonElement
12+ import kotlinx.serialization.json.JsonObject
13+ import kotlinx.serialization.json.JsonPrimitive
14+ import kotlinx.serialization.json.addJsonObject
15+ import kotlinx.serialization.json.buildJsonObject
16+ import kotlinx.serialization.json.putJsonArray
517
618/* *
719 * The class describe Attributes related to a Spans in GenAI system.
@@ -86,6 +98,18 @@ internal object SpanAttributes {
8698 }
8799 }
88100
101+ // gen_ai.provider
102+
103+ sealed interface Provider : GenAIAttribute {
104+ override val key: String
105+ get() = super .key.concatKey(" provider" )
106+
107+ data class Name (private val provider : LLMProvider ) : Provider {
108+ override val key: String = super .key.concatKey(" name" )
109+ override val value: String = provider.id
110+ }
111+ }
112+
89113 // gen_ai.conversation
90114 sealed interface Conversation : GenAIAttribute {
91115 override val key: String
@@ -110,6 +134,31 @@ internal object SpanAttributes {
110134 }
111135 }
112136
137+ // gen_ai.input
138+ sealed interface Input : GenAIAttribute {
139+ override val key: String
140+ get() = super .key.concatKey(" input" )
141+
142+ // gen_ai.input.messages
143+ data class Messages (private val messages : List <Message >) : Input {
144+ override val key: String = super .key.concatKey(" messages" )
145+ override val value: HiddenString = HiddenString (
146+ JsonArray (
147+ messages.map { message ->
148+ buildJsonObject {
149+ put(" role" , JsonPrimitive (message.role.name))
150+ putJsonArray(" parts" ) {
151+ message.parts.forEach { part ->
152+ addContentPart(part, message)
153+ }
154+ }
155+ }
156+ }
157+ ).toString()
158+ )
159+ }
160+ }
161+
113162 // gen_ai.output
114163 sealed interface Output : GenAIAttribute {
115164 override val key: String
@@ -121,6 +170,25 @@ internal object SpanAttributes {
121170 override val value: String = type.id
122171 }
123172
173+ // gen_ai.output.messages
174+ data class Messages (private val messages : List <Message >) : Output {
175+ override val key: String = super .key.concatKey(" messages" )
176+ override val value: HiddenString = HiddenString (
177+ JsonArray (
178+ messages.map { message ->
179+ buildJsonObject {
180+ put(" role" , JsonPrimitive (message.role.name))
181+ putJsonArray(" parts" ) {
182+ message.parts.forEach { part ->
183+ addContentPart(part, message)
184+ }
185+ }
186+ }
187+ }
188+ ).toString()
189+ )
190+ }
191+
124192 enum class OutputType (val id : String ) {
125193 TEXT (" text" ),
126194 JSON (" json" ),
@@ -278,6 +346,18 @@ internal object SpanAttributes {
278346 override val key: String = super .key.concatKey(" id" )
279347 override val value: String = id
280348 }
349+
350+ // gen_ai.tool.call.arguments
351+ data class Arguments (private val arguments : JsonObject ) : Call {
352+ override val key: String = super .key.concatKey(" arguments" )
353+ override val value: HiddenString = HiddenString (arguments.toString())
354+ }
355+
356+ // gen_ai.tool.call.result
357+ data class Result (private val result : JsonElement ) : Call {
358+ override val key: String = super .key.concatKey(" result" )
359+ override val value: HiddenString = HiddenString (result.toString())
360+ }
281361 }
282362
283363 // gen_ai.tool.description
@@ -292,16 +372,111 @@ internal object SpanAttributes {
292372 override val value: String = name
293373 }
294374
295- // Custom tool attribute with tool arguments used for tool calls
296- data class InputValue (private val input : String ) : Attribute {
297- override val key: String = " input.value"
298- override val value: HiddenString = HiddenString (input)
375+ // gen_ai.tool.definitions
376+ data class Definitions (private val tools : List <ToolDescriptor >) : Tool {
377+ override val key: String = super .key.concatKey(" definitions" )
378+ override val value: HiddenString = HiddenString (
379+ JsonArray (
380+ tools.map { tool ->
381+ buildJsonObject {
382+ put(" type" , JsonPrimitive (" function" ))
383+ put(" name" , JsonPrimitive (tool.name))
384+ put(" description" , JsonPrimitive (tool.description))
385+ }
386+ }
387+ ).toString()
388+ )
299389 }
390+ }
391+
392+ // gen_ai.system_instructions
393+ data class SystemInstructions (private val messages : List <Message .System >) : GenAIAttribute {
394+ override val key: String = " system_instructions"
395+ override val value: HiddenString = run {
396+ val jsonObjects = messages.flatMap { (parts, metaInfo) ->
397+ parts.map { part ->
398+ JsonObject (
399+ mapOf (
400+ " type" to JsonPrimitive (" text" ),
401+ " content" to JsonPrimitive (part.text)
402+ )
403+ )
404+ }
405+ }
406+
407+ HiddenString (JsonArray (jsonObjects).toString())
408+ }
409+ }
410+
411+ // region Private Methods
412+
413+ private fun JsonArrayBuilder.addContentPart (part : ContentPart , message : Message ) {
414+ when (part) {
415+ is ContentPart .Text -> {
416+ when (message) {
417+ is Message .Tool .Call -> {
418+ addJsonObject {
419+ put(" type" , JsonPrimitive (" tool_call" ))
420+ message.id?.let { id -> put(" id" , JsonPrimitive (id)) }
421+ put(" name" , JsonPrimitive (message.tool))
422+ put(" arguments" , message.contentJson)
423+ }
424+ }
425+
426+ is Message .Tool .Result -> {
427+ addJsonObject {
428+ put(" type" , JsonPrimitive (" tool_call_response" ))
429+ message.id?.let { id -> put(" id" , JsonPrimitive (id)) }
430+ put(" result" , JsonPrimitive (part.text))
431+ }
432+ }
433+
434+ else -> {
435+ addJsonObject {
436+ put(" type" , JsonPrimitive (" text" ))
437+ put(" content" , JsonPrimitive (part.text))
438+ }
439+ }
440+ }
441+ }
300442
301- // Custom tool attribute with tool execution results used for tool calls
302- data class OutputValue (private val output : String ) : Attribute {
303- override val key: String = " output.value"
304- override val value: HiddenString = HiddenString (output)
443+ is ContentPart .Image -> {
444+ addJsonObject {
445+ put(" type" , JsonPrimitive (" image" ))
446+ put(" format" , JsonPrimitive (part.format))
447+ put(" mimeType" , JsonPrimitive (part.mimeType))
448+ part.fileName?.let { name -> put(" fileName" , JsonPrimitive (name)) }
449+ }
450+ }
451+
452+ is ContentPart .Video -> {
453+ addJsonObject {
454+ put(" type" , JsonPrimitive (" video" ))
455+ put(" format" , JsonPrimitive (part.format))
456+ put(" mimeType" , JsonPrimitive (part.mimeType))
457+ part.fileName?.let { name -> put(" fileName" , JsonPrimitive (name)) }
458+ }
459+ }
460+
461+ is ContentPart .Audio -> {
462+ addJsonObject {
463+ put(" type" , JsonPrimitive (" audio" ))
464+ put(" format" , JsonPrimitive (part.format))
465+ put(" mimeType" , JsonPrimitive (part.mimeType))
466+ part.fileName?.let { name -> put(" fileName" , JsonPrimitive (name)) }
467+ }
468+ }
469+
470+ is ContentPart .File -> {
471+ addJsonObject {
472+ put(" type" , JsonPrimitive (" file" ))
473+ put(" format" , JsonPrimitive (part.format))
474+ put(" mimeType" , JsonPrimitive (part.mimeType))
475+ part.fileName?.let { name -> put(" fileName" , JsonPrimitive (name)) }
476+ }
477+ }
305478 }
306479 }
480+
481+ // endregion Private Methods
307482}
0 commit comments