diff --git a/bootstrap/pom.xml b/bootstrap/pom.xml
index 30c7d9b..2a65804 100644
--- a/bootstrap/pom.xml
+++ b/bootstrap/pom.xml
@@ -43,6 +43,21 @@
launcher-ktor
${project.version}
+
+ io.github.llmagentbuilder
+ open-telemetry
+ ${project.version}
+
+
+ io.github.llmagentbuilder
+ llm-openai
+ ${project.version}
+
+
+ io.github.llmagentbuilder
+ agent-planner-react-json
+ ${project.version}
+
org.slf4j
slf4j-api
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 5dce988..2bab5b7 100644
--- a/bootstrap/src/main/kotlin/io/github/llmagentbuilder/bootstrap/AgentBootstrap.kt
+++ b/bootstrap/src/main/kotlin/io/github/llmagentbuilder/bootstrap/AgentBootstrap.kt
@@ -6,6 +6,8 @@ import io.github.llmagentbuilder.core.*
import io.github.llmagentbuilder.core.tool.AgentToolFunctionCallbackContext
import io.github.llmagentbuilder.core.tool.AgentToolsProviderFactory
import io.github.llmagentbuilder.launcher.ktor.server.KtorLauncher
+import io.github.llmagentbuilder.plugin.observation.opentelemetry.OpenTelemetryPlugin
+import io.micrometer.observation.ObservationRegistry
import org.slf4j.LoggerFactory
import org.springframework.ai.chat.client.ChatClient
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor
@@ -47,6 +49,8 @@ object AgentBootstrap {
AgentToolFunctionCallbackContext(
agentToolsProvider,
)
+ val observationRegistry = ObservationRegistry.create()
+ OpenTelemetryPlugin().install(agentConfig, observationRegistry)
val llmConfigs = agentConfig.llm
val chatModel = ServiceLoader.load(ChatModelProvider::class.java)
.stream()
@@ -60,8 +64,9 @@ object AgentBootstrap {
.firstOrNull()?.also {
logger.info("Loaded ChatModel $it")
} ?: throw RuntimeException("No ChatModel found")
- val chatClientBuilder = ChatClient.builder(chatModel)
- .defaultAdvisors(advisors)
+ val chatClientBuilder =
+ ChatClient.builder(chatModel, observationRegistry, null)
+ .defaultAdvisors(advisors)
val plannerConfigs = agentConfig.planner
val planner = ServiceLoader.load(PlannerProvider::class.java)
.stream()
@@ -82,9 +87,10 @@ object AgentBootstrap {
metadata?.description,
metadata?.usageInstruction,
agentToolsProvider,
+ UUID.randomUUID().toString(),
+ observationRegistry,
)
KtorLauncher.launch(chatAgent)
-// JdkHttpSyncLauncher().launch(chatAgent, agentToolsProvider)
}
private fun profileAdvisor(agentConfig: AgentConfig): Advisor? {
diff --git a/bootstrap/src/main/resources/agent.yaml b/bootstrap/src/main/resources/agent.yaml
index a3fe081..d18b9f0 100644
--- a/bootstrap/src/main/resources/agent.yaml
+++ b/bootstrap/src/main/resources/agent.yaml
@@ -1,5 +1,5 @@
metadata:
- name: Test agent
+ name: TestAgent
llm:
openai:
enabled: true
@@ -11,6 +11,12 @@ memory:
planner:
reActJson:
enabled: true
+observation:
+ tracing:
+ exporter:
+ endpoint: "https://api.honeycomb.io/v1/traces"
+ headers:
+ "x-honeycomb-team": "{{env.HONEYCOMB_API_KEY}}"
tools:
- id: writeLocalFile
config:
diff --git a/cli/pom.xml b/cli/pom.xml
index eb40384..2048dfa 100644
--- a/cli/pom.xml
+++ b/cli/pom.xml
@@ -28,7 +28,6 @@
com.github.jknack
handlebars
- 4.4.0
org.yaml
diff --git a/core/pom.xml b/core/pom.xml
index 3de287d..d4f7851 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -64,6 +64,10 @@
org.yaml
snakeyaml
+
+ com.github.jknack
+ handlebars
+
uk.org.webcompere
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 5478c67..ebf9b51 100644
--- a/core/src/main/kotlin/io/github/llmagentbuilder/core/AgentConfig.kt
+++ b/core/src/main/kotlin/io/github/llmagentbuilder/core/AgentConfig.kt
@@ -1,5 +1,6 @@
package io.github.llmagentbuilder.core
+import com.github.jknack.handlebars.Handlebars
import org.yaml.snakeyaml.LoaderOptions
import org.yaml.snakeyaml.Yaml
import org.yaml.snakeyaml.constructor.Constructor
@@ -36,6 +37,21 @@ class AgentMetadata {
var usageInstruction: String? = null
}
+class TracingExporterConfig {
+ lateinit var endpoint: String
+ var headers: Map? = null
+}
+
+class TracingConfig {
+ var enabled: Boolean? = false
+ var exporter: TracingExporterConfig? = null
+}
+
+class ObservationConfig {
+ var enabled: Boolean? = false
+ var tracing: TracingConfig? = null
+}
+
class AgentConfig {
var metadata: AgentMetadata? = null
var profile: ProfileConfig? = null
@@ -43,6 +59,7 @@ class AgentConfig {
var llm: Map? = null
var planner: Map? = null
var tools: List? = null
+ var observation: ObservationConfig? = null
}
object AgentConfigLoader {
@@ -62,4 +79,16 @@ object AgentConfigLoader {
)
).load(reader)
}
+}
+
+object EvaluationHelper {
+ private val handlebars = Handlebars()
+
+ fun evaluate(input: String): String {
+ return handlebars.compileInline(input).apply(
+ mapOf(
+ "env" to System.getenv(),
+ )
+ )
+ }
}
\ No newline at end of file
diff --git a/core/src/main/kotlin/io/github/llmagentbuilder/core/ObservationPlugin.kt b/core/src/main/kotlin/io/github/llmagentbuilder/core/ObservationPlugin.kt
new file mode 100644
index 0000000..5da560b
--- /dev/null
+++ b/core/src/main/kotlin/io/github/llmagentbuilder/core/ObservationPlugin.kt
@@ -0,0 +1,10 @@
+package io.github.llmagentbuilder.core
+
+import io.micrometer.observation.ObservationRegistry
+
+interface ObservationPlugin {
+ fun install(
+ agentConfig: AgentConfig,
+ observationRegistry: ObservationRegistry
+ )
+}
\ No newline at end of file
diff --git a/launchers/ktor/src/main/kotlin/io/github/llmagentbuilder/launcher/ktor/server/apis/AgentApi.kt b/launchers/ktor/src/main/kotlin/io/github/llmagentbuilder/launcher/ktor/server/apis/AgentApi.kt
index 42db23d..fd2314a 100644
--- a/launchers/ktor/src/main/kotlin/io/github/llmagentbuilder/launcher/ktor/server/apis/AgentApi.kt
+++ b/launchers/ktor/src/main/kotlin/io/github/llmagentbuilder/launcher/ktor/server/apis/AgentApi.kt
@@ -1,14 +1,3 @@
-/**
- * Agent Protocol
- * Specification of the API protocol for communication with an agent.
- *
- * The version of the OpenAPI document: v1
- *
- *
- * NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
- * https://openapi-generator.tech
- * Do not edit the class manually.
- */
package io.github.llmagentbuilder.launcher.ktor.server.apis
import io.github.llmagentbuilder.core.ChatAgent
@@ -28,6 +17,11 @@ import kotlin.collections.set
fun Route.AgentApi(chatAgent: ChatAgent) {
val tasks = ConcurrentHashMap()
+ post("/chat") {
+ val request = call.receive(ChatAgentRequest::class)
+ call.respond(chatAgent.call(request))
+ }
+
post("/ap/v1/agent/tasks") {
val taskId = UUID.randomUUID().toString()
val request = call.receive(TaskRequestBody::class)
diff --git a/observation/open-telemetry/pom.xml b/observation/open-telemetry/pom.xml
new file mode 100644
index 0000000..d611b3f
--- /dev/null
+++ b/observation/open-telemetry/pom.xml
@@ -0,0 +1,47 @@
+
+
+ 4.0.0
+
+ io.github.llmagentbuilder
+ observation
+ 0.2.1
+
+
+ open-telemetry
+
+
+ 21
+ 21
+ UTF-8
+ 1.42.1
+
+
+
+
+ io.github.llmagentbuilder
+ core
+ ${project.version}
+
+
+ io.opentelemetry
+ opentelemetry-sdk
+ ${opentelemetry.version}
+
+
+ io.opentelemetry
+ opentelemetry-exporter-otlp
+ ${opentelemetry.version}
+
+
+ io.micrometer
+ micrometer-registry-otlp
+
+
+ io.micrometer
+ micrometer-tracing-bridge-otel
+
+
+
+
\ 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
new file mode 100644
index 0000000..09a16c5
--- /dev/null
+++ b/observation/open-telemetry/src/main/kotlin/io/github/llmagentbuilder/plugin/observation/opentelemetry/OpenTelemetryPlugin.kt
@@ -0,0 +1,60 @@
+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.micrometer.observation.ObservationRegistry
+import io.micrometer.tracing.handler.DefaultTracingObservationHandler
+import io.micrometer.tracing.otel.bridge.EventListener
+import io.micrometer.tracing.otel.bridge.OtelCurrentTraceContext
+import io.micrometer.tracing.otel.bridge.OtelTracer
+import io.micrometer.tracing.otel.bridge.OtelTracer.EventPublisher
+import io.micrometer.tracing.otel.bridge.Slf4JEventListener
+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
+
+class OpenTelemetryPlugin : ObservationPlugin {
+ override fun install(
+ agentConfig: AgentConfig,
+ observationRegistry: ObservationRegistry
+ ) {
+ val builder = Resource.builder()
+ builder.put("service.name", agentConfig.metadata?.name)
+ val exporterBuilder = OtlpHttpSpanExporter.builder()
+ agentConfig.observation?.tracing?.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(
+ BatchSpanProcessor.builder(
+ exporterBuilder.build()
+ ).build()
+ )
+ .build()
+ .get("llm-agent")
+ val context = OtelCurrentTraceContext()
+ val otelTracer = OtelTracer(
+ tracer, context, OTelEventPublisher(
+ listOf(
+ Slf4JEventListener()
+ )
+ )
+ )
+ val handler = DefaultTracingObservationHandler(otelTracer)
+ observationRegistry.observationConfig().observationHandler(handler)
+ }
+
+ private class OTelEventPublisher(private val listeners: List) :
+ EventPublisher {
+ override fun publishEvent(event: Any?) {
+ listeners.forEach { it.onEvent(event) }
+ }
+ }
+}
\ No newline at end of file
diff --git a/observation/pom.xml b/observation/pom.xml
new file mode 100644
index 0000000..a0c019f
--- /dev/null
+++ b/observation/pom.xml
@@ -0,0 +1,25 @@
+
+
+ 4.0.0
+
+ io.github.llmagentbuilder
+ llm-agent-builder
+ 0.2.1
+
+
+ observation
+ pom
+ LLM Observation
+
+ open-telemetry
+
+
+
+ 21
+ 21
+ UTF-8
+
+
+
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 06c22b5..4fd7aca 100644
--- a/pom.xml
+++ b/pom.xml
@@ -17,6 +17,7 @@
bootstrap
llm
cli
+ observation
LLM Agent Builder
@@ -57,6 +58,7 @@
2.16.1
1.12.4
1.2.4
+ 4.4.0
@@ -290,6 +292,11 @@
pom
import
+
+ com.github.jknack
+ handlebars
+ ${handlebars.version}
+