Skip to main content

nexora-planner

Converts an Intent into a Plan. Composed of a registry of step definitions and a chain of planner implementations.

StepDefinition

Declares what a step does and when it participates in a plan.

StepDefinition validateStep = new StepDefinition(
"validate", // step ID (unique in the plan)
"validate_order", // capability ID to invoke
goal -> goal.contains("order"), // predicate: does this step apply to the goal?
Map.of(
"orderId", InputBinding.fromContext("orderId"), // from intent context
"region", InputBinding.literal("EU") // static value
),
"validationResult", // output key stored in execution context
Set.of(), // dependsOn (empty = no dependencies)
null, // retryPolicyId (null = default policy)
Duration.ofSeconds(10) // per-step timeout
);

Full constructor with compensation

new StepDefinition(
"charge", "charge_card",
goal -> goal.contains("order"),
inputs, "chargeResult",
Set.of("validate"),
"payment-retry",
Duration.ofSeconds(5),
"refund_card" // compensateCapabilityId for saga rollback
);

PlanRegistry

Holds all registered StepDefinition objects and is queried by the planner to select which steps apply to a given intent.

NexoraEngine engine = NexoraEngine.builder()
.withStepDefinition(validateStep)
.withStepDefinition(chargeStep)
.withStepDefinition(receiptStep)
.build();

RulePlanner

The built-in keyword-based planner. For each StepDefinition whose matcher predicate returns true for the intent goal, the step is included in the plan. Dependency ordering from dependsOn is preserved.

// These two steps both match "process_order":
// validate: goal -> goal.contains("order") => true
// charge: goal -> goal.contains("order") => true

// Resulting plan: validate (no deps) + charge (depends on validate)

CompositePlanner

Chains multiple Planner implementations. Tries each planner in priority order (highest first) and returns the first non-empty plan.

// Plugin planners are inserted before the built-in RulePlanner.
// An LLM plugin with priority=100 will be tried before RulePlanner (priority=0).

Implementing a custom Planner

Implement com.nexora.spi.Planner and contribute it via a plugin:

public class LlmPlanner implements Planner {

@Override
public Optional<Plan> plan(Intent intent, PlanRegistry registry) {
String prompt = buildPrompt(intent, registry.allDefinitions());
List<String> stepIds = llmClient.planSteps(prompt);

if (stepIds.isEmpty()) return Optional.empty();

List<Step> steps = stepIds.stream()
.map(registry::getDefinition)
.filter(Optional::isPresent)
.map(opt -> opt.get().toStep())
.toList();

return Optional.of(new Plan(steps));
}

@Override
public int priority() { return 100; }
}

Return Optional.empty() to pass control to the next planner in the chain.