Return errors from schema generation instead of panicking
jsonSchemaFor panicked on unsupported types, which meant FunctionTool, NewOutputType, and RunTyped would crash the process during setup rather than returning a normal error. Signed-off-by: Bryan Frimin <bryan@getprobo.com>
This commit is contained in:
@@ -348,13 +348,14 @@ func TestRun(t *testing.T) {
|
||||
City string `json:"city"`
|
||||
}
|
||||
|
||||
weatherTool := agent.FunctionTool[Params](
|
||||
"get_weather",
|
||||
"Get weather for a city",
|
||||
func(_ context.Context, p Params) (agent.ToolResult, error) {
|
||||
return agent.ToolResult{Content: "Sunny, 22°C in " + p.City}, nil
|
||||
},
|
||||
)
|
||||
weatherTool, err := agent.FunctionTool[Params](
|
||||
"get_weather",
|
||||
"Get weather for a city",
|
||||
func(_ context.Context, p Params) (agent.ToolResult, error) {
|
||||
return agent.ToolResult{Content: "Sunny, 22°C in " + p.City}, nil
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
provider := &mockProvider{
|
||||
responses: []*llm.ChatCompletionResponse{
|
||||
@@ -406,30 +407,31 @@ func TestRun(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
type Params struct{}
|
||||
noopTool := agent.FunctionTool[Params](
|
||||
"noop",
|
||||
"No-op",
|
||||
func(_ context.Context, _ Params) (agent.ToolResult, error) {
|
||||
return agent.ToolResult{Content: "ok"}, nil
|
||||
},
|
||||
type Params struct{}
|
||||
noopTool, err := agent.FunctionTool[Params](
|
||||
"noop",
|
||||
"No-op",
|
||||
func(_ context.Context, _ Params) (agent.ToolResult, error) {
|
||||
return agent.ToolResult{Content: "ok"}, nil
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
ag := agent.New(
|
||||
"assistant",
|
||||
newTestClient(provider),
|
||||
agent.WithModel("test-model"),
|
||||
agent.WithTools(noopTool),
|
||||
agent.WithMaxTurns(2),
|
||||
)
|
||||
|
||||
ag := agent.New(
|
||||
"assistant",
|
||||
newTestClient(provider),
|
||||
agent.WithModel("test-model"),
|
||||
agent.WithTools(noopTool),
|
||||
agent.WithMaxTurns(2),
|
||||
)
|
||||
_, err = ag.Run(
|
||||
context.Background(),
|
||||
[]llm.Message{userMessage("loop")},
|
||||
)
|
||||
|
||||
_, err := ag.Run(
|
||||
context.Background(),
|
||||
[]llm.Message{userMessage("loop")},
|
||||
)
|
||||
|
||||
require.Error(t, err)
|
||||
var maxTurnsErr *agent.MaxTurnsExceededError
|
||||
require.Error(t, err)
|
||||
var maxTurnsErr *agent.MaxTurnsExceededError
|
||||
require.ErrorAs(t, err, &maxTurnsErr)
|
||||
assert.Equal(t, 2, maxTurnsErr.MaxTurns)
|
||||
},
|
||||
@@ -440,16 +442,18 @@ func TestRun(t *testing.T) {
|
||||
func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
type Params struct{}
|
||||
makeTool := func(name string) agent.Tool {
|
||||
return agent.FunctionTool[Params](
|
||||
name,
|
||||
"desc",
|
||||
func(_ context.Context, _ Params) (agent.ToolResult, error) {
|
||||
return agent.ToolResult{Content: "ok"}, nil
|
||||
},
|
||||
)
|
||||
}
|
||||
type Params struct{}
|
||||
makeTool := func(name string) agent.Tool {
|
||||
tool, err := agent.FunctionTool[Params](
|
||||
name,
|
||||
"desc",
|
||||
func(_ context.Context, _ Params) (agent.ToolResult, error) {
|
||||
return agent.ToolResult{Content: "ok"}, nil
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
return tool
|
||||
}
|
||||
|
||||
provider := &mockProvider{
|
||||
responses: []*llm.ChatCompletionResponse{
|
||||
@@ -546,20 +550,22 @@ func TestRun(t *testing.T) {
|
||||
|
||||
type Params struct{}
|
||||
|
||||
tool1 := agent.FunctionTool[Params](
|
||||
"first",
|
||||
"First tool",
|
||||
func(_ context.Context, _ Params) (agent.ToolResult, error) {
|
||||
return agent.ToolResult{Content: "result_1"}, nil
|
||||
},
|
||||
)
|
||||
tool2 := agent.FunctionTool[Params](
|
||||
"second",
|
||||
"Second tool",
|
||||
func(_ context.Context, _ Params) (agent.ToolResult, error) {
|
||||
return agent.ToolResult{Content: "result_2"}, nil
|
||||
},
|
||||
)
|
||||
tool1, err := agent.FunctionTool[Params](
|
||||
"first",
|
||||
"First tool",
|
||||
func(_ context.Context, _ Params) (agent.ToolResult, error) {
|
||||
return agent.ToolResult{Content: "result_1"}, nil
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
tool2, err := agent.FunctionTool[Params](
|
||||
"second",
|
||||
"Second tool",
|
||||
func(_ context.Context, _ Params) (agent.ToolResult, error) {
|
||||
return agent.ToolResult{Content: "result_2"}, nil
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
provider := &mockProvider{
|
||||
responses: []*llm.ChatCompletionResponse{
|
||||
@@ -613,20 +619,22 @@ func TestRun(t *testing.T) {
|
||||
|
||||
type Params struct{}
|
||||
|
||||
successTool := agent.FunctionTool[Params](
|
||||
"succeed",
|
||||
"Always succeeds",
|
||||
func(_ context.Context, _ Params) (agent.ToolResult, error) {
|
||||
return agent.ToolResult{Content: "success_result"}, nil
|
||||
},
|
||||
)
|
||||
failTool := agent.FunctionTool[Params](
|
||||
"fail",
|
||||
"Always fails",
|
||||
func(_ context.Context, _ Params) (agent.ToolResult, error) {
|
||||
return agent.ToolResult{}, errors.New("tool exploded")
|
||||
},
|
||||
)
|
||||
successTool, err := agent.FunctionTool[Params](
|
||||
"succeed",
|
||||
"Always succeeds",
|
||||
func(_ context.Context, _ Params) (agent.ToolResult, error) {
|
||||
return agent.ToolResult{Content: "success_result"}, nil
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
failTool, err := agent.FunctionTool[Params](
|
||||
"fail",
|
||||
"Always fails",
|
||||
func(_ context.Context, _ Params) (agent.ToolResult, error) {
|
||||
return agent.ToolResult{}, errors.New("tool exploded")
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
provider := &mockProvider{
|
||||
responses: []*llm.ChatCompletionResponse{
|
||||
@@ -684,16 +692,17 @@ func TestRun(t *testing.T) {
|
||||
|
||||
var capturedTenantID string
|
||||
|
||||
type Params struct{}
|
||||
tool := agent.FunctionTool[Params](
|
||||
"check_tenant",
|
||||
"Check current tenant",
|
||||
func(ctx context.Context, _ Params) (agent.ToolResult, error) {
|
||||
rc := agent.RunContextFrom[*RequestContext](ctx)
|
||||
capturedTenantID = rc.TenantID
|
||||
return agent.ToolResult{Content: "tenant: " + rc.TenantID}, nil
|
||||
},
|
||||
)
|
||||
type Params struct{}
|
||||
tool, err := agent.FunctionTool[Params](
|
||||
"check_tenant",
|
||||
"Check current tenant",
|
||||
func(ctx context.Context, _ Params) (agent.ToolResult, error) {
|
||||
rc := agent.RunContextFrom[*RequestContext](ctx)
|
||||
capturedTenantID = rc.TenantID
|
||||
return agent.ToolResult{Content: "tenant: " + rc.TenantID}, nil
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
provider := &mockProvider{
|
||||
responses: []*llm.ChatCompletionResponse{
|
||||
@@ -1056,26 +1065,27 @@ func TestRun_Hooks(t *testing.T) {
|
||||
func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
type Params struct{}
|
||||
noopTool := agent.FunctionTool[Params](
|
||||
"noop",
|
||||
"No-op",
|
||||
func(_ context.Context, _ Params) (agent.ToolResult, error) {
|
||||
return agent.ToolResult{Content: "ok"}, nil
|
||||
},
|
||||
)
|
||||
type Params struct{}
|
||||
noopTool, err := agent.FunctionTool[Params](
|
||||
"noop",
|
||||
"No-op",
|
||||
func(_ context.Context, _ Params) (agent.ToolResult, error) {
|
||||
return agent.ToolResult{Content: "ok"}, nil
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
provider := &mockProvider{
|
||||
responses: []*llm.ChatCompletionResponse{
|
||||
toolCallResponse(llm.ToolCall{
|
||||
ID: "tc_1",
|
||||
Function: llm.FunctionCall{Name: "noop", Arguments: `{}`},
|
||||
}),
|
||||
stopResponse("done"),
|
||||
},
|
||||
}
|
||||
provider := &mockProvider{
|
||||
responses: []*llm.ChatCompletionResponse{
|
||||
toolCallResponse(llm.ToolCall{
|
||||
ID: "tc_1",
|
||||
Function: llm.FunctionCall{Name: "noop", Arguments: `{}`},
|
||||
}),
|
||||
stopResponse("done"),
|
||||
},
|
||||
}
|
||||
|
||||
hook := &recordingHook{}
|
||||
hook := &recordingHook{}
|
||||
|
||||
ag := agent.New(
|
||||
"assistant",
|
||||
@@ -1340,30 +1350,31 @@ func TestRun_ToolUseBehavior(t *testing.T) {
|
||||
func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
type Params struct{}
|
||||
tool := agent.FunctionTool[Params](
|
||||
"compute",
|
||||
"Compute something",
|
||||
func(_ context.Context, _ Params) (agent.ToolResult, error) {
|
||||
return agent.ToolResult{Content: "computed_result"}, nil
|
||||
},
|
||||
)
|
||||
type Params struct{}
|
||||
tool, err := agent.FunctionTool[Params](
|
||||
"compute",
|
||||
"Compute something",
|
||||
func(_ context.Context, _ Params) (agent.ToolResult, error) {
|
||||
return agent.ToolResult{Content: "computed_result"}, nil
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
provider := &mockProvider{
|
||||
responses: []*llm.ChatCompletionResponse{
|
||||
toolCallResponse(llm.ToolCall{
|
||||
ID: "tc_1",
|
||||
Function: llm.FunctionCall{Name: "compute", Arguments: `{}`},
|
||||
}),
|
||||
},
|
||||
}
|
||||
provider := &mockProvider{
|
||||
responses: []*llm.ChatCompletionResponse{
|
||||
toolCallResponse(llm.ToolCall{
|
||||
ID: "tc_1",
|
||||
Function: llm.FunctionCall{Name: "compute", Arguments: `{}`},
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
ag := agent.New(
|
||||
"assistant",
|
||||
newTestClient(provider),
|
||||
agent.WithModel("test-model"),
|
||||
agent.WithTools(tool),
|
||||
agent.WithToolUseBehavior(agent.StopOnFirstTool()),
|
||||
ag := agent.New(
|
||||
"assistant",
|
||||
newTestClient(provider),
|
||||
agent.WithModel("test-model"),
|
||||
agent.WithTools(tool),
|
||||
agent.WithToolUseBehavior(agent.StopOnFirstTool()),
|
||||
)
|
||||
|
||||
result, err := ag.Run(
|
||||
@@ -1385,20 +1396,22 @@ func TestRun_ToolUseBehavior(t *testing.T) {
|
||||
|
||||
type Params struct{}
|
||||
|
||||
tool1 := agent.FunctionTool[Params](
|
||||
"search",
|
||||
"Search",
|
||||
func(_ context.Context, _ Params) (agent.ToolResult, error) {
|
||||
return agent.ToolResult{Content: "search_result"}, nil
|
||||
},
|
||||
)
|
||||
tool2 := agent.FunctionTool[Params](
|
||||
"submit",
|
||||
"Submit",
|
||||
func(_ context.Context, _ Params) (agent.ToolResult, error) {
|
||||
return agent.ToolResult{Content: "submitted"}, nil
|
||||
},
|
||||
)
|
||||
tool1, err := agent.FunctionTool[Params](
|
||||
"search",
|
||||
"Search",
|
||||
func(_ context.Context, _ Params) (agent.ToolResult, error) {
|
||||
return agent.ToolResult{Content: "search_result"}, nil
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
tool2, err := agent.FunctionTool[Params](
|
||||
"submit",
|
||||
"Submit",
|
||||
func(_ context.Context, _ Params) (agent.ToolResult, error) {
|
||||
return agent.ToolResult{Content: "submitted"}, nil
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
provider := &mockProvider{
|
||||
responses: []*llm.ChatCompletionResponse{
|
||||
@@ -1432,22 +1445,23 @@ func TestRun_ToolUseBehavior(t *testing.T) {
|
||||
func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
type Params struct{}
|
||||
tool := agent.FunctionTool[Params](
|
||||
"noop",
|
||||
"No-op",
|
||||
func(_ context.Context, _ Params) (agent.ToolResult, error) {
|
||||
return agent.ToolResult{Content: "ok"}, nil
|
||||
},
|
||||
)
|
||||
type Params struct{}
|
||||
tool, err := agent.FunctionTool[Params](
|
||||
"noop",
|
||||
"No-op",
|
||||
func(_ context.Context, _ Params) (agent.ToolResult, error) {
|
||||
return agent.ToolResult{Content: "ok"}, nil
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
provider := &mockProvider{
|
||||
responses: []*llm.ChatCompletionResponse{
|
||||
toolCallResponse(llm.ToolCall{
|
||||
ID: "tc_1",
|
||||
Function: llm.FunctionCall{Name: "noop", Arguments: `{}`},
|
||||
}),
|
||||
stopResponse("Final answer."),
|
||||
provider := &mockProvider{
|
||||
responses: []*llm.ChatCompletionResponse{
|
||||
toolCallResponse(llm.ToolCall{
|
||||
ID: "tc_1",
|
||||
Function: llm.FunctionCall{Name: "noop", Arguments: `{}`},
|
||||
}),
|
||||
stopResponse("Final answer."),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1474,41 +1488,42 @@ func TestRun_ToolUseBehavior(t *testing.T) {
|
||||
func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
type Params struct{}
|
||||
tool := agent.FunctionTool[Params](
|
||||
"compute",
|
||||
"Compute something",
|
||||
func(_ context.Context, _ Params) (agent.ToolResult, error) {
|
||||
return agent.ToolResult{Content: "result"}, nil
|
||||
},
|
||||
)
|
||||
type Params struct{}
|
||||
tool, err := agent.FunctionTool[Params](
|
||||
"compute",
|
||||
"Compute something",
|
||||
func(_ context.Context, _ Params) (agent.ToolResult, error) {
|
||||
return agent.ToolResult{Content: "result"}, nil
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
provider := &mockProvider{
|
||||
responses: []*llm.ChatCompletionResponse{
|
||||
toolCallResponse(llm.ToolCall{
|
||||
ID: "tc_1",
|
||||
Function: llm.FunctionCall{Name: "compute", Arguments: `{}`},
|
||||
}),
|
||||
},
|
||||
}
|
||||
provider := &mockProvider{
|
||||
responses: []*llm.ChatCompletionResponse{
|
||||
toolCallResponse(llm.ToolCall{
|
||||
ID: "tc_1",
|
||||
Function: llm.FunctionCall{Name: "compute", Arguments: `{}`},
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
ag := agent.New(
|
||||
"assistant",
|
||||
newTestClient(provider),
|
||||
agent.WithModel("test-model"),
|
||||
agent.WithTools(tool),
|
||||
agent.WithToolUseBehavior(agent.ToolUseBehavior(func(_ context.Context, _ []agent.ToolCallResult) (string, bool, error) {
|
||||
ag := agent.New(
|
||||
"assistant",
|
||||
newTestClient(provider),
|
||||
agent.WithModel("test-model"),
|
||||
agent.WithTools(tool),
|
||||
agent.WithToolUseBehavior(agent.ToolUseBehavior(func(_ context.Context, _ []agent.ToolCallResult) (string, bool, error) {
|
||||
return "", false, errors.New("custom behavior failed")
|
||||
})),
|
||||
)
|
||||
|
||||
_, err := ag.Run(
|
||||
context.Background(),
|
||||
[]llm.Message{userMessage("compute")},
|
||||
)
|
||||
_, err = ag.Run(
|
||||
context.Background(),
|
||||
[]llm.Message{userMessage("compute")},
|
||||
)
|
||||
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "custom behavior failed")
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "custom behavior failed")
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -1527,11 +1542,14 @@ func TestRun_OutputType(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
infoType, err := agent.NewOutputType[Info]("info")
|
||||
require.NoError(t, err)
|
||||
|
||||
ag := agent.New(
|
||||
"assistant",
|
||||
newTestClient(provider),
|
||||
agent.WithModel("test-model"),
|
||||
agent.WithOutputType(agent.NewOutputType[Info]("info")),
|
||||
agent.WithOutputType(infoType),
|
||||
)
|
||||
|
||||
result, err := ag.Run(
|
||||
@@ -1560,37 +1578,38 @@ func TestRun_Approval(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
deleteTool := agent.FunctionTool[struct{}](
|
||||
"delete_account",
|
||||
"Deletes the user account",
|
||||
func(_ context.Context, _ struct{}) (agent.ToolResult, error) {
|
||||
return agent.ToolResult{Content: "deleted"}, nil
|
||||
},
|
||||
)
|
||||
deleteTool, err := agent.FunctionTool[struct{}](
|
||||
"delete_account",
|
||||
"Deletes the user account",
|
||||
func(_ context.Context, _ struct{}) (agent.ToolResult, error) {
|
||||
return agent.ToolResult{Content: "deleted"}, nil
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
ag := agent.New(
|
||||
"assistant",
|
||||
newTestClient(provider),
|
||||
agent.WithModel("test-model"),
|
||||
agent.WithTools(deleteTool),
|
||||
agent.WithApproval(agent.ApprovalConfig{
|
||||
ToolNames: []string{"delete_account"},
|
||||
}),
|
||||
)
|
||||
ag := agent.New(
|
||||
"assistant",
|
||||
newTestClient(provider),
|
||||
agent.WithModel("test-model"),
|
||||
agent.WithTools(deleteTool),
|
||||
agent.WithApproval(agent.ApprovalConfig{
|
||||
ToolNames: []string{"delete_account"},
|
||||
}),
|
||||
)
|
||||
|
||||
_, err := ag.Run(
|
||||
context.Background(),
|
||||
[]llm.Message{userMessage("Delete my account")},
|
||||
)
|
||||
_, err = ag.Run(
|
||||
context.Background(),
|
||||
[]llm.Message{userMessage("Delete my account")},
|
||||
)
|
||||
|
||||
require.Error(t, err)
|
||||
var interrupted *agent.InterruptedError
|
||||
require.ErrorAs(t, err, &interrupted)
|
||||
assert.Len(t, interrupted.ToolCalls, 1)
|
||||
assert.Equal(t, "delete_account", interrupted.ToolCalls[0].Function.Name)
|
||||
assert.Len(t, interrupted.PendingApprovals, 1)
|
||||
assert.Equal(t, "delete_account", interrupted.PendingApprovals[0].Function.Name)
|
||||
assert.Equal(t, 1, interrupted.Turns)
|
||||
require.Error(t, err)
|
||||
var interrupted *agent.InterruptedError
|
||||
require.ErrorAs(t, err, &interrupted)
|
||||
assert.Len(t, interrupted.ToolCalls, 1)
|
||||
assert.Equal(t, "delete_account", interrupted.ToolCalls[0].Function.Name)
|
||||
assert.Len(t, interrupted.PendingApprovals, 1)
|
||||
assert.Equal(t, "delete_account", interrupted.PendingApprovals[0].Function.Name)
|
||||
assert.Equal(t, 1, interrupted.Turns)
|
||||
},
|
||||
)
|
||||
|
||||
@@ -1599,18 +1618,19 @@ func TestRun_Approval(t *testing.T) {
|
||||
func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var toolExecuted bool
|
||||
var toolExecuted bool
|
||||
|
||||
deleteTool := agent.FunctionTool[struct{}](
|
||||
"delete_account",
|
||||
"Deletes the user account",
|
||||
func(_ context.Context, _ struct{}) (agent.ToolResult, error) {
|
||||
toolExecuted = true
|
||||
return agent.ToolResult{Content: "account deleted"}, nil
|
||||
},
|
||||
)
|
||||
deleteTool, err := agent.FunctionTool[struct{}](
|
||||
"delete_account",
|
||||
"Deletes the user account",
|
||||
func(_ context.Context, _ struct{}) (agent.ToolResult, error) {
|
||||
toolExecuted = true
|
||||
return agent.ToolResult{Content: "account deleted"}, nil
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
provider := &mockProvider{
|
||||
provider := &mockProvider{
|
||||
responses: []*llm.ChatCompletionResponse{
|
||||
toolCallResponse(llm.ToolCall{
|
||||
ID: "tc1",
|
||||
@@ -1630,14 +1650,14 @@ func TestRun_Approval(t *testing.T) {
|
||||
}),
|
||||
)
|
||||
|
||||
_, err := ag.Run(
|
||||
context.Background(),
|
||||
[]llm.Message{userMessage("Delete my account")},
|
||||
)
|
||||
_, err = ag.Run(
|
||||
context.Background(),
|
||||
[]llm.Message{userMessage("Delete my account")},
|
||||
)
|
||||
|
||||
var interrupted *agent.InterruptedError
|
||||
require.ErrorAs(t, err, &interrupted)
|
||||
assert.False(t, toolExecuted)
|
||||
var interrupted *agent.InterruptedError
|
||||
require.ErrorAs(t, err, &interrupted)
|
||||
assert.False(t, toolExecuted)
|
||||
|
||||
result, err := agent.Resume(
|
||||
context.Background(),
|
||||
@@ -1660,22 +1680,23 @@ func TestRun_Approval(t *testing.T) {
|
||||
func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
deleteTool := agent.FunctionTool[struct{}](
|
||||
"delete_account",
|
||||
"Deletes the user account",
|
||||
func(_ context.Context, _ struct{}) (agent.ToolResult, error) {
|
||||
t.Fatal("tool should not be executed")
|
||||
return agent.ToolResult{}, nil
|
||||
},
|
||||
)
|
||||
deleteTool, err := agent.FunctionTool[struct{}](
|
||||
"delete_account",
|
||||
"Deletes the user account",
|
||||
func(_ context.Context, _ struct{}) (agent.ToolResult, error) {
|
||||
t.Fatal("tool should not be executed")
|
||||
return agent.ToolResult{}, nil
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
provider := &mockProvider{
|
||||
responses: []*llm.ChatCompletionResponse{
|
||||
toolCallResponse(llm.ToolCall{
|
||||
ID: "tc1",
|
||||
Function: llm.FunctionCall{Name: "delete_account", Arguments: `{}`},
|
||||
}),
|
||||
stopResponse("OK, I won't delete your account."),
|
||||
provider := &mockProvider{
|
||||
responses: []*llm.ChatCompletionResponse{
|
||||
toolCallResponse(llm.ToolCall{
|
||||
ID: "tc1",
|
||||
Function: llm.FunctionCall{Name: "delete_account", Arguments: `{}`},
|
||||
}),
|
||||
stopResponse("OK, I won't delete your account."),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1689,7 +1710,7 @@ func TestRun_Approval(t *testing.T) {
|
||||
}),
|
||||
)
|
||||
|
||||
_, err := ag.Run(
|
||||
_, err = ag.Run(
|
||||
context.Background(),
|
||||
[]llm.Message{userMessage("Delete my account")},
|
||||
)
|
||||
@@ -1727,21 +1748,22 @@ func TestRun_Approval(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
safeTool := agent.FunctionTool[struct{}](
|
||||
"safe_tool",
|
||||
"A safe tool",
|
||||
func(_ context.Context, _ struct{}) (agent.ToolResult, error) {
|
||||
return agent.ToolResult{Content: "safe result"}, nil
|
||||
},
|
||||
)
|
||||
safeTool, err := agent.FunctionTool[struct{}](
|
||||
"safe_tool",
|
||||
"A safe tool",
|
||||
func(_ context.Context, _ struct{}) (agent.ToolResult, error) {
|
||||
return agent.ToolResult{Content: "safe result"}, nil
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
ag := agent.New(
|
||||
"assistant",
|
||||
newTestClient(provider),
|
||||
agent.WithModel("test-model"),
|
||||
agent.WithTools(safeTool),
|
||||
agent.WithApproval(agent.ApprovalConfig{
|
||||
ShouldApprove: func(_ context.Context, tc llm.ToolCall) bool {
|
||||
ag := agent.New(
|
||||
"assistant",
|
||||
newTestClient(provider),
|
||||
agent.WithModel("test-model"),
|
||||
agent.WithTools(safeTool),
|
||||
agent.WithApproval(agent.ApprovalConfig{
|
||||
ShouldApprove: func(_ context.Context, tc llm.ToolCall) bool {
|
||||
return tc.Function.Name == "dangerous_tool"
|
||||
},
|
||||
}),
|
||||
@@ -1764,23 +1786,25 @@ func TestRun_Approval(t *testing.T) {
|
||||
|
||||
var safeExecuted, dangerExecuted bool
|
||||
|
||||
safeTool := agent.FunctionTool[struct{}](
|
||||
"safe_action",
|
||||
"A safe action",
|
||||
func(_ context.Context, _ struct{}) (agent.ToolResult, error) {
|
||||
safeExecuted = true
|
||||
return agent.ToolResult{Content: "safe done"}, nil
|
||||
},
|
||||
)
|
||||
safeTool, err := agent.FunctionTool[struct{}](
|
||||
"safe_action",
|
||||
"A safe action",
|
||||
func(_ context.Context, _ struct{}) (agent.ToolResult, error) {
|
||||
safeExecuted = true
|
||||
return agent.ToolResult{Content: "safe done"}, nil
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
dangerTool := agent.FunctionTool[struct{}](
|
||||
"danger_action",
|
||||
"A dangerous action",
|
||||
func(_ context.Context, _ struct{}) (agent.ToolResult, error) {
|
||||
dangerExecuted = true
|
||||
return agent.ToolResult{Content: "danger done"}, nil
|
||||
},
|
||||
)
|
||||
dangerTool, err := agent.FunctionTool[struct{}](
|
||||
"danger_action",
|
||||
"A dangerous action",
|
||||
func(_ context.Context, _ struct{}) (agent.ToolResult, error) {
|
||||
dangerExecuted = true
|
||||
return agent.ToolResult{Content: "danger done"}, nil
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
provider := &mockProvider{
|
||||
responses: []*llm.ChatCompletionResponse{
|
||||
@@ -1808,7 +1832,7 @@ func TestRun_Approval(t *testing.T) {
|
||||
}),
|
||||
)
|
||||
|
||||
_, err := ag.Run(
|
||||
_, err = ag.Run(
|
||||
context.Background(),
|
||||
[]llm.Message{userMessage("Do both")},
|
||||
)
|
||||
@@ -1916,21 +1940,22 @@ func TestResume(t *testing.T) {
|
||||
func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
deleteTool := agent.FunctionTool[struct{}](
|
||||
"delete_account",
|
||||
"Deletes the user account",
|
||||
func(_ context.Context, _ struct{}) (agent.ToolResult, error) {
|
||||
return agent.ToolResult{Content: "deleted"}, nil
|
||||
},
|
||||
)
|
||||
deleteTool, err := agent.FunctionTool[struct{}](
|
||||
"delete_account",
|
||||
"Deletes the user account",
|
||||
func(_ context.Context, _ struct{}) (agent.ToolResult, error) {
|
||||
return agent.ToolResult{Content: "deleted"}, nil
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
provider := &mockProvider{
|
||||
responses: []*llm.ChatCompletionResponse{
|
||||
toolCallResponse(llm.ToolCall{
|
||||
ID: "tc1",
|
||||
Function: llm.FunctionCall{Name: "delete_account", Arguments: `{}`},
|
||||
}),
|
||||
stopResponse("Account deleted."),
|
||||
provider := &mockProvider{
|
||||
responses: []*llm.ChatCompletionResponse{
|
||||
toolCallResponse(llm.ToolCall{
|
||||
ID: "tc1",
|
||||
Function: llm.FunctionCall{Name: "delete_account", Arguments: `{}`},
|
||||
}),
|
||||
stopResponse("Account deleted."),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -1944,7 +1969,7 @@ func TestResume(t *testing.T) {
|
||||
}),
|
||||
)
|
||||
|
||||
_, err := ag.Run(
|
||||
_, err = ag.Run(
|
||||
context.Background(),
|
||||
[]llm.Message{userMessage("Delete my account")},
|
||||
)
|
||||
@@ -2123,16 +2148,17 @@ func TestRunStreamed(t *testing.T) {
|
||||
func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
type Params struct{}
|
||||
tool := agent.FunctionTool[Params](
|
||||
"noop",
|
||||
"No-op",
|
||||
func(_ context.Context, _ Params) (agent.ToolResult, error) {
|
||||
return agent.ToolResult{Content: "ok"}, nil
|
||||
},
|
||||
)
|
||||
type Params struct{}
|
||||
tool, err := agent.FunctionTool[Params](
|
||||
"noop",
|
||||
"No-op",
|
||||
func(_ context.Context, _ Params) (agent.ToolResult, error) {
|
||||
return agent.ToolResult{Content: "ok"}, nil
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
stream1 := &mockChatStream{
|
||||
stream1 := &mockChatStream{
|
||||
events: []llm.ChatCompletionStreamEvent{
|
||||
{
|
||||
Delta: llm.MessageDelta{
|
||||
@@ -2280,21 +2306,23 @@ func TestClone(t *testing.T) {
|
||||
func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
type Params struct{}
|
||||
tool1 := agent.FunctionTool[Params](
|
||||
"t1",
|
||||
"desc",
|
||||
func(_ context.Context, _ Params) (agent.ToolResult, error) {
|
||||
return agent.ToolResult{Content: "ok"}, nil
|
||||
},
|
||||
)
|
||||
tool2 := agent.FunctionTool[Params](
|
||||
"t2",
|
||||
"desc",
|
||||
func(_ context.Context, _ Params) (agent.ToolResult, error) {
|
||||
return agent.ToolResult{Content: "ok"}, nil
|
||||
},
|
||||
)
|
||||
type Params struct{}
|
||||
tool1, err := agent.FunctionTool[Params](
|
||||
"t1",
|
||||
"desc",
|
||||
func(_ context.Context, _ Params) (agent.ToolResult, error) {
|
||||
return agent.ToolResult{Content: "ok"}, nil
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
tool2, err := agent.FunctionTool[Params](
|
||||
"t2",
|
||||
"desc",
|
||||
func(_ context.Context, _ Params) (agent.ToolResult, error) {
|
||||
return agent.ToolResult{Content: "ok"}, nil
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
provider := &mockProvider{
|
||||
responses: []*llm.ChatCompletionResponse{
|
||||
@@ -2401,13 +2429,14 @@ func TestGenerateSchema_EmbeddedStruct(t *testing.T) {
|
||||
Name string `json:"name"`
|
||||
}
|
||||
|
||||
tool := agent.FunctionTool[Params](
|
||||
tool, err := agent.FunctionTool[Params](
|
||||
"create",
|
||||
"Create item",
|
||||
func(_ context.Context, _ Params) (agent.ToolResult, error) {
|
||||
return agent.ToolResult{Content: "ok"}, nil
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
var schema map[string]any
|
||||
require.NoError(t, json.Unmarshal(tool.Definition().Parameters, &schema))
|
||||
@@ -2575,13 +2604,14 @@ func TestRun_UnknownToolCall(t *testing.T) {
|
||||
}
|
||||
|
||||
type Params struct{}
|
||||
tool := agent.FunctionTool[Params](
|
||||
tool, err := agent.FunctionTool[Params](
|
||||
"real_tool",
|
||||
"A real tool",
|
||||
func(_ context.Context, _ Params) (agent.ToolResult, error) {
|
||||
return agent.ToolResult{Content: "ok"}, nil
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
ag := agent.New(
|
||||
"assistant",
|
||||
@@ -2590,7 +2620,7 @@ func TestRun_UnknownToolCall(t *testing.T) {
|
||||
agent.WithTools(tool),
|
||||
)
|
||||
|
||||
_, err := ag.Run(
|
||||
_, err = ag.Run(
|
||||
context.Background(),
|
||||
[]llm.Message{userMessage("test")},
|
||||
)
|
||||
@@ -2642,13 +2672,14 @@ func TestClone_WithApprovalConfig(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
deleteTool := agent.FunctionTool[struct{}](
|
||||
deleteTool, err := agent.FunctionTool[struct{}](
|
||||
"delete",
|
||||
"Delete something",
|
||||
func(_ context.Context, _ struct{}) (agent.ToolResult, error) {
|
||||
return agent.ToolResult{Content: "deleted"}, nil
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
original := agent.New(
|
||||
"assistant",
|
||||
@@ -2662,7 +2693,7 @@ func TestClone_WithApprovalConfig(t *testing.T) {
|
||||
|
||||
cloned := original.Clone()
|
||||
|
||||
_, err := cloned.Run(
|
||||
_, err = cloned.Run(
|
||||
context.Background(),
|
||||
[]llm.Message{userMessage("delete it")},
|
||||
)
|
||||
@@ -2692,15 +2723,16 @@ func TestRun_HandoffWithPreHandoffTools(t *testing.T) {
|
||||
|
||||
var executionOrder []string
|
||||
|
||||
type Params struct{}
|
||||
tool1 := agent.FunctionTool[Params](
|
||||
"prepare",
|
||||
"Prepare data",
|
||||
func(_ context.Context, _ Params) (agent.ToolResult, error) {
|
||||
executionOrder = append(executionOrder, "prepare")
|
||||
return agent.ToolResult{Content: "prepared"}, nil
|
||||
},
|
||||
)
|
||||
type Params struct{}
|
||||
tool1, err := agent.FunctionTool[Params](
|
||||
"prepare",
|
||||
"Prepare data",
|
||||
func(_ context.Context, _ Params) (agent.ToolResult, error) {
|
||||
executionOrder = append(executionOrder, "prepare")
|
||||
return agent.ToolResult{Content: "prepared"}, nil
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
provider := &mockProvider{
|
||||
responses: []*llm.ChatCompletionResponse{
|
||||
@@ -2751,22 +2783,24 @@ func TestRun_HandoffWithPreHandoffTools(t *testing.T) {
|
||||
func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
type Params struct{}
|
||||
tool1 := agent.FunctionTool[Params](
|
||||
"prepare",
|
||||
"Prepare data",
|
||||
func(_ context.Context, _ Params) (agent.ToolResult, error) {
|
||||
return agent.ToolResult{Content: "prepared"}, nil
|
||||
},
|
||||
)
|
||||
tool2 := agent.FunctionTool[Params](
|
||||
"finalize",
|
||||
"Finalize data",
|
||||
func(_ context.Context, _ Params) (agent.ToolResult, error) {
|
||||
t.Fatal("tool after handoff should not be executed")
|
||||
return agent.ToolResult{}, nil
|
||||
},
|
||||
)
|
||||
type Params struct{}
|
||||
tool1, err := agent.FunctionTool[Params](
|
||||
"prepare",
|
||||
"Prepare data",
|
||||
func(_ context.Context, _ Params) (agent.ToolResult, error) {
|
||||
return agent.ToolResult{Content: "prepared"}, nil
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
tool2, err := agent.FunctionTool[Params](
|
||||
"finalize",
|
||||
"Finalize data",
|
||||
func(_ context.Context, _ Params) (agent.ToolResult, error) {
|
||||
t.Fatal("tool after handoff should not be executed")
|
||||
return agent.ToolResult{}, nil
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
provider := &mockProvider{
|
||||
responses: []*llm.ChatCompletionResponse{
|
||||
@@ -2835,14 +2869,15 @@ func TestRun_HandoffWithPreHandoffTools(t *testing.T) {
|
||||
func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
type Params struct{}
|
||||
failingTool := agent.FunctionTool[Params](
|
||||
"prepare",
|
||||
"Prepare data",
|
||||
func(_ context.Context, _ Params) (agent.ToolResult, error) {
|
||||
return agent.ToolResult{}, errors.New("preparation failed")
|
||||
},
|
||||
)
|
||||
type Params struct{}
|
||||
failingTool, err := agent.FunctionTool[Params](
|
||||
"prepare",
|
||||
"Prepare data",
|
||||
func(_ context.Context, _ Params) (agent.ToolResult, error) {
|
||||
return agent.ToolResult{}, errors.New("preparation failed")
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
provider := &mockProvider{
|
||||
responses: []*llm.ChatCompletionResponse{
|
||||
@@ -2875,7 +2910,7 @@ func TestRun_HandoffWithPreHandoffTools(t *testing.T) {
|
||||
agent.WithHandoffs(specialist),
|
||||
)
|
||||
|
||||
_, err := router.Run(
|
||||
_, err = router.Run(
|
||||
context.Background(),
|
||||
[]llm.Message{userMessage("prepare and transfer")},
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user