OneShotPerLoopTool

class OneShotPerLoopTool(val delegate: Tool, advice: String) : DelegatingTool

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.

Constructors

Link copied to clipboard
constructor(delegate: Tool, advice: String)

Properties

Link copied to clipboard
open override val definition: Tool.Definition

Tool definition for LLM

Link copied to clipboard
open override val delegate: Tool

The underlying tool being delegated to.

Link copied to clipboard

Optional metadata

Functions

Link copied to clipboard
open override fun call(input: String): Tool.Result

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.

open override fun call(input: String, context: ToolCallContext): Tool.Result

Canonical entry point for decorator logic. Override this method to add behavior while preserving context propagation to delegate.

Link copied to clipboard
inline fun <T : Any> Tool.requireType(message: String? = null): Tool

Wrap this tool to require a value of type T before execution (reified).

fun <T : Any> Tool.requireType(type: Class<T>, messageProvider: (String) -> String? = { null }): Tool

Wrap this tool to require a value of type T before execution.

Link copied to clipboard
fun Tool.toSpringToolCallback(): <Error class: unknown class>

Extension function to convert an Embabel Tool to a Spring AI ToolCallback.

Link copied to clipboard
inline fun <T : Tool> Tool.unwrapAs(): T?

Unwrap a tool to find a specific type, or return null if not found.

Link copied to clipboard

Wrap this tool to conditionally await before execution.

Link copied to clipboard
fun Tool.withConfirmation(messageProvider: (String) -> String): Tool

Wrap this tool to always require confirmation before execution.

Link copied to clipboard
open fun withDefinitionMetadata(entries: Map<String, Any>): Tool

Create a new tool with additional definition metadata entries merged in. Existing keys are overwritten by the new values.

open fun withDefinitionMetadata(key: String, value: Any): Tool

Create a new tool with a single definition metadata entry added.

Link copied to clipboard
open fun withDescription(newDescription: String): Tool

Create a new tool with a different description. Useful for providing context-specific descriptions while keeping the same functionality.

Link copied to clipboard
fun Tool.withEventPublication(agentProcess: AgentProcess, action: Action?, llmOptions: <Error class: unknown class>): Tool

Extension function to wrap a Tool with event publication.

Link copied to clipboard
open fun withName(newName: String): Tool

Create a new tool with a different name. Useful for namespacing tools when combining multiple tool sources.

Link copied to clipboard
open fun withNote(note: String): Tool

Create a new tool with an additional note appended to the description. Useful for adding context-specific hints to an existing tool.