up
This commit is contained in:
@@ -0,0 +1,207 @@
|
||||
package calculator
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"math"
|
||||
"strconv"
|
||||
|
||||
"meshtastic_mqtt_server/agenttool"
|
||||
|
||||
"github.com/volcengine/volcengine-go-sdk/service/arkruntime/model"
|
||||
)
|
||||
|
||||
// Tool is a calculator tool that can evaluate simple math expressions
|
||||
type Tool struct {
|
||||
enabled bool
|
||||
}
|
||||
|
||||
// Name returns the tool name
|
||||
func (t *Tool) Name() string {
|
||||
return "calculator"
|
||||
}
|
||||
|
||||
// Enabled returns whether the tool is enabled
|
||||
func (t *Tool) Enabled() bool {
|
||||
return t.enabled
|
||||
}
|
||||
|
||||
// ToolDefinition returns the OpenAI tool definition
|
||||
func (t *Tool) ToolDefinition(description string) *model.Tool {
|
||||
desc := "一个计算器工具,可以计算数学表达式。支持加减乘除、平方根、幂运算等。当用户需要计算数学表达式时使用此工具。"
|
||||
if description != "" {
|
||||
desc = description
|
||||
}
|
||||
return &model.Tool{
|
||||
Type: model.ToolTypeFunction,
|
||||
Function: &model.FunctionDefinition{
|
||||
Name: "calculator",
|
||||
Description: desc,
|
||||
Parameters: map[string]any{
|
||||
"type": "object",
|
||||
"properties": map[string]any{
|
||||
"expression": map[string]any{
|
||||
"type": "string",
|
||||
"description": "要计算的数学表达式,例如 \"2 + 3 * 4\" 或 \"sqrt(16)\"",
|
||||
},
|
||||
},
|
||||
"required": []string{"expression"},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Execute executes the calculator tool
|
||||
func (t *Tool) Execute(ctx context.Context, args string, runtime agenttool.Runtime) (string, error) {
|
||||
var params struct {
|
||||
Expression string `json:"expression"`
|
||||
}
|
||||
if err := json.Unmarshal([]byte(args), ¶ms); err != nil {
|
||||
return "", fmt.Errorf("failed to parse arguments: %w", err)
|
||||
}
|
||||
|
||||
if params.Expression == "" {
|
||||
return "", fmt.Errorf("expression is required")
|
||||
}
|
||||
|
||||
result, err := evaluateExpression(params.Expression)
|
||||
if err != nil {
|
||||
return fmt.Sprintf("计算错误: %v", err), nil
|
||||
}
|
||||
|
||||
return fmt.Sprintf("计算结果: %s = %g", params.Expression, result), nil
|
||||
}
|
||||
|
||||
// RawState returns the tool state
|
||||
func (t *Tool) RawState() any {
|
||||
return map[string]any{"enabled": t.enabled}
|
||||
}
|
||||
|
||||
// evaluateExpression evaluates a simple mathematical expression using Go AST
|
||||
func evaluateExpression(expr string) (float64, error) {
|
||||
// Parse the expression
|
||||
node, err := parser.ParseExpr(expr)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("无效的表达式: %w", err)
|
||||
}
|
||||
|
||||
return evalNode(node)
|
||||
}
|
||||
|
||||
// evalNode evaluates an AST node
|
||||
func evalNode(node ast.Expr) (float64, error) {
|
||||
switch n := node.(type) {
|
||||
case *ast.BasicLit:
|
||||
if n.Kind == token.INT || n.Kind == token.FLOAT {
|
||||
return strconv.ParseFloat(n.Value, 64)
|
||||
}
|
||||
return 0, fmt.Errorf("不支持的字面量类型: %v", n.Kind)
|
||||
|
||||
case *ast.BinaryExpr:
|
||||
left, err := evalNode(n.X)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
right, err := evalNode(n.Y)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
switch n.Op {
|
||||
case token.ADD:
|
||||
return left + right, nil
|
||||
case token.SUB:
|
||||
return left - right, nil
|
||||
case token.MUL:
|
||||
return left * right, nil
|
||||
case token.QUO:
|
||||
if right == 0 {
|
||||
return 0, fmt.Errorf("除数不能为零")
|
||||
}
|
||||
return left / right, nil
|
||||
case token.REM:
|
||||
return math.Mod(left, right), nil
|
||||
default:
|
||||
return 0, fmt.Errorf("不支持的运算符: %v", n.Op)
|
||||
}
|
||||
|
||||
case *ast.ParenExpr:
|
||||
return evalNode(n.X)
|
||||
|
||||
case *ast.UnaryExpr:
|
||||
val, err := evalNode(n.X)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
switch n.Op {
|
||||
case token.ADD:
|
||||
return val, nil
|
||||
case token.SUB:
|
||||
return -val, nil
|
||||
default:
|
||||
return 0, fmt.Errorf("不支持的一元运算符: %v", n.Op)
|
||||
}
|
||||
|
||||
case *ast.CallExpr:
|
||||
ident, ok := n.Fun.(*ast.Ident)
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("不支持的函数调用形式")
|
||||
}
|
||||
if len(n.Args) != 1 {
|
||||
return 0, fmt.Errorf("函数只接受一个参数")
|
||||
}
|
||||
arg, err := evalNode(n.Args[0])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
switch ident.Name {
|
||||
case "sqrt":
|
||||
if arg < 0 {
|
||||
return 0, fmt.Errorf("平方根的参数不能为负数")
|
||||
}
|
||||
return math.Sqrt(arg), nil
|
||||
case "abs":
|
||||
return math.Abs(arg), nil
|
||||
case "sin":
|
||||
return math.Sin(arg), nil
|
||||
case "cos":
|
||||
return math.Cos(arg), nil
|
||||
case "tan":
|
||||
return math.Tan(arg), nil
|
||||
case "log":
|
||||
if arg <= 0 {
|
||||
return 0, fmt.Errorf("对数的参数必须为正数")
|
||||
}
|
||||
return math.Log(arg), nil
|
||||
case "log10":
|
||||
if arg <= 0 {
|
||||
return 0, fmt.Errorf("对数的参数必须为正数")
|
||||
}
|
||||
return math.Log10(arg), nil
|
||||
case "exp":
|
||||
return math.Exp(arg), nil
|
||||
case "pow":
|
||||
// For pow, we need two arguments - this is simplified
|
||||
return 0, fmt.Errorf("pow 函数需要两个参数,请使用 ** 运算符代替")
|
||||
default:
|
||||
return 0, fmt.Errorf("不支持的函数: %s", ident.Name)
|
||||
}
|
||||
|
||||
default:
|
||||
return 0, fmt.Errorf("不支持的表达式节点类型: %T", node)
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
agenttool.Register(agenttool.Descriptor{
|
||||
Name: "calculator",
|
||||
Load: func(path string, options agenttool.LoadOptions) (agenttool.LoadedTool, error) {
|
||||
return &Tool{enabled: true}, nil
|
||||
},
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user