Package com.tnsai.capabilities
Annotation Interface Capability
Marks an interface as a reusable capability contract whose methods are
dispatched by the framework (LLM / MCP / WEB_SERVICE) rather than implemented
by the developer.
A capability interface carries one or more @ActionSpec-annotated
methods that describe what the capability does. A Role
gains the capability by implementsing the interface; the framework's
ActionDiscovery picks up the capability methods during role initialisation
and routes LLM tool calls to them via ActionExecutor.
Why this exists
Before capabilities, every dispatched method required a return null
body:
@ActionSpec(type = ActionType.LLM, ...)
public String summarize(String text) {
return null; // body unused — LLMRoleExecutor handles dispatch
}
The body is dead code — ActionExecutor.processWithActionResult skips
the invocation when there is no ActionResult parameter. Yet the method
declaration forces the author to write a misleading body that:
- reads as "always returns null" to static analysis,
- silently returns null if ever invoked outside the framework path (should be a loud failure), and
- duplicates the same specification across every role that needs the capability.
With a capability interface the method body is the interface's
default — Actions.dispatchedByFramework() —
so every implementing role inherits the same contract with zero body boilerplate:
@Capability
public interface Summarizer {
@ActionSpec(type = ActionType.LLM,
description = "Summarise the text in one short paragraph",
llmSystemPrompt = "You are concise.",
llmTemperature = 0.2f)
default String summarize(String text) {
throw Actions.dispatchedByFramework();
}
}
public class EditorRole extends Role implements Summarizer, Translator {
// no capability method bodies — inherited from the interfaces
}
Constraints
- Every method declared on a
@Capabilityinterface must carry an@ActionSpecannotation. This is validated at action-discovery time; a violation throws anIllegalStateException. - Capability methods are pure dispatch — they must not declare an
ActionResultparameter. Post-processing (ActionResult-based) still lives on the concrete role class, not the interface. - The default body must throw, not return. Returning
nullfrom a default would silently re-introduce the problem this annotation exists to solve.
- Since:
- 0.4.0
- See Also: