Skip to main content

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​

ParameterTypeDescription
toolNames...stringNames 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​