diff --git a/agent-components/planner/planner-executor/src/main/kotlin/io/github/llmagentbuilder/planner/executor/LLMPlanExecutor.kt b/agent-components/planner/planner-executor/src/main/kotlin/io/github/llmagentbuilder/planner/executor/LLMPlanExecutor.kt index f16b52c..3551194 100644 --- a/agent-components/planner/planner-executor/src/main/kotlin/io/github/llmagentbuilder/planner/executor/LLMPlanExecutor.kt +++ b/agent-components/planner/planner-executor/src/main/kotlin/io/github/llmagentbuilder/planner/executor/LLMPlanExecutor.kt @@ -4,16 +4,31 @@ import io.github.llmagentbuilder.core.AgentFinish import io.github.llmagentbuilder.core.IntermediateAgentStep import io.github.llmagentbuilder.core.Planner import io.github.llmagentbuilder.core.executor.ActionPlanningResult +import io.github.llmagentbuilder.core.observation.AgentPlanningObservationContext +import io.github.llmagentbuilder.core.observation.AgentPlanningObservationDocumentation +import io.github.llmagentbuilder.core.observation.DefaultAgentPlanningObservationConvention import io.github.llmagentbuilder.core.planner.OutputParser +import io.micrometer.observation.ObservationRegistry import org.springframework.ai.chat.client.ChatClient open class LLMPlanExecutor( private val chatClient: ChatClient, private val outputParser: OutputParser, + private val observationRegistry: ObservationRegistry? = null, ) : Planner { override fun plan( inputs: Map, intermediateSteps: List + ): ActionPlanningResult { + val action = { internalPlan(inputs, intermediateSteps) } + return observationRegistry?.let { registry -> + instrumentedPlan(inputs, action, registry) + } ?: action.invoke() + } + + private fun internalPlan( + inputs: Map, + intermediateSteps: List ): ActionPlanningResult { val userInput = (inputs["input"] as? String) @@ -36,6 +51,34 @@ open class LLMPlanExecutor( return ActionPlanningResult.fromParseResult(result) } + private fun instrumentedPlan( + input: Map, + action: () -> ActionPlanningResult, + registry: ObservationRegistry + ): ActionPlanningResult { + val observationContext = + AgentPlanningObservationContext(input) + val observation = + AgentPlanningObservationDocumentation.AGENT_PLANNING.observation( + null, + DefaultAgentPlanningObservationConvention(), + { observationContext }, + registry + ).start() + return try { + observation.openScope().use { + val response = action.invoke() + observationContext.setResponse(response) + response + } + } catch (e: Exception) { + observation.error(e) + throw e + } finally { + observation.stop() + } + } + private fun constructScratchpad(intermediateSteps: List): String { return intermediateSteps.joinToString(" ") { val (action, observation) = it diff --git a/agent-components/planner/react-json/src/main/kotlin/io/github/llmagentbuilder/planner/reactjson/ReActJsonPlanner.kt b/agent-components/planner/react-json/src/main/kotlin/io/github/llmagentbuilder/planner/reactjson/ReActJsonPlanner.kt index bcc690e..f6c2b4e 100644 --- a/agent-components/planner/react-json/src/main/kotlin/io/github/llmagentbuilder/planner/reactjson/ReActJsonPlanner.kt +++ b/agent-components/planner/react-json/src/main/kotlin/io/github/llmagentbuilder/planner/reactjson/ReActJsonPlanner.kt @@ -1,7 +1,15 @@ package io.github.llmagentbuilder.planner.reactjson import io.github.llmagentbuilder.planner.executor.LLMPlanExecutor +import io.micrometer.observation.ObservationRegistry import org.springframework.ai.chat.client.ChatClient -class ReActJsonPlanner(chatClient: ChatClient) : - LLMPlanExecutor(chatClient, ReActJsonOutputParser.INSTANCE) \ No newline at end of file +class ReActJsonPlanner( + chatClient: ChatClient, + observationRegistry: ObservationRegistry? = null, +) : + LLMPlanExecutor( + chatClient, + ReActJsonOutputParser.INSTANCE, + observationRegistry + ) \ No newline at end of file diff --git a/agent-components/planner/react-json/src/main/kotlin/io/github/llmagentbuilder/planner/reactjson/ReActJsonPlannerProvider.kt b/agent-components/planner/react-json/src/main/kotlin/io/github/llmagentbuilder/planner/reactjson/ReActJsonPlannerProvider.kt index bcff3ad..c2e9234 100644 --- a/agent-components/planner/react-json/src/main/kotlin/io/github/llmagentbuilder/planner/reactjson/ReActJsonPlannerProvider.kt +++ b/agent-components/planner/react-json/src/main/kotlin/io/github/llmagentbuilder/planner/reactjson/ReActJsonPlannerProvider.kt @@ -3,6 +3,7 @@ package io.github.llmagentbuilder.planner.reactjson import io.github.llmagentbuilder.core.MapToObject import io.github.llmagentbuilder.core.Planner import io.github.llmagentbuilder.core.PlannerProvider +import io.micrometer.observation.ObservationRegistry import org.springframework.ai.chat.client.ChatClient class ReActJsonPlannerProvider : PlannerProvider { @@ -12,7 +13,8 @@ class ReActJsonPlannerProvider : PlannerProvider { override fun providePlanner( chatClientBuilder: ChatClient.Builder, - config: Map? + config: Map?, + observationRegistry: ObservationRegistry?, ): Planner? { val plannerConfig = MapToObject.toObject(config) if (plannerConfig?.enabled == false) { @@ -21,6 +23,6 @@ class ReActJsonPlannerProvider : PlannerProvider { val chatClient = chatClientBuilder.defaultAdvisors(ReActJsonPromptAdvisor()) .build() - return ReActJsonPlanner(chatClient) + return ReActJsonPlanner(chatClient, observationRegistry) } } \ No newline at end of file diff --git a/agent-components/planner/react-json/src/main/kotlin/io/github/llmagentbuilder/planner/reactjson/ReActJsonPromptAdvisor.kt b/agent-components/planner/react-json/src/main/kotlin/io/github/llmagentbuilder/planner/reactjson/ReActJsonPromptAdvisor.kt index 9cea363..e8708e9 100644 --- a/agent-components/planner/react-json/src/main/kotlin/io/github/llmagentbuilder/planner/reactjson/ReActJsonPromptAdvisor.kt +++ b/agent-components/planner/react-json/src/main/kotlin/io/github/llmagentbuilder/planner/reactjson/ReActJsonPromptAdvisor.kt @@ -56,7 +56,7 @@ internal const val defaultUserTextTemplate = """ class ReActJsonPromptAdvisor : CallAroundAdvisor { override fun getName(): String { - return "ReAct Json Planner - Prompt" + return javaClass.simpleName } override fun getOrder(): Int { diff --git a/agent-components/planner/react/src/main/kotlin/io/github/llmagentbuilder/planner/react/ReActPlanner.kt b/agent-components/planner/react/src/main/kotlin/io/github/llmagentbuilder/planner/react/ReActPlanner.kt index 2186bec..a723e21 100644 --- a/agent-components/planner/react/src/main/kotlin/io/github/llmagentbuilder/planner/react/ReActPlanner.kt +++ b/agent-components/planner/react/src/main/kotlin/io/github/llmagentbuilder/planner/react/ReActPlanner.kt @@ -1,7 +1,11 @@ package io.github.llmagentbuilder.planner.react import io.github.llmagentbuilder.planner.executor.LLMPlanExecutor +import io.micrometer.observation.ObservationRegistry import org.springframework.ai.chat.client.ChatClient -class ReActPlanner(chatClient: ChatClient) : - LLMPlanExecutor(chatClient, ReActOutputParser.INSTANCE) +class ReActPlanner( + chatClient: ChatClient, + observationRegistry: ObservationRegistry? = null, +) : + LLMPlanExecutor(chatClient, ReActOutputParser.INSTANCE, observationRegistry) diff --git a/agent-components/planner/react/src/main/kotlin/io/github/llmagentbuilder/planner/react/ReActPlannerProvider.kt b/agent-components/planner/react/src/main/kotlin/io/github/llmagentbuilder/planner/react/ReActPlannerProvider.kt index 90a9046..ef2b28a 100644 --- a/agent-components/planner/react/src/main/kotlin/io/github/llmagentbuilder/planner/react/ReActPlannerProvider.kt +++ b/agent-components/planner/react/src/main/kotlin/io/github/llmagentbuilder/planner/react/ReActPlannerProvider.kt @@ -3,6 +3,7 @@ package io.github.llmagentbuilder.planner.react import io.github.llmagentbuilder.core.MapToObject import io.github.llmagentbuilder.core.Planner import io.github.llmagentbuilder.core.PlannerProvider +import io.micrometer.observation.ObservationRegistry import org.springframework.ai.chat.client.ChatClient class ReActPlannerProvider : PlannerProvider { @@ -12,7 +13,8 @@ class ReActPlannerProvider : PlannerProvider { override fun providePlanner( chatClientBuilder: ChatClient.Builder, - config: Map? + config: Map?, + observationRegistry: ObservationRegistry?, ): Planner? { val plannerConfig = MapToObject.toObject(config) if (plannerConfig?.enabled == false) { @@ -20,6 +22,6 @@ class ReActPlannerProvider : PlannerProvider { } val chatClient = chatClientBuilder.defaultAdvisors(ReActPromptAdvisor()) .build() - return ReActPlanner(chatClient) + return ReActPlanner(chatClient, observationRegistry) } } \ No newline at end of file diff --git a/agent-components/planner/react/src/main/kotlin/io/github/llmagentbuilder/planner/react/ReActPromptAdvisor.kt b/agent-components/planner/react/src/main/kotlin/io/github/llmagentbuilder/planner/react/ReActPromptAdvisor.kt index 30007fd..9f31e3b 100644 --- a/agent-components/planner/react/src/main/kotlin/io/github/llmagentbuilder/planner/react/ReActPromptAdvisor.kt +++ b/agent-components/planner/react/src/main/kotlin/io/github/llmagentbuilder/planner/react/ReActPromptAdvisor.kt @@ -38,7 +38,7 @@ internal const val defaultUserTextTemplate = """ class ReActPromptAdvisor : CallAroundAdvisor { override fun getName(): String { - return "ReAct Planner - Prompt" + return javaClass.simpleName } override fun getOrder(): Int { diff --git a/agent-components/planner/simple/src/main/kotlin/io/github/llmagentbuilder/planner/simple/SimplePlanner.kt b/agent-components/planner/simple/src/main/kotlin/io/github/llmagentbuilder/planner/simple/SimplePlanner.kt index c454e28..85005a0 100644 --- a/agent-components/planner/simple/src/main/kotlin/io/github/llmagentbuilder/planner/simple/SimplePlanner.kt +++ b/agent-components/planner/simple/src/main/kotlin/io/github/llmagentbuilder/planner/simple/SimplePlanner.kt @@ -1,8 +1,15 @@ package io.github.llmagentbuilder.planner.simple import io.github.llmagentbuilder.planner.executor.LLMPlanExecutor +import io.micrometer.observation.ObservationRegistry import org.springframework.ai.chat.client.ChatClient -class SimplePlanner(chatClient: ChatClient) : - LLMPlanExecutor(chatClient, SimpleOutputParser.INSTANCE) { -} \ No newline at end of file +class SimplePlanner( + chatClient: ChatClient, + observationRegistry: ObservationRegistry? = null +) : + LLMPlanExecutor( + chatClient, + SimpleOutputParser.INSTANCE, + observationRegistry + ) \ No newline at end of file diff --git a/agent-components/planner/simple/src/main/kotlin/io/github/llmagentbuilder/planner/simple/SimplePlannerProvider.kt b/agent-components/planner/simple/src/main/kotlin/io/github/llmagentbuilder/planner/simple/SimplePlannerProvider.kt index 7acde4f..7dd5917 100644 --- a/agent-components/planner/simple/src/main/kotlin/io/github/llmagentbuilder/planner/simple/SimplePlannerProvider.kt +++ b/agent-components/planner/simple/src/main/kotlin/io/github/llmagentbuilder/planner/simple/SimplePlannerProvider.kt @@ -3,6 +3,7 @@ package io.github.llmagentbuilder.planner.simple import io.github.llmagentbuilder.core.MapToObject import io.github.llmagentbuilder.core.Planner import io.github.llmagentbuilder.core.PlannerProvider +import io.micrometer.observation.ObservationRegistry import org.springframework.ai.chat.client.ChatClient class SimplePlannerProvider : PlannerProvider { @@ -12,12 +13,13 @@ class SimplePlannerProvider : PlannerProvider { override fun providePlanner( chatClientBuilder: ChatClient.Builder, - config: Map? + config: Map?, + observationRegistry: ObservationRegistry?, ): Planner? { val plannerConfig = MapToObject.toObject(config) if (plannerConfig?.enabled == false) { return null } - return SimplePlanner(chatClientBuilder.build()) + return SimplePlanner(chatClientBuilder.build(), observationRegistry) } } \ No newline at end of file diff --git a/agent-components/planner/structuredchat/src/main/kotlin/io/github/llmagentbuilder/planner/structuredchat/StructuredChatPlanner.kt b/agent-components/planner/structuredchat/src/main/kotlin/io/github/llmagentbuilder/planner/structuredchat/StructuredChatPlanner.kt index e305a3a..4cd3ec7 100644 --- a/agent-components/planner/structuredchat/src/main/kotlin/io/github/llmagentbuilder/planner/structuredchat/StructuredChatPlanner.kt +++ b/agent-components/planner/structuredchat/src/main/kotlin/io/github/llmagentbuilder/planner/structuredchat/StructuredChatPlanner.kt @@ -1,7 +1,15 @@ package io.github.llmagentbuilder.planner.structuredchat import io.github.llmagentbuilder.planner.executor.LLMPlanExecutor +import io.micrometer.observation.ObservationRegistry import org.springframework.ai.chat.client.ChatClient -class StructuredChatPlanner(chatClient: ChatClient) : - LLMPlanExecutor(chatClient, StructuredChatOutputParser.INSTANCE) \ No newline at end of file +class StructuredChatPlanner( + chatClient: ChatClient, + observationRegistry: ObservationRegistry? = null, +) : + LLMPlanExecutor( + chatClient, + StructuredChatOutputParser.INSTANCE, + observationRegistry + ) \ No newline at end of file diff --git a/agent-components/planner/structuredchat/src/main/kotlin/io/github/llmagentbuilder/planner/structuredchat/StructuredChatPlannerProvider.kt b/agent-components/planner/structuredchat/src/main/kotlin/io/github/llmagentbuilder/planner/structuredchat/StructuredChatPlannerProvider.kt index a45d69a..6cd833e 100644 --- a/agent-components/planner/structuredchat/src/main/kotlin/io/github/llmagentbuilder/planner/structuredchat/StructuredChatPlannerProvider.kt +++ b/agent-components/planner/structuredchat/src/main/kotlin/io/github/llmagentbuilder/planner/structuredchat/StructuredChatPlannerProvider.kt @@ -3,6 +3,7 @@ package io.github.llmagentbuilder.planner.structuredchat import io.github.llmagentbuilder.core.MapToObject import io.github.llmagentbuilder.core.Planner import io.github.llmagentbuilder.core.PlannerProvider +import io.micrometer.observation.ObservationRegistry import org.springframework.ai.chat.client.ChatClient class StructuredChatPlannerProvider : PlannerProvider { @@ -12,7 +13,8 @@ class StructuredChatPlannerProvider : PlannerProvider { override fun providePlanner( chatClientBuilder: ChatClient.Builder, - config: Map? + config: Map?, + observationRegistry: ObservationRegistry?, ): Planner? { val plannerConfig = MapToObject.toObject(config) @@ -22,6 +24,6 @@ class StructuredChatPlannerProvider : PlannerProvider { val chatClient = chatClientBuilder.defaultAdvisors(StructuredChatPromptAdvisor()) .build() - return StructuredChatPlanner(chatClient) + return StructuredChatPlanner(chatClient, observationRegistry) } } \ No newline at end of file diff --git a/agent-components/planner/structuredchat/src/main/kotlin/io/github/llmagentbuilder/planner/structuredchat/StructuredChatPromptAdvisor.kt b/agent-components/planner/structuredchat/src/main/kotlin/io/github/llmagentbuilder/planner/structuredchat/StructuredChatPromptAdvisor.kt index 5775cf0..1bdb45f 100644 --- a/agent-components/planner/structuredchat/src/main/kotlin/io/github/llmagentbuilder/planner/structuredchat/StructuredChatPromptAdvisor.kt +++ b/agent-components/planner/structuredchat/src/main/kotlin/io/github/llmagentbuilder/planner/structuredchat/StructuredChatPromptAdvisor.kt @@ -55,7 +55,7 @@ internal const val defaultUserTextTemplate = """ class StructuredChatPromptAdvisor : CallAroundAdvisor { override fun getName(): String { - return "StructuredChat Planner - Prompt" + return javaClass.simpleName } override fun getOrder(): Int { diff --git a/agent-components/profile/system-message/src/main/kotlin/io/github/llmagentbuilder/agent/profile/systemmessage/SystemMessageProfileAdvisor.kt b/agent-components/profile/system-message/src/main/kotlin/io/github/llmagentbuilder/agent/profile/systemmessage/SystemMessageProfileAdvisor.kt index a211d9c..19e7a3e 100644 --- a/agent-components/profile/system-message/src/main/kotlin/io/github/llmagentbuilder/agent/profile/systemmessage/SystemMessageProfileAdvisor.kt +++ b/agent-components/profile/system-message/src/main/kotlin/io/github/llmagentbuilder/agent/profile/systemmessage/SystemMessageProfileAdvisor.kt @@ -11,7 +11,7 @@ class SystemMessageProfileAdvisor( private val systemMessageParams: Map? = mapOf() ) : CallAroundAdvisor { override fun getName(): String { - return "Profile - System Message" + return javaClass.simpleName } override fun getOrder(): Int { diff --git a/agent-components/tool/src/main/kotlin/io/github/llmagentbuilder/agent/tool/AgentToolContextAdvisor.kt b/agent-components/tool/src/main/kotlin/io/github/llmagentbuilder/agent/tool/AgentToolInfoAdvisor.kt similarity index 93% rename from agent-components/tool/src/main/kotlin/io/github/llmagentbuilder/agent/tool/AgentToolContextAdvisor.kt rename to agent-components/tool/src/main/kotlin/io/github/llmagentbuilder/agent/tool/AgentToolInfoAdvisor.kt index f7720f0..a50976b 100644 --- a/agent-components/tool/src/main/kotlin/io/github/llmagentbuilder/agent/tool/AgentToolContextAdvisor.kt +++ b/agent-components/tool/src/main/kotlin/io/github/llmagentbuilder/agent/tool/AgentToolInfoAdvisor.kt @@ -10,10 +10,10 @@ import org.springframework.core.Ordered const val SYSTEM_PARAM_TOOL_NAMES = "tool_names" const val SYSTEM_PARAM_TOOLS = "tools" -class AgentToolContextAdvisor(private val tools: Map>) : +class AgentToolInfoAdvisor(private val tools: Map>) : CallAroundAdvisor { override fun getName(): String { - return "AgentTool" + return javaClass.simpleName } override fun getOrder(): Int { diff --git a/bootstrap/src/main/kotlin/io/github/llmagentbuilder/bootstrap/AgentBootstrap.kt b/bootstrap/src/main/kotlin/io/github/llmagentbuilder/bootstrap/AgentBootstrap.kt index 993b524..241f203 100644 --- a/bootstrap/src/main/kotlin/io/github/llmagentbuilder/bootstrap/AgentBootstrap.kt +++ b/bootstrap/src/main/kotlin/io/github/llmagentbuilder/bootstrap/AgentBootstrap.kt @@ -1,7 +1,7 @@ package io.github.llmagentbuilder.bootstrap import io.github.llmagentbuilder.agent.profile.systemmessage.SystemMessageProfileAdvisor -import io.github.llmagentbuilder.agent.tool.AgentToolContextAdvisor +import io.github.llmagentbuilder.agent.tool.AgentToolInfoAdvisor import io.github.llmagentbuilder.core.* import io.github.llmagentbuilder.core.tool.AgentToolFunctionCallbackContext import io.github.llmagentbuilder.core.tool.AgentToolsProviderFactory @@ -40,17 +40,20 @@ object AgentBootstrap { val agentToolsProvider = AgentToolsProviderFactory.create(agentConfig.tools ?: listOf()) advisors.addLast( - AgentToolContextAdvisor( + AgentToolInfoAdvisor( agentToolsProvider.get() ) ) advisors.addLast(SimpleLoggerAdvisor()) + val observationEnabled = agentConfig.observation?.enabled == true + val observationRegistry = + if (observationEnabled) ObservationRegistry.create() else ObservationRegistry.NOOP val functionCallbackContext = AgentToolFunctionCallbackContext( agentToolsProvider, + observationRegistry, ) - val observationRegistry = ObservationRegistry.create() - if (agentConfig.observation?.enabled == true) { + if (observationEnabled) { OpenTelemetryPlugin().install(agentConfig, observationRegistry) } val llmConfigs = agentConfig.llm @@ -77,7 +80,11 @@ object AgentBootstrap { .mapNotNull { val config = plannerConfigs?.get(it.configKey()) as? Map - it.providePlanner(chatClientBuilder, config) + it.providePlanner( + chatClientBuilder, + config, + observationRegistry, + ) } .firstOrNull()?.also { logger.info("Loaded Planner $it") diff --git a/bootstrap/src/main/resources/agent.yaml b/bootstrap/src/main/resources/agent.yaml index d18b9f0..889fa1d 100644 --- a/bootstrap/src/main/resources/agent.yaml +++ b/bootstrap/src/main/resources/agent.yaml @@ -12,11 +12,19 @@ planner: reActJson: enabled: true observation: + enabled: true tracing: + enabled: true exporter: endpoint: "https://api.honeycomb.io/v1/traces" headers: "x-honeycomb-team": "{{env.HONEYCOMB_API_KEY}}" + metrics: + enabled: true + exporter: + endpoint: "https://api.honeycomb.io/v1/metrics" + headers: + "x-honeycomb-team": "{{env.HONEYCOMB_API_KEY}}" tools: - id: writeLocalFile config: diff --git a/core/src/main/kotlin/io/github/llmagentbuilder/core/AgentConfig.kt b/core/src/main/kotlin/io/github/llmagentbuilder/core/AgentConfig.kt index ebf9b51..691f118 100644 --- a/core/src/main/kotlin/io/github/llmagentbuilder/core/AgentConfig.kt +++ b/core/src/main/kotlin/io/github/llmagentbuilder/core/AgentConfig.kt @@ -47,9 +47,20 @@ class TracingConfig { var exporter: TracingExporterConfig? = null } +class MetricsExporterConfig { + lateinit var endpoint: String + var headers: Map? = null +} + +class MetricsConfig { + var enabled: Boolean? = false + var exporter: MetricsExporterConfig? = null +} + class ObservationConfig { var enabled: Boolean? = false var tracing: TracingConfig? = null + var metrics: MetricsConfig? = null } class AgentConfig { diff --git a/core/src/main/kotlin/io/github/llmagentbuilder/core/ObservationPlugin.kt b/core/src/main/kotlin/io/github/llmagentbuilder/core/ObservationPlugin.kt index 5da560b..5b4e103 100644 --- a/core/src/main/kotlin/io/github/llmagentbuilder/core/ObservationPlugin.kt +++ b/core/src/main/kotlin/io/github/llmagentbuilder/core/ObservationPlugin.kt @@ -5,6 +5,6 @@ import io.micrometer.observation.ObservationRegistry interface ObservationPlugin { fun install( agentConfig: AgentConfig, - observationRegistry: ObservationRegistry + observationRegistry: ObservationRegistry, ) } \ No newline at end of file diff --git a/core/src/main/kotlin/io/github/llmagentbuilder/core/PlannerProvider.kt b/core/src/main/kotlin/io/github/llmagentbuilder/core/PlannerProvider.kt index b6c1664..e6b1f09 100644 --- a/core/src/main/kotlin/io/github/llmagentbuilder/core/PlannerProvider.kt +++ b/core/src/main/kotlin/io/github/llmagentbuilder/core/PlannerProvider.kt @@ -1,5 +1,6 @@ package io.github.llmagentbuilder.core +import io.micrometer.observation.ObservationRegistry import org.springframework.ai.chat.client.ChatClient interface PlannerProvider { @@ -8,5 +9,6 @@ interface PlannerProvider { fun providePlanner( chatClientBuilder: ChatClient.Builder, config: Map? = null, + observationRegistry: ObservationRegistry? = null, ): Planner? } \ No newline at end of file diff --git a/observation/open-telemetry/src/main/kotlin/io/github/llmagentbuilder/plugin/observation/opentelemetry/OpenTelemetryPlugin.kt b/observation/open-telemetry/src/main/kotlin/io/github/llmagentbuilder/plugin/observation/opentelemetry/OpenTelemetryPlugin.kt index 09a16c5..13e9d93 100644 --- a/observation/open-telemetry/src/main/kotlin/io/github/llmagentbuilder/plugin/observation/opentelemetry/OpenTelemetryPlugin.kt +++ b/observation/open-telemetry/src/main/kotlin/io/github/llmagentbuilder/plugin/observation/opentelemetry/OpenTelemetryPlugin.kt @@ -1,9 +1,10 @@ package io.github.llmagentbuilder.plugin.observation.opentelemetry -import io.github.llmagentbuilder.core.AgentConfig -import io.github.llmagentbuilder.core.EvaluationHelper -import io.github.llmagentbuilder.core.ObservationPlugin +import io.github.llmagentbuilder.core.* +import io.micrometer.core.instrument.Clock import io.micrometer.observation.ObservationRegistry +import io.micrometer.registry.otlp.OtlpConfig +import io.micrometer.registry.otlp.OtlpMeterRegistry import io.micrometer.tracing.handler.DefaultTracingObservationHandler import io.micrometer.tracing.otel.bridge.EventListener import io.micrometer.tracing.otel.bridge.OtelCurrentTraceContext @@ -14,22 +15,40 @@ import io.opentelemetry.exporter.otlp.http.trace.OtlpHttpSpanExporter import io.opentelemetry.sdk.resources.Resource import io.opentelemetry.sdk.trace.SdkTracerProvider import io.opentelemetry.sdk.trace.export.BatchSpanProcessor +import org.springframework.ai.chat.observation.ChatModelMeterObservationHandler class OpenTelemetryPlugin : ObservationPlugin { override fun install( agentConfig: AgentConfig, - observationRegistry: ObservationRegistry + observationRegistry: ObservationRegistry, ) { val builder = Resource.builder() builder.put("service.name", agentConfig.metadata?.name) + val resource = builder.build() + agentConfig.observation?.tracing?.let { + applyTracingConfig(it, observationRegistry, resource) + } + agentConfig.observation?.metrics?.let { + applyMetricsConfig(it, observationRegistry) + } + } + + private fun applyTracingConfig( + tracingConfig: TracingConfig, + observationRegistry: ObservationRegistry, + resource: Resource, + ) { + if (tracingConfig.enabled != true) { + return + } val exporterBuilder = OtlpHttpSpanExporter.builder() - agentConfig.observation?.tracing?.exporter?.let { + tracingConfig.exporter?.let { exporterBuilder.setEndpoint(it.endpoint) it.headers?.entries?.forEach { (k, v) -> exporterBuilder.addHeader(k, EvaluationHelper.evaluate(v)) } } - val resource = builder.build() + val tracer = SdkTracerProvider.builder() .setResource(resource) .addSpanProcessor( @@ -51,6 +70,37 @@ class OpenTelemetryPlugin : ObservationPlugin { observationRegistry.observationConfig().observationHandler(handler) } + private fun applyMetricsConfig( + metricsConfig: MetricsConfig, + observationRegistry: ObservationRegistry, + ) { + if (metricsConfig.enabled != true) { + return + } + metricsConfig.exporter?.let { + + val otlpConfig = object : OtlpConfig { + override fun get(key: String): String? { + return null + } + + override fun url(): String { + return it.endpoint + } + + override fun headers(): Map { + return it.headers?.mapValues { (_, v) -> + EvaluationHelper.evaluate(v) + } ?: mapOf() + } + } + val meterRegistry = OtlpMeterRegistry(otlpConfig, Clock.SYSTEM) + val handler = ChatModelMeterObservationHandler(meterRegistry) + observationRegistry.observationConfig().observationHandler(handler) + } + + } + private class OTelEventPublisher(private val listeners: List) : EventPublisher { override fun publishEvent(event: Any?) {