all refactor done

This commit is contained in:
duwenxin99
2026-02-04 11:22:32 -05:00
committed by duwenxin
parent 803161c0b3
commit 10d7397c01
74 changed files with 1504 additions and 1270 deletions

View File

@@ -17,9 +17,11 @@ package elasticsearchesql
import (
"context"
"fmt"
"net/http"
"time"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
"github.com/goccy/go-yaml"
@@ -89,10 +91,10 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
var cancel context.CancelFunc
@@ -119,11 +121,15 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
for _, param := range t.Parameters {
if param.GetType() == "array" {
return nil, fmt.Errorf("array parameters are not supported yet")
return nil, util.NewAgentError("array parameters are not supported yet", nil)
}
sqlParams = append(sqlParams, map[string]any{param.GetName(): paramMap[param.GetName()]})
}
return source.RunSQL(ctx, t.Format, query, sqlParams)
resp, err := source.RunSQL(ctx, t.Format, query, sqlParams)
if err != nil {
return nil, util.ProcessGeneralError(err)
}
return resp, nil
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -16,12 +16,14 @@ package lookerdeleteprojectfile
import (
"context"
"fmt"
"net/http"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/tools/looker/lookercommon"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
"github.com/looker-open-source/sdk-codegen/go/rtl"
@@ -111,30 +113,30 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
sdk, err := source.GetLookerSDK(string(accessToken))
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
return nil, util.NewClientServerError("error getting sdk", http.StatusInternalServerError, err)
}
mapParams := params.AsMap()
projectId, ok := mapParams["project_id"].(string)
if !ok {
return nil, fmt.Errorf("'project_id' must be a string, got %T", mapParams["project_id"])
return nil, util.NewAgentError(fmt.Sprintf("'project_id' must be a string, got %T", mapParams["project_id"]), nil)
}
filePath, ok := mapParams["file_path"].(string)
if !ok {
return nil, fmt.Errorf("'file_path' must be a string, got %T", mapParams["file_path"])
return nil, util.NewAgentError(fmt.Sprintf("'file_path' must be a string, got %T", mapParams["file_path"]), nil)
}
err = lookercommon.DeleteProjectFile(sdk, projectId, filePath, source.LookerApiSettings())
if err != nil {
return nil, fmt.Errorf("error making delete_project_file request: %s", err)
return nil, util.ProcessGeneralError(err)
}
data := make(map[string]any)

View File

@@ -16,6 +16,7 @@ package lookerdevmode
import (
"context"
"fmt"
"net/http"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
@@ -60,7 +61,6 @@ type Config struct {
Annotations *tools.ToolAnnotations `yaml:"annotations,omitempty"`
}
// validate interface
var _ tools.ToolConfig = Config{}
func (cfg Config) ToolConfigType() string {
@@ -81,7 +81,6 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, params, annotations)
// finish tool setup
return Tool{
Config: cfg,
Parameters: params,
@@ -94,7 +93,6 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
}, nil
}
// validate interface
var _ tools.Tool = Tool{}
type Tool struct {
@@ -108,25 +106,25 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
logger, err := util.LoggerFromContext(ctx)
if err != nil {
return nil, fmt.Errorf("unable to get logger from ctx: %s", err)
return nil, util.NewClientServerError("unable to get logger from ctx", http.StatusInternalServerError, err)
}
mapParams := params.AsMap()
devMode, ok := mapParams["devMode"].(bool)
if !ok {
return nil, fmt.Errorf("'devMode' must be a boolean, got %T", mapParams["devMode"])
return nil, util.NewAgentError(fmt.Sprintf("'devMode' must be a boolean, got %T", mapParams["devMode"]), nil)
}
sdk, err := source.GetLookerSDK(string(accessToken))
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
return nil, util.NewClientServerError("error getting sdk", http.StatusInternalServerError, err)
}
var devModeString string
if devMode {
@@ -139,7 +137,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
}
resp, err := sdk.UpdateSession(req, source.LookerApiSettings())
if err != nil {
return nil, fmt.Errorf("error setting/resetting dev mode: %w", err)
return nil, util.ProcessGeneralError(err)
}
logger.DebugContext(ctx, "result = ", resp)

View File

@@ -4,7 +4,7 @@
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
@@ -17,6 +17,7 @@ package lookergenerateembedurl
import (
"context"
"fmt"
"net/http"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
@@ -114,15 +115,15 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
logger, err := util.LoggerFromContext(ctx)
if err != nil {
return nil, fmt.Errorf("unable to get logger from ctx: %s", err)
return nil, util.NewClientServerError("unable to get logger from ctx", http.StatusInternalServerError, err)
}
paramsMap := params.AsMap()
embedType := paramsMap["type"].(string)
@@ -138,7 +139,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
sdk, err := source.GetLookerSDK(string(accessToken))
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
return nil, util.NewClientServerError("error getting sdk", http.StatusInternalServerError, err)
}
forceLogoutLogin := true
@@ -151,7 +152,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
logger.ErrorContext(ctx, "Making request %v", req)
resp, err := sdk.CreateEmbedUrlAsMe(req, nil)
if err != nil {
return nil, fmt.Errorf("error making create_embed_url_as_me request: %s", err)
return nil, util.ProcessGeneralError(err)
}
logger.ErrorContext(ctx, "Got response %v", resp)

View File

@@ -16,11 +16,13 @@ package lookergetconnectiondatabases
import (
"context"
"fmt"
"net/http"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
"github.com/looker-open-source/sdk-codegen/go/rtl"
@@ -107,27 +109,26 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
mapParams := params.AsMap()
conn, ok := mapParams["conn"].(string)
if !ok {
return nil, fmt.Errorf("'conn' must be a string, got %T", mapParams["conn"])
return nil, util.NewAgentError(fmt.Sprintf("'conn' must be a string, got %T", mapParams["conn"]), nil)
}
sdk, err := source.GetLookerSDK(string(accessToken))
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
return nil, util.NewClientServerError("error getting sdk", http.StatusInternalServerError, err)
}
resp, err := sdk.ConnectionDatabases(conn, source.LookerApiSettings())
if err != nil {
return nil, fmt.Errorf("error making get_connection_databases request: %s", err)
return nil, util.ProcessGeneralError(err)
}
//logger.DebugContext(ctx, "Got response of %v\n", resp)
return resp, nil
}

View File

@@ -16,6 +16,7 @@ package lookergetconnections
import (
"context"
"fmt"
"net/http"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
@@ -107,24 +108,24 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
logger, err := util.LoggerFromContext(ctx)
if err != nil {
return nil, fmt.Errorf("unable to get logger from ctx: %s", err)
return nil, util.NewClientServerError("unable to get logger from ctx", http.StatusInternalServerError, err)
}
sdk, err := source.GetLookerSDK(string(accessToken))
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
return nil, util.NewClientServerError("error getting sdk", http.StatusInternalServerError, err)
}
resp, err := sdk.AllConnections("name, dialect(name), database, schema", source.LookerApiSettings())
if err != nil {
return nil, fmt.Errorf("error making get_connections request: %s", err)
return nil, util.ProcessGeneralError(err)
}
var data []any
@@ -140,7 +141,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
}
conn, err := sdk.ConnectionFeatures(*v.Name, "multiple_databases", source.LookerApiSettings())
if err != nil {
return nil, fmt.Errorf("error making get_connection_features request: %s", err)
return nil, util.ProcessGeneralError(err)
}
vMap["supports_multiple_databases"] = *conn.MultipleDatabases
logger.DebugContext(ctx, "Converted to %v\n", vMap)

View File

@@ -16,11 +16,13 @@ package lookergetconnectionschemas
import (
"context"
"fmt"
"net/http"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
"github.com/looker-open-source/sdk-codegen/go/rtl"
@@ -108,22 +110,22 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
mapParams := params.AsMap()
conn, ok := mapParams["conn"].(string)
if !ok {
return nil, fmt.Errorf("'conn' must be a string, got %T", mapParams["conn"])
return nil, util.NewAgentError(fmt.Sprintf("'conn' must be a string, got %T", mapParams["conn"]), nil)
}
db, _ := mapParams["db"].(string)
sdk, err := source.GetLookerSDK(string(accessToken))
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
return nil, util.NewClientServerError("error getting sdk", http.StatusInternalServerError, err)
}
req := v4.RequestConnectionSchemas{
ConnectionName: conn,
@@ -133,7 +135,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
}
resp, err := sdk.ConnectionSchemas(req, source.LookerApiSettings())
if err != nil {
return nil, fmt.Errorf("error making get_connection_schemas request: %s", err)
return nil, util.ProcessGeneralError(err)
}
return resp, nil
}

View File

@@ -8,7 +8,7 @@
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY, either express or implied.
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package lookergetconnectiontablecolumns
@@ -16,6 +16,7 @@ package lookergetconnectiontablecolumns
import (
"context"
"fmt"
"net/http"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
@@ -111,34 +112,34 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
logger, err := util.LoggerFromContext(ctx)
if err != nil {
return nil, fmt.Errorf("unable to get logger from ctx: %s", err)
return nil, util.NewClientServerError("unable to get logger from ctx", http.StatusInternalServerError, err)
}
mapParams := params.AsMap()
conn, ok := mapParams["conn"].(string)
if !ok {
return nil, fmt.Errorf("'conn' must be a string, got %T", mapParams["conn"])
return nil, util.NewAgentError(fmt.Sprintf("'conn' must be a string, got %T", mapParams["conn"]), nil)
}
db, _ := mapParams["db"].(string)
schema, ok := mapParams["schema"].(string)
if !ok {
return nil, fmt.Errorf("'schema' must be a string, got %T", mapParams["schema"])
return nil, util.NewAgentError(fmt.Sprintf("'schema' must be a string, got %T", mapParams["schema"]), nil)
}
tables, ok := mapParams["tables"].(string)
if !ok {
return nil, fmt.Errorf("'tables' must be a string, got %T", mapParams["tables"])
return nil, util.NewAgentError(fmt.Sprintf("'tables' must be a string, got %T", mapParams["tables"]), nil)
}
sdk, err := source.GetLookerSDK(string(accessToken))
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
return nil, util.NewClientServerError("error getting sdk", http.StatusInternalServerError, err)
}
req := v4.RequestConnectionColumns{
ConnectionName: conn,
@@ -150,7 +151,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
}
resp, err := sdk.ConnectionColumns(req, source.LookerApiSettings())
if err != nil {
return nil, fmt.Errorf("error making get_connection_table_columns request: %s", err)
return nil, util.ProcessGeneralError(err)
}
var data []any
for _, t := range resp {

View File

@@ -16,6 +16,7 @@ package lookergetconnectiontables
import (
"context"
"fmt"
"net/http"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
@@ -110,30 +111,30 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
logger, err := util.LoggerFromContext(ctx)
if err != nil {
return nil, fmt.Errorf("unable to get logger from ctx: %s", err)
return nil, util.NewClientServerError("unable to get logger from ctx", http.StatusInternalServerError, err)
}
mapParams := params.AsMap()
conn, ok := mapParams["conn"].(string)
if !ok {
return nil, fmt.Errorf("'conn' must be a string, got %T", mapParams["conn"])
return nil, util.NewAgentError(fmt.Sprintf("'conn' must be a string, got %T", mapParams["conn"]), nil)
}
db, _ := mapParams["db"].(string)
schema, ok := mapParams["schema"].(string)
if !ok {
return nil, fmt.Errorf("'schema' must be a string, got %T", mapParams["schema"])
return nil, util.NewAgentError(fmt.Sprintf("'schema' must be a string, got %T", mapParams["schema"]), nil)
}
sdk, err := source.GetLookerSDK(string(accessToken))
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
return nil, util.NewClientServerError("error getting sdk", http.StatusInternalServerError, err)
}
req := v4.RequestConnectionTables{
ConnectionName: conn,
@@ -144,7 +145,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
}
resp, err := sdk.ConnectionTables(req, source.LookerApiSettings())
if err != nil {
return nil, fmt.Errorf("error making get_connection_tables request: %s", err)
return nil, util.ProcessGeneralError(err)
}
var data []any
for _, s := range resp {

View File

@@ -16,6 +16,7 @@ package lookergetdashboards
import (
"context"
"fmt"
"net/http"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
@@ -116,15 +117,15 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
logger, err := util.LoggerFromContext(ctx)
if err != nil {
return nil, fmt.Errorf("unable to get logger from ctx: %s", err)
return nil, util.NewClientServerError("unable to get logger from ctx", http.StatusInternalServerError, err)
}
paramsMap := params.AsMap()
title := paramsMap["title"].(string)
@@ -142,7 +143,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
sdk, err := source.GetLookerSDK(string(accessToken))
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
return nil, util.NewClientServerError("error getting sdk", http.StatusInternalServerError, err)
}
req := v4.RequestSearchDashboards{
Title: title_ptr,
@@ -153,7 +154,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
logger.ErrorContext(ctx, "Making request %v", req)
resp, err := sdk.SearchDashboards(req, source.LookerApiSettings())
if err != nil {
return nil, fmt.Errorf("error making get_dashboards request: %s", err)
return nil, util.ProcessGeneralError(err)
}
logger.ErrorContext(ctx, "Got response %v", resp)
var data []any

View File

@@ -16,6 +16,7 @@ package lookergetdimensions
import (
"context"
"fmt"
"net/http"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
@@ -109,24 +110,24 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
logger, err := util.LoggerFromContext(ctx)
if err != nil {
return nil, fmt.Errorf("unable to get logger from ctx: %s", err)
return nil, util.NewClientServerError("unable to get logger from ctx", http.StatusInternalServerError, err)
}
model, explore, err := lookercommon.ProcessFieldArgs(ctx, params)
if err != nil {
return nil, fmt.Errorf("error processing model or explore: %w", err)
return nil, util.NewAgentError("error processing model or explore", err)
}
sdk, err := source.GetLookerSDK(string(accessToken))
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
return nil, util.NewClientServerError("error getting sdk", http.StatusInternalServerError, err)
}
fields := lookercommon.DimensionsFields
req := v4.RequestLookmlModelExplore{
@@ -136,16 +137,16 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
}
resp, err := sdk.LookmlModelExplore(req, source.LookerApiSettings())
if err != nil {
return nil, fmt.Errorf("error making get_dimensions request: %w", err)
return nil, util.ProcessGeneralError(err)
}
if err := lookercommon.CheckLookerExploreFields(&resp); err != nil {
return nil, fmt.Errorf("error processing get_dimensions response: %w", err)
return nil, util.ProcessGeneralError(err)
}
data, err := lookercommon.ExtractLookerFieldProperties(ctx, resp.Fields.Dimensions, source.LookerShowHiddenFields())
if err != nil {
return nil, fmt.Errorf("error extracting get_dimensions response: %w", err)
return nil, util.ProcessGeneralError(err)
}
logger.DebugContext(ctx, "data = ", data)

View File

@@ -16,6 +16,7 @@ package lookergetexplores
import (
"context"
"fmt"
"net/http"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
@@ -109,29 +110,29 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
logger, err := util.LoggerFromContext(ctx)
if err != nil {
return nil, fmt.Errorf("unable to get logger from ctx: %s", err)
return nil, util.NewClientServerError("unable to get logger from ctx", http.StatusInternalServerError, err)
}
mapParams := params.AsMap()
model, ok := mapParams["model"].(string)
if !ok {
return nil, fmt.Errorf("'model' must be a string, got %T", mapParams["model"])
return nil, util.NewAgentError(fmt.Sprintf("'model' must be a string, got %T", mapParams["model"]), nil)
}
sdk, err := source.GetLookerSDK(string(accessToken))
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
return nil, util.NewClientServerError("error getting sdk", http.StatusInternalServerError, err)
}
resp, err := sdk.LookmlModel(model, "explores(name,description,label,group_label,hidden)", source.LookerApiSettings())
if err != nil {
return nil, fmt.Errorf("error making get_explores request: %s", err)
return nil, util.ProcessGeneralError(err)
}
var data []any

View File

@@ -17,6 +17,7 @@ import (
"context"
"encoding/json"
"fmt"
"net/http"
"strings"
yaml "github.com/goccy/go-yaml"
@@ -116,20 +117,20 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
logger, err := util.LoggerFromContext(ctx)
if err != nil {
return nil, fmt.Errorf("unable to get logger from ctx: %s", err)
return nil, util.NewClientServerError("unable to get logger from ctx", http.StatusInternalServerError, err)
}
sdk, err := source.GetLookerSDK(string(accessToken))
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
return nil, util.NewClientServerError("error getting sdk", http.StatusInternalServerError, err)
}
pulseTool := &pulseTool{
@@ -140,7 +141,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
paramsMap := params.AsMap()
action, ok := paramsMap["action"].(string)
if !ok {
return nil, fmt.Errorf("action parameter not found")
return nil, util.NewAgentError("action parameter not found", nil)
}
pulseParams := PulseParams{
@@ -149,7 +150,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
result, err := pulseTool.RunPulse(ctx, source, pulseParams)
if err != nil {
return nil, fmt.Errorf("error running pulse: %w", err)
return nil, util.ProcessGeneralError(err)
}
logger.DebugContext(ctx, "result = ", result)

View File

@@ -17,6 +17,7 @@ import (
"context"
"encoding/json"
"fmt"
"net/http"
"regexp"
"strings"
@@ -125,15 +126,15 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
sdk, err := source.GetLookerSDK(string(accessToken))
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
return nil, util.NewClientServerError("error getting sdk", http.StatusInternalServerError, err)
}
paramsMap := params.AsMap()
@@ -154,21 +155,29 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
action, ok := paramsMap["action"].(string)
if !ok {
return nil, fmt.Errorf("action parameter not found")
return nil, util.NewAgentError("action parameter not found", nil)
}
var res []map[string]interface{}
switch action {
case "models":
project, _ := paramsMap["project"].(string)
model, _ := paramsMap["model"].(string)
return vacuumTool.models(ctx, project, model)
res, err = vacuumTool.models(ctx, project, model)
case "explores":
model, _ := paramsMap["model"].(string)
explore, _ := paramsMap["explore"].(string)
return vacuumTool.explores(ctx, model, explore)
res, err = vacuumTool.explores(ctx, model, explore)
default:
return nil, fmt.Errorf("unknown action: %s", action)
return nil, util.NewAgentError(fmt.Sprintf("unknown action: %s", action), nil)
}
if err != nil {
return nil, util.ProcessGeneralError(err)
}
return res, nil
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,6 +17,7 @@ import (
"context"
"encoding/json"
"fmt"
"net/http"
"slices"
yaml "github.com/goccy/go-yaml"
@@ -116,21 +117,21 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
logger, err := util.LoggerFromContext(ctx)
if err != nil {
return nil, fmt.Errorf("unable to get logger from ctx: %s", err)
return nil, util.NewClientServerError("unable to get logger from ctx", http.StatusInternalServerError, err)
}
logger.DebugContext(ctx, "params = ", params)
sdk, err := source.GetLookerSDK(string(accessToken))
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
return nil, util.NewClientServerError("error getting sdk", http.StatusInternalServerError, err)
}
paramsMap := params.AsMap()
@@ -141,19 +142,19 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
mrespFields := "id,personal_folder_id"
mresp, err := sdk.Me(mrespFields, source.LookerApiSettings())
if err != nil {
return nil, fmt.Errorf("error making me request: %s", err)
return nil, util.ProcessGeneralError(err)
}
if folder == "" {
if mresp.PersonalFolderId == nil || *mresp.PersonalFolderId == "" {
return nil, fmt.Errorf("user does not have a personal folder. A folder must be specified")
return nil, util.NewAgentError("user does not have a personal folder. A folder must be specified", nil)
}
folder = *mresp.PersonalFolderId
}
dashs, err := sdk.FolderDashboards(folder, "title", source.LookerApiSettings())
if err != nil {
return nil, fmt.Errorf("error getting existing dashboards in folder: %s", err)
return nil, util.ProcessGeneralError(err)
}
dashTitles := []string{}
@@ -162,7 +163,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
}
if slices.Contains(dashTitles, title) {
lt, _ := json.Marshal(dashTitles)
return nil, fmt.Errorf("title %s already used in folder. Currently used titles are %v. Make the call again with a unique title", title, string(lt))
return nil, util.NewAgentError(fmt.Sprintf("title %s already used in folder. Currently used titles are %v. Make the call again with a unique title", title, string(lt)), nil)
}
wd := v4.WriteDashboard{
@@ -172,7 +173,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
}
resp, err := sdk.CreateDashboard(wd, source.LookerApiSettings())
if err != nil {
return nil, fmt.Errorf("error making create dashboard request: %s", err)
return nil, util.ProcessGeneralError(err)
}
logger.DebugContext(ctx, "resp = %v", resp)

View File

@@ -17,6 +17,7 @@ import (
"context"
"encoding/json"
"fmt"
"net/http"
"slices"
yaml "github.com/goccy/go-yaml"
@@ -123,25 +124,25 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
logger, err := util.LoggerFromContext(ctx)
if err != nil {
return nil, fmt.Errorf("unable to get logger from ctx: %s", err)
return nil, util.NewClientServerError("unable to get logger from ctx", http.StatusInternalServerError, err)
}
logger.DebugContext(ctx, "params = ", params)
wq, err := lookercommon.ProcessQueryArgs(ctx, params)
if err != nil {
return nil, fmt.Errorf("error building query request: %w", err)
return nil, util.NewAgentError("error building query request", err)
}
sdk, err := source.GetLookerSDK(string(accessToken))
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
return nil, util.NewClientServerError("error getting sdk", http.StatusInternalServerError, err)
}
paramsMap := params.AsMap()
title := paramsMap["title"].(string)
@@ -152,19 +153,19 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
mrespFields := "id,personal_folder_id"
mresp, err := sdk.Me(mrespFields, source.LookerApiSettings())
if err != nil {
return nil, fmt.Errorf("error making me request: %s", err)
return nil, util.ProcessGeneralError(err)
}
if folder == "" {
if mresp.PersonalFolderId == nil || *mresp.PersonalFolderId == "" {
return nil, fmt.Errorf("user does not have a personal folder. A folder must be specified")
return nil, util.NewAgentError("user does not have a personal folder. A folder must be specified", nil)
}
folder = *mresp.PersonalFolderId
}
looks, err := sdk.FolderLooks(folder, "title", source.LookerApiSettings())
if err != nil {
return nil, fmt.Errorf("error getting existing looks in folder: %s", err)
return nil, util.ProcessGeneralError(err)
}
lookTitles := []string{}
@@ -173,7 +174,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
}
if slices.Contains(lookTitles, title) {
lt, _ := json.Marshal(lookTitles)
return nil, fmt.Errorf("title %s already used in folder. Currently used titles are %v. Make the call again with a unique title", title, string(lt))
return nil, util.NewAgentError(fmt.Sprintf("title %s already used in folder. Currently used titles are %v. Make the call again with a unique title", title, string(lt)), nil)
}
wq.VisConfig = &visConfig
@@ -181,7 +182,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
qrespFields := "id"
qresp, err := sdk.CreateQuery(*wq, qrespFields, source.LookerApiSettings())
if err != nil {
return nil, fmt.Errorf("error making create query request: %s", err)
return nil, util.ProcessGeneralError(err)
}
wlwq := v4.WriteLookWithQuery{
@@ -193,7 +194,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
}
resp, err := sdk.CreateLook(wlwq, "", source.LookerApiSettings())
if err != nil {
return nil, fmt.Errorf("error making create look request: %s", err)
return nil, util.ProcessGeneralError(err)
}
logger.DebugContext(ctx, "resp = %v", resp)

View File

@@ -17,6 +17,7 @@ import (
"context"
"encoding/json"
"fmt"
"net/http"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
@@ -109,27 +110,27 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
logger, err := util.LoggerFromContext(ctx)
if err != nil {
return nil, fmt.Errorf("unable to get logger from ctx: %s", err)
return nil, util.NewClientServerError("unable to get logger from ctx", http.StatusInternalServerError, err)
}
wq, err := lookercommon.ProcessQueryArgs(ctx, params)
if err != nil {
return nil, fmt.Errorf("error building WriteQuery request: %w", err)
return nil, util.NewAgentError("error building WriteQuery request", err)
}
sdk, err := source.GetLookerSDK(string(accessToken))
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
return nil, util.NewClientServerError("error getting sdk", http.StatusInternalServerError, err)
}
resp, err := lookercommon.RunInlineQuery(ctx, sdk, wq, "json", source.LookerApiSettings())
if err != nil {
return nil, fmt.Errorf("error making query request: %s", err)
return nil, util.ProcessGeneralError(err)
}
logger.DebugContext(ctx, "resp = ", resp)
@@ -137,7 +138,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
var data []any
e := json.Unmarshal([]byte(resp), &data)
if e != nil {
return nil, fmt.Errorf("error unmarshaling query response: %s", e)
return nil, util.NewClientServerError("error unmarshaling query response", http.StatusInternalServerError, e)
}
logger.DebugContext(ctx, "data = ", data)

View File

@@ -16,6 +16,7 @@ package lookerquerysql
import (
"context"
"fmt"
"net/http"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
@@ -108,27 +109,27 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
logger, err := util.LoggerFromContext(ctx)
if err != nil {
return nil, fmt.Errorf("unable to get logger from ctx: %s", err)
return nil, util.NewClientServerError("unable to get logger from ctx", http.StatusInternalServerError, err)
}
wq, err := lookercommon.ProcessQueryArgs(ctx, params)
if err != nil {
return nil, fmt.Errorf("error building query request: %w", err)
return nil, util.NewAgentError("error building query request", err)
}
sdk, err := source.GetLookerSDK(string(accessToken))
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
return nil, util.NewClientServerError("error getting sdk", http.StatusInternalServerError, err)
}
resp, err := lookercommon.RunInlineQuery(ctx, sdk, wq, "sql", source.LookerApiSettings())
if err != nil {
return nil, fmt.Errorf("error making query request: %s", err)
return nil, util.ProcessGeneralError(err)
}
logger.DebugContext(ctx, "resp = ", resp)

View File

@@ -16,6 +16,7 @@ package lookerqueryurl
import (
"context"
"fmt"
"net/http"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
@@ -115,34 +116,37 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
logger, err := util.LoggerFromContext(ctx)
if err != nil {
return nil, fmt.Errorf("unable to get logger from ctx: %s", err)
return nil, util.NewClientServerError("unable to get logger from ctx", http.StatusInternalServerError, err)
}
logger.DebugContext(ctx, "params = ", params)
wq, err := lookercommon.ProcessQueryArgs(ctx, params)
if err != nil {
return nil, fmt.Errorf("error building query request: %w", err)
return nil, util.NewAgentError("error building query request", err)
}
paramsMap := params.AsMap()
visConfig := paramsMap["vis_config"].(map[string]any)
visConfig, ok := paramsMap["vis_config"].(map[string]any)
if !ok {
visConfig = make(map[string]any)
}
wq.VisConfig = &visConfig
sdk, err := source.GetLookerSDK(string(accessToken))
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
return nil, util.NewClientServerError("error getting sdk", http.StatusInternalServerError, err)
}
respFields := "id,slug,share_url,expanded_share_url"
resp, err := sdk.CreateQuery(*wq, respFields, source.LookerApiSettings())
if err != nil {
return nil, fmt.Errorf("error making query request: %s", err)
return nil, util.ProcessGeneralError(err)
}
logger.DebugContext(ctx, "resp = ", resp)

View File

@@ -17,6 +17,7 @@ import (
"context"
"encoding/json"
"fmt"
"net/http"
"sync"
yaml "github.com/goccy/go-yaml"
@@ -114,15 +115,15 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
logger, err := util.LoggerFromContext(ctx)
if err != nil {
return nil, fmt.Errorf("unable to get logger from ctx: %s", err)
return nil, util.NewClientServerError("unable to get logger from ctx", http.StatusInternalServerError, err)
}
logger.DebugContext(ctx, "params = ", params)
paramsMap := params.AsMap()
@@ -131,11 +132,11 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
sdk, err := source.GetLookerSDK(string(accessToken))
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
return nil, util.NewClientServerError("error getting sdk", http.StatusInternalServerError, err)
}
dashboard, err := sdk.Dashboard(dashboard_id, "", source.LookerApiSettings())
if err != nil {
return nil, fmt.Errorf("error getting dashboard: %w", err)
return nil, util.ProcessGeneralError(err)
}
data := make(map[string]any)

View File

@@ -17,6 +17,7 @@ import (
"context"
"encoding/json"
"fmt"
"net/http"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
@@ -115,15 +116,15 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
logger, err := util.LoggerFromContext(ctx)
if err != nil {
return nil, fmt.Errorf("unable to get logger from ctx: %s", err)
return nil, util.NewClientServerError("unable to get logger from ctx", http.StatusInternalServerError, err)
}
logger.DebugContext(ctx, "params = ", params)
paramsMap := params.AsMap()
@@ -134,12 +135,12 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
sdk, err := source.GetLookerSDK(string(accessToken))
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
return nil, util.NewClientServerError("error getting sdk", http.StatusInternalServerError, err)
}
look, err := sdk.Look(look_id, "", source.LookerApiSettings())
if err != nil {
return nil, fmt.Errorf("error getting look definition: %s", err)
return nil, util.ProcessGeneralError(err)
}
wq := v4.WriteQuery{
@@ -155,14 +156,14 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
resp, err := lookercommon.RunInlineQuery(ctx, sdk, &wq, "json", source.LookerApiSettings())
if err != nil {
return nil, fmt.Errorf("error making run_look request: %s", err)
return nil, util.ProcessGeneralError(err)
}
logger.DebugContext(ctx, "resp = ", resp)
var data []any
e := json.Unmarshal([]byte(resp), &data)
if e != nil {
return nil, fmt.Errorf("error Unmarshaling run_look response: %s", e)
return nil, util.NewClientServerError("error Unmarshaling run_look response", http.StatusInternalServerError, e)
}
logger.DebugContext(ctx, "data = ", data)

View File

@@ -16,12 +16,14 @@ package lookerupdateprojectfile
import (
"context"
"fmt"
"net/http"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/tools/looker/lookercommon"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
"github.com/looker-open-source/sdk-codegen/go/rtl"
@@ -111,29 +113,29 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
sdk, err := source.GetLookerSDK(string(accessToken))
if err != nil {
return nil, fmt.Errorf("error getting sdk: %w", err)
return nil, util.NewClientServerError("error getting sdk", http.StatusInternalServerError, err)
}
mapParams := params.AsMap()
projectId, ok := mapParams["project_id"].(string)
if !ok {
return nil, fmt.Errorf("'project_id' must be a string, got %T", mapParams["project_id"])
return nil, util.NewAgentError(fmt.Sprintf("'project_id' must be a string, got %T", mapParams["project_id"]), nil)
}
filePath, ok := mapParams["file_path"].(string)
if !ok {
return nil, fmt.Errorf("'file_path' must be a string, got %T", mapParams["file_path"])
return nil, util.NewAgentError(fmt.Sprintf("'file_path' must be a string, got %T", mapParams["file_path"]), nil)
}
fileContent, ok := mapParams["file_content"].(string)
if !ok {
return nil, fmt.Errorf("'file_content' must be a string, got %T", mapParams["file_content"])
return nil, util.NewAgentError(fmt.Sprintf("'file_content' must be a string, got %T", mapParams["file_content"]), nil)
}
req := lookercommon.FileContent{
@@ -143,7 +145,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
err = lookercommon.UpdateProjectFile(sdk, projectId, req, source.LookerApiSettings())
if err != nil {
return nil, fmt.Errorf("error making update_project_file request: %s", err)
return nil, util.ProcessGeneralError(err)
}
data := make(map[string]any)

View File

@@ -18,11 +18,13 @@ import (
"context"
"database/sql"
"fmt"
"net/http"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
)
@@ -55,7 +57,6 @@ type Config struct {
AuthRequired []string `yaml:"authRequired"`
}
// validate interface
var _ tools.ToolConfig = Config{}
func (cfg Config) ToolConfigType() string {
@@ -73,7 +74,6 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
InputSchema: inputSchema,
}
// finish tool setup
t := Tool{
Config: cfg,
Parameters: params,
@@ -83,7 +83,6 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
return t, nil
}
// validate interface
var _ tools.Tool = Tool{}
type Tool struct {
@@ -97,19 +96,23 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
paramsMap := params.AsMap()
sql, ok := paramsMap["sql"].(string)
sqlStr, ok := paramsMap["sql"].(string)
if !ok {
return nil, fmt.Errorf("unable to get cast %s", paramsMap["sql"])
return nil, util.NewAgentError(fmt.Sprintf("unable to get cast %s", paramsMap["sql"]), nil)
}
return source.RunSQL(ctx, sql, nil)
resp, err := source.RunSQL(ctx, sqlStr, nil)
if err != nil {
return nil, util.ProcessGeneralError(err)
}
return resp, nil
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -18,11 +18,13 @@ import (
"context"
"database/sql"
"fmt"
"net/http"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
)
@@ -58,7 +60,6 @@ type Config struct {
TemplateParameters parameters.Parameters `yaml:"templateParameters"`
}
// validate interface
var _ tools.ToolConfig = Config{}
func (cfg Config) ToolConfigType() string {
@@ -79,7 +80,6 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
InputSchema: paramMcpManifest,
}
// finish tool setup
t := Tool{
Config: cfg,
AllParams: allParameters,
@@ -89,7 +89,6 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
return t, nil
}
// validate interface
var _ tools.Tool = Tool{}
type Tool struct {
@@ -99,25 +98,29 @@ type Tool struct {
mcpManifest tools.McpManifest
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
paramsMap := params.AsMap()
newStatement, err := parameters.ResolveTemplateParams(t.TemplateParameters, t.Statement, paramsMap)
if err != nil {
return nil, fmt.Errorf("unable to extract template params %w", err)
return nil, util.NewAgentError("unable to extract template params", err)
}
newParams, err := parameters.GetParams(t.Parameters, paramsMap)
if err != nil {
return nil, fmt.Errorf("unable to extract standard params %w", err)
return nil, util.NewAgentError("unable to extract standard params", err)
}
sliceParams := newParams.AsSlice()
return source.RunSQL(ctx, newStatement, sliceParams)
resp, err := source.RunSQL(ctx, newStatement, sliceParams)
if err != nil {
return nil, util.ProcessGeneralError(err)
}
return resp, nil
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -16,10 +16,12 @@ package mongodbaggregate
import (
"context"
"fmt"
"net/http"
"slices"
"github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
"go.mongodb.org/mongo-driver/v2/mongo"
@@ -102,18 +104,22 @@ type Tool struct {
mcpManifest tools.McpManifest
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
paramsMap := params.AsMap()
pipelineString, err := parameters.PopulateTemplateWithJSON("MongoDBAggregatePipeline", t.PipelinePayload, paramsMap)
if err != nil {
return nil, fmt.Errorf("error populating pipeline: %s", err)
return nil, util.NewAgentError("error populating pipeline", err)
}
return source.Aggregate(ctx, pipelineString, t.Canonical, t.ReadOnly, t.Database, t.Collection)
resp, err := source.Aggregate(ctx, pipelineString, t.Canonical, t.ReadOnly, t.Database, t.Collection)
if err != nil {
return nil, util.ProcessGeneralError(err)
}
return resp, nil
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -16,10 +16,12 @@ package mongodbdeletemany
import (
"context"
"fmt"
"net/http"
"slices"
"github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
"go.mongodb.org/mongo-driver/v2/mongo"
@@ -106,18 +108,22 @@ type Tool struct {
mcpManifest tools.McpManifest
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
paramsMap := params.AsMap()
filterString, err := parameters.PopulateTemplateWithJSON("MongoDBDeleteManyFilter", t.FilterPayload, paramsMap)
if err != nil {
return nil, fmt.Errorf("error populating filter: %s", err)
return nil, util.NewAgentError("error populating filter", err)
}
return source.DeleteMany(ctx, filterString, t.Database, t.Collection)
resp, err := source.DeleteMany(ctx, filterString, t.Database, t.Collection)
if err != nil {
return nil, util.ProcessGeneralError(err)
}
return resp, nil
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -9,17 +9,19 @@
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// See the License for the language governing permissions and
// limitations under the License.
package mongodbdeleteone
import (
"context"
"fmt"
"net/http"
"slices"
"github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
"go.mongodb.org/mongo-driver/v2/mongo"
@@ -106,19 +108,23 @@ type Tool struct {
mcpManifest tools.McpManifest
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
paramsMap := params.AsMap()
filterString, err := parameters.PopulateTemplateWithJSON("MongoDBDeleteOneFilter", t.FilterPayload, paramsMap)
if err != nil {
return nil, fmt.Errorf("error populating filter: %s", err)
return nil, util.NewAgentError("error populating filter", err)
}
return source.DeleteOne(ctx, filterString, t.Database, t.Collection)
resp, err := source.DeleteOne(ctx, filterString, t.Database, t.Collection)
if err != nil {
return nil, util.ProcessGeneralError(err)
}
return resp, nil
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -16,6 +16,7 @@ package mongodbfind
import (
"context"
"fmt"
"net/http"
"slices"
"github.com/goccy/go-yaml"
@@ -121,7 +122,7 @@ type Tool struct {
func getOptions(ctx context.Context, sortParameters parameters.Parameters, projectPayload string, limit int64, paramsMap map[string]any) (*options.FindOptionsBuilder, error) {
logger, err := util.LoggerFromContext(ctx)
if err != nil {
panic(err)
return nil, err
}
opts := options.Find()
@@ -157,22 +158,26 @@ func getOptions(ctx context.Context, sortParameters parameters.Parameters, proje
return opts, nil
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
paramsMap := params.AsMap()
filterString, err := parameters.PopulateTemplateWithJSON("MongoDBFindFilterString", t.FilterPayload, paramsMap)
if err != nil {
return nil, fmt.Errorf("error populating filter: %s", err)
return nil, util.NewAgentError("error populating filter", err)
}
opts, err := getOptions(ctx, t.SortParams, t.ProjectPayload, t.Limit, paramsMap)
if err != nil {
return nil, fmt.Errorf("error populating options: %s", err)
return nil, util.NewAgentError("error populating options", err)
}
return source.Find(ctx, filterString, t.Database, t.Collection, opts)
resp, err := source.Find(ctx, filterString, t.Database, t.Collection, opts)
if err != nil {
return nil, util.ProcessGeneralError(err)
}
return resp, nil
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -16,10 +16,12 @@ package mongodbfindone
import (
"context"
"fmt"
"net/http"
"slices"
"github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
"go.mongodb.org/mongo-driver/v2/bson"
"go.mongodb.org/mongo-driver/v2/mongo"
@@ -110,32 +112,36 @@ type Tool struct {
mcpManifest tools.McpManifest
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
paramsMap := params.AsMap()
filterString, err := parameters.PopulateTemplateWithJSON("MongoDBFindOneFilterString", t.FilterPayload, paramsMap)
if err != nil {
return nil, fmt.Errorf("error populating filter: %s", err)
return nil, util.NewAgentError("error populating filter", err)
}
opts := options.FindOne()
if len(t.ProjectPayload) > 0 {
result, err := parameters.PopulateTemplateWithJSON("MongoDBFindOneProjectString", t.ProjectPayload, paramsMap)
if err != nil {
return nil, fmt.Errorf("error populating project payload: %s", err)
return nil, util.NewAgentError("error populating project payload", err)
}
var projection any
err = bson.UnmarshalExtJSON([]byte(result), false, &projection)
if err != nil {
return nil, fmt.Errorf("error unmarshalling projection: %s", err)
return nil, util.NewAgentError("error unmarshalling projection", err)
}
opts = opts.SetProjection(projection)
}
return source.FindOne(ctx, filterString, t.Database, t.Collection, opts)
resp, err := source.FindOne(ctx, filterString, t.Database, t.Collection, opts)
if err != nil {
return nil, util.ProcessGeneralError(err)
}
return resp, nil
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -15,13 +15,14 @@ package mongodbinsertmany
import (
"context"
"errors"
"fmt"
"net/http"
"github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
"go.mongodb.org/mongo-driver/v2/mongo"
)
@@ -100,23 +101,27 @@ type Tool struct {
mcpManifest tools.McpManifest
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
if len(params) == 0 {
return nil, errors.New("no input found")
return nil, util.NewAgentError("no input found", nil)
}
paramsMap := params.AsMap()
jsonData, ok := paramsMap[paramDataKey].(string)
if !ok {
return nil, errors.New("no input found")
return nil, util.NewAgentError("no input found or invalid type for data", nil)
}
return source.InsertMany(ctx, jsonData, t.Canonical, t.Database, t.Collection)
resp, err := source.InsertMany(ctx, jsonData, t.Canonical, t.Database, t.Collection)
if err != nil {
return nil, util.ProcessGeneralError(err)
}
return resp, nil
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -15,13 +15,14 @@ package mongodbinsertone
import (
"context"
"errors"
"fmt"
"net/http"
"github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
"go.mongodb.org/mongo-driver/v2/mongo"
)
@@ -101,20 +102,24 @@ type Tool struct {
mcpManifest tools.McpManifest
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
if len(params) == 0 {
return nil, errors.New("no input found")
return nil, util.NewAgentError("no input found", nil)
}
// use the first, assume it's a string
jsonData, ok := params[0].Value.(string)
if !ok {
return nil, errors.New("no input found")
return nil, util.NewAgentError("no input found or invalid type for data", nil)
}
return source.InsertOne(ctx, jsonData, t.Canonical, t.Database, t.Collection)
resp, err := source.InsertOne(ctx, jsonData, t.Canonical, t.Database, t.Collection)
if err != nil {
return nil, util.ProcessGeneralError(err)
}
return resp, nil
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -16,12 +16,14 @@ package mongodbupdatemany
import (
"context"
"fmt"
"net/http"
"slices"
"github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
"go.mongodb.org/mongo-driver/v2/mongo"
)
@@ -109,22 +111,26 @@ type Tool struct {
mcpManifest tools.McpManifest
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
paramsMap := params.AsMap()
filterString, err := parameters.PopulateTemplateWithJSON("MongoDBUpdateManyFilter", t.FilterPayload, paramsMap)
if err != nil {
return nil, fmt.Errorf("error populating filter: %s", err)
return nil, util.NewAgentError("error populating filter", err)
}
updateString, err := parameters.PopulateTemplateWithJSON("MongoDBUpdateMany", t.UpdatePayload, paramsMap)
if err != nil {
return nil, fmt.Errorf("unable to get update: %w", err)
return nil, util.NewAgentError("unable to get update", err)
}
return source.UpdateMany(ctx, filterString, t.Canonical, updateString, t.Database, t.Collection, t.Upsert)
resp, err := source.UpdateMany(ctx, filterString, t.Canonical, updateString, t.Database, t.Collection, t.Upsert)
if err != nil {
return nil, util.ProcessGeneralError(err)
}
return resp, nil
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -16,12 +16,14 @@ package mongodbupdateone
import (
"context"
"fmt"
"net/http"
"slices"
"github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
"go.mongodb.org/mongo-driver/v2/mongo"
)
@@ -110,22 +112,26 @@ type Tool struct {
mcpManifest tools.McpManifest
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
paramsMap := params.AsMap()
filterString, err := parameters.PopulateTemplateWithJSON("MongoDBUpdateOneFilter", t.FilterPayload, paramsMap)
if err != nil {
return nil, fmt.Errorf("error populating filter: %s", err)
return nil, util.NewAgentError("error populating filter", err)
}
updateString, err := parameters.PopulateTemplateWithJSON("MongoDBUpdateOne", t.UpdatePayload, paramsMap)
if err != nil {
return nil, fmt.Errorf("unable to get update: %w", err)
return nil, util.NewAgentError("unable to get update", err)
}
return source.UpdateOne(ctx, filterString, t.Canonical, updateString, t.Database, t.Collection, t.Upsert)
resp, err := source.UpdateOne(ctx, filterString, t.Canonical, updateString, t.Database, t.Collection, t.Upsert)
if err != nil {
return nil, util.ProcessGeneralError(err)
}
return resp, nil
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -18,6 +18,7 @@ import (
"context"
"database/sql"
"fmt"
"net/http"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
@@ -89,25 +90,29 @@ type Tool struct {
mcpManifest tools.McpManifest
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
paramsMap := params.AsMap()
sql, ok := paramsMap["sql"].(string)
sqlStr, ok := paramsMap["sql"].(string)
if !ok {
return nil, fmt.Errorf("unable to get cast %s", paramsMap["sql"])
return nil, util.NewAgentError(fmt.Sprintf("unable to get cast %s", paramsMap["sql"]), nil)
}
// Log the query executed for debugging.
logger, err := util.LoggerFromContext(ctx)
if err != nil {
return nil, fmt.Errorf("error getting logger: %s", err)
return nil, util.NewClientServerError("error getting logger", http.StatusInternalServerError, err)
}
logger.DebugContext(ctx, fmt.Sprintf("executing `%s` tool query: %s", resourceType, sql))
return source.RunSQL(ctx, sql, nil)
logger.DebugContext(ctx, fmt.Sprintf("executing `%s` tool query: %s", resourceType, sqlStr))
resp, err := source.RunSQL(ctx, sqlStr, nil)
if err != nil {
return nil, util.ProcessGeneralError(err)
}
return resp, nil
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -18,263 +18,265 @@ import (
"context"
"database/sql"
"fmt"
"net/http"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
)
const resourceType string = "mssql-list-tables"
const listTablesStatement = `
WITH table_info AS (
SELECT
t.object_id AS table_oid,
s.name AS schema_name,
t.name AS table_name,
dp.name AS table_owner, -- Schema's owner principal name
CAST(ep.value AS NVARCHAR(MAX)) AS table_comment, -- Cast for JSON compatibility
CASE
WHEN EXISTS ( -- Check if the table has more than one partition for any of its indexes or heap
SELECT 1 FROM sys.partitions p
WHERE p.object_id = t.object_id AND p.partition_number > 1
) THEN 'PARTITIONED TABLE'
ELSE 'TABLE'
END AS object_type_detail
FROM
sys.tables t
INNER JOIN
sys.schemas s ON t.schema_id = s.schema_id
LEFT JOIN
sys.database_principals dp ON s.principal_id = dp.principal_id
LEFT JOIN
sys.extended_properties ep ON ep.major_id = t.object_id AND ep.minor_id = 0 AND ep.class = 1 AND ep.name = 'MS_Description'
WHERE
t.type = 'U' -- User tables
AND s.name NOT IN ('sys', 'INFORMATION_SCHEMA', 'guest', 'db_owner', 'db_accessadmin', 'db_backupoperator', 'db_datareader', 'db_datawriter', 'db_ddladmin', 'db_denydatareader', 'db_denydatawriter', 'db_securityadmin')
AND (@table_names IS NULL OR LTRIM(RTRIM(@table_names)) = '' OR t.name IN (SELECT LTRIM(RTRIM(value)) FROM STRING_SPLIT(@table_names, ',')))
),
columns_info AS (
SELECT
c.object_id AS table_oid,
c.name AS column_name,
CONCAT(
UPPER(TY.name), -- Base type name
CASE
WHEN TY.name IN ('char', 'varchar', 'nchar', 'nvarchar', 'binary', 'varbinary') THEN
CONCAT('(', IIF(c.max_length = -1, 'MAX', CAST(c.max_length / CASE WHEN TY.name IN ('nchar', 'nvarchar') THEN 2 ELSE 1 END AS VARCHAR(10))), ')')
WHEN TY.name IN ('decimal', 'numeric') THEN
CONCAT('(', c.precision, ',', c.scale, ')')
WHEN TY.name IN ('datetime2', 'datetimeoffset', 'time') THEN
CONCAT('(', c.scale, ')')
ELSE ''
END
) AS data_type,
c.column_id AS column_ordinal_position,
IIF(c.is_nullable = 0, CAST(1 AS BIT), CAST(0 AS BIT)) AS is_not_nullable,
dc.definition AS column_default,
CAST(epc.value AS NVARCHAR(MAX)) AS column_comment
FROM
sys.columns c
JOIN
table_info ti ON c.object_id = ti.table_oid
JOIN
sys.types TY ON c.user_type_id = TY.user_type_id AND TY.is_user_defined = 0 -- Ensure we get base types
LEFT JOIN
sys.default_constraints dc ON c.object_id = dc.parent_object_id AND c.column_id = dc.parent_column_id
LEFT JOIN
sys.extended_properties epc ON epc.major_id = c.object_id AND epc.minor_id = c.column_id AND epc.class = 1 AND epc.name = 'MS_Description'
),
constraints_info AS (
-- Primary Keys & Unique Constraints
SELECT
kc.parent_object_id AS table_oid,
kc.name AS constraint_name,
REPLACE(kc.type_desc, '_CONSTRAINT', '') AS constraint_type, -- 'PRIMARY_KEY', 'UNIQUE'
STUFF((SELECT ', ' + col.name
FROM sys.index_columns ic
JOIN sys.columns col ON ic.object_id = col.object_id AND ic.column_id = col.column_id
WHERE ic.object_id = kc.parent_object_id AND ic.index_id = kc.unique_index_id
ORDER BY ic.key_ordinal
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 2, '') AS constraint_columns,
NULL AS foreign_key_referenced_table,
NULL AS foreign_key_referenced_columns,
CASE kc.type
WHEN 'PK' THEN 'PRIMARY KEY (' + STUFF((SELECT ', ' + col.name FROM sys.index_columns ic JOIN sys.columns col ON ic.object_id = col.object_id AND ic.column_id = col.column_id WHERE ic.object_id = kc.parent_object_id AND ic.index_id = kc.unique_index_id ORDER BY ic.key_ordinal FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 2, '') + ')'
WHEN 'UQ' THEN 'UNIQUE (' + STUFF((SELECT ', ' + col.name FROM sys.index_columns ic JOIN sys.columns col ON ic.object_id = col.object_id AND ic.column_id = col.column_id WHERE ic.object_id = kc.parent_object_id AND ic.index_id = kc.unique_index_id ORDER BY ic.key_ordinal FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 2, '') + ')'
END AS constraint_definition
FROM sys.key_constraints kc
JOIN table_info ti ON kc.parent_object_id = ti.table_oid
UNION ALL
-- Foreign Keys
SELECT
fk.parent_object_id AS table_oid,
fk.name AS constraint_name,
'FOREIGN KEY' AS constraint_type,
STUFF((SELECT ', ' + pc.name
FROM sys.foreign_key_columns fkc
JOIN sys.columns pc ON fkc.parent_object_id = pc.object_id AND fkc.parent_column_id = pc.column_id
WHERE fkc.constraint_object_id = fk.object_id
ORDER BY fkc.constraint_column_id
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 2, '') AS constraint_columns,
SCHEMA_NAME(rt.schema_id) + '.' + OBJECT_NAME(fk.referenced_object_id) AS foreign_key_referenced_table,
STUFF((SELECT ', ' + rc.name
FROM sys.foreign_key_columns fkc
JOIN sys.columns rc ON fkc.referenced_object_id = rc.object_id AND fkc.referenced_column_id = rc.column_id
WHERE fkc.constraint_object_id = fk.object_id
ORDER BY fkc.constraint_column_id
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 2, '') AS foreign_key_referenced_columns,
OBJECT_DEFINITION(fk.object_id) AS constraint_definition
FROM sys.foreign_keys fk
JOIN sys.tables rt ON fk.referenced_object_id = rt.object_id
JOIN table_info ti ON fk.parent_object_id = ti.table_oid
UNION ALL
-- Check Constraints
SELECT
cc.parent_object_id AS table_oid,
cc.name AS constraint_name,
'CHECK' AS constraint_type,
NULL AS constraint_columns, -- Definition includes column context
NULL AS foreign_key_referenced_table,
NULL AS foreign_key_referenced_columns,
cc.definition AS constraint_definition
FROM sys.check_constraints cc
JOIN table_info ti ON cc.parent_object_id = ti.table_oid
),
indexes_info AS (
SELECT
i.object_id AS table_oid,
i.name AS index_name,
i.type_desc AS index_method, -- CLUSTERED, NONCLUSTERED, XML, etc.
i.is_unique,
i.is_primary_key AS is_primary,
STUFF((SELECT ', ' + c.name
FROM sys.index_columns ic
JOIN sys.columns c ON i.object_id = c.object_id AND ic.column_id = c.column_id
WHERE ic.object_id = i.object_id AND ic.index_id = i.index_id AND ic.is_included_column = 0
ORDER BY ic.key_ordinal
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 2, '') AS index_columns,
(
'COLUMNS: (' + ISNULL(STUFF((SELECT ', ' + c.name + CASE WHEN ic.is_descending_key = 1 THEN ' DESC' ELSE '' END
FROM sys.index_columns ic
JOIN sys.columns c ON i.object_id = c.object_id AND ic.column_id = c.column_id
WHERE ic.object_id = i.object_id AND ic.index_id = i.index_id AND ic.is_included_column = 0
ORDER BY ic.key_ordinal FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 2, ''), 'N/A') + ')' +
ISNULL(CHAR(13)+CHAR(10) + 'INCLUDE: (' + STUFF((SELECT ', ' + c.name
FROM sys.index_columns ic
JOIN sys.columns c ON i.object_id = c.object_id AND ic.column_id = c.column_id
WHERE ic.object_id = i.object_id AND ic.index_id = i.index_id AND ic.is_included_column = 1
ORDER BY ic.index_column_id FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 2, '') + ')', '') +
ISNULL(CHAR(13)+CHAR(10) + 'FILTER: (' + i.filter_definition + ')', '')
) AS index_definition_details
FROM
sys.indexes i
JOIN
table_info ti ON i.object_id = ti.table_oid
WHERE i.type <> 0 -- Exclude Heaps
AND i.name IS NOT NULL -- Exclude unnamed heap indexes; named indexes (PKs are often named) are preferred.
),
triggers_info AS (
SELECT
tr.parent_id AS table_oid,
tr.name AS trigger_name,
OBJECT_DEFINITION(tr.object_id) AS trigger_definition,
CASE tr.is_disabled WHEN 0 THEN 'ENABLED' ELSE 'DISABLED' END AS trigger_enabled_state
FROM
sys.triggers tr
JOIN
table_info ti ON tr.parent_id = ti.table_oid
WHERE
tr.is_ms_shipped = 0
AND tr.parent_class_desc = 'OBJECT_OR_COLUMN' -- DML Triggers on tables/views
)
SELECT
ti.schema_name,
ti.table_name AS object_name,
CASE
WHEN @output_format = 'simple' THEN
(SELECT ti.table_name AS name FOR JSON PATH, WITHOUT_ARRAY_WRAPPER)
ELSE
(
SELECT
ti.schema_name AS schema_name,
ti.table_name AS object_name,
ti.object_type_detail AS object_type,
ti.table_owner AS owner,
ti.table_comment AS comment,
JSON_QUERY(ISNULL((
SELECT
ci.column_name,
ci.data_type,
ci.column_ordinal_position,
ci.is_not_nullable,
ci.column_default,
ci.column_comment
FROM columns_info ci
WHERE ci.table_oid = ti.table_oid
ORDER BY ci.column_ordinal_position
FOR JSON PATH
), '[]')) AS columns,
JSON_QUERY(ISNULL((
SELECT
cons.constraint_name,
cons.constraint_type,
cons.constraint_definition,
JSON_QUERY(
CASE
WHEN cons.constraint_columns IS NOT NULL AND LTRIM(RTRIM(cons.constraint_columns)) <> ''
THEN '[' + (SELECT STRING_AGG('"' + LTRIM(RTRIM(value)) + '"', ',') FROM STRING_SPLIT(cons.constraint_columns, ',')) + ']'
ELSE '[]'
END
) AS constraint_columns,
cons.foreign_key_referenced_table,
JSON_QUERY(
CASE
WHEN cons.foreign_key_referenced_columns IS NOT NULL AND LTRIM(RTRIM(cons.foreign_key_referenced_columns)) <> ''
THEN '[' + (SELECT STRING_AGG('"' + LTRIM(RTRIM(value)) + '"', ',') FROM STRING_SPLIT(cons.foreign_key_referenced_columns, ',')) + ']'
ELSE '[]'
END
) AS foreign_key_referenced_columns
FROM constraints_info cons
WHERE cons.table_oid = ti.table_oid
FOR JSON PATH
), '[]')) AS constraints,
JSON_QUERY(ISNULL((
SELECT
ii.index_name,
ii.index_definition_details AS index_definition,
ii.is_unique,
ii.is_primary,
ii.index_method,
JSON_QUERY(
CASE
WHEN ii.index_columns IS NOT NULL AND LTRIM(RTRIM(ii.index_columns)) <> ''
THEN '[' + (SELECT STRING_AGG('"' + LTRIM(RTRIM(value)) + '"', ',') FROM STRING_SPLIT(ii.index_columns, ',')) + ']'
ELSE '[]'
END
) AS index_columns
FROM indexes_info ii
WHERE ii.table_oid = ti.table_oid
FOR JSON PATH
), '[]')) AS indexes,
JSON_QUERY(ISNULL((
SELECT
tri.trigger_name,
tri.trigger_definition,
tri.trigger_enabled_state
FROM triggers_info tri
WHERE tri.table_oid = ti.table_oid
FOR JSON PATH
), '[]')) AS triggers
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER -- Creates a single JSON object for this table's details
)
END AS object_details
FROM
table_info ti
ORDER BY
ti.schema_name, ti.table_name;
WITH table_info AS (
SELECT
t.object_id AS table_oid,
s.name AS schema_name,
t.name AS table_name,
dp.name AS table_owner, -- Schema's owner principal name
CAST(ep.value AS NVARCHAR(MAX)) AS table_comment, -- Cast for JSON compatibility
CASE
WHEN EXISTS ( -- Check if the table has more than one partition for any of its indexes or heap
SELECT 1 FROM sys.partitions p
WHERE p.object_id = t.object_id AND p.partition_number > 1
) THEN 'PARTITIONED TABLE'
ELSE 'TABLE'
END AS object_type_detail
FROM
sys.tables t
INNER JOIN
sys.schemas s ON t.schema_id = s.schema_id
LEFT JOIN
sys.database_principals dp ON s.principal_id = dp.principal_id
LEFT JOIN
sys.extended_properties ep ON ep.major_id = t.object_id AND ep.minor_id = 0 AND ep.class = 1 AND ep.name = 'MS_Description'
WHERE
t.type = 'U' -- User tables
AND s.name NOT IN ('sys', 'INFORMATION_SCHEMA', 'guest', 'db_owner', 'db_accessadmin', 'db_backupoperator', 'db_datareader', 'db_datawriter', 'db_ddladmin', 'db_denydatareader', 'db_denydatawriter', 'db_securityadmin')
AND (@table_names IS NULL OR LTRIM(RTRIM(@table_names)) = '' OR t.name IN (SELECT LTRIM(RTRIM(value)) FROM STRING_SPLIT(@table_names, ',')))
),
columns_info AS (
SELECT
c.object_id AS table_oid,
c.name AS column_name,
CONCAT(
UPPER(TY.name), -- Base type name
CASE
WHEN TY.name IN ('char', 'varchar', 'nchar', 'nvarchar', 'binary', 'varbinary') THEN
CONCAT('(', IIF(c.max_length = -1, 'MAX', CAST(c.max_length / CASE WHEN TY.name IN ('nchar', 'nvarchar') THEN 2 ELSE 1 END AS VARCHAR(10))), ')')
WHEN TY.name IN ('decimal', 'numeric') THEN
CONCAT('(', c.precision, ',', c.scale, ')')
WHEN TY.name IN ('datetime2', 'datetimeoffset', 'time') THEN
CONCAT('(', c.scale, ')')
ELSE ''
END
) AS data_type,
c.column_id AS column_ordinal_position,
IIF(c.is_nullable = 0, CAST(1 AS BIT), CAST(0 AS BIT)) AS is_not_nullable,
dc.definition AS column_default,
CAST(epc.value AS NVARCHAR(MAX)) AS column_comment
FROM
sys.columns c
JOIN
table_info ti ON c.object_id = ti.table_oid
JOIN
sys.types TY ON c.user_type_id = TY.user_type_id AND TY.is_user_defined = 0 -- Ensure we get base types
LEFT JOIN
sys.default_constraints dc ON c.object_id = dc.parent_object_id AND c.column_id = dc.parent_column_id
LEFT JOIN
sys.extended_properties epc ON epc.major_id = c.object_id AND epc.minor_id = c.column_id AND epc.class = 1 AND epc.name = 'MS_Description'
),
constraints_info AS (
-- Primary Keys & Unique Constraints
SELECT
kc.parent_object_id AS table_oid,
kc.name AS constraint_name,
REPLACE(kc.type_desc, '_CONSTRAINT', '') AS constraint_type, -- 'PRIMARY_KEY', 'UNIQUE'
STUFF((SELECT ', ' + col.name
FROM sys.index_columns ic
JOIN sys.columns col ON ic.object_id = col.object_id AND ic.column_id = col.column_id
WHERE ic.object_id = kc.parent_object_id AND ic.index_id = kc.unique_index_id
ORDER BY ic.key_ordinal
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 2, '') AS constraint_columns,
NULL AS foreign_key_referenced_table,
NULL AS foreign_key_referenced_columns,
CASE kc.type
WHEN 'PK' THEN 'PRIMARY KEY (' + STUFF((SELECT ', ' + col.name FROM sys.index_columns ic JOIN sys.columns col ON ic.object_id = col.object_id AND ic.column_id = col.column_id WHERE ic.object_id = kc.parent_object_id AND ic.index_id = kc.unique_index_id ORDER BY ic.key_ordinal FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 2, '') + ')'
WHEN 'UQ' THEN 'UNIQUE (' + STUFF((SELECT ', ' + col.name FROM sys.index_columns ic JOIN sys.columns col ON ic.object_id = col.object_id AND ic.column_id = col.column_id WHERE ic.object_id = kc.parent_object_id AND ic.index_id = kc.unique_index_id ORDER BY ic.key_ordinal FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 2, '') + ')'
END AS constraint_definition
FROM sys.key_constraints kc
JOIN table_info ti ON kc.parent_object_id = ti.table_oid
UNION ALL
-- Foreign Keys
SELECT
fk.parent_object_id AS table_oid,
fk.name AS constraint_name,
'FOREIGN KEY' AS constraint_type,
STUFF((SELECT ', ' + pc.name
FROM sys.foreign_key_columns fkc
JOIN sys.columns pc ON fkc.parent_object_id = pc.object_id AND fkc.parent_column_id = pc.column_id
WHERE fkc.constraint_object_id = fk.object_id
ORDER BY fkc.constraint_column_id
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 2, '') AS constraint_columns,
SCHEMA_NAME(rt.schema_id) + '.' + OBJECT_NAME(fk.referenced_object_id) AS foreign_key_referenced_table,
STUFF((SELECT ', ' + rc.name
FROM sys.foreign_key_columns fkc
JOIN sys.columns rc ON fkc.referenced_object_id = rc.object_id AND fkc.referenced_column_id = rc.column_id
WHERE fkc.constraint_object_id = fk.object_id
ORDER BY fkc.constraint_column_id
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 2, '') AS foreign_key_referenced_columns,
OBJECT_DEFINITION(fk.object_id) AS constraint_definition
FROM sys.foreign_keys fk
JOIN sys.tables rt ON fk.referenced_object_id = rt.object_id
JOIN table_info ti ON fk.parent_object_id = ti.table_oid
UNION ALL
-- Check Constraints
SELECT
cc.parent_object_id AS table_oid,
cc.name AS constraint_name,
'CHECK' AS constraint_type,
NULL AS constraint_columns, -- Definition includes column context
NULL AS foreign_key_referenced_table,
NULL AS foreign_key_referenced_columns,
cc.definition AS constraint_definition
FROM sys.check_constraints cc
JOIN table_info ti ON cc.parent_object_id = ti.table_oid
),
indexes_info AS (
SELECT
i.object_id AS table_oid,
i.name AS index_name,
i.type_desc AS index_method, -- CLUSTERED, NONCLUSTERED, XML, etc.
i.is_unique,
i.is_primary_key AS is_primary,
STUFF((SELECT ', ' + c.name
FROM sys.index_columns ic
JOIN sys.columns c ON i.object_id = c.object_id AND ic.column_id = c.column_id
WHERE ic.object_id = i.object_id AND ic.index_id = i.index_id AND ic.is_included_column = 0
ORDER BY ic.key_ordinal
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 2, '') AS index_columns,
(
'COLUMNS: (' + ISNULL(STUFF((SELECT ', ' + c.name + CASE WHEN ic.is_descending_key = 1 THEN ' DESC' ELSE '' END
FROM sys.index_columns ic
JOIN sys.columns c ON i.object_id = c.object_id AND ic.column_id = c.column_id
WHERE ic.object_id = i.object_id AND ic.index_id = i.index_id AND ic.is_included_column = 0
ORDER BY ic.key_ordinal FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 2, ''), 'N/A') + ')' +
ISNULL(CHAR(13)+CHAR(10) + 'INCLUDE: (' + STUFF((SELECT ', ' + c.name
FROM sys.index_columns ic
JOIN sys.columns c ON i.object_id = c.object_id AND ic.column_id = c.column_id
WHERE ic.object_id = i.object_id AND ic.index_id = i.index_id AND ic.is_included_column = 1
ORDER BY ic.index_column_id FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 2, '') + ')', '') +
ISNULL(CHAR(13)+CHAR(10) + 'FILTER: (' + i.filter_definition + ')', '')
) AS index_definition_details
FROM
sys.indexes i
JOIN
table_info ti ON i.object_id = ti.table_oid
WHERE i.type <> 0 -- Exclude Heaps
AND i.name IS NOT NULL -- Exclude unnamed heap indexes; named indexes (PKs are often named) are preferred.
),
triggers_info AS (
SELECT
tr.parent_id AS table_oid,
tr.name AS trigger_name,
OBJECT_DEFINITION(tr.object_id) AS trigger_definition,
CASE tr.is_disabled WHEN 0 THEN 'ENABLED' ELSE 'DISABLED' END AS trigger_enabled_state
FROM
sys.triggers tr
JOIN
table_info ti ON tr.parent_id = ti.table_oid
WHERE
tr.is_ms_shipped = 0
AND tr.parent_class_desc = 'OBJECT_OR_COLUMN' -- DML Triggers on tables/views
)
SELECT
ti.schema_name,
ti.table_name AS object_name,
CASE
WHEN @output_format = 'simple' THEN
(SELECT ti.table_name AS name FOR JSON PATH, WITHOUT_ARRAY_WRAPPER)
ELSE
(
SELECT
ti.schema_name AS schema_name,
ti.table_name AS object_name,
ti.object_type_detail AS object_type,
ti.table_owner AS owner,
ti.table_comment AS comment,
JSON_QUERY(ISNULL((
SELECT
ci.column_name,
ci.data_type,
ci.column_ordinal_position,
ci.is_not_nullable,
ci.column_default,
ci.column_comment
FROM columns_info ci
WHERE ci.table_oid = ti.table_oid
ORDER BY ci.column_ordinal_position
FOR JSON PATH
), '[]')) AS columns,
JSON_QUERY(ISNULL((
SELECT
cons.constraint_name,
cons.constraint_type,
cons.constraint_definition,
JSON_QUERY(
CASE
WHEN cons.constraint_columns IS NOT NULL AND LTRIM(RTRIM(cons.constraint_columns)) <> ''
THEN '[' + (SELECT STRING_AGG('"' + LTRIM(RTRIM(value)) + '"', ',') FROM STRING_SPLIT(cons.constraint_columns, ',')) + ']'
ELSE '[]'
END
) AS constraint_columns,
cons.foreign_key_referenced_table,
JSON_QUERY(
CASE
WHEN cons.foreign_key_referenced_columns IS NOT NULL AND LTRIM(RTRIM(cons.foreign_key_referenced_columns)) <> ''
THEN '[' + (SELECT STRING_AGG('"' + LTRIM(RTRIM(value)) + '"', ',') FROM STRING_SPLIT(cons.foreign_key_referenced_columns, ',')) + ']'
ELSE '[]'
END
) AS foreign_key_referenced_columns
FROM constraints_info cons
WHERE cons.table_oid = ti.table_oid
FOR JSON PATH
), '[]')) AS constraints,
JSON_QUERY(ISNULL((
SELECT
ii.index_name,
ii.index_definition_details AS index_definition,
ii.is_unique,
ii.is_primary,
ii.index_method,
JSON_QUERY(
CASE
WHEN ii.index_columns IS NOT NULL AND LTRIM(RTRIM(ii.index_columns)) <> ''
THEN '[' + (SELECT STRING_AGG('"' + LTRIM(RTRIM(value)) + '"', ',') FROM STRING_SPLIT(ii.index_columns, ',')) + ']'
ELSE '[]'
END
) AS index_columns
FROM indexes_info ii
WHERE ii.table_oid = ti.table_oid
FOR JSON PATH
), '[]')) AS indexes,
JSON_QUERY(ISNULL((
SELECT
tri.trigger_name,
tri.trigger_definition,
tri.trigger_enabled_state
FROM triggers_info tri
WHERE tri.table_oid = ti.table_oid
FOR JSON PATH
), '[]')) AS triggers
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER -- Creates a single JSON object for this table's details
)
END AS object_details
FROM
table_info ti
ORDER BY
ti.schema_name, ti.table_name;
`
func init() {
@@ -339,17 +341,17 @@ type Tool struct {
mcpManifest tools.McpManifest
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
paramsMap := params.AsMap()
outputFormat, _ := paramsMap["output_format"].(string)
if outputFormat != "simple" && outputFormat != "detailed" {
return nil, fmt.Errorf("invalid value for output_format: must be 'simple' or 'detailed', but got %q", outputFormat)
return nil, util.NewAgentError(fmt.Sprintf("invalid value for output_format: must be 'simple' or 'detailed', but got %q", outputFormat), nil)
}
namedArgs := []any{
@@ -358,14 +360,14 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
}
resp, err := source.RunSQL(ctx, listTablesStatement, namedArgs)
if err != nil {
return nil, err
return nil, util.ProcessGeneralError(err)
}
// if there's no results, return empty list instead of null
resSlice, ok := resp.([]any)
if !ok || len(resSlice) == 0 {
return []any{}, nil
}
return resp, err
return resp, nil
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -18,12 +18,14 @@ import (
"context"
"database/sql"
"fmt"
"net/http"
"strings"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
)
@@ -94,21 +96,21 @@ type Tool struct {
mcpManifest tools.McpManifest
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
paramsMap := params.AsMap()
newStatement, err := parameters.ResolveTemplateParams(t.TemplateParameters, t.Statement, paramsMap)
if err != nil {
return nil, fmt.Errorf("unable to extract template params %w", err)
return nil, util.NewAgentError("unable to extract template params", err)
}
newParams, err := parameters.GetParams(t.Parameters, paramsMap)
if err != nil {
return nil, fmt.Errorf("unable to extract standard params %w", err)
return nil, util.NewAgentError("unable to extract standard params", err)
}
namedArgs := make([]any, 0, len(newParams))
@@ -123,7 +125,11 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
namedArgs = append(namedArgs, value)
}
}
return source.RunSQL(ctx, newStatement, namedArgs)
resp, err := source.RunSQL(ctx, newStatement, namedArgs)
if err != nil {
return nil, util.ProcessGeneralError(err)
}
return resp, nil
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -18,6 +18,7 @@ import (
"context"
"database/sql"
"fmt"
"net/http"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
@@ -89,25 +90,29 @@ type Tool struct {
mcpManifest tools.McpManifest
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
paramsMap := params.AsMap()
sql, ok := paramsMap["sql"].(string)
sqlStr, ok := paramsMap["sql"].(string)
if !ok {
return nil, fmt.Errorf("unable to get cast %s", paramsMap["sql"])
return nil, util.NewAgentError(fmt.Sprintf("unable to get cast %s", paramsMap["sql"]), nil)
}
// Log the query executed for debugging.
logger, err := util.LoggerFromContext(ctx)
if err != nil {
return nil, fmt.Errorf("error getting logger: %s", err)
return nil, util.NewClientServerError("error getting logger", http.StatusInternalServerError, err)
}
logger.DebugContext(ctx, fmt.Sprintf("executing `%s` tool query: %s", resourceType, sql))
return source.RunSQL(ctx, sql, nil)
logger.DebugContext(ctx, fmt.Sprintf("executing `%s` tool query: %s", resourceType, sqlStr))
resp, err := source.RunSQL(ctx, sqlStr, nil)
if err != nil {
return nil, util.ProcessGeneralError(err)
}
return resp, nil
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -19,6 +19,7 @@ import (
"database/sql"
"encoding/json"
"fmt"
"net/http"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
@@ -91,46 +92,46 @@ type Tool struct {
mcpManifest tools.McpManifest
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
paramsMap := params.AsMap()
sql, ok := paramsMap["sql_statement"].(string)
sqlStr, ok := paramsMap["sql_statement"].(string)
if !ok {
return nil, fmt.Errorf("unable to get cast %s", paramsMap["sql_statement"])
return nil, util.NewAgentError(fmt.Sprintf("unable to get cast %s", paramsMap["sql_statement"]), nil)
}
// Log the query executed for debugging.
logger, err := util.LoggerFromContext(ctx)
if err != nil {
return nil, fmt.Errorf("error getting logger: %s", err)
return nil, util.NewClientServerError("error getting logger", http.StatusInternalServerError, err)
}
logger.DebugContext(ctx, fmt.Sprintf("executing `%s` tool query: %s", resourceType, sql))
logger.DebugContext(ctx, fmt.Sprintf("executing `%s` tool query: %s", resourceType, sqlStr))
query := fmt.Sprintf("EXPLAIN FORMAT=JSON %s", sql)
query := fmt.Sprintf("EXPLAIN FORMAT=JSON %s", sqlStr)
result, err := source.RunSQL(ctx, query, nil)
if err != nil {
return nil, err
return nil, util.ProcessGeneralError(err)
}
// extract and return only the query plan object
resSlice, ok := result.([]any)
if !ok || len(resSlice) == 0 {
return nil, fmt.Errorf("no query plan returned")
return nil, util.NewClientServerError("no query plan returned", http.StatusInternalServerError, nil)
}
row, ok := resSlice[0].(orderedmap.Row)
if !ok || len(row.Columns) == 0 {
return nil, fmt.Errorf("no query plan returned in row")
return nil, util.NewClientServerError("no query plan returned in row", http.StatusInternalServerError, nil)
}
plan, ok := row.Columns[0].Value.(string)
if !ok {
return nil, fmt.Errorf("unable to convert plan object to string")
return nil, util.NewClientServerError("unable to convert plan object to string", http.StatusInternalServerError, nil)
}
var out map[string]any
if err := json.Unmarshal([]byte(plan), &out); err != nil {
return nil, fmt.Errorf("failed to unmarshal query plan json: %w", err)
return nil, util.NewClientServerError("failed to unmarshal query plan json", http.StatusInternalServerError, err)
}
return out, nil
}

View File

@@ -18,6 +18,7 @@ import (
"context"
"database/sql"
"fmt"
"net/http"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
@@ -32,65 +33,65 @@ import (
const resourceType string = "mysql-list-active-queries"
const listActiveQueriesStatementMySQL = `
SELECT
p.id AS processlist_id,
substring(IFNULL(p.info, t.trx_query), 1, 100) AS query,
t.trx_started AS trx_started,
(UNIX_TIMESTAMP(UTC_TIMESTAMP()) - UNIX_TIMESTAMP(t.trx_started)) AS trx_duration_seconds,
(UNIX_TIMESTAMP(UTC_TIMESTAMP()) - UNIX_TIMESTAMP(t.trx_wait_started)) AS trx_wait_duration_seconds,
p.time AS query_time,
t.trx_state AS trx_state,
p.state AS process_state,
IF(p.host IS NULL OR p.host = '', p.user, concat(p.user, '@', SUBSTRING_INDEX(p.host, ':', 1))) AS user,
t.trx_rows_locked AS trx_rows_locked,
t.trx_rows_modified AS trx_rows_modified,
p.db AS db
FROM
information_schema.processlist p
LEFT OUTER JOIN
information_schema.innodb_trx t
ON p.id = t.trx_mysql_thread_id
WHERE
(? IS NULL OR p.time >= ?)
AND p.id != CONNECTION_ID()
AND Command NOT IN ('Binlog Dump', 'Binlog Dump GTID', 'Connect', 'Connect Out', 'Register Slave')
AND User NOT IN ('system user', 'event_scheduler')
AND (t.trx_id is NOT NULL OR command != 'Sleep')
ORDER BY
t.trx_started
LIMIT ?;
SELECT
p.id AS processlist_id,
substring(IFNULL(p.info, t.trx_query), 1, 100) AS query,
t.trx_started AS trx_started,
(UNIX_TIMESTAMP(UTC_TIMESTAMP()) - UNIX_TIMESTAMP(t.trx_started)) AS trx_duration_seconds,
(UNIX_TIMESTAMP(UTC_TIMESTAMP()) - UNIX_TIMESTAMP(t.trx_wait_started)) AS trx_wait_duration_seconds,
p.time AS query_time,
t.trx_state AS trx_state,
p.state AS process_state,
IF(p.host IS NULL OR p.host = '', p.user, concat(p.user, '@', SUBSTRING_INDEX(p.host, ':', 1))) AS user,
t.trx_rows_locked AS trx_rows_locked,
t.trx_rows_modified AS trx_rows_modified,
p.db AS db
FROM
information_schema.processlist p
LEFT OUTER JOIN
information_schema.innodb_trx t
ON p.id = t.trx_mysql_thread_id
WHERE
(? IS NULL OR p.time >= ?)
AND p.id != CONNECTION_ID()
AND Command NOT IN ('Binlog Dump', 'Binlog Dump GTID', 'Connect', 'Connect Out', 'Register Slave')
AND User NOT IN ('system user', 'event_scheduler')
AND (t.trx_id is NOT NULL OR command != 'Sleep')
ORDER BY
t.trx_started
LIMIT ?;
`
const listActiveQueriesStatementCloudSQLMySQL = `
SELECT
p.id AS processlist_id,
substring(IFNULL(p.info, t.trx_query), 1, 100) AS query,
t.trx_started AS trx_started,
(UNIX_TIMESTAMP(UTC_TIMESTAMP()) - UNIX_TIMESTAMP(t.trx_started)) AS trx_duration_seconds,
(UNIX_TIMESTAMP(UTC_TIMESTAMP()) - UNIX_TIMESTAMP(t.trx_wait_started)) AS trx_wait_duration_seconds,
p.time AS query_time,
t.trx_state AS trx_state,
p.state AS process_state,
IF(p.host IS NULL OR p.host = '', p.user, concat(p.user, '@', SUBSTRING_INDEX(p.host, ':', 1))) AS user,
t.trx_rows_locked AS trx_rows_locked,
t.trx_rows_modified AS trx_rows_modified,
p.db AS db
FROM
information_schema.processlist p
LEFT OUTER JOIN
information_schema.innodb_trx t
ON p.id = t.trx_mysql_thread_id
WHERE
(? IS NULL OR p.time >= ?)
AND p.id != CONNECTION_ID()
AND SUBSTRING_INDEX(IFNULL(p.host,''), ':', 1) NOT IN ('localhost', '127.0.0.1')
AND IFNULL(p.host,'') NOT LIKE '::1%'
AND Command NOT IN ('Binlog Dump', 'Binlog Dump GTID', 'Connect', 'Connect Out', 'Register Slave')
AND User NOT IN ('system user', 'event_scheduler')
AND (t.trx_id is NOT NULL OR command != 'sleep')
ORDER BY
t.trx_started
LIMIT ?;
SELECT
p.id AS processlist_id,
substring(IFNULL(p.info, t.trx_query), 1, 100) AS query,
t.trx_started AS trx_started,
(UNIX_TIMESTAMP(UTC_TIMESTAMP()) - UNIX_TIMESTAMP(t.trx_started)) AS trx_duration_seconds,
(UNIX_TIMESTAMP(UTC_TIMESTAMP()) - UNIX_TIMESTAMP(t.trx_wait_started)) AS trx_wait_duration_seconds,
p.time AS query_time,
t.trx_state AS trx_state,
p.state AS process_state,
IF(p.host IS NULL OR p.host = '', p.user, concat(p.user, '@', SUBSTRING_INDEX(p.host, ':', 1))) AS user,
t.trx_rows_locked AS trx_rows_locked,
t.trx_rows_modified AS trx_rows_modified,
p.db AS db
FROM
information_schema.processlist p
LEFT OUTER JOIN
information_schema.innodb_trx t
ON p.id = t.trx_mysql_thread_id
WHERE
(? IS NULL OR p.time >= ?)
AND p.id != CONNECTION_ID()
AND SUBSTRING_INDEX(IFNULL(p.host,''), ':', 1) NOT IN ('localhost', '127.0.0.1')
AND IFNULL(p.host,'') NOT LIKE '::1%'
AND Command NOT IN ('Binlog Dump', 'Binlog Dump GTID', 'Connect', 'Connect Out', 'Register Slave')
AND User NOT IN ('system user', 'event_scheduler')
AND (t.trx_id is NOT NULL OR command != 'sleep')
ORDER BY
t.trx_started
LIMIT ?;
`
func init() {
@@ -177,30 +178,34 @@ type Tool struct {
statement string
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
paramsMap := params.AsMap()
duration, ok := paramsMap["min_duration_secs"].(int)
if !ok {
return nil, fmt.Errorf("invalid 'min_duration_secs' parameter; expected an integer")
return nil, util.NewAgentError("invalid 'min_duration_secs' parameter; expected an integer", nil)
}
limit, ok := paramsMap["limit"].(int)
if !ok {
return nil, fmt.Errorf("invalid 'limit' parameter; expected an integer")
return nil, util.NewAgentError("invalid 'limit' parameter; expected an integer", nil)
}
// Log the query executed for debugging.
logger, err := util.LoggerFromContext(ctx)
if err != nil {
return nil, fmt.Errorf("error getting logger: %s", err)
return nil, util.NewClientServerError("error getting logger", http.StatusInternalServerError, err)
}
logger.DebugContext(ctx, fmt.Sprintf("executing `%s` tool query: %s", resourceType, t.statement))
return source.RunSQL(ctx, t.statement, []any{duration, duration, limit})
resp, err := source.RunSQL(ctx, t.statement, []any{duration, duration, limit})
if err != nil {
return nil, util.ProcessGeneralError(err)
}
return resp, nil
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -18,6 +18,7 @@ import (
"context"
"database/sql"
"fmt"
"net/http"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
@@ -30,25 +31,25 @@ import (
const resourceType string = "mysql-list-table-fragmentation"
const listTableFragmentationStatement = `
SELECT
table_schema,
table_name,
data_length AS data_size,
index_length AS index_size,
data_free AS data_free,
ROUND((data_free / (data_length + index_length)) * 100, 2) AS fragmentation_percentage
FROM
information_schema.tables
WHERE
table_schema NOT IN ('sys', 'performance_schema', 'mysql', 'information_schema')
AND (COALESCE(?, '') = '' OR table_schema = ?)
AND (COALESCE(?, '') = '' OR table_name = ?)
AND data_free >= ?
ORDER BY
fragmentation_percentage DESC,
table_schema,
table_name
LIMIT ?;
SELECT
table_schema,
table_name,
data_length AS data_size,
index_length AS index_size,
data_free AS data_free,
ROUND((data_free / (data_length + index_length)) * 100, 2) AS fragmentation_percentage
FROM
information_schema.tables
WHERE
table_schema NOT IN ('sys', 'performance_schema', 'mysql', 'information_schema')
AND (COALESCE(?, '') = '' OR table_schema = ?)
AND (COALESCE(?, '') = '' OR table_name = ?)
AND data_free >= ?
ORDER BY
fragmentation_percentage DESC,
table_schema,
table_name
LIMIT ?;
`
func init() {
@@ -114,39 +115,43 @@ type Tool struct {
mcpManifest tools.McpManifest
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
paramsMap := params.AsMap()
table_schema, ok := paramsMap["table_schema"].(string)
if !ok {
return nil, fmt.Errorf("invalid 'table_schema' parameter; expected a string")
return nil, util.NewAgentError("invalid 'table_schema' parameter; expected a string", nil)
}
table_name, ok := paramsMap["table_name"].(string)
if !ok {
return nil, fmt.Errorf("invalid 'table_name' parameter; expected a string")
return nil, util.NewAgentError("invalid 'table_name' parameter; expected a string", nil)
}
data_free_threshold_bytes, ok := paramsMap["data_free_threshold_bytes"].(int)
if !ok {
return nil, fmt.Errorf("invalid 'data_free_threshold_bytes' parameter; expected an integer")
return nil, util.NewAgentError("invalid 'data_free_threshold_bytes' parameter; expected an integer", nil)
}
limit, ok := paramsMap["limit"].(int)
if !ok {
return nil, fmt.Errorf("invalid 'limit' parameter; expected an integer")
return nil, util.NewAgentError("invalid 'limit' parameter; expected an integer", nil)
}
// Log the query executed for debugging.
logger, err := util.LoggerFromContext(ctx)
if err != nil {
return nil, fmt.Errorf("error getting logger: %s", err)
return nil, util.NewClientServerError("error getting logger", http.StatusInternalServerError, err)
}
logger.DebugContext(ctx, fmt.Sprintf("executing `%s` tool query: %s", resourceType, listTableFragmentationStatement))
sliceParams := []any{table_schema, table_schema, table_name, table_name, data_free_threshold_bytes, limit}
return source.RunSQL(ctx, listTableFragmentationStatement, sliceParams)
resp, err := source.RunSQL(ctx, listTableFragmentationStatement, sliceParams)
if err != nil {
return nil, util.ProcessGeneralError(err)
}
return resp, nil
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -18,11 +18,13 @@ import (
"context"
"database/sql"
"fmt"
"net/http"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
)
@@ -244,32 +246,32 @@ type Tool struct {
mcpManifest tools.McpManifest
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
paramsMap := params.AsMap()
tableNames, ok := paramsMap["table_names"].(string)
if !ok {
return nil, fmt.Errorf("invalid '%s' parameter; expected a string", tableNames)
return nil, util.NewAgentError(fmt.Sprintf("invalid '%s' parameter; expected a string", tableNames), nil)
}
outputFormat, _ := paramsMap["output_format"].(string)
if outputFormat != "simple" && outputFormat != "detailed" {
return nil, fmt.Errorf("invalid value for output_format: must be 'simple' or 'detailed', but got %q", outputFormat)
return nil, util.NewAgentError(fmt.Sprintf("invalid value for output_format: must be 'simple' or 'detailed', but got %q", outputFormat), nil)
}
resp, err := source.RunSQL(ctx, listTablesStatement, []any{tableNames, outputFormat})
if err != nil {
return nil, err
return nil, util.ProcessGeneralError(err)
}
// if there's no results, return empty list instead of null
resSlice, ok := resp.([]any)
if !ok || len(resSlice) == 0 {
return []any{}, nil
}
return resp, err
return resp, nil
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -18,6 +18,7 @@ import (
"context"
"database/sql"
"fmt"
"net/http"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
@@ -30,26 +31,26 @@ import (
const resourceType string = "mysql-list-tables-missing-unique-indexes"
const listTablesMissingUniqueIndexesStatement = `
SELECT
tab.table_schema AS table_schema,
tab.table_name AS table_name
FROM
information_schema.tables tab
LEFT JOIN
information_schema.table_constraints tco
ON
tab.table_schema = tco.table_schema
AND tab.table_name = tco.table_name
AND tco.constraint_type IN ('PRIMARY KEY', 'UNIQUE')
WHERE
tco.constraint_type IS NULL
AND tab.table_schema NOT IN('mysql', 'information_schema', 'performance_schema', 'sys')
AND tab.table_type = 'BASE TABLE'
AND (COALESCE(?, '') = '' OR tab.table_schema = ?)
ORDER BY
tab.table_schema,
tab.table_name
LIMIT ?;
SELECT
tab.table_schema AS table_schema,
tab.table_name AS table_name
FROM
information_schema.tables tab
LEFT JOIN
information_schema.table_constraints tco
ON
tab.table_schema = tco.table_schema
AND tab.table_name = tco.table_name
AND tco.constraint_type IN ('PRIMARY KEY', 'UNIQUE')
WHERE
tco.constraint_type IS NULL
AND tab.table_schema NOT IN('mysql', 'information_schema', 'performance_schema', 'sys')
AND tab.table_type = 'BASE TABLE'
AND (COALESCE(?, '') = '' OR tab.table_schema = ?)
ORDER BY
tab.table_schema,
tab.table_name
LIMIT ?;
`
func init() {
@@ -113,30 +114,34 @@ type Tool struct {
mcpManifest tools.McpManifest
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
paramsMap := params.AsMap()
table_schema, ok := paramsMap["table_schema"].(string)
if !ok {
return nil, fmt.Errorf("invalid 'table_schema' parameter; expected a string")
return nil, util.NewAgentError("invalid 'table_schema' parameter; expected a string", nil)
}
limit, ok := paramsMap["limit"].(int)
if !ok {
return nil, fmt.Errorf("invalid 'limit' parameter; expected an integer")
return nil, util.NewAgentError("invalid 'limit' parameter; expected an integer", nil)
}
// Log the query executed for debugging.
logger, err := util.LoggerFromContext(ctx)
if err != nil {
return nil, fmt.Errorf("error getting logger: %s", err)
return nil, util.NewClientServerError("error getting logger", http.StatusInternalServerError, err)
}
logger.DebugContext(ctx, fmt.Sprintf("executing `%s` tool query: %s", resourceType, listTablesMissingUniqueIndexesStatement))
return source.RunSQL(ctx, listTablesMissingUniqueIndexesStatement, []any{table_schema, table_schema, limit})
resp, err := source.RunSQL(ctx, listTablesMissingUniqueIndexesStatement, []any{table_schema, table_schema, limit})
if err != nil {
return nil, util.ProcessGeneralError(err)
}
return resp, nil
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -18,11 +18,13 @@ import (
"context"
"database/sql"
"fmt"
"net/http"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
)
@@ -93,25 +95,29 @@ type Tool struct {
mcpManifest tools.McpManifest
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
paramsMap := params.AsMap()
newStatement, err := parameters.ResolveTemplateParams(t.TemplateParameters, t.Statement, paramsMap)
if err != nil {
return nil, fmt.Errorf("unable to extract template params %w", err)
return nil, util.NewAgentError("unable to extract template params", err)
}
newParams, err := parameters.GetParams(t.Parameters, paramsMap)
if err != nil {
return nil, fmt.Errorf("unable to extract standard params %w", err)
return nil, util.NewAgentError("unable to extract standard params", err)
}
sliceParams := newParams.AsSlice()
return source.RunSQL(ctx, newStatement, sliceParams)
resp, err := source.RunSQL(ctx, newStatement, sliceParams)
if err != nil {
return nil, util.ProcessGeneralError(err)
}
return resp, nil
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,12 +17,14 @@ package neo4jcypher
import (
"context"
"fmt"
"net/http"
"github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
)
@@ -85,14 +87,18 @@ type Tool struct {
mcpManifest tools.McpManifest
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
paramsMap := params.AsMap()
return source.RunQuery(ctx, t.Statement, paramsMap, false, false)
resp, err := source.RunQuery(ctx, t.Statement, paramsMap, false, false)
if err != nil {
return nil, util.ProcessGeneralError(err)
}
return resp, nil
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,11 +17,13 @@ package neo4jexecutecypher
import (
"context"
"fmt"
"net/http"
"github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
)
@@ -94,28 +96,32 @@ type Tool struct {
mcpManifest tools.McpManifest
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
paramsMap := params.AsMap()
cypherStr, ok := paramsMap["cypher"].(string)
if !ok {
return nil, fmt.Errorf("unable to cast cypher parameter %s", paramsMap["cypher"])
return nil, util.NewAgentError(fmt.Sprintf("unable to cast cypher parameter %s", paramsMap["cypher"]), nil)
}
if cypherStr == "" {
return nil, fmt.Errorf("parameter 'cypher' must be a non-empty string")
return nil, util.NewAgentError("parameter 'cypher' must be a non-empty string", nil)
}
dryRun, ok := paramsMap["dry_run"].(bool)
if !ok {
return nil, fmt.Errorf("unable to cast dry_run parameter %s", paramsMap["dry_run"])
return nil, util.NewAgentError(fmt.Sprintf("unable to cast dry_run parameter %s", paramsMap["dry_run"]), nil)
}
return source.RunQuery(ctx, cypherStr, nil, t.ReadOnly, dryRun)
resp, err := source.RunQuery(ctx, cypherStr, nil, t.ReadOnly, dryRun)
if err != nil {
return nil, util.ProcessGeneralError(err)
}
return resp, nil
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,6 +17,7 @@ package neo4jschema
import (
"context"
"fmt"
"net/http"
"sync"
"time"
@@ -27,6 +28,7 @@ import (
"github.com/googleapis/genai-toolbox/internal/tools/neo4j/neo4jschema/cache"
"github.com/googleapis/genai-toolbox/internal/tools/neo4j/neo4jschema/helpers"
"github.com/googleapis/genai-toolbox/internal/tools/neo4j/neo4jschema/types"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
"github.com/neo4j/neo4j-go-driver/v5/neo4j"
)
@@ -113,10 +115,10 @@ type Tool struct {
// Invoke executes the tool's main logic: fetching the Neo4j schema.
// It first checks the cache for a valid schema before extracting it from the database.
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
// Check if a valid schema is already in the cache.
@@ -129,7 +131,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
// If not cached, extract the schema from the database.
schema, err := t.extractSchema(ctx, source)
if err != nil {
return nil, fmt.Errorf("failed to extract database schema: %w", err)
return nil, util.ProcessGeneralError(err)
}
// Cache the newly extracted schema for future use.
@@ -372,14 +374,14 @@ func (t Tool) GetAPOCSchema(ctx context.Context, source compatibleSource) ([]typ
name: "apoc-relationships",
fn: func(session neo4j.SessionWithContext) error {
query := `
MATCH (startNode)-[rel]->(endNode)
WITH
labels(startNode)[0] AS startNode,
type(rel) AS relType,
apoc.meta.cypher.types(rel) AS relProperties,
labels(endNode)[0] AS endNode,
count(*) AS count
RETURN relType, startNode, endNode, relProperties, count`
MATCH (startNode)-[rel]->(endNode)
WITH
labels(startNode)[0] AS startNode,
type(rel) AS relType,
apoc.meta.cypher.types(rel) AS relProperties,
labels(endNode)[0] AS endNode,
count(*) AS count
RETURN relType, startNode, endNode, relProperties, count`
result, err := session.Run(ctx, query, nil)
if err != nil {
return fmt.Errorf("failed to extract relationships: %w", err)
@@ -520,10 +522,10 @@ func (t Tool) GetSchemaWithoutAPOC(ctx context.Context, source compatibleSource,
name: "relationship-schema",
fn: func(session neo4j.SessionWithContext) error {
relQuery := `
MATCH (start)-[r]->(end)
WITH type(r) AS relType, labels(start) AS startLabels, labels(end) AS endLabels, count(*) AS count
RETURN relType, CASE WHEN size(startLabels) > 0 THEN startLabels[0] ELSE null END AS startLabel, CASE WHEN size(endLabels) > 0 THEN endLabels[0] ELSE null END AS endLabel, sum(count) AS totalCount
ORDER BY totalCount DESC`
MATCH (start)-[r]->(end)
WITH type(r) AS relType, labels(start) AS startLabels, labels(end) AS endLabels, count(*) AS count
RETURN relType, CASE WHEN size(startLabels) > 0 THEN startLabels[0] ELSE null END AS startLabel, CASE WHEN size(endLabels) > 0 THEN endLabels[0] ELSE null END AS endLabel, sum(count) AS totalCount
ORDER BY totalCount DESC`
relResult, err := session.Run(ctx, relQuery, nil)
if err != nil {
return fmt.Errorf("relationship count query failed: %w", err)

View File

@@ -18,11 +18,13 @@ import (
"context"
"database/sql"
"fmt"
"net/http"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
)
@@ -89,18 +91,22 @@ type Tool struct {
}
// Invoke executes the SQL statement provided in the parameters.
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
sliceParams := params.AsSlice()
sqlStr, ok := sliceParams[0].(string)
if !ok {
return nil, fmt.Errorf("unable to get cast %s", sliceParams[0])
return nil, util.NewAgentError(fmt.Sprintf("unable to get cast %s", sliceParams[0]), nil)
}
return source.RunSQL(ctx, sqlStr, nil)
resp, err := source.RunSQL(ctx, sqlStr, nil)
if err != nil {
return nil, util.ProcessGeneralError(err)
}
return resp, nil
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -18,11 +18,13 @@ import (
"context"
"database/sql"
"fmt"
"net/http"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
)
@@ -94,24 +96,28 @@ type Tool struct {
}
// Invoke executes the SQL statement with the provided parameters.
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
paramsMap := params.AsMap()
newStatement, err := parameters.ResolveTemplateParams(t.TemplateParameters, t.Statement, paramsMap)
if err != nil {
return nil, fmt.Errorf("unable to extract template params %w", err)
return nil, util.NewAgentError("unable to extract template params", err)
}
newParams, err := parameters.GetParams(t.Parameters, paramsMap)
if err != nil {
return nil, fmt.Errorf("unable to extract standard params %w", err)
return nil, util.NewAgentError("unable to extract standard params", err)
}
sliceParams := newParams.AsSlice()
return source.RunSQL(ctx, newStatement, sliceParams)
resp, err := source.RunSQL(ctx, newStatement, sliceParams)
if err != nil {
return nil, util.ProcessGeneralError(err)
}
return resp, nil
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -6,6 +6,7 @@ import (
"context"
"database/sql"
"fmt"
"net/http"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
@@ -77,25 +78,29 @@ type Tool struct {
mcpManifest tools.McpManifest
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
paramsMap := params.AsMap()
sqlParam, ok := paramsMap["sql"].(string)
if !ok {
return nil, fmt.Errorf("unable to get cast %s", paramsMap["sql"])
return nil, util.NewAgentError(fmt.Sprintf("unable to get cast %s", paramsMap["sql"]), nil)
}
// Log the query executed for debugging.
logger, err := util.LoggerFromContext(ctx)
if err != nil {
return nil, fmt.Errorf("error getting logger: %s", err)
return nil, util.NewClientServerError("error getting logger", http.StatusInternalServerError, err)
}
logger.DebugContext(ctx, "executing `%s` tool query: %s", resourceType, sqlParam)
return source.RunSQL(ctx, sqlParam, nil)
resp, err := source.RunSQL(ctx, sqlParam, nil)
if err != nil {
return nil, util.ProcessGeneralError(err)
}
return resp, nil
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -6,11 +6,13 @@ import (
"context"
"database/sql"
"fmt"
"net/http"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
)
@@ -81,21 +83,21 @@ type Tool struct {
mcpManifest tools.McpManifest
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
paramsMap := params.AsMap()
newStatement, err := parameters.ResolveTemplateParams(t.TemplateParameters, t.Statement, paramsMap)
if err != nil {
return nil, fmt.Errorf("unable to extract template params %w", err)
return nil, util.NewAgentError("unable to extract template params", err)
}
newParams, err := parameters.GetParams(t.Parameters, paramsMap)
if err != nil {
return nil, fmt.Errorf("unable to extract standard params %w", err)
return nil, util.NewAgentError("unable to extract standard params", err)
}
sliceParams := newParams.AsSlice()
@@ -103,7 +105,11 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
fmt.Printf("[%d]=%T ", i, p)
}
fmt.Printf("\n")
return source.RunSQL(ctx, newStatement, sliceParams)
resp, err := source.RunSQL(ctx, newStatement, sliceParams)
if err != nil {
return nil, util.ProcessGeneralError(err)
}
return resp, nil
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,11 +17,13 @@ package postgresdatabaseoverview
import (
"context"
"fmt"
"net/http"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
"github.com/jackc/pgx/v5/pgxpool"
)
@@ -29,13 +31,13 @@ import (
const resourceType string = "postgres-database-overview"
const databaseOverviewStatement = `
SELECT
SELECT
current_setting('server_version') AS pg_version,
pg_is_in_recovery() AS is_replica,
(now() - pg_postmaster_start_time())::TEXT AS uptime,
current_setting('max_connections')::int AS max_connections,
(SELECT count(*) FROM pg_stat_activity) AS current_connections,
(SELECT count(*) FROM pg_stat_activity WHERE state = 'active') AS active_connections,
(SELECT count(*) FROM pg_stat_activity) AS current_connections,
(SELECT count(*) FROM pg_stat_activity WHERE state = 'active') AS active_connections,
round(
(100.0 * (SELECT count(*) FROM pg_stat_activity) / current_setting('max_connections')::int),
2
@@ -57,7 +59,7 @@ func newConfig(ctx context.Context, name string, decoder *yaml.Decoder) (tools.T
}
type compatibleSource interface {
PostgresPool() *pgxpool.Pool // keep this so that sources are postgres compatible
PostgresPool() *pgxpool.Pool
RunSQL(context.Context, string, []any) (any, error)
}
@@ -69,7 +71,6 @@ type Config struct {
AuthRequired []string `yaml:"authRequired"`
}
// validate interface
var _ tools.ToolConfig = Config{}
func (cfg Config) ToolConfigType() string {
@@ -83,7 +84,6 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
}
mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters, nil)
// finish tool setup
return Tool{
Config: cfg,
allParams: allParameters,
@@ -96,7 +96,6 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
}, nil
}
// validate interface
var _ tools.Tool = Tool{}
type Tool struct {
@@ -110,20 +109,24 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
paramsMap := params.AsMap()
newParams, err := parameters.GetParams(t.allParams, paramsMap)
if err != nil {
return nil, fmt.Errorf("unable to extract standard params %w", err)
return nil, util.NewAgentError("unable to extract standard params", err)
}
sliceParams := newParams.AsSlice()
return source.RunSQL(ctx, databaseOverviewStatement, sliceParams)
resp, err := source.RunSQL(ctx, databaseOverviewStatement, sliceParams)
if err != nil {
return nil, util.ProcessGeneralError(err)
}
return resp, nil
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,6 +17,7 @@ package postgresexecutesql
import (
"context"
"fmt"
"net/http"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
@@ -56,7 +57,6 @@ type Config struct {
AuthRequired []string `yaml:"authRequired"`
}
// validate interface
var _ tools.ToolConfig = Config{}
func (cfg Config) ToolConfigType() string {
@@ -69,7 +69,6 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, params, nil)
// finish tool setup
t := Tool{
Config: cfg,
Parameters: params,
@@ -79,7 +78,6 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
return t, nil
}
// validate interface
var _ tools.Tool = Tool{}
type Tool struct {
@@ -89,25 +87,28 @@ type Tool struct {
mcpManifest tools.McpManifest
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
paramsMap := params.AsMap()
sql, ok := paramsMap["sql"].(string)
if !ok {
return nil, fmt.Errorf("unable to get cast %s", paramsMap["sql"])
return nil, util.NewAgentError(fmt.Sprintf("unable to get cast %s", paramsMap["sql"]), nil)
}
// Log the query executed for debugging.
logger, err := util.LoggerFromContext(ctx)
if err != nil {
return nil, fmt.Errorf("error getting logger: %s", err)
return nil, util.NewClientServerError("error getting logger", http.StatusInternalServerError, err)
}
logger.DebugContext(ctx, fmt.Sprintf("executing `%s` tool query: %s", resourceType, sql))
return source.RunSQL(ctx, sql, nil)
resp, err := source.RunSQL(ctx, sql, nil)
if err != nil {
return nil, util.ProcessGeneralError(err)
}
return resp, nil
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,11 +17,13 @@ package postgresgetcolumncardinality
import (
"context"
"fmt"
"net/http"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
"github.com/jackc/pgx/v5/pgxpool"
)
@@ -29,7 +31,7 @@ import (
const resourceType string = "postgres-get-column-cardinality"
const getColumnCardinality = `
SELECT
SELECT
s.attname AS column_name,
ROUND(
CASE
@@ -74,7 +76,6 @@ type Config struct {
AuthRequired []string `yaml:"authRequired"`
}
// validate interface
var _ tools.ToolConfig = Config{}
func (cfg Config) ToolConfigType() string {
@@ -95,7 +96,6 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters, nil)
// finish tool setup
return Tool{
Config: cfg,
allParams: allParameters,
@@ -108,7 +108,6 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
}, nil
}
// validate interface
var _ tools.Tool = Tool{}
type Tool struct {
@@ -122,20 +121,24 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
paramsMap := params.AsMap()
newParams, err := parameters.GetParams(t.allParams, paramsMap)
if err != nil {
return nil, fmt.Errorf("unable to extract standard params %w", err)
return nil, util.NewAgentError("unable to extract standard params", err)
}
sliceParams := newParams.AsSlice()
return source.RunSQL(ctx, getColumnCardinality, sliceParams)
resp, err := source.RunSQL(ctx, getColumnCardinality, sliceParams)
if err != nil {
return nil, util.ProcessGeneralError(err)
}
return resp, nil
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,11 +17,13 @@ package postgreslistactivequeries
import (
"context"
"fmt"
"net/http"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
"github.com/jackc/pgx/v5/pgxpool"
)
@@ -29,26 +31,26 @@ import (
const resourceType string = "postgres-list-active-queries"
const listActiveQueriesStatement = `
SELECT
pid,
usename AS user,
datname,
application_name,
client_addr,
state,
wait_event_type,
wait_event,
backend_start,
xact_start,
query_start,
now() - query_start AS query_duration,
query
FROM pg_stat_activity
WHERE state = 'active'
AND ($1::INTERVAL IS NULL OR now() - query_start >= $1::INTERVAL)
AND ($2::text IS NULL OR application_name NOT IN (SELECT trim(app) FROM unnest(string_to_array($2, ',')) AS app))
ORDER BY query_duration DESC
LIMIT COALESCE($3::int, 50);
SELECT
pid,
usename AS user,
datname,
application_name,
client_addr,
state,
wait_event_type,
wait_event,
backend_start,
xact_start,
query_start,
now() - query_start AS query_duration,
query
FROM pg_stat_activity
WHERE state = 'active'
AND ($1::INTERVAL IS NULL OR now() - query_start >= $1::INTERVAL)
AND ($2::text IS NULL OR application_name NOT IN (SELECT trim(app) FROM unnest(string_to_array($2, ',')) AS app))
ORDER BY query_duration DESC
LIMIT COALESCE($3::int, 50);
`
func init() {
@@ -78,7 +80,6 @@ type Config struct {
AuthRequired []string `yaml:"authRequired"`
}
// validate interface
var _ tools.ToolConfig = Config{}
func (cfg Config) ToolConfigType() string {
@@ -94,8 +95,7 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
paramManifest := allParameters.Manifest()
mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters, nil)
// finish tool setup
t := Tool{
return Tool{
Config: cfg,
allParams: allParameters,
manifest: tools.Manifest{
@@ -104,11 +104,9 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
AuthRequired: cfg.AuthRequired,
},
mcpManifest: mcpManifest,
}
return t, nil
}, nil
}
// validate interface
var _ tools.Tool = Tool{}
type Tool struct {
@@ -118,21 +116,25 @@ type Tool struct {
mcpManifest tools.McpManifest
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
paramsMap := params.AsMap()
newParams, err := parameters.GetParams(t.allParams, paramsMap)
if err != nil {
return nil, fmt.Errorf("unable to extract standard params %w", err)
return nil, util.NewAgentError("unable to extract standard params", err)
}
sliceParams := newParams.AsSlice()
return source.RunSQL(ctx, listActiveQueriesStatement, sliceParams)
resp, err := source.RunSQL(ctx, listActiveQueriesStatement, sliceParams)
if err != nil {
return nil, util.ProcessGeneralError(err)
}
return resp, nil
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,11 +17,13 @@ package postgreslistavailableextensions
import (
"context"
"fmt"
"net/http"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
"github.com/jackc/pgx/v5/pgxpool"
)
@@ -29,13 +31,13 @@ import (
const resourceType string = "postgres-list-available-extensions"
const listAvailableExtensionsQuery = `
SELECT
name,
default_version,
comment as description
FROM
pg_available_extensions
ORDER BY name;
SELECT
name,
default_version,
comment as description
FROM
pg_available_extensions
ORDER BY name;
`
func init() {
@@ -65,7 +67,6 @@ type Config struct {
AuthRequired []string `yaml:"authRequired"`
}
// validate interface
var _ tools.ToolConfig = Config{}
func (cfg Config) ToolConfigType() string {
@@ -76,7 +77,6 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
params := parameters.Parameters{}
mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, params, nil)
// finish tool setup
t := Tool{
Config: cfg,
manifest: tools.Manifest{
@@ -90,7 +90,6 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
return t, nil
}
// validate interface
var _ tools.Tool = Tool{}
type Tool struct {
@@ -100,12 +99,16 @@ type Tool struct {
Parameters parameters.Parameters
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
return source.RunSQL(ctx, listAvailableExtensionsQuery, nil)
resp, err := source.RunSQL(ctx, listAvailableExtensionsQuery, nil)
if err != nil {
return nil, util.ProcessGeneralError(err)
}
return resp, nil
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,82 +17,83 @@ package postgreslistdatabasestats
import (
"context"
"fmt"
"net/http"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
"github.com/jackc/pgx/v5/pgxpool"
)
const resourceType string = "postgres-list-database-stats"
// SQL query to list database statistics
const listDatabaseStats = `
WITH database_stats AS (
SELECT
s.datname AS database_name,
-- Database Metadata
d.datallowconn AS is_connectable,
pg_get_userbyid(d.datdba) AS database_owner,
ts.spcname AS default_tablespace,
WITH database_stats AS (
SELECT
s.datname AS database_name,
-- Database Metadata
d.datallowconn AS is_connectable,
pg_get_userbyid(d.datdba) AS database_owner,
ts.spcname AS default_tablespace,
-- Cache Performance
CASE
WHEN (s.blks_hit + s.blks_read) = 0 THEN 0
ELSE round((s.blks_hit * 100.0) / (s.blks_hit + s.blks_read), 2)
END AS cache_hit_ratio_percent,
s.blks_read AS blocks_read_from_disk,
s.blks_hit AS blocks_hit_in_cache,
-- Cache Performance
CASE
WHEN (s.blks_hit + s.blks_read) = 0 THEN 0
ELSE round((s.blks_hit * 100.0) / (s.blks_hit + s.blks_read), 2)
END AS cache_hit_ratio_percent,
s.blks_read AS blocks_read_from_disk,
s.blks_hit AS blocks_hit_in_cache,
-- Transaction Throughput
s.xact_commit,
s.xact_rollback,
round(s.xact_rollback * 100.0 / (s.xact_commit + s.xact_rollback + 1), 2) AS rollback_ratio_percent,
-- Transaction Throughput
s.xact_commit,
s.xact_rollback,
round(s.xact_rollback * 100.0 / (s.xact_commit + s.xact_rollback + 1), 2) AS rollback_ratio_percent,
-- Tuple Activity
s.tup_returned AS rows_returned_by_queries,
s.tup_fetched AS rows_fetched_by_scans,
s.tup_inserted,
s.tup_updated,
s.tup_deleted,
-- Tuple Activity
s.tup_returned AS rows_returned_by_queries,
s.tup_fetched AS rows_fetched_by_scans,
s.tup_inserted,
s.tup_updated,
s.tup_deleted,
-- Temporary File Usage
s.temp_files,
s.temp_bytes AS temp_size_bytes,
-- Temporary File Usage
s.temp_files,
s.temp_bytes AS temp_size_bytes,
-- Conflicts & Deadlocks
s.conflicts,
s.deadlocks,
-- Conflicts & Deadlocks
s.conflicts,
s.deadlocks,
-- General Info
s.numbackends AS active_connections,
s.stats_reset AS statistics_last_reset,
pg_database_size(s.datid) AS database_size_bytes
FROM
pg_stat_database s
JOIN
pg_database d ON d.oid = s.datid
JOIN
pg_tablespace ts ON ts.oid = d.dattablespace
WHERE
-- Exclude cloudsql internal databases
s.datname NOT IN ('cloudsqladmin')
-- Exclude template databases if not requested
AND ( $2::boolean IS TRUE OR d.datistemplate IS FALSE )
)
SELECT *
FROM database_stats
WHERE
($1::text IS NULL OR database_name LIKE '%' || $1::text || '%')
AND ($3::text IS NULL OR database_owner LIKE '%' || $3::text || '%')
AND ($4::text IS NULL OR default_tablespace LIKE '%' || $4::text || '%')
ORDER BY
CASE WHEN $5::text = 'size' THEN database_size_bytes END DESC,
CASE WHEN $5::text = 'commit' THEN xact_commit END DESC,
database_name
LIMIT COALESCE($6::int, 10);
-- General Info
s.numbackends AS active_connections,
s.stats_reset AS statistics_last_reset,
pg_database_size(s.datid) AS database_size_bytes
FROM
pg_stat_database s
JOIN
pg_database d ON d.oid = s.datid
JOIN
pg_tablespace ts ON ts.oid = d.dattablespace
WHERE
-- Exclude cloudsql internal databases
s.datname NOT IN ('cloudsqladmin')
-- Exclude template databases if not requested
AND ( $2::boolean IS TRUE OR d.datistemplate IS FALSE )
)
SELECT *
FROM database_stats
WHERE
($1::text IS NULL OR database_name LIKE '%' || $1::text || '%')
AND ($3::text IS NULL OR database_owner LIKE '%' || $3::text || '%')
AND ($4::text IS NULL OR default_tablespace LIKE '%' || $4::text || '%')
ORDER BY
CASE WHEN $5::text = 'size' THEN database_size_bytes END DESC,
CASE WHEN $5::text = 'commit' THEN xact_commit END DESC,
database_name
LIMIT COALESCE($6::int, 10);
`
func init() {
@@ -122,7 +123,6 @@ type Config struct {
AuthRequired []string `yaml:"authRequired"`
}
// validate interface
var _ tools.ToolConfig = Config{}
func (cfg Config) ToolConfigType() string {
@@ -164,7 +164,6 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
}
mcpManifest := tools.GetMcpManifest(cfg.Name, description, cfg.AuthRequired, allParameters, nil)
// finish tool setup
return Tool{
Config: cfg,
allParams: allParameters,
@@ -177,7 +176,6 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
}, nil
}
// validate interface
var _ tools.Tool = Tool{}
type Tool struct {
@@ -187,21 +185,25 @@ type Tool struct {
mcpManifest tools.McpManifest
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
paramsMap := params.AsMap()
newParams, err := parameters.GetParams(t.allParams, paramsMap)
if err != nil {
return nil, fmt.Errorf("unable to extract standard params %w", err)
return nil, util.NewAgentError("unable to extract standard params", err)
}
sliceParams := newParams.AsSlice()
return source.RunSQL(ctx, listDatabaseStats, sliceParams)
resp, err := source.RunSQL(ctx, listDatabaseStats, sliceParams)
if err != nil {
return nil, util.ProcessGeneralError(err)
}
return resp, nil
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,11 +17,13 @@ package postgreslistindexes
import (
"context"
"fmt"
"net/http"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
"github.com/jackc/pgx/v5/pgxpool"
)
@@ -29,49 +31,49 @@ import (
const resourceType string = "postgres-list-indexes"
const listIndexesStatement = `
WITH IndexDetails AS (
SELECT
s.schemaname AS schema_name,
t.relname AS table_name,
i.relname AS index_name,
am.amname AS index_type,
ix.indisunique AS is_unique,
ix.indisprimary AS is_primary,
pg_get_indexdef(i.oid) AS index_definition,
pg_relation_size(i.oid) AS index_size_bytes,
s.idx_scan AS index_scans,
s.idx_tup_read AS tuples_read,
s.idx_tup_fetch AS tuples_fetched,
CASE
WHEN s.idx_scan > 0 THEN true
ELSE false
END AS is_used
FROM pg_catalog.pg_class t
JOIN pg_catalog.pg_index ix
ON t.oid = ix.indrelid
JOIN pg_catalog.pg_class i
ON i.oid = ix.indexrelid
JOIN pg_catalog.pg_am am
ON i.relam = am.oid
JOIN pg_catalog.pg_stat_all_indexes s
ON i.oid = s.indexrelid
WHERE
t.relkind = 'r'
AND s.schemaname NOT IN ('pg_catalog', 'information_schema', 'pg_toast')
AND s.schemaname NOT LIKE 'pg_temp_%'
)
SELECT *
FROM IndexDetails
WHERE
($1::text IS NULL OR schema_name LIKE '%' || $1 || '%')
AND ($2::text IS NULL OR table_name LIKE '%' || $2 || '%')
AND ($3::text IS NULL OR index_name LIKE '%' || $3 || '%')
AND ($4::boolean IS NOT TRUE OR is_used IS FALSE)
ORDER BY
schema_name,
table_name,
index_name
LIMIT COALESCE($5::int, 50);
WITH IndexDetails AS (
SELECT
s.schemaname AS schema_name,
t.relname AS table_name,
i.relname AS index_name,
am.amname AS index_type,
ix.indisunique AS is_unique,
ix.indisprimary AS is_primary,
pg_get_indexdef(i.oid) AS index_definition,
pg_relation_size(i.oid) AS index_size_bytes,
s.idx_scan AS index_scans,
s.idx_tup_read AS tuples_read,
s.idx_tup_fetch AS tuples_fetched,
CASE
WHEN s.idx_scan > 0 THEN true
ELSE false
END AS is_used
FROM pg_catalog.pg_class t
JOIN pg_catalog.pg_index ix
ON t.oid = ix.indrelid
JOIN pg_catalog.pg_class i
ON i.oid = ix.indexrelid
JOIN pg_catalog.pg_am am
ON i.relam = am.oid
JOIN pg_catalog.pg_stat_all_indexes s
ON i.oid = s.indexrelid
WHERE
t.relkind = 'r'
AND s.schemaname NOT IN ('pg_catalog', 'information_schema', 'pg_toast')
AND s.schemaname NOT LIKE 'pg_temp_%'
)
SELECT *
FROM IndexDetails
WHERE
($1::text IS NULL OR schema_name LIKE '%' || $1 || '%')
AND ($2::text IS NULL OR table_name LIKE '%' || $2 || '%')
AND ($3::text IS NULL OR index_name LIKE '%' || $3 || '%')
AND ($4::boolean IS NOT TRUE OR is_used IS FALSE)
ORDER BY
schema_name,
table_name,
index_name
LIMIT COALESCE($5::int, 50);
`
func init() {
@@ -101,7 +103,6 @@ type Config struct {
AuthRequired []string `yaml:"authRequired"`
}
// validate interface
var _ tools.ToolConfig = Config{}
func (cfg Config) ToolConfigType() string {
@@ -122,7 +123,6 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
}
mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters, nil)
// finish tool setup
return Tool{
Config: cfg,
allParams: allParameters,
@@ -135,7 +135,6 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
}, nil
}
// validate interface
var _ tools.Tool = Tool{}
type Tool struct {
@@ -149,21 +148,25 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
paramsMap := params.AsMap()
newParams, err := parameters.GetParams(t.allParams, paramsMap)
if err != nil {
return nil, fmt.Errorf("unable to extract standard params %w", err)
return nil, util.NewAgentError("unable to extract standard params", err)
}
sliceParams := newParams.AsSlice()
return source.RunSQL(ctx, listIndexesStatement, sliceParams)
resp, err := source.RunSQL(ctx, listIndexesStatement, sliceParams)
if err != nil {
return nil, util.ProcessGeneralError(err)
}
return resp, nil
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,11 +17,13 @@ package postgreslistinstalledextensions
import (
"context"
"fmt"
"net/http"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
"github.com/jackc/pgx/v5/pgxpool"
)
@@ -29,24 +31,24 @@ import (
const resourceType string = "postgres-list-installed-extensions"
const listAvailableExtensionsQuery = `
SELECT
e.extname AS name,
e.extversion AS version,
n.nspname AS schema,
pg_get_userbyid(e.extowner) AS owner,
c.description AS description
FROM
pg_catalog.pg_extension e
LEFT JOIN
pg_catalog.pg_namespace n
ON
n.oid = e.extnamespace
LEFT JOIN
pg_catalog.pg_description c
ON
c.objoid = e.oid
AND c.classoid = 'pg_catalog.pg_extension'::pg_catalog.regclass
ORDER BY 1;
SELECT
e.extname AS name,
e.extversion AS version,
n.nspname AS schema,
pg_get_userbyid(e.extowner) AS owner,
c.description AS description
FROM
pg_catalog.pg_extension e
LEFT JOIN
pg_catalog.pg_namespace n
ON
n.oid = e.extnamespace
LEFT JOIN
pg_catalog.pg_description c
ON
c.objoid = e.oid
AND c.classoid = 'pg_catalog.pg_extension'::pg_catalog.regclass
ORDER BY 1;
`
func init() {
@@ -76,7 +78,6 @@ type Config struct {
AuthRequired []string `yaml:"authRequired"`
}
// validate interface
var _ tools.ToolConfig = Config{}
func (cfg Config) ToolConfigType() string {
@@ -87,7 +88,6 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
params := parameters.Parameters{}
mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, params, nil)
// finish tool setup
t := Tool{
Config: cfg,
manifest: tools.Manifest{
@@ -100,7 +100,6 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
return t, nil
}
// validate interface
var _ tools.Tool = Tool{}
type Tool struct {
@@ -109,12 +108,16 @@ type Tool struct {
mcpManifest tools.McpManifest
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
return source.RunSQL(ctx, listAvailableExtensionsQuery, nil)
resp, err := source.RunSQL(ctx, listAvailableExtensionsQuery, nil)
if err != nil {
return nil, util.ProcessGeneralError(err)
}
return resp, nil
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {
@@ -145,7 +148,6 @@ func (t Tool) GetAuthTokenHeaderName(resourceMgr tools.SourceProvider) (string,
return "Authorization", nil
}
// This tool does not have parameters, so return an empty set.
func (t Tool) GetParameters() parameters.Parameters {
return parameters.Parameters{}
}

View File

@@ -17,11 +17,13 @@ package postgreslistlocks
import (
"context"
"fmt"
"net/http"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
"github.com/jackc/pgx/v5/pgxpool"
)
@@ -29,7 +31,7 @@ import (
const resourceType string = "postgres-list-locks"
const listLocks = `
SELECT
SELECT
locked.pid,
locked.usename,
locked.query,
@@ -76,7 +78,6 @@ type Config struct {
AuthRequired []string `yaml:"authRequired"`
}
// validate interface
var _ tools.ToolConfig = Config{}
func (cfg Config) ToolConfigType() string {
@@ -93,7 +94,6 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters, nil)
// finish tool setup
return Tool{
Config: cfg,
allParams: allParameters,
@@ -106,7 +106,6 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
}, nil
}
// validate interface
var _ tools.Tool = Tool{}
type Tool struct {
@@ -120,21 +119,25 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
paramsMap := params.AsMap()
newParams, err := parameters.GetParams(t.allParams, paramsMap)
if err != nil {
return nil, fmt.Errorf("unable to extract standard params %w", err)
return nil, util.NewAgentError("unable to extract standard params", err)
}
sliceParams := newParams.AsSlice()
return source.RunSQL(ctx, listLocks, sliceParams)
resp, err := source.RunSQL(ctx, listLocks, sliceParams)
if err != nil {
return nil, util.ProcessGeneralError(err)
}
return resp, nil
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,11 +17,13 @@ package postgreslistpgsettings
import (
"context"
"fmt"
"net/http"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
"github.com/jackc/pgx/v5/pgxpool"
)
@@ -29,7 +31,7 @@ import (
const resourceType string = "postgres-list-pg-settings"
const listPgSettingsStatement = `
SELECT
SELECT
name,
setting AS current_value,
unit,
@@ -41,10 +43,10 @@ const listPgSettingsStatement = `
ELSE 'No'
END
AS requires_restart
FROM pg_settings
WHERE ($1::text IS NULL OR name LIKE '%' || $1::text || '%')
ORDER BY name
LIMIT COALESCE($2::int, 50);
FROM pg_settings
WHERE ($1::text IS NULL OR name LIKE '%' || $1::text || '%')
ORDER BY name
LIMIT COALESCE($2::int, 50);
`
func init() {
@@ -74,7 +76,6 @@ type Config struct {
AuthRequired []string `yaml:"authRequired"`
}
// validate interface
var _ tools.ToolConfig = Config{}
func (cfg Config) ToolConfigType() string {
@@ -92,7 +93,6 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
}
mcpManifest := tools.GetMcpManifest(cfg.Name, description, cfg.AuthRequired, allParameters, nil)
// finish tool setup
return Tool{
Config: cfg,
allParams: allParameters,
@@ -105,7 +105,6 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
}, nil
}
// validate interface
var _ tools.Tool = Tool{}
type Tool struct {
@@ -115,19 +114,23 @@ type Tool struct {
mcpManifest tools.McpManifest
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
paramsMap := params.AsMap()
newParams, err := parameters.GetParams(t.allParams, paramsMap)
if err != nil {
return nil, fmt.Errorf("unable to extract standard params %w", err)
return nil, util.NewAgentError("unable to extract standard params", err)
}
sliceParams := newParams.AsSlice()
return source.RunSQL(ctx, listPgSettingsStatement, sliceParams)
resp, err := source.RunSQL(ctx, listPgSettingsStatement, sliceParams)
if err != nil {
return nil, util.ProcessGeneralError(err)
}
return resp, nil
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,11 +17,13 @@ package postgreslistpublicationtables
import (
"context"
"fmt"
"net/http"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
"github.com/jackc/pgx/v5/pgxpool"
)
@@ -29,33 +31,33 @@ import (
const resourceType string = "postgres-list-publication-tables"
const listPublicationTablesStatement = `
WITH
publication_details AS (
SELECT
pt.pubname AS publication_name,
pt.schemaname AS schema_name,
pt.tablename AS table_name,
-- Definition details
p.puballtables AS publishes_all_tables,
p.pubinsert AS publishes_inserts,
p.pubupdate AS publishes_updates,
p.pubdelete AS publishes_deletes,
p.pubtruncate AS publishes_truncates,
-- Owner information
pg_catalog.pg_get_userbyid(p.pubowner) AS publication_owner
FROM pg_catalog.pg_publication_tables pt
JOIN pg_catalog.pg_publication p
ON pt.pubname = p.pubname
)
SELECT *
FROM publication_details
WHERE
(NULLIF(TRIM($1::text), '') IS NULL OR table_name = ANY(regexp_split_to_array(TRIM($1::text), '\s*,\s*')))
AND (NULLIF(TRIM($2::text), '') IS NULL OR publication_name = ANY(regexp_split_to_array(TRIM($2::text), '\s*,\s*')))
AND (NULLIF(TRIM($3::text), '') IS NULL OR schema_name = ANY(regexp_split_to_array(TRIM($3::text), '\s*,\s*')))
ORDER BY
publication_name, schema_name, table_name
LIMIT COALESCE($4::int, 50);
WITH
publication_details AS (
SELECT
pt.pubname AS publication_name,
pt.schemaname AS schema_name,
pt.tablename AS table_name,
-- Definition details
p.puballtables AS publishes_all_tables,
p.pubinsert AS publishes_inserts,
p.pubupdate AS publishes_updates,
p.pubdelete AS publishes_deletes,
p.pubtruncate AS publishes_truncates,
-- Owner information
pg_catalog.pg_get_userbyid(p.pubowner) AS publication_owner
FROM pg_catalog.pg_publication_tables pt
JOIN pg_catalog.pg_publication p
ON pt.pubname = p.pubname
)
SELECT *
FROM publication_details
WHERE
(NULLIF(TRIM($1::text), '') IS NULL OR table_name = ANY(regexp_split_to_array(TRIM($1::text), '\s*,\s*')))
AND (NULLIF(TRIM($2::text), '') IS NULL OR publication_name = ANY(regexp_split_to_array(TRIM($2::text), '\s*,\s*')))
AND (NULLIF(TRIM($3::text), '') IS NULL OR schema_name = ANY(regexp_split_to_array(TRIM($3::text), '\s*,\s*')))
ORDER BY
publication_name, schema_name, table_name
LIMIT COALESCE($4::int, 50);
`
func init() {
@@ -85,7 +87,6 @@ type Config struct {
AuthRequired []string `yaml:"authRequired"`
}
// validate interface
var _ tools.ToolConfig = Config{}
func (cfg Config) ToolConfigType() string {
@@ -105,7 +106,6 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
}
mcpManifest := tools.GetMcpManifest(cfg.Name, description, cfg.AuthRequired, allParameters, nil)
// finish tool setup
return Tool{
Config: cfg,
allParams: allParameters,
@@ -118,7 +118,6 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
}, nil
}
// validate interface
var _ tools.Tool = Tool{}
type Tool struct {
@@ -128,20 +127,24 @@ type Tool struct {
mcpManifest tools.McpManifest
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
paramsMap := params.AsMap()
newParams, err := parameters.GetParams(t.allParams, paramsMap)
if err != nil {
return nil, fmt.Errorf("unable to extract standard params %w", err)
return nil, util.NewAgentError("unable to extract standard params", err)
}
sliceParams := newParams.AsSlice()
return source.RunSQL(ctx, listPublicationTablesStatement, sliceParams)
resp, err := source.RunSQL(ctx, listPublicationTablesStatement, sliceParams)
if err != nil {
return nil, util.ProcessGeneralError(err)
}
return resp, nil
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,11 +17,13 @@ package postgreslistquerystats
import (
"context"
"fmt"
"net/http"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
"github.com/jackc/pgx/v5/pgxpool"
)
@@ -29,7 +31,7 @@ import (
const resourceType string = "postgres-list-query-stats"
const listQueryStats = `
SELECT
SELECT
d.datname,
s.query,
s.calls,
@@ -75,7 +77,6 @@ type Config struct {
AuthRequired []string `yaml:"authRequired"`
}
// validate interface
var _ tools.ToolConfig = Config{}
func (cfg Config) ToolConfigType() string {
@@ -95,7 +96,6 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters, nil)
// finish tool setup
return Tool{
Config: cfg,
allParams: allParameters,
@@ -108,7 +108,6 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
}, nil
}
// validate interface
var _ tools.Tool = Tool{}
type Tool struct {
@@ -122,19 +121,23 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
paramsMap := params.AsMap()
newParams, err := parameters.GetParams(t.allParams, paramsMap)
if err != nil {
return nil, fmt.Errorf("unable to extract standard params %w", err)
return nil, util.NewAgentError("unable to extract standard params", err)
}
sliceParams := newParams.AsSlice()
return source.RunSQL(ctx, listQueryStats, sliceParams)
resp, err := source.RunSQL(ctx, listQueryStats, sliceParams)
if err != nil {
return nil, util.ProcessGeneralError(err)
}
return resp, nil
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,11 +17,13 @@ package postgreslistroles
import (
"context"
"fmt"
"net/http"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
"github.com/jackc/pgx/v5/pgxpool"
)
@@ -29,45 +31,45 @@ import (
const resourceType string = "postgres-list-roles"
const listRolesStatement = `
WITH RoleDetails AS (
SELECT
r.rolname AS role_name,
r.oid AS oid,
r.rolconnlimit AS connection_limit,
r.rolsuper AS is_superuser,
r.rolinherit AS inherits_privileges,
r.rolcreaterole AS can_create_roles,
r.rolcreatedb AS can_create_db,
r.rolcanlogin AS can_login,
r.rolreplication AS is_replication_role,
r.rolbypassrls AS bypass_rls,
r.rolvaliduntil AS valid_until,
-- List of roles that belong to this role (Direct Members)
ARRAY(
SELECT m_r.rolname
FROM pg_auth_members pam
JOIN pg_roles m_r ON pam.member = m_r.oid
WHERE pam.roleid = r.oid
) AS direct_members,
-- List of roles that this role belongs to (Member Of)
ARRAY(
SELECT g_r.rolname
FROM pg_auth_members pam
JOIN pg_roles g_r ON pam.roleid = g_r.oid
WHERE pam.member = r.oid
) AS member_of
FROM pg_roles r
-- Exclude system and internal roles
WHERE r.rolname NOT LIKE 'cloudsql%'
AND r.rolname NOT LIKE 'alloydb_%'
AND r.rolname NOT LIKE 'pg_%'
)
SELECT *
FROM RoleDetails
WHERE
($1::text IS NULL OR role_name LIKE '%' || $1 || '%')
ORDER BY role_name
LIMIT COALESCE($2::int, 50);
WITH RoleDetails AS (
SELECT
r.rolname AS role_name,
r.oid AS oid,
r.rolconnlimit AS connection_limit,
r.rolsuper AS is_superuser,
r.rolinherit AS inherits_privileges,
r.rolcreaterole AS can_create_roles,
r.rolcreatedb AS can_create_db,
r.rolcanlogin AS can_login,
r.rolreplication AS is_replication_role,
r.rolbypassrls AS bypass_rls,
r.rolvaliduntil AS valid_until,
-- List of roles that belong to this role (Direct Members)
ARRAY(
SELECT m_r.rolname
FROM pg_auth_members pam
JOIN pg_roles m_r ON pam.member = m_r.oid
WHERE pam.roleid = r.oid
) AS direct_members,
-- List of roles that this role belongs to (Member Of)
ARRAY(
SELECT g_r.rolname
FROM pg_auth_members pam
JOIN pg_roles g_r ON pam.roleid = g_r.oid
WHERE pam.member = r.oid
) AS member_of
FROM pg_roles r
-- Exclude system and internal roles
WHERE r.rolname NOT LIKE 'cloudsql%'
AND r.rolname NOT LIKE 'alloydb_%'
AND r.rolname NOT LIKE 'pg_%'
)
SELECT *
FROM RoleDetails
WHERE
($1::text IS NULL OR role_name LIKE '%' || $1 || '%')
ORDER BY role_name
LIMIT COALESCE($2::int, 50);
`
func init() {
@@ -97,7 +99,6 @@ type Config struct {
AuthRequired []string `yaml:"authRequired"`
}
// validate interface
var _ tools.ToolConfig = Config{}
func (cfg Config) ToolConfigType() string {
@@ -116,7 +117,6 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
}
mcpManifest := tools.GetMcpManifest(cfg.Name, description, cfg.AuthRequired, allParameters, nil)
// finish tool setup
return Tool{
Config: cfg,
allParams: allParameters,
@@ -129,7 +129,6 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
}, nil
}
// validate interface
var _ tools.Tool = Tool{}
type Tool struct {
@@ -143,20 +142,24 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
paramsMap := params.AsMap()
newParams, err := parameters.GetParams(t.allParams, paramsMap)
if err != nil {
return nil, fmt.Errorf("unable to extract standard params %w", err)
return nil, util.NewAgentError("unable to extract standard params", err)
}
sliceParams := newParams.AsSlice()
return source.RunSQL(ctx, listRolesStatement, sliceParams)
resp, err := source.RunSQL(ctx, listRolesStatement, sliceParams)
if err != nil {
return nil, util.ProcessGeneralError(err)
}
return resp, nil
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,11 +17,13 @@ package postgreslistschemas
import (
"context"
"fmt"
"net/http"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
"github.com/jackc/pgx/v5/pgxpool"
)
@@ -29,7 +31,7 @@ import (
const resourceType string = "postgres-list-schemas"
const listSchemasStatement = `
WITH
WITH
schema_grants AS (
SELECT schema_oid, jsonb_object_agg(grantee, privileges) AS grants
FROM
@@ -52,27 +54,27 @@ const listSchemasStatement = `
SELECT
n.nspname AS schema_name,
pg_catalog.pg_get_userbyid(n.nspowner) AS owner,
COALESCE(sg.grants, '{}'::jsonb) AS grants,
(
SELECT COUNT(*)
FROM pg_catalog.pg_class c
WHERE c.relnamespace = n.oid AND c.relkind = 'r'
) AS tables,
(
SELECT COUNT(*)
FROM pg_catalog.pg_class c
WHERE c.relnamespace = n.oid AND c.relkind = 'v'
) AS views,
(SELECT COUNT(*) FROM pg_catalog.pg_proc p WHERE p.pronamespace = n.oid)
AS functions
COALESCE(sg.grants, '{}'::jsonb) AS grants,
(
SELECT COUNT(*)
FROM pg_catalog.pg_class c
WHERE c.relnamespace = n.oid AND c.relkind = 'r'
) AS tables,
(
SELECT COUNT(*)
FROM pg_catalog.pg_class c
WHERE c.relnamespace = n.oid AND c.relkind = 'v'
) AS views,
(SELECT COUNT(*) FROM pg_catalog.pg_proc p WHERE p.pronamespace = n.oid)
AS functions
FROM pg_catalog.pg_namespace n
LEFT JOIN schema_grants sg
ON n.oid = sg.schema_oid
)
SELECT *
FROM all_schemas
-- Exclude system schemas and temporary schemas created per session.
WHERE
SELECT *
FROM all_schemas
-- Exclude system and temporary schemas created per session.
WHERE
schema_name NOT IN ('pg_catalog', 'information_schema', 'pg_toast')
AND schema_name NOT LIKE 'pg_temp_%'
AND schema_name NOT LIKE 'pg_toast_temp_%'
@@ -109,7 +111,6 @@ type Config struct {
AuthRequired []string `yaml:"authRequired"`
}
// validate interface
var _ tools.ToolConfig = Config{}
func (cfg Config) ToolConfigType() string {
@@ -128,7 +129,6 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
}
mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters, nil)
// finish tool setup
return Tool{
Config: cfg,
allParams: allParameters,
@@ -141,7 +141,6 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
}, nil
}
// validate interface
var _ tools.Tool = Tool{}
type Tool struct {
@@ -151,20 +150,24 @@ type Tool struct {
mcpManifest tools.McpManifest
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
paramsMap := params.AsMap()
newParams, err := parameters.GetParams(t.allParams, paramsMap)
if err != nil {
return nil, fmt.Errorf("unable to extract standard params %w", err)
return nil, util.NewAgentError("unable to extract standard params", err)
}
sliceParams := newParams.AsSlice()
return source.RunSQL(ctx, listSchemasStatement, sliceParams)
resp, err := source.RunSQL(ctx, listSchemasStatement, sliceParams)
if err != nil {
return nil, util.ProcessGeneralError(err)
}
return resp, nil
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,11 +17,13 @@ package postgreslistsequences
import (
"context"
"fmt"
"net/http"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
"github.com/jackc/pgx/v5/pgxpool"
)
@@ -29,22 +31,22 @@ import (
const resourceType string = "postgres-list-sequences"
const listSequencesStatement = `
SELECT
sequencename as sequence_name,
schemaname as schema_name,
sequenceowner as sequence_owner,
data_type,
start_value,
min_value,
max_value,
increment_by,
last_value
FROM pg_sequences
WHERE
($1::text IS NULL OR schemaname LIKE '%' || $1 || '%')
AND ($2::text IS NULL OR sequencename LIKE '%' || $2 || '%')
ORDER BY schema_name, sequence_name
LIMIT COALESCE($3::int, 50);
SELECT
sequencename as sequence_name,
schemaname as schema_name,
sequenceowner as sequence_owner,
data_type,
start_value,
min_value,
max_value,
increment_by,
last_value
FROM pg_sequences
WHERE
($1::text IS NULL OR schemaname LIKE '%' || $1 || '%')
AND ($2::text IS NULL OR sequencename LIKE '%' || $2 || '%')
ORDER BY schema_name, sequence_name
LIMIT COALESCE($3::int, 50);
`
@@ -75,7 +77,6 @@ type Config struct {
AuthRequired []string `yaml:"authRequired"`
}
// validate interface
var _ tools.ToolConfig = Config{}
func (cfg Config) ToolConfigType() string {
@@ -94,7 +95,6 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
}
mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters, nil)
// finish tool setup
return Tool{
Config: cfg,
allParams: allParameters,
@@ -107,7 +107,6 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
}, nil
}
// validate interface
var _ tools.Tool = Tool{}
type Tool struct {
@@ -121,21 +120,25 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
paramsMap := params.AsMap()
newParams, err := parameters.GetParams(t.allParams, paramsMap)
if err != nil {
return nil, fmt.Errorf("unable to extract standard params %w", err)
return nil, util.NewAgentError("unable to extract standard params", err)
}
sliceParams := newParams.AsSlice()
return source.RunSQL(ctx, listSequencesStatement, sliceParams)
resp, err := source.RunSQL(ctx, listSequencesStatement, sliceParams)
if err != nil {
return nil, util.ProcessGeneralError(err)
}
return resp, nil
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,6 +17,7 @@ package postgresliststoredprocedure
import (
"context"
"fmt"
"net/http"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
@@ -25,6 +26,7 @@ import (
"github.com/googleapis/genai-toolbox/internal/sources/cloudsqlpg"
"github.com/googleapis/genai-toolbox/internal/sources/postgres"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
"github.com/jackc/pgx/v5/pgxpool"
)
@@ -32,7 +34,7 @@ import (
const resourceType string = "postgres-list-stored-procedure"
const listStoredProcedure = `
SELECT
SELECT
n.nspname AS schema_name,
p.proname AS name,
r.rolname AS owner,
@@ -85,7 +87,6 @@ type Config struct {
AuthRequired []string `yaml:"authRequired"`
}
// validate interface
var _ tools.ToolConfig = Config{}
func (cfg Config) ToolConfigType() string {
@@ -118,7 +119,6 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters, nil)
// finish tool setup
return Tool{
Config: cfg,
allParams: allParameters,
@@ -132,7 +132,6 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
}, nil
}
// validate interface
var _ tools.Tool = Tool{}
type Tool struct {
@@ -147,18 +146,18 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
paramsMap := params.AsMap()
newParams, err := parameters.GetParams(t.allParams, paramsMap)
if err != nil {
return nil, fmt.Errorf("unable to extract standard params %w", err)
return nil, util.NewAgentError("unable to extract standard params", err)
}
sliceParams := newParams.AsSlice()
results, err := t.pool.Query(ctx, listStoredProcedure, sliceParams...)
if err != nil {
return nil, fmt.Errorf("unable to execute query: %w", err)
return nil, util.ProcessGeneralError(err)
}
defer results.Close()
@@ -168,7 +167,7 @@ func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, para
for results.Next() {
values, err := results.Values()
if err != nil {
return nil, fmt.Errorf("unable to parse row: %w", err)
return nil, util.NewClientServerError("unable to parse row", http.StatusInternalServerError, err)
}
rowMap := make(map[string]any)
for i, field := range fields {

View File

@@ -17,11 +17,13 @@ package postgreslisttables
import (
"context"
"fmt"
"net/http"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
"github.com/jackc/pgx/v5/pgxpool"
)
@@ -29,81 +31,81 @@ import (
const resourceType string = "postgres-list-tables"
const listTablesStatement = `
WITH desired_relkinds AS (
SELECT ARRAY['r', 'p']::char[] AS kinds -- Always consider both 'TABLE' and 'PARTITIONED TABLE'
),
table_info AS (
SELECT
t.oid AS table_oid,
ns.nspname AS schema_name,
t.relname AS table_name,
pg_get_userbyid(t.relowner) AS table_owner,
obj_description(t.oid, 'pg_class') AS table_comment,
t.relkind AS object_kind
FROM
pg_class t
JOIN
pg_namespace ns ON ns.oid = t.relnamespace
CROSS JOIN desired_relkinds dk
WHERE
t.relkind = ANY(dk.kinds) -- Filter by selected table relkinds ('r', 'p')
AND (NULLIF(TRIM($1), '') IS NULL OR t.relname = ANY(string_to_array($1,','))) -- $1 is object_names
AND ns.nspname NOT IN ('pg_catalog', 'information_schema', 'pg_toast','google_ml')
AND ns.nspname NOT LIKE 'pg_temp_%' AND ns.nspname NOT LIKE 'pg_toast_temp_%'
),
columns_info AS (
SELECT
att.attrelid AS table_oid, att.attname AS column_name, format_type(att.atttypid, att.atttypmod) AS data_type,
att.attnum AS column_ordinal_position, att.attnotnull AS is_not_nullable,
pg_get_expr(ad.adbin, ad.adrelid) AS column_default, col_description(att.attrelid, att.attnum) AS column_comment
FROM pg_attribute att LEFT JOIN pg_attrdef ad ON att.attrelid = ad.adrelid AND att.attnum = ad.adnum
JOIN table_info ti ON att.attrelid = ti.table_oid WHERE att.attnum > 0 AND NOT att.attisdropped
),
constraints_info AS (
SELECT
con.conrelid AS table_oid, con.conname AS constraint_name, pg_get_constraintdef(con.oid) AS constraint_definition,
CASE con.contype WHEN 'p' THEN 'PRIMARY KEY' WHEN 'f' THEN 'FOREIGN KEY' WHEN 'u' THEN 'UNIQUE' WHEN 'c' THEN 'CHECK' ELSE con.contype::text END AS constraint_type,
(SELECT array_agg(att.attname ORDER BY u.attposition) FROM unnest(con.conkey) WITH ORDINALITY AS u(attnum, attposition) JOIN pg_attribute att ON att.attrelid = con.conrelid AND att.attnum = u.attnum) AS constraint_columns,
NULLIF(con.confrelid, 0)::regclass AS foreign_key_referenced_table,
(SELECT array_agg(att.attname ORDER BY u.attposition) FROM unnest(con.confkey) WITH ORDINALITY AS u(attnum, attposition) JOIN pg_attribute att ON att.attrelid = con.confrelid AND att.attnum = u.attnum WHERE con.contype = 'f') AS foreign_key_referenced_columns
FROM pg_constraint con JOIN table_info ti ON con.conrelid = ti.table_oid
),
indexes_info AS (
SELECT
idx.indrelid AS table_oid, ic.relname AS index_name, pg_get_indexdef(idx.indexrelid) AS index_definition,
idx.indisunique AS is_unique, idx.indisprimary AS is_primary, am.amname AS index_method,
(SELECT array_agg(att.attname ORDER BY u.ord) FROM unnest(idx.indkey::int[]) WITH ORDINALITY AS u(colidx, ord) LEFT JOIN pg_attribute att ON att.attrelid = idx.indrelid AND att.attnum = u.colidx WHERE u.colidx <> 0) AS index_columns
FROM pg_index idx JOIN pg_class ic ON ic.oid = idx.indexrelid JOIN pg_am am ON am.oid = ic.relam JOIN table_info ti ON idx.indrelid = ti.table_oid
),
triggers_info AS (
SELECT tg.tgrelid AS table_oid, tg.tgname AS trigger_name, pg_get_triggerdef(tg.oid) AS trigger_definition, tg.tgenabled AS trigger_enabled_state
FROM pg_trigger tg JOIN table_info ti ON tg.tgrelid = ti.table_oid WHERE NOT tg.tgisinternal
)
SELECT
ti.schema_name,
ti.table_name AS object_name,
CASE
WHEN $2 = 'simple' THEN
-- IF format is 'simple', return basic JSON
json_build_object('name', ti.table_name)
ELSE
json_build_object(
'schema_name', ti.schema_name,
'object_name', ti.table_name,
'object_type', CASE ti.object_kind
WHEN 'r' THEN 'TABLE'
WHEN 'p' THEN 'PARTITIONED TABLE'
ELSE ti.object_kind::text -- Should not happen due to WHERE clause
END,
'owner', ti.table_owner,
'comment', ti.table_comment,
'columns', COALESCE((SELECT json_agg(json_build_object('column_name',ci.column_name,'data_type',ci.data_type,'ordinal_position',ci.column_ordinal_position,'is_not_nullable',ci.is_not_nullable,'column_default',ci.column_default,'column_comment',ci.column_comment) ORDER BY ci.column_ordinal_position) FROM columns_info ci WHERE ci.table_oid = ti.table_oid), '[]'::json),
'constraints', COALESCE((SELECT json_agg(json_build_object('constraint_name',cons.constraint_name,'constraint_type',cons.constraint_type,'constraint_definition',cons.constraint_definition,'constraint_columns',cons.constraint_columns,'foreign_key_referenced_table',cons.foreign_key_referenced_table,'foreign_key_referenced_columns',cons.foreign_key_referenced_columns)) FROM constraints_info cons WHERE cons.table_oid = ti.table_oid), '[]'::json),
'indexes', COALESCE((SELECT json_agg(json_build_object('index_name',ii.index_name,'index_definition',ii.index_definition,'is_unique',ii.is_unique,'is_primary',ii.is_primary,'index_method',ii.index_method,'index_columns',ii.index_columns)) FROM indexes_info ii WHERE ii.table_oid = ti.table_oid), '[]'::json),
'triggers', COALESCE((SELECT json_agg(json_build_object('trigger_name',tri.trigger_name,'trigger_definition',tri.trigger_definition,'trigger_enabled_state',tri.trigger_enabled_state)) FROM triggers_info tri WHERE tri.table_oid = ti.table_oid), '[]'::json)
)
END AS object_details
FROM table_info ti ORDER BY ti.schema_name, ti.table_name;
WITH desired_relkinds AS (
SELECT ARRAY['r', 'p']::char[] AS kinds -- Always consider both 'TABLE' and 'PARTITIONED TABLE'
),
table_info AS (
SELECT
t.oid AS table_oid,
ns.nspname AS schema_name,
t.relname AS table_name,
pg_get_userbyid(t.relowner) AS table_owner,
obj_description(t.oid, 'pg_class') AS table_comment,
t.relkind AS object_kind
FROM
pg_class t
JOIN
pg_namespace ns ON ns.oid = t.relnamespace
CROSS JOIN desired_relkinds dk
WHERE
t.relkind = ANY(dk.kinds) -- Filter by selected table relkinds ('r', 'p')
AND (NULLIF(TRIM($1), '') IS NULL OR t.relname = ANY(string_to_array($1,','))) -- $1 is object_names
AND ns.nspname NOT IN ('pg_catalog', 'information_schema', 'pg_toast','google_ml')
AND ns.nspname NOT LIKE 'pg_temp_%' AND ns.nspname NOT LIKE 'pg_toast_temp_%'
),
columns_info AS (
SELECT
att.attrelid AS table_oid, att.attname AS column_name, format_type(att.atttypid, att.atttypmod) AS data_type,
att.attnum AS column_ordinal_position, att.attnotnull AS is_not_nullable,
pg_get_expr(ad.adbin, ad.adrelid) AS column_default, col_description(att.attrelid, att.attnum) AS column_comment
FROM pg_attribute att LEFT JOIN pg_attrdef ad ON att.attrelid = ad.adrelid AND att.attnum = ad.adnum
JOIN table_info ti ON att.attrelid = ti.table_oid WHERE att.attnum > 0 AND NOT att.attisdropped
),
constraints_info AS (
SELECT
con.conrelid AS table_oid, con.conname AS constraint_name, pg_get_constraintdef(con.oid) AS constraint_definition,
CASE con.contype WHEN 'p' THEN 'PRIMARY KEY' WHEN 'f' THEN 'FOREIGN KEY' WHEN 'u' THEN 'UNIQUE' WHEN 'c' THEN 'CHECK' ELSE con.contype::text END AS constraint_type,
(SELECT array_agg(att.attname ORDER BY u.attposition) FROM unnest(con.conkey) WITH ORDINALITY AS u(attnum, attposition) JOIN pg_attribute att ON att.attrelid = con.conrelid AND att.attnum = u.attnum) AS constraint_columns,
NULLIF(con.confrelid, 0)::regclass AS foreign_key_referenced_table,
(SELECT array_agg(att.attname ORDER BY u.attposition) FROM unnest(con.confkey) WITH ORDINALITY AS u(attnum, attposition) JOIN pg_attribute att ON att.attrelid = con.confrelid AND att.attnum = u.attnum WHERE con.contype = 'f') AS foreign_key_referenced_columns
FROM pg_constraint con JOIN table_info ti ON con.conrelid = ti.table_oid
),
indexes_info AS (
SELECT
idx.indrelid AS table_oid, ic.relname AS index_name, pg_get_indexdef(idx.indexrelid) AS index_definition,
idx.indisunique AS is_unique, idx.indisprimary AS is_primary, am.amname AS index_method,
(SELECT array_agg(att.attname ORDER BY u.ord) FROM unnest(idx.indkey::int[]) WITH ORDINALITY AS u(colidx, ord) LEFT JOIN pg_attribute att ON att.attrelid = idx.indrelid AND att.attnum = u.colidx WHERE u.colidx <> 0) AS index_columns
FROM pg_index idx JOIN pg_class ic ON ic.oid = idx.indexrelid JOIN pg_am am ON am.oid = ic.relam JOIN table_info ti ON idx.indrelid = ti.table_oid
),
triggers_info AS (
SELECT tg.tgrelid AS table_oid, tg.tgname AS trigger_name, pg_get_triggerdef(tg.oid) AS trigger_definition, tg.tgenabled AS trigger_enabled_state
FROM pg_trigger tg JOIN table_info ti ON tg.tgrelid = ti.table_oid WHERE NOT tg.tgisinternal
)
SELECT
ti.schema_name,
ti.table_name AS object_name,
CASE
WHEN $2 = 'simple' THEN
-- IF format is 'simple', return basic JSON
json_build_object('name', ti.table_name)
ELSE
json_build_object(
'schema_name', ti.schema_name,
'object_name', ti.table_name,
'object_type', CASE ti.object_kind
WHEN 'r' THEN 'TABLE'
WHEN 'p' THEN 'PARTITIONED TABLE'
ELSE ti.object_kind::text -- Should not happen due to WHERE clause
END,
'owner', ti.table_owner,
'comment', ti.table_comment,
'columns', COALESCE((SELECT json_agg(json_build_object('column_name',ci.column_name,'data_type',ci.data_type,'ordinal_position',ci.column_ordinal_position,'is_not_nullable',ci.is_not_nullable,'column_default',ci.column_default,'column_comment',ci.column_comment) ORDER BY ci.column_ordinal_position) FROM columns_info ci WHERE ci.table_oid = ti.table_oid), '[]'::json),
'constraints', COALESCE((SELECT json_agg(json_build_object('constraint_name',cons.constraint_name,'constraint_type',cons.constraint_type,'constraint_definition',cons.constraint_definition,'constraint_columns',cons.constraint_columns,'foreign_key_referenced_table',cons.foreign_key_referenced_table,'foreign_key_referenced_columns',cons.foreign_key_referenced_columns)) FROM constraints_info cons WHERE cons.table_oid = ti.table_oid), '[]'::json),
'indexes', COALESCE((SELECT json_agg(json_build_object('index_name',ii.index_name,'index_definition',ii.index_definition,'is_unique',ii.is_unique,'is_primary',ii.is_primary,'index_method',ii.index_method,'index_columns',ii.index_columns)) FROM indexes_info ii WHERE ii.table_oid = ti.table_oid), '[]'::json),
'triggers', COALESCE((SELECT json_agg(json_build_object('trigger_name',tri.trigger_name,'trigger_definition',tri.trigger_definition,'trigger_enabled_state',tri.trigger_enabled_state)) FROM triggers_info tri WHERE tri.table_oid = ti.table_oid), '[]'::json)
)
END AS object_details
FROM table_info ti ORDER BY ti.schema_name, ti.table_name;
`
func init() {
@@ -133,7 +135,6 @@ type Config struct {
AuthRequired []string `yaml:"authRequired"`
}
// validate interface
var _ tools.ToolConfig = Config{}
func (cfg Config) ToolConfigType() string {
@@ -158,7 +159,6 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
return t, nil
}
// validate interface
var _ tools.Tool = Tool{}
type Tool struct {
@@ -168,31 +168,31 @@ type Tool struct {
mcpManifest tools.McpManifest
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
paramsMap := params.AsMap()
tableNames, ok := paramsMap["table_names"].(string)
if !ok {
return nil, fmt.Errorf("invalid 'table_names' parameter; expected a string")
return nil, util.NewAgentError("invalid 'table_names' parameter; expected a string", nil)
}
outputFormat, _ := paramsMap["output_format"].(string)
if outputFormat != "simple" && outputFormat != "detailed" {
return nil, fmt.Errorf("invalid value for output_format: must be 'simple' or 'detailed', but got %q", outputFormat)
return nil, util.NewAgentError(fmt.Sprintf("invalid value for output_format: must be 'simple' or 'detailed', but got %q", outputFormat), nil)
}
resp, err := source.RunSQL(ctx, listTablesStatement, []any{tableNames, outputFormat})
if err != nil {
return nil, err
return nil, util.ProcessGeneralError(err)
}
resSlice, ok := resp.([]any)
if !ok || len(resSlice) == 0 {
return []any{}, nil
}
return resp, err
return resp, nil
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,11 +17,13 @@ package postgreslisttablespaces
import (
"context"
"fmt"
"net/http"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
"github.com/jackc/pgx/v5/pgxpool"
)
@@ -29,29 +31,29 @@ import (
const resourceType string = "postgres-list-tablespaces"
const listTableSpacesStatement = `
WITH
tablespace_info AS (
SELECT
spcname AS tablespace_name,
pg_catalog.pg_get_userbyid(spcowner) AS owner_name,
CASE
WHEN pg_catalog.has_tablespace_privilege(oid, 'CREATE') THEN pg_tablespace_size(oid)
ELSE NULL
END AS size_in_bytes,
oid,
spcacl,
spcoptions
FROM
pg_tablespace
)
SELECT *
FROM
tablespace_info
WHERE
($1::text IS NULL OR tablespace_name LIKE '%' || $1::text || '%')
ORDER BY
tablespace_name
LIMIT COALESCE($2::int, 50);
WITH
tablespace_info AS (
SELECT
spcname AS tablespace_name,
pg_catalog.pg_get_userbyid(spcowner) AS owner_name,
CASE
WHEN pg_catalog.has_tablespace_privilege(oid, 'CREATE') THEN pg_tablespace_size(oid)
ELSE NULL
END AS size_in_bytes,
oid,
spcacl,
spcoptions
FROM
pg_tablespace
)
SELECT *
FROM
tablespace_info
WHERE
($1::text IS NULL OR tablespace_name LIKE '%' || $1::text || '%')
ORDER BY
tablespace_name
LIMIT COALESCE($2::int, 50);
`
func init() {
@@ -81,7 +83,6 @@ type Config struct {
AuthRequired []string `yaml:"authRequired"`
}
// validate interface
var _ tools.ToolConfig = Config{}
func (cfg Config) ToolConfigType() string {
@@ -99,7 +100,6 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
}
mcpManifest := tools.GetMcpManifest(cfg.Name, description, cfg.AuthRequired, allParameters, nil)
// finish tool setup
return Tool{
Config: cfg,
allParams: allParameters,
@@ -112,7 +112,6 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
}, nil
}
// validate interface
var _ tools.Tool = Tool{}
type Tool struct {
@@ -126,24 +125,28 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
paramsMap := params.AsMap()
tablespaceName, ok := paramsMap["tablespace_name"].(string)
if !ok {
return nil, fmt.Errorf("invalid 'tablespace_name' parameter; expected a string")
return nil, util.NewAgentError("invalid 'tablespace_name' parameter; expected a string", nil)
}
limit, ok := paramsMap["limit"].(int)
if !ok {
return nil, fmt.Errorf("invalid 'limit' parameter; expected an integer")
return nil, util.NewAgentError("invalid 'limit' parameter; expected an integer", nil)
}
return source.RunSQL(ctx, listTableSpacesStatement, []any{tablespaceName, limit})
resp, err := source.RunSQL(ctx, listTableSpacesStatement, []any{tablespaceName, limit})
if err != nil {
return nil, util.ProcessGeneralError(err)
}
return resp, nil
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,11 +17,13 @@ package postgreslisttablestats
import (
"context"
"fmt"
"net/http"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
"github.com/jackc/pgx/v5/pgxpool"
)
@@ -29,7 +31,7 @@ import (
const resourceType string = "postgres-list-table-stats"
const listTableStats = `
WITH table_stats AS (
WITH table_stats AS (
SELECT
s.schemaname AS schema_name,
s.relname AS table_name,
@@ -102,7 +104,6 @@ type Config struct {
AuthRequired []string `yaml:"authRequired"`
}
// validate interface
var _ tools.ToolConfig = Config{}
func (cfg Config) ToolConfigType() string {
@@ -121,19 +122,18 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
if cfg.Description == "" {
cfg.Description = `Lists the user table statistics in the database ordered by number of
sequential scans with a default limit of 50 rows. Returns the following
columns: schema name, table name, table size in bytes, number of
sequential scans, number of index scans, idx_scan_ratio_percent (showing
the percentage of total scans that utilized an index, where a low ratio
indicates missing or ineffective indexes), number of live rows, number
of dead rows, dead_row_ratio_percent (indicating potential table bloat),
total number of rows inserted, updated, and deleted, the timestamps
for the last_vacuum, last_autovacuum, and last_autoanalyze operations.`
sequential scans with a default limit of 50 rows. Returns the following
columns: schema name, table name, table size in bytes, number of
sequential scans, number of index scans, idx_scan_ratio_percent (showing
the percentage of total scans that utilized an index, where a low ratio
indicates missing or ineffective indexes), number of live rows, number
of dead rows, dead_row_ratio_percent (indicating potential table bloat),
total number of rows inserted, updated, and deleted, the timestamps
for the last_vacuum, last_autovacuum, and last_autoanalyze operations.`
}
mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters, nil)
// finish tool setup
return Tool{
Config: cfg,
allParams: allParameters,
@@ -146,7 +146,6 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
}, nil
}
// validate interface
var _ tools.Tool = Tool{}
type Tool struct {
@@ -160,21 +159,25 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
paramsMap := params.AsMap()
newParams, err := parameters.GetParams(t.allParams, paramsMap)
if err != nil {
return nil, fmt.Errorf("unable to extract standard params %w", err)
return nil, util.NewAgentError("unable to extract standard params", err)
}
sliceParams := newParams.AsSlice()
return source.RunSQL(ctx, listTableStats, sliceParams)
resp, err := source.RunSQL(ctx, listTableStats, sliceParams)
if err != nil {
return nil, util.ProcessGeneralError(err)
}
return resp, nil
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,11 +17,13 @@ package postgreslisttriggers
import (
"context"
"fmt"
"net/http"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
"github.com/jackc/pgx/v5/pgxpool"
)
@@ -29,49 +31,49 @@ import (
const resourceType string = "postgres-list-triggers"
const listTriggersStatement = `
WITH
trigger_list AS (
SELECT
t.tgname AS trigger_name,
n.nspname AS schema_name,
c.relname AS table_name,
CASE t.tgenabled
WHEN 'O' THEN 'ENABLED'
WHEN 'D' THEN 'DISABLED'
WHEN 'R' THEN 'REPLICA'
WHEN 'A' THEN 'ALWAYS'
END AS status,
CASE
WHEN (t.tgtype::int & 2) = 2 THEN 'BEFORE'
WHEN (t.tgtype::int & 64) = 64 THEN 'INSTEAD OF'
ELSE 'AFTER'
END AS timing,
concat_ws(
', ',
CASE WHEN (t.tgtype::int & 4) = 4 THEN 'INSERT' END,
CASE WHEN (t.tgtype::int & 16) = 16 THEN 'UPDATE' END,
CASE WHEN (t.tgtype::int & 8) = 8 THEN 'DELETE' END,
CASE WHEN (t.tgtype::int & 32) = 32 THEN 'TRUNCATE' END) AS events,
CASE WHEN (t.tgtype::int & 1) = 1 THEN 'ROW' ELSE 'STATEMENT' END AS activation_level,
p.proname AS function_name,
pg_get_triggerdef(t.oid) AS definition
FROM pg_trigger t
JOIN pg_class c
ON t.tgrelid = c.oid
JOIN pg_namespace n
ON c.relnamespace = n.oid
LEFT JOIN pg_proc p
ON t.tgfoid = p.oid
WHERE NOT t.tgisinternal
)
SELECT *
FROM trigger_list
WHERE
($1::text IS NULL OR trigger_name LIKE '%' || $1::text || '%')
AND ($2::text IS NULL OR schema_name LIKE '%' || $2::text || '%')
AND ($3::text IS NULL OR table_name LIKE '%' || $3::text || '%')
ORDER BY schema_name, table_name, trigger_name
LIMIT COALESCE($4::int, 50);
WITH
trigger_list AS (
SELECT
t.tgname AS trigger_name,
n.nspname AS schema_name,
c.relname AS table_name,
CASE t.tgenabled
WHEN 'O' THEN 'ENABLED'
WHEN 'D' THEN 'DISABLED'
WHEN 'R' THEN 'REPLICA'
WHEN 'A' THEN 'ALWAYS'
END AS status,
CASE
WHEN (t.tgtype::int & 2) = 2 THEN 'BEFORE'
WHEN (t.tgtype::int & 64) = 64 THEN 'INSTEAD OF'
ELSE 'AFTER'
END AS timing,
concat_ws(
', ',
CASE WHEN (t.tgtype::int & 4) = 4 THEN 'INSERT' END,
CASE WHEN (t.tgtype::int & 16) = 16 THEN 'UPDATE' END,
CASE WHEN (t.tgtype::int & 8) = 8 THEN 'DELETE' END,
CASE WHEN (t.tgtype::int & 32) = 32 THEN 'TRUNCATE' END) AS events,
CASE WHEN (t.tgtype::int & 1) = 1 THEN 'ROW' ELSE 'STATEMENT' END AS activation_level,
p.proname AS function_name,
pg_get_triggerdef(t.oid) AS definition
FROM pg_trigger t
JOIN pg_class c
ON t.tgrelid = c.oid
JOIN pg_namespace n
ON c.relnamespace = n.oid
LEFT JOIN pg_proc p
ON t.tgfoid = p.oid
WHERE NOT t.tgisinternal
)
SELECT *
FROM trigger_list
WHERE
($1::text IS NULL OR trigger_name LIKE '%' || $1::text || '%')
AND ($2::text IS NULL OR schema_name LIKE '%' || $2::text || '%')
AND ($3::text IS NULL OR table_name LIKE '%' || $3::text || '%')
ORDER BY schema_name, table_name, trigger_name
LIMIT COALESCE($4::int, 50);
`
func init() {
@@ -101,7 +103,6 @@ type Config struct {
AuthRequired []string `yaml:"authRequired"`
}
// validate interface
var _ tools.ToolConfig = Config{}
func (cfg Config) ToolConfigType() string {
@@ -121,7 +122,6 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
}
mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters, nil)
// finish tool setup
return Tool{
Config: cfg,
allParams: allParameters,
@@ -134,7 +134,6 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
}, nil
}
// validate interface
var _ tools.Tool = Tool{}
type Tool struct {
@@ -148,20 +147,24 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
paramsMap := params.AsMap()
newParams, err := parameters.GetParams(t.allParams, paramsMap)
if err != nil {
return nil, fmt.Errorf("unable to extract standard params %w", err)
return nil, util.NewAgentError("unable to extract standard params", err)
}
sliceParams := newParams.AsSlice()
return source.RunSQL(ctx, listTriggersStatement, sliceParams)
resp, err := source.RunSQL(ctx, listTriggersStatement, sliceParams)
if err != nil {
return nil, util.ProcessGeneralError(err)
}
return resp, nil
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,11 +17,13 @@ package postgreslistviews
import (
"context"
"fmt"
"net/http"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
"github.com/jackc/pgx/v5/pgxpool"
)
@@ -29,24 +31,24 @@ import (
const resourceType string = "postgres-list-views"
const listViewsStatement = `
WITH list_views AS (
SELECT
schemaname AS schema_name,
viewname AS view_name,
viewowner AS owner_name,
definition
FROM pg_views
)
SELECT *
FROM list_views
WHERE
schema_name NOT IN ('pg_catalog', 'information_schema', 'pg_toast')
AND schema_name NOT LIKE 'pg_temp_%'
AND ($1::text IS NULL OR view_name ILIKE '%' || $1::text || '%')
AND ($2::text IS NULL OR schema_name ILIKE '%' || $2::text || '%')
ORDER BY
schema_name, view_name
LIMIT COALESCE($3::int, 50);
WITH list_views AS (
SELECT
schemaname AS schema_name,
viewname AS view_name,
viewowner AS owner_name,
definition
FROM pg_views
)
SELECT *
FROM list_views
WHERE
schema_name NOT IN ('pg_catalog', 'information_schema', 'pg_toast')
AND schema_name NOT LIKE 'pg_temp_%'
AND ($1::text IS NULL OR view_name ILIKE '%' || $1::text || '%')
AND ($2::text IS NULL OR schema_name ILIKE '%' || $2::text || '%')
ORDER BY
schema_name, view_name
LIMIT COALESCE($3::int, 50);
`
func init() {
@@ -76,7 +78,6 @@ type Config struct {
AuthRequired []string `yaml:"authRequired"`
}
// validate interface
var _ tools.ToolConfig = Config{}
func (cfg Config) ToolConfigType() string {
@@ -95,7 +96,6 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
}
mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters, nil)
// finish tool setup
return Tool{
Config: cfg,
allParams: allParameters,
@@ -108,7 +108,6 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
}, nil
}
// validate interface
var _ tools.Tool = Tool{}
type Tool struct {
@@ -118,20 +117,24 @@ type Tool struct {
mcpManifest tools.McpManifest
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
paramsMap := params.AsMap()
newParams, err := parameters.GetParams(t.allParams, paramsMap)
if err != nil {
return nil, fmt.Errorf("unable to extract standard params %w", err)
return nil, util.NewAgentError("unable to extract standard params", err)
}
sliceParams := newParams.AsSlice()
return source.RunSQL(ctx, listViewsStatement, sliceParams)
resp, err := source.RunSQL(ctx, listViewsStatement, sliceParams)
if err != nil {
return nil, util.ProcessGeneralError(err)
}
return resp, nil
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,11 +17,13 @@ package postgreslongrunningtransactions
import (
"context"
"fmt"
"net/http"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
"github.com/jackc/pgx/v5/pgxpool"
)
@@ -29,7 +31,7 @@ import (
const resourceType string = "postgres-long-running-transactions"
const longRunningTransactions = `
SELECT
SELECT
pid,
datname,
usename,
@@ -83,7 +85,6 @@ type Config struct {
AuthRequired []string `yaml:"authRequired"`
}
// validate interface
var _ tools.ToolConfig = Config{}
func (cfg Config) ToolConfigType() string {
@@ -103,7 +104,6 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters, nil)
// finish tool setup
return Tool{
Config: cfg,
allParams: allParameters,
@@ -116,7 +116,6 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
}, nil
}
// validate interface
var _ tools.Tool = Tool{}
type Tool struct {
@@ -130,20 +129,24 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
paramsMap := params.AsMap()
newParams, err := parameters.GetParams(t.allParams, paramsMap)
if err != nil {
return nil, fmt.Errorf("unable to extract standard params %w", err)
return nil, util.NewAgentError("unable to extract standard params", err)
}
sliceParams := newParams.AsSlice()
return source.RunSQL(ctx, longRunningTransactions, sliceParams)
resp, err := source.RunSQL(ctx, longRunningTransactions, sliceParams)
if err != nil {
return nil, util.ProcessGeneralError(err)
}
return resp, nil
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,11 +17,13 @@ package postgresreplicationstats
import (
"context"
"fmt"
"net/http"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
"github.com/jackc/pgx/v5/pgxpool"
)
@@ -29,11 +31,11 @@ import (
const resourceType string = "postgres-replication-stats"
const replicationStats = `
SELECT
pid,
usename,
SELECT
pid,
usename,
application_name,
backend_xmin,
backend_xmin,
client_addr,
state,
sync_state,
@@ -73,7 +75,6 @@ type Config struct {
AuthRequired []string `yaml:"authRequired"`
}
// validate interface
var _ tools.ToolConfig = Config{}
func (cfg Config) ToolConfigType() string {
@@ -90,7 +91,6 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters, nil)
// finish tool setup
return Tool{
Config: cfg,
allParams: allParameters,
@@ -103,7 +103,6 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
}, nil
}
// validate interface
var _ tools.Tool = Tool{}
type Tool struct {
@@ -117,20 +116,24 @@ func (t Tool) ToConfig() tools.ToolConfig {
return t.Config
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
paramsMap := params.AsMap()
newParams, err := parameters.GetParams(t.allParams, paramsMap)
if err != nil {
return nil, fmt.Errorf("unable to extract standard params %w", err)
return nil, util.NewAgentError("unable to extract standard params", err)
}
sliceParams := newParams.AsSlice()
return source.RunSQL(ctx, replicationStats, sliceParams)
resp, err := source.RunSQL(ctx, replicationStats, sliceParams)
if err != nil {
return nil, util.ProcessGeneralError(err)
}
return resp, nil
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {

View File

@@ -17,11 +17,13 @@ package postgressql
import (
"context"
"fmt"
"net/http"
yaml "github.com/goccy/go-yaml"
"github.com/googleapis/genai-toolbox/internal/embeddingmodels"
"github.com/googleapis/genai-toolbox/internal/sources"
"github.com/googleapis/genai-toolbox/internal/tools"
"github.com/googleapis/genai-toolbox/internal/util"
"github.com/googleapis/genai-toolbox/internal/util/parameters"
"github.com/jackc/pgx/v5/pgxpool"
)
@@ -58,7 +60,6 @@ type Config struct {
TemplateParameters parameters.Parameters `yaml:"templateParameters"`
}
// validate interface
var _ tools.ToolConfig = Config{}
func (cfg Config) ToolConfigType() string {
@@ -73,7 +74,6 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
mcpManifest := tools.GetMcpManifest(cfg.Name, cfg.Description, cfg.AuthRequired, allParameters, nil)
// finish tool setup
t := Tool{
Config: cfg,
AllParams: allParameters,
@@ -83,7 +83,6 @@ func (cfg Config) Initialize(srcs map[string]sources.Source) (tools.Tool, error)
return t, nil
}
// validate interface
var _ tools.Tool = Tool{}
type Tool struct {
@@ -93,24 +92,28 @@ type Tool struct {
mcpManifest tools.McpManifest
}
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, error) {
func (t Tool) Invoke(ctx context.Context, resourceMgr tools.SourceProvider, params parameters.ParamValues, accessToken tools.AccessToken) (any, util.ToolboxError) {
source, err := tools.GetCompatibleSource[compatibleSource](resourceMgr, t.Source, t.Name, t.Type)
if err != nil {
return nil, err
return nil, util.NewClientServerError("source used is not compatible with the tool", http.StatusInternalServerError, err)
}
paramsMap := params.AsMap()
newStatement, err := parameters.ResolveTemplateParams(t.TemplateParameters, t.Statement, paramsMap)
if err != nil {
return nil, fmt.Errorf("unable to extract template params %w", err)
return nil, util.NewAgentError("unable to extract template params", err)
}
newParams, err := parameters.GetParams(t.Parameters, paramsMap)
if err != nil {
return nil, fmt.Errorf("unable to extract standard params %w", err)
return nil, util.NewAgentError("unable to extract standard params", err)
}
sliceParams := newParams.AsSlice()
return source.RunSQL(ctx, newStatement, sliceParams)
resp, err := source.RunSQL(ctx, newStatement, sliceParams)
if err != nil {
return nil, util.ProcessGeneralError(err)
}
return resp, nil
}
func (t Tool) EmbedParams(ctx context.Context, paramValues parameters.ParamValues, embeddingModelsMap map[string]embeddingmodels.EmbeddingModel) (parameters.ParamValues, error) {