llmagent_test.go

Overview

This file contains comprehensive tests for the llmagent package, which implements AI agents integrating with large language models (LLMs) following the patterns described in LLM Integration and Agents. The tests validate the behavior of the LLMAgent through various scenarios including core agent invocation, streaming modes, model and tool callbacks, instruction handling, tool execution, and agent transfer workflows.

The tests ensure the correctness of:

The file uses mocking, HTTP transport recording, and utility test runners (testutil.NewTestAgentRunner) to simulate realistic agent invocations and validate output event streams.


Detailed Explanations

Constants and Helpers

Test Functions

TestLLMAgent(t *testing.T)

Tests basic instantiation and invocation of an LLMAgent with different backend conditions:

This test validates that the agent can handle normal and error conditions gracefully.


TestLLMAgentStreamingModeSSE(t *testing.T)

Validates the agent's behavior in streaming mode with SSE (Server-Sent Events):

This tests the integration of streaming LLM responses and thought inclusion.


TestModelCallbacks(t *testing.T)

Tests the behavior of before and after model callbacks that can modify or short-circuit LLM requests and responses:

This test ensures the callback mechanism in the agent invocation lifecycle works as intended, supporting flexible response customization.


TestToolCallback(t *testing.T)

Verifies the lifecycle of tool invocation callbacks and their effect on tool execution and results:

This confirms the agent properly integrates tool callbacks and respects their short-circuiting behavior.


TestInstructionProvider(t *testing.T)

Tests dynamic instruction and global instruction providers replacing or merging static instructions:

This test validates the integration with the dynamic instruction injection system as described in Instruction Template Processing and Instruction Injection.


TestFunctionTool(t *testing.T)

Tests integration of a function tool that computes the sum of two numbers:

This test demonstrates the usage of function tools within the agent framework, as outlined in Function Tools.


TestAgentTransfer(t *testing.T)

Tests agent transfer workflows between hierarchical agents and sub-agents:

This test covers hierarchical agent delegation and transfer logic as described in Agent Lifecycle and Callbacks and Agent Selection Logic.


Important Implementation Details


File Interactions with Other System Parts


Functions and Test Descriptions

TestLLMAgent

TestLLMAgentStreamingModeSSE

TestModelCallbacks

TestToolCallback

TestInstructionProvider

TestFunctionTool

TestAgentTransfer

newGeminiModel

newGeminiTestClientConfig


Mermaid Diagram: Flowchart of Main Test Functions and Their Relationships

flowchart TD
A[TestLLMAgent] -->|Uses| B[newGeminiModel]
C[TestLLMAgentStreamingModeSSE] -->|Uses| B
D[TestModelCallbacks] --> E[testutil.MockModel]
D --> F[testutil.NewTestAgentRunner]
G[TestToolCallback] --> H[functiontool.New]
G --> F
I[TestInstructionProvider] --> E
I --> F
J[TestFunctionTool] --> H
J --> F
K[TestAgentTransfer] --> L[testutil.MockModel]
K --> F
B --> M[newGeminiTestClientConfig]
classDef helper fill:none,stroke:none
class B,M helper

Usage Examples from Tests

// Create agent with Gemini model and basic instructions
model := newGeminiModel(t, modelName, nil)
agent, err := llmagent.New(llmagent.Config{
    Name:        "hello_world_agent",
    Description: "hello world agent",
    Model:       model,
    Instruction: "Roll the dice and report only the result.",
    GlobalInstruction: "Answer as precisely as possible.",
    DisallowTransferToParent: true,
    DisallowTransferToPeers:  true,
})
if err != nil {
    t.Fatalf("NewLLMAgent failed: %v", err)
}
runner := testutil.NewTestAgentRunner(t, agent)
stream := runner.Run(t, "test_session", "")
texts, err := testutil.CollectTextParts(stream)
// Function tool example: sum of two numbers
type Args struct { A, B int }
type Result struct { Sum int }
handler := func(_ tool.Context, input Args) (Result, error) {
    return Result{Sum: input.A + input.B}, nil
}
sumTool, _ := functiontool.New(functiontool.Config{
    Name: "sum",
    Description: "computes the sum of two numbers",
}, handler)

agent, err := llmagent.New(llmagent.Config{
    Name: "agent",
    Model: model,
    Instruction: "output ONLY the result computed by the provided function",
    Tools: []tool.Tool{sumTool},
})

References to Related Topics