Java SDK
Add com.llmjury:sdk (Java 11+; the only runtime dependency is Gson — HTTP is java.net.http):
dependencies {
implementation("com.llmjury:sdk:0.1.0")
}
Authentication
LlmjuryClient.builder() with no key reads your org's publishable key (llmj_pk_…) from the
LLMJURY_API_KEY environment variable; LlmjuryClient.builder(apiKey) passes it explicitly. The
key is sent as X-API-Key; the SDK defaults to the production API (LLMJURY_BASE_URL /
.baseUrl(…) override for a local stack). See
where keys come from.
Quickstart
// ---- setup, once at startup -------------------------------------------------
// Reads LLMJURY_API_KEY from the environment; defaults to https://api.llmjury.com.
LlmjuryClient client = LlmjuryClient.builder()
.experiments("checkout-prompt") // prefetch by experiment NAME
.build();
// ---- per request ------------------------------------------------------------
// One session resolves the variant, prompt, and tracking together.
ExperimentSession s = client.session("checkout-prompt", userId);
String prompt = s.prompt(DEFAULT_PROMPT); // in-code fallback survives an outage
String reply = s.intercept(call -> { // latency/tokens/errors are intercepted
Response r = llm.create("claude-haiku-4-5", prompt, userInput);
call.response(r.text()).tokensOutput(r.outputTokens());
return r.text();
});
s.trackOutcome("conversion", 1); // the ONLY explicit metricWhat the client does
session(experiment, user)— the recommended entrypoint: anExperimentSessionbundles one user's interaction with one experiment.s.prompt(defaultPrompt)resolves the variant's prompt (your in-code default survives a full LLMJury outage),s.variables(defaults)resolves custom variables (model, temperature, …) from client memory,s.intercept(call -> …)wraps the model call so latency, tokens, and errors are recorded automatically, ands.trackOutcome(metric, value)records the business outcome — the only explicit metric.getPrompt(…)/getVariables(…)— the session's building blocks, callable directly; never block, never throw, and fall back to your in-code defaults.assign(experiment, user)— the low-level call: deterministic local bucketing, bit-for-bit identical to the Python and TypeScript SDKs; returns the variant key ornulluntil the config loads — treatnullas "use control".track(type, payload)— enqueue-only; a daemon flusher thread batches with bounded retries, then spills to an optionalFileOfflineStore(24h replay, original timestamps) or drops with a log line. Never blocks or throws into the host application.- Experiments are addressed by unique name or id — names resolve through the polled config and bucketing always runs on the canonical id, so both address forms assign identically.
- Config is polled from
GET /v1/config(ETag/304, 60s) with an immediate refresh on a newer ingest-ackconfig_version. The client isAutoCloseable— closing flushes and stops the background threads.
Full details in the SDK README.