HasToolCall
Returns a StopCondition that stops the tool-calling loop when the model calls
a tool with one of the given names. Use this for semantic completion
signals — for example, when the model is expected to call a "finish" or
"submit" tool to indicate that it has completed its task.
Signature
func HasToolCall(toolNames ...string) StopCondition
Parameters
| Parameter | Type | Description |
|---|---|---|
| toolNames | ...string | Names of the tools whose invocation stops the loop |
Return Value
A StopCondition function. When the condition fires it returns the reason string
"tool 'toolName' was called"; otherwise it returns "".
type StopCondition func(state StopConditionState) string
Behavior
HasToolCall inspects the most recent step's tool calls. If any of the
given toolNames appears in them the condition fires. It returns "" when
there are no completed steps yet (safe to evaluate on step zero).
Examples
Semantic completion signal
Define a finish tool that the model calls when it is satisfied with its
research. Use HasToolCall("finish") to stop the loop the moment it fires.
Pair it with IsStepCount as a hard-limit fallback in case the model never
calls the tool.
finishTool := types.Tool{
Name: "finish",
Description: "Signal that the task is complete. Provide a summary of your findings.",
Parameters: map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"summary": map[string]interface{}{
"type": "string",
"description": "Final summary of the research",
},
},
"required": []string{"summary"},
},
Execute: func(ctx context.Context, args map[string]interface{}, opts types.ToolExecutionOptions) (interface{}, error) {
return map[string]interface{}{"status": "complete", "summary": args["summary"]}, nil
},
}
result, err := ai.GenerateText(ctx, ai.GenerateTextOptions{
Model: model,
Prompt: `Research Go concurrency patterns.
When you are satisfied with your findings, call the finish tool.`,
Tools: []types.Tool{searchTool, finishTool},
StopWhen: []ai.StopCondition{
ai.HasToolCall("finish"), // stop on semantic completion
ai.IsStepCount(10), // safety ceiling
},
})
if err != nil {
log.Fatal(err)
}
fmt.Printf("StopReason: %s\n", result.StopReason)
// → "StopReason: tool 'finish' was called" (if model finished early)
// → "StopReason: maximum number of steps (10) reached" (if it hit the ceiling)
With ToolLoopAgent
myAgent := agent.NewToolLoopAgent(agent.AgentConfig{
Model: model,
Tools: []types.Tool{searchTool, submitTool},
StopWhen: []ai.StopCondition{
ai.HasToolCall("submit"),
ai.IsStepCount(20),
},
})
result, err := myAgent.Execute(ctx, "Complete the report and submit it")
fmt.Printf("StopReason: %s\n", result.StopReason)
Condition ordering and side effects
EvaluateStopConditions runs all conditions before returning the first match.
This means every condition in StopWhen always executes, regardless of order.
Side-effectful conditions (e.g. logging, metric recording) can therefore be placed
anywhere and are guaranteed to run.
// Both conditions always run; the first non-empty reason is returned.
StopWhen: []ai.StopCondition{
ai.HasToolCall("finish"), // fires → "tool 'finish' was called"
ai.IsStepCount(20), // also evaluated, but result ignored if finish fired first
},
See Also
- IsStepCount — stop after n steps (safety ceiling pattern)
- Loop Control — full guide with examples
- GenerateText —
GenerateTextOptions.StopWhenfield - ToolLoopAgent —
AgentConfig.StopWhenfield - Stop-When Example — runnable example