JamJet

Java SDK

Write JamJet workflows and agents in Java using builders, records, and the @Tool annotation.

Java SDK

The JamJet Java SDK lets you write workflows, agents, and tools in Java. It compiles to the same canonical IR and runs on the same Rust runtime as the Python SDK.

Installation

Requires Java 21+ (virtual threads).

<dependency>
    <groupId>dev.jamjet</groupId>
    <artifactId>jamjet-sdk</artifactId>
    <version>0.1.0</version>
</dependency>

Quick start

import dev.jamjet.agent.Agent;
import dev.jamjet.ir.IrValidator;

var agent = Agent.builder("hello-agent")
    .model("claude-haiku-4-5-20251001")
    .instructions("Answer questions clearly and concisely.")
    .strategy("react")
    .maxIterations(1)
    .build();

var ir = agent.compile();
IrValidator.validateOrThrow(ir);

var result = agent.run("What is JamJet?");
System.out.println(result.output());

Workflows

Build directed acyclic workflows with typed state. State is a Java record — immutable, validated, serializable.

import dev.jamjet.workflow.Workflow;

record ResearchState(String query, String results, String answer) {}

var wf = Workflow.builder("research-flow")
    .version("0.1.0")
    .state(ResearchState.class)
    .step("search", state ->
        new ResearchState(state.query(), "[search results]", state.answer()))
    .step("synthesize", state ->
        new ResearchState(state.query(), state.results(), "Final answer: ..."))
    .build();

var result = wf.run(new ResearchState("Latest AI frameworks?", "", ""));
System.out.println(result.state().answer());
System.out.printf("Ran %d steps in %.1fms%n",
    result.stepsExecuted(), result.totalDurationUs() / 1000.0);

Conditional routing

import dev.jamjet.workflow.Step;

var route = Step.builder("route")
    .handler(state -> state)
    .when("approved", state -> state.approved())
    .when("rejected", state -> !state.approved())
    .defaultNext("escalate")
    .timeout("30s")
    .retryPolicy("exponential")
    .build();

Compiling to IR

Every workflow compiles to canonical IR for submission to the runtime:

var ir = wf.compile();
System.out.println(ir.toJson());

// Validate before submission
var errors = IrValidator.validate(ir);
errors.forEach(System.err::println);

Agents

Agents combine a model, tools, and a reasoning strategy. Three built-in strategies:

StrategyBehavior
reactThought → tool use → observation loop
plan-and-executePlan → execute steps → synthesize
criticDraft → criticize → revise loop
import dev.jamjet.agent.Agent;

var agent = Agent.builder("research-agent")
    .model("claude-sonnet-4-6")
    .tools(WebSearch.class, FetchPage.class)
    .instructions("You are a thorough researcher.")
    .strategy("plan-and-execute")
    .maxIterations(8)
    .maxCostUsd(0.50)
    .timeoutSeconds(300)
    .build();

var result = agent.run("Compare JamJet and LangGraph");
System.out.println(result.output());
System.out.printf("Duration: %.1fms%n", result.durationUs() / 1000.0);

Annotation-based agents

For simple agents, use the @Task annotation on an interface:

import dev.jamjet.agent.JamjetAgent;
import dev.jamjet.agent.Task;

@Task(
    model = "gpt-4o",
    tools = { WebSearch.class },
    strategy = "react",
    maxIterations = 5,
    instructions = "You are helpful.",
    maxCostUsd = 1.0
)
interface ResearchTask {
    String research(String topic);
}

// Run directly
String result = JamjetAgent.run(ResearchTask.class, "What is JamJet?");

// Or get a reusable proxy
ResearchTask task = JamjetAgent.proxy(ResearchTask.class);
String answer = task.research("What is JamJet?");

Tools

Tools are Java records annotated with @Tool that implement ToolCall<T>. The SDK auto-generates JSON Schema from record component types.

import dev.jamjet.tool.Tool;
import dev.jamjet.tool.ToolCall;

@Tool(description = "Search the web for information")
record WebSearch(String query) implements ToolCall<String> {
    @Override
    public String execute() {
        // Replace with real implementation
        return "Results for: " + query;
    }
}

@Tool(description = "Fetch page content from a URL")
record FetchPage(String url) implements ToolCall<String> {
    @Override
    public String execute() {
        return "Page content for " + url;
    }
}

Tool registry

import dev.jamjet.tool.ToolRegistry;

// Register globally
ToolRegistry.global()
    .register(WebSearch.class)
    .register(FetchPage.class);

// Query
ToolRegistry.global().names();          // ["web_search", "fetch_page"]
ToolRegistry.global().toOpenAiFormat(); // OpenAI function-calling schema

Tool names are derived from the record class name (WebSearchweb_search).


Runtime client

JamjetClient is a blocking HTTP client (uses virtual threads) for the JamJet runtime API.

import dev.jamjet.JamjetClient;
import dev.jamjet.client.ClientConfig;

var client = new JamjetClient(
    ClientConfig.builder()
        .baseUrl("http://localhost:7700")
        .apiToken("YOUR_TOKEN")  // or JAMJET_TOKEN env var
        .timeoutSeconds(30)
        .build()
);

Workflow lifecycle

// Register workflow
client.createWorkflow(ir.toMap());

// Start execution
var execId = client.startExecution("research-flow", Map.of("query", "..."));

// Poll for status
var status = client.getExecution(execId);
System.out.println(status.get("status")); // running | completed | failed

// Get event timeline
var events = client.getEvents(execId);

Human-in-the-loop

// Approve a paused execution
client.approve(execId, "approved", "Looks good", Map.of("priority", "high"));

// Send external event to wake a waiting execution
client.sendExternalEvent(execId, "payment-received", Map.of("amount", 500));

Agent management

// Register agent
client.registerAgent(agentCard);

// Discovery
client.discoverAgent("https://remote-agent.example.com");

// Lifecycle
client.activateAgent(agentId);
client.deactivateAgent(agentId);

// List with filters
client.listAgents(); // all agents

State management

State is a Java record. Steps receive the current state and return a new instance — state is always immutable.

record OrderState(
    String orderId,
    double amount,
    boolean approved,
    String result
) {}

// Steps return new state instances
.step("validate", state ->
    new OrderState(state.orderId(), state.amount(), state.amount() < 10000, state.result()))

tip: Java records give you immutability, equals/hashCode, and toString for free. The SDK auto-generates JSON Schema from record component types for IR validation.


IR compilation and validation

Both workflows and agents compile to WorkflowIr — the canonical intermediate representation shared with the Python SDK and YAML workflows.

import dev.jamjet.ir.IrCompiler;
import dev.jamjet.ir.IrValidator;

// Compile
var ir = IrCompiler.compileWorkflow(wf);
// or: var ir = IrCompiler.compileAgent(agent);

// Inspect
System.out.println(ir.id());          // workflow name
System.out.println(ir.version());     // version string
System.out.println(ir.nodes());       // node definitions
System.out.println(ir.toJson());      // canonical JSON

// Validate
var errors = IrValidator.validate(ir);
if (!errors.isEmpty()) {
    errors.forEach(System.err::println);
    System.exit(1);
}

// Or throw on first error
IrValidator.validateOrThrow(ir);

Evaluation

Test agents and workflows against datasets with pluggable scorers:

import dev.jamjet.eval.EvalRunner;
import dev.jamjet.eval.Scorer;

var results = EvalRunner.builder()
    .dataset(dataset)
    .agent(myAgent)
    .scorers(new Scorer.ExactMatchScorer("expected"))
    .parallelism(4)
    .failBelow(0.8)  // fail if mean score < 0.8
    .build()
    .run();

Python vs Java comparison

FeatureJava SDKPython SDK
Workflow definitionFluent builder + recordsDecorators + TypedDict
Tool definition@Tool record + ToolCall<T>@tool decorator / MCP
Agent definitionAgent.builder()@agent decorator
StateImmutable Java recordsTypedDict / Pydantic
HTTP clientBlocking (virtual threads)Async (asyncio)
IR compilationworkflow.compile()@workflow decorator
Minimum versionJava 21Python 3.10

Both compile to identical canonical IR and run on the same Rust runtime.


Examples

Working examples in the examples repository:

ExampleWhat it demonstrates
java-hello-agentMinimal agent with a single model call
java-research-agentTools + plan-and-execute strategy
java-support-botMulti-step workflow with conditional routing
java-approval-workflowRuntime integration + human-in-the-loop
java-multi-tenantTenant-isolated workflow execution
java-data-governancePII detection and redaction patterns
java-oauth-agentOAuth delegation + scope narrowing
git clone https://github.com/jamjet-labs/examples
cd examples/java-hello-agent
mvn compile exec:java

tip: Every Java example has a Python/YAML equivalent in the same repository.

On this page