Jaal

Jaal vs inference orchestration

Frameworks and servers that run models together: Ray, LangGraph, LlamaIndex, NVIDIA Dynamo and Triton, KServe, BentoML, Modal. Each product checked against its own documentation on 26 October 2026, sources below. Where we could not find a feature in a product's docs we write not documented: that means we did not find it, not that it is missing. For Jaal we say what is built and what is planned. Corrections are welcome.

The short version

A plain loop, checked by the compiler

Ray asks for .remote() futures, LangGraph for Send and edges, Triton for ensemble configs, KServe for YAML graphs. In Jaal you write a loop and mark it parallel; the compiler checks the iterations are independent and refuses the mark by name if not.

A model that has answered is never called again

Every model call is a recorded step: after a crash, or in a replay, the recorded answer stands in. Ray keeps no execution journal; LangGraph's time travel re-executes nodes from a checkpoint.

Every engine in its own process (being built)

vLLM, Triton, llama.cpp and plain CUDA as services of one orchestrator (being built; the CUDA core runs today on our CUDA simulator). A GPU server reached as its own endpoint cannot take the orchestrator down with it. The model servers are partners, not rivals.

Where others are ahead

Serving itself: NVIDIA Dynamo's disaggregated prefill and decode and KV-aware routing, Ray's scale-out, LangGraph's ecosystem. Jaal's inference orchestration is in progress; the four-engine demo is being built.

The same fan-out, two ways

Describe every photo in parallel, then summarize. Both simplified; the LangGraph version follows its own map-reduce pattern with Send (graph API docs).

LangGraph: declare the graph

class State(TypedDict):
    photos: list
    notes: Annotated[list, operator.add]
    summary: str

def fan_out(state):
    return [Send("describe", {"photo": p})
            for p in state["photos"]]

def describe(item):
    return {"notes": [describe_photo(item["photo"])]}

def summarize(state):
    return {"summary": write_summary(state["notes"])}

builder = StateGraph(State)
builder.add_node("describe", describe)
builder.add_node("summarize", summarize)
builder.add_conditional_edges(START, fan_out, ["describe"])
builder.add_edge("describe", "summarize")
builder.add_edge("summarize", END)
graph = builder.compile()

Jaal: write the loop, mark it parallel, the graph is derived

import jaal.x.Parallel;

public class Album extends jaal.x.Service<Photos, Summary> {
    private jaal.x.Service<Photo, Note> describe;
    private jaal.x.Service<Notes, Summary> summarize;

    @Override public Summary invoke(Photos in) {
        int n = in.urls().length;
        String[] notes = new String[n];
        for (@Parallel int i = 0; i < n; i++) {
            Note note = describe.invoke(PhotoBuilder.photo().url(in.urls()[i]).build());
            notes[i] = note.text();
        }
        return summarize.invoke(NotesBuilder.notes().texts(notes).build());
    }
}

Every call recorded, a crash finished by another process, the graph drawn for you. Or ask the assistant for it in a sentence (in progress).

AI and inference orchestration

The model servers are partners more than rivals: Jaal is being built to orchestrate them, each in its own process. The orchestration layers are the comparison.

ProductHow you write itDurabilityParallel workCompensationReplay and debuggingVersionsLicense and hosting
JaalPlain Java (Python in progress); model servers: being builtEvery call recorded; a replay never calls the model againIndependent calls automatic; a parallel loop is one annotation, checked by the compilerDeclared (the effect contract)Replay after a crash from the record; stepping through a run plannedYesPrivate beta
Ray / Ray Serve@ray.remote tasks and actors; Serve deployments; PythonNo execution journal; lineage reconstruction of lost objectsExplicit futuresnonenot documentedn/aApache-2.0; Anyscale
LangGraphGraph API (nodes and edges) or Functional API (@entrypoint, @task); Python, JSCheckpointers (Postgres, SQLite)Fan-out edges, Send, task futuresnot documentedTime travel: replay from a checkpoint, forkLatest code runs on saved stateMIT; LangSmith Deployment
LlamaIndex WorkflowsEvent-driven @step methods; PythonEphemeral by default; snapshots by hand; optional DBOS runtimeEvents, num_workersnot documentednot documentednot documentedMIT
NVIDIA DynamoServing layer over vLLM, SGLang, TensorRT-LLM; Kubernetes deploymentRequest migration on worker failure; no workflow persistenceDisaggregated prefill and decode, KV-aware routingn/an/an/aApache-2.0; self-host
NVIDIA TritonEnsembles in config.pbtxt; Business Logic Scripting in Pythonnone documentedEnsemble steps run when inputs are ready; async_execnonenot documentedPer-model versionsBSD-3-Clause; self-host
KServe InferenceGraphYAML: Sequence, Switch, Ensemble, Splitter nodesnone documentedDeclarative: Ensemble nodenonenot documentednot documentedApache-2.0; Kubernetes
BentoMLDecorators @bentoml.service, @bentoml.api; PythonTask results kept 24 hours by defaultnot documentednonenot documentednot documentedApache-2.0; part of Modular since Feb 2026
ModalDecorators @app.function, @app.cls; PythonRetries; crashed containers rescheduledExplicit: .map(), .starmap()nonenot documentednot documentedManaged; client SDK Apache-2.0

Sources

Each product's own documentation, blog or repository, read on 26 October 2026.