From 22bda711e358f70474f47b2d89801390505ed1a8 Mon Sep 17 00:00:00 2001 From: conneroisu Date: Tue, 22 Oct 2024 18:04:19 -0400 Subject: [PATCH] Added get tools params --- extensions/composio/README.md | 1 - extensions/composio/composio.go | 104 +++++++++++++++++++++------ extensions/composio/composio_test.go | 27 +++++++ extensions/composio/test.sh | 7 +- pkg/test/helpers.go | 34 +++++++++ 5 files changed, 146 insertions(+), 27 deletions(-) diff --git a/extensions/composio/README.md b/extensions/composio/README.md index a8916cc..96f2332 100644 --- a/extensions/composio/README.md +++ b/extensions/composio/README.md @@ -1,4 +1,3 @@ # composio Compose AI is a powerful tool for creating complex and high-quality compositions of ai tools. This package provides a client for the composio api easily accessible through the groq-go library. - diff --git a/extensions/composio/composio.go b/extensions/composio/composio.go index 152ac22..8de924b 100644 --- a/extensions/composio/composio.go +++ b/extensions/composio/composio.go @@ -37,6 +37,48 @@ type ( } // ComposerOption is an option for the composio client. ComposerOption func(*Composio) + // Tool represents a composio tool. + Tool struct { + groq.Tool + Enum string `json:"enum"` + Tags []string `json:"tags"` + Logo string `json:"logo"` + AppID string `json:"appId"` + AppName string `json:"appName"` + DisplayName string `json:"displayName"` + Response struct { + Properties struct { + Data struct { + Title string `json:"title"` + Type string `json:"type"` + } `json:"data"` + Successful struct { + Description string `json:"description"` + Title string `json:"title"` + Type string `json:"type"` + } `json:"successful"` + Error struct { + AnyOf []struct { + Type string `json:"type"` + } `json:"anyOf"` + Default any `json:"default"` + Description string `json:"description"` + Title string `json:"title"` + } `json:"error"` + } `json:"properties"` + Required []string `json:"required"` + Title string `json:"title"` + Type string `json:"type"` + } `json:"response"` + Deprecated bool `json:"deprecated"` + } + // ToolsParams represents the parameters for the tools request. + ToolsParams struct { + App string `url:"appNames"` + Tags string `url:"tags"` + EntityID string `url:"user_uuid"` + UseCase string `url:"useCase"` + } ) // NewComposer creates a new composio client. @@ -53,15 +95,44 @@ func NewComposer(apiKey string, opts ...ComposerOption) (*Composio, error) { for _, opt := range opts { opt(c) } - if c.client == nil { - c.client = &http.Client{} - } - if c.logger == nil { - c.logger = slog.Default() - } return c, nil } +// GetTools returns the tools for the composio client. +func (c *Composio) GetTools(params ToolsParams) ([]Tool, error) { + url := fmt.Sprintf("%s/actions", c.baseURL) + if params.App != "" { + url = fmt.Sprintf("%s?appNames=%s", url, params.App) + } + if params.Tags != "" { + url = fmt.Sprintf("%s?tags=%s", url, params.Tags) + } + if params.EntityID != "" { + url = fmt.Sprintf("%s?user_uuid=%s", url, params.EntityID) + } + if params.UseCase != "" { + url = fmt.Sprintf("%s?useCase=%s", url, params.UseCase) + } + req, err := builders.NewRequest( + context.Background(), + c.header, + http.MethodGet, + url, + builders.WithBody(nil), + ) + if err != nil { + return nil, err + } + var tools struct { + Tools []Tool `json:"items"` + } + err = c.doRequest(req, &tools) + if err != nil { + return nil, err + } + c.logger.Debug("tools", "toolslen", len(tools.Tools)) + return tools.Tools, nil +} func (c *Composio) doRequest(req *http.Request, v interface{}) error { req.Header.Set("Accept", "application/json") contentType := req.Header.Get("Content-Type") @@ -93,22 +164,9 @@ func (c *Composio) doRequest(req *http.Request, v interface{}) error { } } -// GetTools returns the tools for the composio client. -func (c *Composio) GetTools() ([]groq.Tool, error) { - req, err := builders.NewRequest( - context.Background(), - c.header, - http.MethodGet, - fmt.Sprintf("%s/actions", c.baseURL), - builders.WithBody(nil), - ) - if err != nil { - return nil, err - } - var tools []groq.Tool - err = c.doRequest(req, &tools) - if err != nil { - return nil, err +// WithLogger sets the logger for the composio client. +func WithLogger(logger *slog.Logger) ComposerOption { + return func(c *Composio) { + c.logger = logger } - return tools, nil } diff --git a/extensions/composio/composio_test.go b/extensions/composio/composio_test.go index fed4439..95847f0 100644 --- a/extensions/composio/composio_test.go +++ b/extensions/composio/composio_test.go @@ -1 +1,28 @@ package composio + +import ( + "testing" + + "github.com/conneroisu/groq-go/pkg/test" + "github.com/stretchr/testify/assert" +) + +// TestGetTools tests the ability of the composio client to get tools. +func TestGetTools(t *testing.T) { + if !test.IsUnitTest() { + t.Skip() + } + a := assert.New(t) + key, err := test.GetAPIKey("COMPOSIO_API_KEY") + a.NoError(err) + client, err := NewComposer( + key, + WithLogger(test.DefaultLogger), + ) + a.NoError(err) + ts, err := client.GetTools(ToolsParams{ + Tags: "Authentication", + }) + a.NoError(err) + a.NotEmpty(ts) +} diff --git a/extensions/composio/test.sh b/extensions/composio/test.sh index 87de167..a7afc08 100644 --- a/extensions/composio/test.sh +++ b/extensions/composio/test.sh @@ -1,3 +1,4 @@ -curl --request GET \ - --url https://backend.composio.dev/api/v2/actions \ - --header 'X-API-Key: awcv1natkyr6336c1x2f8q' > composio.json +echo "APIKEY: $COMPOSIO_API_KEY" +apikey=$COMPOSIO_API_KEY +curl --request GET --url https://backend.composio.dev/api/v2/actions?tags=Authentication --header 'X-API-Key: '$apikey \ +> composio.json diff --git a/pkg/test/helpers.go b/pkg/test/helpers.go index c0428f5..f4b691d 100644 --- a/pkg/test/helpers.go +++ b/pkg/test/helpers.go @@ -1,8 +1,11 @@ package test import ( + "fmt" + "log/slog" "net/http" "os" + "strings" "testing" ) @@ -60,3 +63,34 @@ func (t *TokenRoundTripper) RoundTrip( func IsUnitTest() bool { return os.Getenv("UNIT") != "" } + +// GetAPIKey returns the api key. +func GetAPIKey(key string) (string, error) { + apiKey := os.Getenv(key) + if apiKey == "" { + return "", fmt.Errorf("api key is required") + } + return apiKey, nil +} + +// DefaultLogger is a default logger. +var DefaultLogger = slog.New(slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{ + AddSource: true, + Level: slog.LevelDebug, + ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr { + if a.Key == "time" { + return slog.Attr{} + } + if a.Key == "level" { + return slog.Attr{} + } + if a.Key == slog.SourceKey { + str := a.Value.String() + split := strings.Split(str, "/") + if len(split) > 2 { + a.Value = slog.StringValue(strings.Join(split[len(split)-2:], "/")) + a.Value = slog.StringValue(strings.Replace(a.Value.String(), "}", "", -1)) + } + } + return a + }}))