init
diff --git a/tools/func.go b/tools/func.go
new file mode 100644
index 0000000..9ad2add
--- /dev/null
+++ b/tools/func.go
@@ -0,0 +1,66 @@
+package tools
+
+import (
+ "encoding/json"
+ "fmt"
+ "strings"
+
+ "github.com/invopop/jsonschema"
+)
+
+type FuncTool[I any, O any] struct {
+ name string
+ desc string
+ schema *jsonschema.Schema
+ fn func(args I) (O, error)
+}
+
+func NewFuncTool[I any, O any](name string, fn func(args I) (O, error), desc string) *FuncTool[I, O] {
+ return &FuncTool[I, O]{
+ name,
+ fmt.Sprintf("%s\nResult schema: %s", desc, GenerateSchema[O]()),
+ GenerateSchema[I](),
+ fn,
+ }
+}
+
+func (ft *FuncTool[I, O]) Name() string {
+ return ft.name
+}
+
+func (ft *FuncTool[I, O]) Description() string {
+ return ft.desc
+}
+
+func (ft *FuncTool[I, O]) InputSchema() *jsonschema.Schema {
+ return ft.schema
+}
+
+func (ft *FuncTool[I, O]) Call(inp string) (string, error) {
+ var args I
+ if err := json.NewDecoder(strings.NewReader(inp)).Decode(&args); err != nil {
+ return "", err
+ }
+ out, err := ft.fn(args)
+ if err != nil {
+ return "", err
+ }
+ var resp strings.Builder
+ if err := json.NewEncoder(&resp).Encode(out); err != nil {
+ return "", err
+ }
+ ret := resp.String()
+ fmt.Printf("$$$ %s\n", ret)
+ return ret, nil
+
+}
+
+func GenerateSchema[T any]() *jsonschema.Schema {
+ reflector := jsonschema.Reflector{
+ AllowAdditionalProperties: false,
+ RequiredFromJSONSchemaTags: true,
+ DoNotReference: true,
+ }
+ var v T
+ return reflector.Reflect(v)
+}