Java SDK
ai.trefur:trefur-observe — Java client for Trefur Observe. Targets JDK 17+.
Install
Add to your Maven pom.xml:
<dependency>
<groupId>ai.trefur</groupId>
<artifactId>trefur-observe</artifactId>
<version>0.5.0</version>
</dependency>Or Gradle:
implementation 'ai.trefur:trefur-observe:0.5.0'Init
import ai.trefur.observe.client.TrefurObserveClient;
TrefurObserveClient client = TrefurObserveClient.builder()
.apiKey(System.getenv("TREFUR_API_KEY")) // trf_obs_*
.endpoint("https://observe.trefur.com") // default
.build();Recording telemetry
import ai.trefur.observe.model.AgentRun;
import ai.trefur.observe.model.AgentStep;
import java.time.Instant;
AgentRun run = new AgentRun(
"custom", "search-bot", "completed",
Instant.now().toString());
AgentStep step = new AgentStep(
"tool_call", Instant.now().toString(), "completed");
step.setToolName("search");
run.getSteps().add(step);
client.record(run);
client.flush();
// try-with-resources or client.close() flushes buffered runs on exit.Tool instrumentation (LangChain4j)
Beyond manual record() calls, the SDK ships a LangChain4j adapter that wraps a tool invocation and emits the paired tool_call / tool_result steps around it. The error case is captured (with the error message) and the exception is re-thrown, so wrapping is transparent to your control flow:
import ai.trefur.observe.adapter.langchain4j.LangChain4jAdapter;
import java.util.Map;
String result = LangChain4jAdapter.instrumentTool(
emitter, // your StepEmitter sink
"search", // logical tool name
Map.of("query", "trefur"), // tool args
() -> runSearch("trefur")); // the tool bodyThe adapter is decoupled from the client via a caller-supplied StepEmitter sink. The full adapter surface (the StepEmitter and ToolFn interfaces) lives in the ai.trefur.observe.adapter.langchain4j package.
Environment variables
Canonical list: env-vars reference.
Source + tests live in trefur-ai/trefur-sdks under java/observe-java. License: Apache 2.0.