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:
Bryan Frimin
2026-03-13 18:56:56 +01:00
parent def8f417ca
commit f3239a1a7b
12 changed files with 505 additions and 406 deletions

View File

@@ -252,7 +252,7 @@ func TestAgentTool_Execute(t *testing.T) {
var captured string
type Params struct{}
tenantTool := agent.FunctionTool[Params](
tenantTool, err := agent.FunctionTool[Params](
"get_tenant",
"Get tenant",
func(ctx context.Context, _ Params) (agent.ToolResult, error) {
@@ -261,6 +261,7 @@ func TestAgentTool_Execute(t *testing.T) {
return agent.ToolResult{Content: rc.TenantID}, nil
},
)
require.NoError(t, err)
provider := &mockProvider{
responses: []*llm.ChatCompletionResponse{
@@ -303,13 +304,14 @@ func TestAgentTool_Execute(t *testing.T) {
Expr string `json:"expr"`
}
calcTool := agent.FunctionTool[Params](
calcTool, err := agent.FunctionTool[Params](
"calc",
"Calculate expression",
func(_ context.Context, p Params) (agent.ToolResult, error) {
return agent.ToolResult{Content: "42"}, nil
},
)
require.NoError(t, err)
provider := &mockProvider{
responses: []*llm.ChatCompletionResponse{
@@ -379,13 +381,14 @@ func TestAgentTool_Execute_NestedApproval(t *testing.T) {
func(t *testing.T) {
t.Parallel()
deleteTool := agent.FunctionTool[struct{}](
deleteTool, err := agent.FunctionTool[struct{}](
"delete_file",
"Delete a file",
func(_ context.Context, _ struct{}) (agent.ToolResult, error) {
return agent.ToolResult{Content: "file deleted"}, nil
},
)
require.NoError(t, err)
innerProvider := &mockProvider{
responses: []*llm.ChatCompletionResponse{
@@ -422,7 +425,7 @@ func TestAgentTool_Execute_NestedApproval(t *testing.T) {
agent.WithTools(innerAgent.AsTool("file_expert", "Manage files")),
)
_, err := outerAgent.Run(
_, err = outerAgent.Run(
context.Background(),
[]llm.Message{userMessage("Delete the file")},
)
@@ -443,7 +446,7 @@ func TestAgentTool_Execute_NestedApproval(t *testing.T) {
var toolExecuted bool
deleteTool := agent.FunctionTool[struct{}](
deleteTool, err := agent.FunctionTool[struct{}](
"delete_file",
"Delete a file",
func(_ context.Context, _ struct{}) (agent.ToolResult, error) {
@@ -451,6 +454,7 @@ func TestAgentTool_Execute_NestedApproval(t *testing.T) {
return agent.ToolResult{Content: "file deleted"}, nil
},
)
require.NoError(t, err)
innerProvider := &mockProvider{
responses: []*llm.ChatCompletionResponse{
@@ -489,7 +493,7 @@ func TestAgentTool_Execute_NestedApproval(t *testing.T) {
agent.WithTools(innerAgent.AsTool("file_expert", "Manage files")),
)
_, err := outerAgent.Run(
_, err = outerAgent.Run(
context.Background(),
[]llm.Message{userMessage("Delete the file")},
)
@@ -520,7 +524,7 @@ func TestAgentTool_Execute_NestedApproval(t *testing.T) {
func(t *testing.T) {
t.Parallel()
deleteTool := agent.FunctionTool[struct{}](
deleteTool, err := agent.FunctionTool[struct{}](
"delete_file",
"Delete a file",
func(_ context.Context, _ struct{}) (agent.ToolResult, error) {
@@ -528,6 +532,7 @@ func TestAgentTool_Execute_NestedApproval(t *testing.T) {
return agent.ToolResult{}, nil
},
)
require.NoError(t, err)
innerProvider := &mockProvider{
responses: []*llm.ChatCompletionResponse{
@@ -566,7 +571,7 @@ func TestAgentTool_Execute_NestedApproval(t *testing.T) {
agent.WithTools(innerAgent.AsTool("file_expert", "Manage files")),
)
_, err := outerAgent.Run(
_, err = outerAgent.Run(
context.Background(),
[]llm.Message{userMessage("Delete the file")},
)
@@ -598,7 +603,7 @@ func TestAgentTool_Execute_NestedApproval(t *testing.T) {
var siblingCalled bool
type Params struct{}
siblingTool := agent.FunctionTool[Params](
siblingTool, err := agent.FunctionTool[Params](
"list_files",
"List files",
func(_ context.Context, _ Params) (agent.ToolResult, error) {
@@ -606,14 +611,16 @@ func TestAgentTool_Execute_NestedApproval(t *testing.T) {
return agent.ToolResult{Content: "file1.txt, file2.txt"}, nil
},
)
require.NoError(t, err)
deleteTool := agent.FunctionTool[struct{}](
deleteTool, err := agent.FunctionTool[struct{}](
"delete_file",
"Delete a file",
func(_ context.Context, _ struct{}) (agent.ToolResult, error) {
return agent.ToolResult{Content: "file deleted"}, nil
},
)
require.NoError(t, err)
innerProvider := &mockProvider{
responses: []*llm.ChatCompletionResponse{
@@ -661,7 +668,7 @@ func TestAgentTool_Execute_NestedApproval(t *testing.T) {
),
)
_, err := outerAgent.Run(
_, err = outerAgent.Run(
context.Background(),
[]llm.Message{userMessage("List and delete files")},
)
@@ -692,7 +699,7 @@ func TestAgentTool_Execute_NestedApproval(t *testing.T) {
var toolExecuted bool
dangerTool := agent.FunctionTool[struct{}](
dangerTool, err := agent.FunctionTool[struct{}](
"danger",
"Dangerous operation",
func(_ context.Context, _ struct{}) (agent.ToolResult, error) {
@@ -700,6 +707,7 @@ func TestAgentTool_Execute_NestedApproval(t *testing.T) {
return agent.ToolResult{Content: "danger executed"}, nil
},
)
require.NoError(t, err)
cProvider := &mockProvider{
responses: []*llm.ChatCompletionResponse{
@@ -755,7 +763,7 @@ func TestAgentTool_Execute_NestedApproval(t *testing.T) {
agent.WithTools(agentB.AsTool("call_b", "Call agent B")),
)
_, err := agentA.Run(
_, err = agentA.Run(
context.Background(),
[]llm.Message{userMessage("start")},
)