One Shot Per Loop Tool
Decorator that allows the underlying delegate to run at most once per agentic loop (as identified by ToolCallContext.LOOP_ID_KEY). Subsequent calls within the same loop short-circuit with an "ALREADY LOADED" message that includes the caller-supplied advice — instead of re-running the tool.
Use when:
A tool returns content that, once delivered, lives in the LLM's conversation history for the rest of the turn — calling it again wastes tokens and round-trips and produces no new information. Skill-activator tools and reference-loaders are the canonical case.
A weak chat model (qwen, gpt-oss, smaller open models) reflexively re-calls the same tool turn after turn even when system-prompt discipline says not to. Words alone don't hold; this wrapper makes the constraint mechanical.
The advice string lets callers tailor the second-call message to whatever the LLM should do next ("write your js block now", "answer the user", "stop and report"). Required because the default would be scenario-specific guesswork — better to make the caller think about it.
Per-loop isolation is provided by LoopMemo reading the loop id from ToolCallContext. The orchestrator is responsible for stamping a fresh loop id per turn (typically via PromptRunner.withToolCallContext(...)); without one, every call registers as "first time" and the wrapper degrades to a passthrough.
Example:
val gated = OneShotPerLoopTool(
delegate = skillActivator,
advice = "Write your script now using the skill body above.",
)Implements DelegatingTool so framework strategies that unwrap decorators can find the underlying tool, and so both call overloads route through the canonical two-arg method.
Properties
Functions
Routes single-arg calls through the canonical two-arg method, ensuring decorator logic in call (String, ToolCallContext) is always executed regardless of which overload the caller uses.
Canonical entry point for decorator logic. Override this method to add behavior while preserving context propagation to delegate.
Extension function to convert an Embabel Tool to a Spring AI ToolCallback.
Wrap this tool to conditionally await before execution.
Wrap this tool to always require confirmation before execution.
Create a new tool with additional definition metadata entries merged in. Existing keys are overwritten by the new values.
Create a new tool with a single definition metadata entry added.
Create a new tool with a different description. Useful for providing context-specific descriptions while keeping the same functionality.
Extension function to wrap a Tool with event publication.