package calc import ( "context" "encoding/json" "strings" "testing" "gitea.stevedudenhoeffer.com/steve/majordomo/agent" "gitea.stevedudenhoeffer.com/steve/majordomo/llm" "gitea.stevedudenhoeffer.com/steve/majordomo/provider/fake" ) func TestEval(t *testing.T) { tests := []struct { expr string want float64 }{ {"1+2", 3}, {"2 * 3 + 4", 10}, {"2 + 3 * 4", 14}, {"(2 + 3) * 4", 20}, {"10 / 4", 2.5}, {"-3 + 5", 2}, {"--3", 3}, {"2^10", 1024}, {"2^3^2", 512}, // right-associative {"-2^2", -4}, // unary binds looser than power {"7 % 3", 1}, {"1.5e2 + 1", 151}, {" ( 1 + 1 ) * ( 2 + 2 ) ", 8}, } for _, tt := range tests { got, err := Eval(tt.expr) if err != nil { t.Errorf("Eval(%q): %v", tt.expr, err) continue } if got != tt.want { t.Errorf("Eval(%q) = %v, want %v", tt.expr, got, tt.want) } } } func TestEvalErrors(t *testing.T) { for _, expr := range []string{"", "1/0", "5 % 0", "2 +", "(1+2", "1 + abc", "1 2", "2^99999"} { if _, err := Eval(expr); err == nil { t.Errorf("Eval(%q) should error", expr) } } } // TestSkillInAgentLoop: an agent actually invokes calculate and answers // from its result. func TestSkillInAgentLoop(t *testing.T) { fp := fake.New("fp") fp.Enqueue("m", fake.ReplyWith(llm.Response{ ToolCalls: []llm.ToolCall{{ID: "c1", Name: "calculate", Arguments: json.RawMessage(`{"expression":"(2+3)*4"}`)}}, FinishReason: llm.FinishToolCalls, }), fake.Reply("the answer is 20"), ) m, _ := fp.Model("m") a := agent.New(m, "Math helper.", agent.WithSkill(New())) res, err := a.Run(context.Background(), "what is (2+3)*4?") if err != nil { t.Fatalf("Run: %v", err) } if res.Output != "the answer is 20" { t.Errorf("output = %q", res.Output) } result := res.Steps[0].Results[0] if result.IsError || !strings.Contains(result.Content, `"result":20`) { t.Errorf("tool result = %+v", result) } }